It has been the first time since I started blogging somewhere in 2009 that a full calendar month (of April 2012) passed without me writing any blog in that month. It feels good to be back to the blogging world.

To jump on to the topic of the present blog post, I recently wrote a Drupal module (let’s call it myappfeed) for a client enabling them to push RSS feeds to their intranet app in an easily configurable way. Now I was supposed to provide reporting for various user activities related to the rss feeds. As the content was originating from Drupal, Views was the obvious choice for reporting.

The major table involved for reporting was a custom table that had no relation to standard Drupal node or user bundle types. So it needed to act as the “base” table for Views which itself was pretty easy. You would easily find examples on the web (like here) demonstrating how to define a new base table for Views and then fields on that table.

I was quickly able to define the plumbing required for Views for my module and moved on to create the View based on the custom base table. I browsed to the page for creating a new View in D7, and selected my table on which the view was to be based. Next I expanded the “sorted by” drop-down and to my surprise, it had no option there (see image).

No option to sort in order or creation date

 

The table had a timestamp column with the column name being “timestamp” and I was expecting the Views to pick that up and automatically provide the “Newest first” and “Oldest first” sort options like the way Views provide for Node/User types etc (I knew I could do this manually by adding a filter on the next page, but for some reason, I made the call to make the option available on the first Wizard of creating the View itself). A search on the web threw up nothing in that context. After browsing around for a while, I decided to take the plunge into Views code itself and see how Views provide this facility for Node entity type.

After poking around in Views code for sometime, I was able to locate the node.inc file in “plugins/views_wizard” folder inside the Views folder (with the below contents):

 

{syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }$plugin = array(
‘name’ => ‘node’,
‘base_table’ => ‘node’,
‘created_column’ => ‘created’,
‘available_sorts’ => array(
‘title:DESC’ => t(‘Title’)
),
‘form_wizard_class’ => array(
‘file’ => ‘views_ui_node_views_wizard.class.php’,
‘class’ => ‘ViewsUiNodeViewsWizard’,
),
‘title’ => t(‘Content’),
‘filters’ => array(
‘status’ => array(
‘value’ => NODE_PUBLISHED,
‘table’ => ‘node’,
‘field’ => ‘status’,
),
),
‘path_field’ => array(
‘id’ => ‘nid’,
‘table’ => ‘node’,
‘field’ => ‘nid’,
‘exclude’ => TRUE,
‘link_to_node’ => FALSE,
‘alter’ => array(
‘alter_text’ => 1,
‘text’ => ‘node/[nid]’,
),
),
);

if (module_exists(‘statistics’)) {
$plugin[‘available_sorts’][‘node_counter-totalcount:DESC’] = t(‘Number of hits’);
}{/syntaxhighlighter}

 

A look at this file was sufficient to indicate that I needed to create a similar plugin for my table and define “created_column” and “available_sorts” options for the plugin.

So I started out simple, created “plugins/views_wizard” folder structure inside my module and created a myappfeed.inc file inside it with these contents:

 

$plugin = array(
  'name' => 'myappfeed',
  'base_table' => 'myappfeed_click',
  'created_column' => 'timestamp',
  'available_sorts' => array(
    'title:DESC' => t('Title')
  ),
  'title' => t('CL Feed Click'),
);

I went on to clear Drupal caches and when I refreshed the page to create a new View and again selected my table, I was up for an unpleasant surprise, the “Newest first” and “Oldest first” options were still not there. For a moment, I thought of continuing and adding the filter manually on next Views Wizard but something within kept preventing me from doing so, and I continued to find a way to make it work.

It appeared to me that Views have not found my plugin and I somehow need to inform Views about the existence of this plugin. A hair-pulling exploration of Views code accompanied with desperate googling and var_dump session brought me to one simple conclusion: a Views plugin is actually a CTools plugin.

From there on, the search focused instead on finding a way to make CTools recognize a plugin, which came through easily; define hook_ctools_plugin_directory for my module. And once I did it, I got what I wanted, the sort options on the first Views wizard itself (see the image).

Option becomes available to sort on creation date and Title

 

To make it simple for you to implement the same (plugins for Views wizards), I would now define the process in a few easy to follow steps:

  1. Implement the hook_ctools_plugin_directory on your module and let CTools know the folder where plugins reside for views_ui module:

    {syntaxhighlighter brush: php;fontsize: 100; first-line: 1; }function myappfeed_ctools_plugin_directory($module, $plugin) {
    if ($module == ‘views_ui’) {
    return “plugins/views_wizard”;
    }
    }{/syntaxhighlighter}

  2. Add plugins/views_wizard directory inside your module directory and add a suitably named “inc” file to it (in my case, the filename was myappfeed.inc).
  3. Open this file and add the following code to it:
    <?php
    $plugin = array(
      'name' => 'myappfeed',
      'base_table' => 'myappfeed_click',
      'created_column' => 'timestamp',
      'available_sorts' => array(
        'title:DESC' => t('Title')
      ),
      'title' => t('CL Feed Click'),
      'path' => drupal_get_path('module', 'views') . '/plugins/views_wizard',
      'form_wizard_class' => array(
        'file' => 'views_ui_base_views_wizard.class.php',
        'class' => 'ViewsUiBaseViewsWizard',
      ),
    );

That should be it. Once you clear Drupal cache, Views should be recognizing your plugin and adapting its behavior as per the plugin. I would like to highlight a few important points from the last code sample above:

  • created_column would usually be a datetime or integer column in your base table containing a timestamp for sorting rows based on created/updated time.
  • available_sorts are additional field names you want to appear as available sort options in the first wizard of creating a “New View”. My base table had a Title column and I provided that here. This is optional and you can even provide multiple fields.
  • You should definitely add the path and form_wizard_class options as defined above with exactly the same values if your plugin is meant to be for the first Wizard in “Add View” workflow (unless you have a custom Wizard class).
    Without specifying it, Views will try to locate similarly names classes in the same folder as your plugin and you would get the following notice in Drupal’s log entries everytime Views cache is refreshed:

    Plugin myappfeed of plugin type views_ui:views_wizard points to nonexistent file sites/all/modules/myappfeed/plugins/views_wizard/views_ui_base_views_wizard for class handler form_wizard_class.

The process for defining a plugin for any Views wizard should be similar as above, but the sample code for the plugin in this blog post is specific for the first Wizard in “Add View” workflow, the options are different for other wizard plugins. And the best way to know these options is to look out for Views implementations of the corresponding plugins for node and user bundle types (in fact, the same helped me a lot to figure out a solution for my problem too).