Ctools content type for Panels

Ctools content type for Panels

Our company often uses the Panels module for web development. Although this option adds a considerable amount of HTML structures, it provides us with a flexible, user-friendly, and easy-to-operate mechanism. However, sometimes the built-in features of the module are not sufficient (non-standard customer requirements or an in-house desire to perform the same functions as before, but in an even better manner).Ctools content types are like standard blocks in Drupal, but they also have certain advantages:

  • ability to display any information - views, forms, HTML
  • these entities are well-structured - theming, preprocess and the setting form for each ctools content type contains a built-in description.
  • they work with contexts panels
  • easy-to-implement form of display settings.

Let us look through the process of creating a ctools content type, shall we. We will create a module "ctools_example" for this. The module call hook_ctools_plugin_directory (), which indicates the ctools plugins folder location specified type. The hook has two options:

  • $ Plugin_type – the name of the parameter is self-explanatory - this is the name of the string type plug-in ('Panels', 'layouts', 'styles', etc.)
  • $ Owner - required parameter, because ctools observes namespace plugins depending on what modules they belong to. Usually, the path is "plugins/$ plugin_type".
 * Implements hook_ctools_plugin_directory -
function ctools_example_ctools_plugin_directory($owner, $plugin_type) {
  if ($owner == 'ctools' && $plugin_type == 'content_types') {
    return 'plugins/content_types';

Then, create a folder "plugins / content_types" and insert the file with our plug-in, such as "". First, describe the plugin in this file. Available options are:

  • 'title' - the name of our content type
  • 'description' – a detailed description of it
  • 'single' - if this is labeled TRUE, then the content type has no subtypes
  • 'content_types' - an array that defines the properties of a particular content type or a function that returns this array
  • 'render callback' - the name of the function that will be responsible for the display of the block
  • 'defaults' - the default context
  • 'edit form' - the name of the function which will be responsible for the display of the content type edit form
  • 'icon' - the icon displayed in the admin interface. The icon should be placed in the folder with the plugin 'category'
  • 'category' - the category to which the newly-created content type will belong

The entire list can be found by installing the parameters of the module Advanced help.

 * Plugins are described by creating a $plugin array which will be used
 * by the system that includes this file.
$plugin = array(
  'title' => t('Example block'),
  'description' => t('Example block.'),
  // 'single' => TRUE means has no subtypes.
  'single' => TRUE,
  // Constructor.
  'content_types' => array('no_context_content_type'),
  // Name of a function which will render the block.
  'render callback' => 'ctools_example_static_block_render',
  // The default context.
  'defaults' => array(),

  // This explicitly declares the config form. Without this line, the func would be
  // ctools_plugin_example_no_context_content_type_edit_form.
  'edit form' => 'ctools_example_example_block_edit_form',
  'icon' => 'favicon.ico',
  'category' => array(t('Example'), -9),

There is no doubt that we need to declare the function of display and editing of our object. Create a simple type of content that is using the autocomplete will pull some node and display its title and text. Header display, its link / no link display and text display is optional. And here is the form itself:

 * Generates example block edit form.
function ctools_example_example_block_edit_form($form, &$form_state) {
  $conf = $form_state['conf'];

  // We don't want to use standart pane title functionallity.
  $form['override_title']['#access'] = FALSE;
  $form['override_title_text']['#access'] = FALSE;
  $form['override_title_markup']['#access'] = FALSE;
  $form['title_nid'] = array(
    '#type' => 'textfield',
    '#title' => t('Enter content title'),
    '#autocomplete_path' => 'ctools_example/autocomplete',
    '#default_value' =>  $conf['title_nid'] ? $conf['title_nid'] : '',
  $form['show_body'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show body'),
    '#default_value' =>  $conf['show_body'] ? TRUE : FALSE,
  $form['display_title'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display title'),
    '#default_value' =>  $conf['display_title'] ? TRUE : FALSE,
  $form['title_as_link'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display title as link'),
    '#default_value' =>  $conf['title_as_link'] ? TRUE : FALSE,
    '#states' => array(
      'visible' => array(
        ':input[name="display_title"]' => array('checked' => TRUE),

  return $form;

You can add submit / validate - handlers to the editing form ctools content type, as well as to the standard forms in Drupal. In our case, if you select the ‘validate’ option, the handler will check your entry in the auto-complete field, and if you select ‘submit ‘, the handler will save the settings for a particular block:

 * Validate handler for example block edit form.
function ctools_example_example_block_edit_form_validate($form, &$form_state) {
  $text = $image = $video = $audio = FALSE;
  if (!preg_match('/.*\[nid:([\d]+)\]/', $form_state['values']['title_nid'], $m)) {
    form_set_error('title_nid', t('Title should be in format "Title [nid:xxx]"'));

 * Submit handler for example block edit form.
function ctools_example_example_block_edit_form_submit($form, &$form_state) {
  foreach (array('title_nid', 'display_title', 'title_as_link', 'show_body') as $key) {
    $form_state['conf'][$key] = $form_state['values'][$key];

Finally, I will describe the function that is responsible for displaying the content of our block. As you can see from the code, the display logic resembles the logic that is used in hook_block_view (). During the display, we need to create an object with the following possible properties:

  • 'title' - the block header
  • 'content' - the content block
  • 'links' - an array of links related to the content. The format of the array must be the same as for the function theme_links
  • 'more' - optional link like "Read more"

A complete list of the properties can be found once the very same module, Advanced help has been installed.

 * Run-time rendering of the body of the block.
 * @param $subtype
 * @param $conf
 *   Configuration as done at admin time.
 * @param $args
 * @param $context
 *   Context - in this case we don't have any.
 * @return
 *   An object with at least title and content members.
function ctools_example_example_block_render($subtype, $conf, $args, $context) {
  $block = new stdClass();
  $block->content = '';
  if (!empty($conf)) {
    preg_match('/(.*) \[nid:([\d]+)\]/', $conf['title_nid'], $m);
    if (isset($m[1]) && is_numeric($m[2])) {
      $nid = $m[2];
      $node = node_load($nid);
      if ($conf['display_title']) {
        $block->title = $conf['title_as_link'] ? l($node->title, "node/{$node->nid}") : $node->title;
      $lang = field_language('node', $node, 'body');
      if ($conf['show_body']) {
        $block->content .= $node->body[$lang][0]['value'];
  return $block;

All the code module can be seen in the attached files, and the appropriate type of admin interface can be observed in the screenshots.

Our Drupal development company appreciates your time.

8 votes, Rating: 5

Read also


You sometimes find yourself trying to handle the tasks that require creating a custom table in the database so as to later interact with this table through your own requests. Inherent in such...


In my previous article we discussed capabilities of PHP code profiler XHprof...


Nowadays, everyone knows about such Internet shops like eBayAmazon, etc. But...


Not all of us know that the ...


Following the two previous articles about profilers, namely about XHprof profiler from facebook developers, we'll look at how it can possibly be  applied for analysing productivity of...

Subscribe to our blog updates