Khanh Hoang - Kenn
Kenn is a user experience designer and front end developer who enjoys creating beautiful and usable web and mobile experiences.
Our company often uses the Panels module for creating sites. 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:
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:
/* * 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 "ctools_example_content_type.inc". First, describe the plugin in this file. Available options are:
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 standart 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:
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.
I appreciate your time.