cTools is one of
those critical Drupal 7 modules many others depend on. It provides a lot
of APIs and functionality that makes life easier when developing
modules. Views and Panels are just two examples of such powerhouses that
depend on it.
cTools makes available different kinds of functionality.
Object caching,
configuration exportability, form wizards, dialogs and plugins are but a
few. A lot of the credit you would normally attribute to Views or
Panels is actually owed to cTools.

In this article, we are going to take a look at cTools plugins,
especially how we can create our very own. After a brief introduction,
we will immediately go hands on with a custom module that will use the
cTools plugins to make defining Drupal blocks nicer (more in tune to how
we define them in Drupal 8).
Introduction
cTools plugins in Drupal 7 (conceptually not so dissimilar to the
plugin system in Drupal 8) are meant for easily defining reusable bits
of functionality. That is to say, for the ability to define isolated
business logic that is used in some
context. The goal is to set up that
context and plugin type once, and allow other modules to then define plugins that can be used in that
context automatically.
If you’ve been developing Drupal sites for more than a year you’ve
probably encountered cTools plugins in one shape or form. I think the
first plugin type we usually deal with is the
content_type
plugin which allows us to create our own custom panel panes that
display dynamic content. And that is awesome. Some of the others you may
have encountered in the same realm of Panels are probably
context
and
access
(visibility rules). Maybe even
relationships
and
arguments
. These are all provided by cTools. Panels adds to this list by introducing
layouts
and
styles
that we normally use for creating Panels layouts and individual pane styles. These are I think the more common ones.
However, all of the above are to a certain extent a black box to
many. All we know is that we need to define a hook to specify a
directory and then provide an include file with some definition and
logic code and the rest happens by magic. Going forward, I would like us
to look into how a plugin type is
defined so that if the case
arises, we can create our own plugins to represent some reusable bits of
functionality. To demonstrate this, we will create a module that turns
the pesky hook system of defining custom Drupal blocks into a plugin
based approach similar to what Drupal 8 is using.
The final code (+ a bit more) can be found in
this repository if you want to follow along. And I do expect you are familiar with the steps necessary for defining
custom Drupal blocks.
The block_plugin module
As I mentioned, I would like to illustrate the power of cTools
plugins with a custom plugin type that makes defining Drupal 7 blocks
saner. Instead of implementing the 2 main hooks (
hook_block_info()
and
hook_block_view()
)
necessary to define a block, we’ll be able to have separate plugin
files each responsible for all the logic related to their own block. No
more switch cases and changing the hook implementation every time we
need a new block. So how do we do this?
First, let’s create our
block_plugin.info
file to get started with our module:
name = Block Plugin
description = Using cTools plugins to define Drupal core blocks
core = 7.x
dependencies[] = ctools
Simple enough.
The plugin type
In order to define our news plugin type, inside the
block_plugin.module
file we need to implement
hook_ctools_plugin_type()
which is responsible for defining new plugin types cTools will recognize:
function block_plugin_ctools_plugin_type() {
return array(
'block' => array(
'label' => 'Block',
'use hooks' => FALSE,
'process' => 'block_plugin_process_plugin'
)
);
}
In this hook we need to return an associative array of all the plugin
type definitions we need keyed by the machine name of the plugin type
name. Today we are only creating one called
block
. For more information on all the options available here, feel free to consult the
plugins-creating.html
help file within the cTools module. No use repeating all that information here.
The
process
key defines a function
name that gets triggered every time cTools loads for us a plugin and is
responsible for shaping or massaging the plugin data before we use it.
It’s sort of a helper function that prepares the plugin for us each time
so we don’t have to bother. So let’s see what we can do inside that
function:
function block_plugin_process_plugin(&$plugin, $info) {
if (!isset($plugin['admin title'])) {
$exploded = explode('_', $plugin['name']);
$name = '';
foreach ($exploded as $part) {
$name .= ucfirst($part) . ' ';
}
$plugin['admin title'] = $name;
}
if (!isset($plugin['show title'])) {
$plugin['show title'] = TRUE;
}
if (!isset($plugin['view'])) {
$plugin['view'] = $plugin['module'] . '_' . $plugin['name'] . '_view';
}
if (!isset($plugin['configure'])) {
$plugin['configure'] = $plugin['module'] . '_' . $plugin['name'] . '_configure';
}
if (!isset($plugin['save'])) {
$plugin['save'] = $plugin['module'] . '_' . $plugin['name'] . '_save';
}
}
This callback receives the plugin array as a reference and some
information about the plugin type. The task at hand is to either change
or add data to the plugin dynamically. So what do we achieve above?
First, if the developer hasn’t defined an admin title for the block
plugin, we generate one automatically based on the machine name of the
plugin. This is so that we always have an admin title in the Drupal
block interface.
Second, we choose to always display the title of the block so we mark the
show title
key of the plugin array as TRUE. When defining the block plugin, the
developer has the option of setting this to FALSE in which case we
won’t show a block title (subject).
Third, fourth and fifth, we generate a callback function for the
block view, save and configure actions (if they haven’t already been set
by the developer for a given plugin). These callbacks will be used when
implementing
hook_block_view()
,
hook_block_configure()
and
hook_block_save()
,
respectively. We won’t be covering the latter two in this article but
feel free to check out the repository to see what these can look like.
And that’s pretty much all we need for defining our custom plugin type. We should, however, also implement
hook_ctools_plugin_directory()
which, as you may know, is responsible for telling cTools where a plugin of a certain type can be found in the current module:
function block_plugin_ctools_plugin_directory($module, $plugin) {
if ($module == 'block_plugin' && in_array($plugin, array_keys(block_plugin_ctools_plugin_type())) ) {
return 'plugins/' . $plugin;
}
}
This will need to be implemented also by any other module that wants to define
block
plugins.
Drupal blocks
Now that we have the plugin type, let’s write the code which turns any defined
block
plugin into a Drupal block. Will start with the
hook_block_info()
implementation:
function block_plugin_block_info() {
$blocks = array();
$plugins = block_plugin_get_all_plugins();
foreach ($plugins as $plugin) {
$blocks[DELTA_PREFIX . $plugin['name']] = array(
'info' => $plugin['admin title'],
);
}
return $blocks;
}
Here we load all of the plugins using a helper function and define
the minimum required information for the block. Here you can add also
more information but we are keeping it simple for brevity.
We know each plugin will have a machine name (the name of the include file basically) and an
admin title
because we generate one in the processing phase if one doesn’t exist. The
DELTA_PREFIX
is a simple constant in which we define the prefix we want for the
block machine name because we need to reuse it and should be able to
easily change it if we want to:
define('DELTA_PREFIX', 'block_plugin_');
Our helper function we saw earlier looks like this:
function block_plugin_get_all_plugins() {
return ctools_get_plugins('block_plugin', 'block');
}
It’s a simple wrapper around the respective cTools function. And for
that matter, we also have the following function responsible for loading
a single plugin by its machine name:
function block_plugin_get_plugin($name) {
return ctools_get_plugins('block_plugin', 'block', $name);
}
This is very similar to the one before.
In order to make our Drupal block definitions complete, we need to implement
hook_block_view()
:
function block_plugin_block_view($delta = '') {
$plugin = block_plugin_plugin_from_delta($delta);
if (!$plugin) {
return;
}
$block = array();
if (isset($plugin['title']) && $plugin['show title'] !== FALSE) {
$block['subject'] = $plugin['title'];
}
$block['content'] = $plugin['view']($delta);
return $block;
}
So what’s happening here?
First, we use another helper function to try to load a plugin based
on the delta of the current block and do nothing if we are not dealing
with a plugin block.
Second, we build the block. If the user specified a
title
key on the plugin and the
show title
key is not false, we set the subject of the block (its title basically)
as the former’s value. As for the actual block content, we simply call
the
view
callback defined in the plugin. And that’s it.
Let us quickly see also the helper function responsible for loading a plugin based on a block delta:
function block_plugin_plugin_from_delta($delta) {
$prefix_length = strlen(DELTA_PREFIX);
$name = substr($delta, $prefix_length);
$plugin = block_plugin_get_plugin($name);
return $plugin ? $plugin : FALSE;
}
Nothing complicated going on here.
Defining block plugins
Since we told cTools that it can find
block
plugins inside the
plugins/block
folder of our module, let’s go ahead and create that folder. In it, we can add our first block inside a file with the
.inc
extension, for example
my_block.inc
:
<?php
$plugin = array(
'title' => t('This is my block'),
);
function block_plugin_my_block_view($delta) {
return array(
'#type' => 'markup',
'#markup' => 'Yo block!'
);
}
Like we do with all other plugins (
content_type
,
context
, etc), the plugin definition is in the form of an array inside a variable called
$plugin
. And for our case all we need at this point is a
title
(and not even that since without it the block simply won’t show a title).
Below it, we defined our callback function to display the block. The
naming of this function is important. It matches the pattern we used for
it during the processing phase (
module_name_plugin_name_view
). If we want to name it differently, all we have to do is reference the function name in the
view
key of the
$plugin
and it will use that one instead.
And that is basically it. We can now clear our caches and go to the
Block administration screen where we can find our block and add it to a
region. Showing that block on the page should trigger the
view
callback for that block plugin and render the contents.