Book Image

Drupal Rules How-to

By : Robert Varkonyi
Book Image

Drupal Rules How-to

By: Robert Varkonyi

Overview of this book

Rules is what every Drupal site builder and developer has to use when creating event ñ action-based applications. The framework provides a highly flexible way to create sophisticated, condition-based functions any Drupal based system into an interactive application. Rules makes Drupal rule the CMS world."Drupal Rules How-to" is a practical, hands-on guide that provides you with a number of clear step-by-step exercises, which will help you take advantage of the real power of the Rules framework, and understand how to use it on a site builder and developer levelThis book demonstrates the power and flexibility of the Rules framework. It discusses the main aspects of the module both from the site builder and developer perspective, from basic and advanced Rule configurations using Events, Conditions, Actions and Components to getting familiar with the Rules API. You will also learn how to use additional modules together with Rules to further extend the possibilities of your Drupal system, such as Rules Scheduler to schedule the execution of your Rule configurations and Views Bulk Operations to execute Rule configurations on a view result list. The book also demonstrates the main API features that enable you to create your own Events, Conditions and Actions, provide new data types to Rules and execute your configurations programmatically
Table of Contents (7 chapters)

Providing new Events, Conditions, and Actions (Become an expert)


This recipe explains how to create our custom Events, Conditions, and Actions.

In this example, we'll act on a view that's being rendered on the site. We'll create a new condition, where we set the view that's being rendered, and in our action, we'll update a custom database table with the number of times the view has been rendered.

Getting ready

Enable the Views and Views UI modules, and create a view of the latest content on the site. In this example, we'll use the latest content as the view name, and create a block displaying the latest content by the admin that lists all new content posted by user 1.

We also need to create a new database table where we'll store the information; we'll call it custom_view_render. We use hook_schema() in our .install file so our custom table will be available in all supported database engines automatically.

How to do it...

  1. Create a new custom module with the following structure:

    {modules_folder}/custom/
     custom.info
     custom.module
     custom.rules.inc
     custom.install
  2. Define the module's information in the custom.info file:

    name = Custom
    description = Provides an integration with the Rules framework to store the number of times a view was rendered
    core = 7.x
    package = Rules Custom
    dependencies[] = rules
    dependencies[] = rules_admin
    dependencies[] = views
    dependencies[] = views_ui
  3. Define our custom database table in the custom.install file:

    /**
     * Implements hook_schema()
     */
    function custom_schema() {
      $schema['custom_views_render'] = array (		
        'description' => 'The base table for custom views render.',
        'fields' => array(
          'view' => array(
            'description' => 'The name and display ID of the view.',
            'type' => 'varchar',        
            'length' => '32',
            'not null' => TRUE,
            'default' => '',
          ),
          'rendered' => array(
            'description' => 'The number of times the view was rendered.',
            'type' => 'int',
            'unsigned' => TRUE,
            'not null' => TRUE,       
          ),      
        ),       
        'primary key' => array('view'),
      );
      return $schema;
    }
  4. Define our new custom Event in custom.rules.inc:

    /**
    * Implements hook_rules_event_info()
    * Define our new custom event for Rules
    */
    function custom_rules_event_info() {
      return array(
        'custom_views_render' => array (
          'label' => 'A view is rendered',
          'group' => 'Rules Custom',
          'variables' => array(
            'view' => array(
              'type' => 'custom_view_datatype',
              'label' => t('View being rendered')
            )
          )
        )
      );
    }
  5. Because views are not regular data types natively available to Rules; we provided the custom_view_datatype type as the variable type. We also need to define this new data type in our hook_rules_data_info() function:

    /**
     * Implements hook_rules_data_info().
     * This hook should be used to define new data types to Rules.
     *
     * In this case, we simply pass on the view object to Rules
     */
    function custom_rules_data_info() {
      return array(
        'custom_view_datatype' => array(
          'label' => t('view')			
        ),
      );
    }
  6. We want Rules to invoke our event when a view is being rendered, so we'll use hook_views_pre_render() in custom.rules.inc and use rules_invoke_event_by_args() function to notify Rules that the event needs to be invoked:

    /**
     * Implements hook_views_pre_render()
     * Invoke our custom event when a view is being rendered
     */
    function custom_views_pre_render(&$view) {
      rules_invoke_event_by_args('custom_views_render', array($view));
    }
  7. Define our new Condition, that will compare a rendered view's name and display ID with a specified view:

    /**
    * Implements hook_rules_condition_info()
    */
    function custom_rules_condition_info() {
      return array(
        'custom_views_condition' => array(
          'label' => t('View being rendered'),
          'parameter' => array(
            'view' => array(
              'type' => 'text',
              'label' => t('View and display'),
              'options list' => 'custom_views_list',
              'description' => t('Select the view and display ID'),
              'restriction' => 'input',
            ),
          ),
          'group' => t('Rules Custom')
          )
      );
    }
  8. In the options list attribute, we define a custom function custom_views_list that returns an array of the available views on our site:

    /**
     * Helper function that returns all available views on our site
     */
    function custom_views_list() {
      $views = array();
      foreach (views_get_enabled_views() as $view_name => $view) {
        foreach ($view->display as $display_name => $display) {
          $views[$view_name . '-' . $display_name] =
          check_plain($view->human_name) . ' - ' . check_plain($display->display_title);
        }
      }
      return $views;
    }
  9. The array key custom_views_condition, defined in our custom_rules_condition_info() function, will be used to execute the actual comparison that will return a Boolean value, so we'll add a function with the same name:

    /**
     * Callback function for our custom condition
     * The function name must match the array key defined in hook_rules_condition_info()
     */
    function custom_views_condition($view = array()) {
      $current_view = views_get_current_view();
      $parts = explode('-', $view);
      if (($parts[0] == $current_view->name) && ($parts[1] == $current_view->current_display)) {
        return TRUE;
      }
      return FALSE;
    }
  10. Let's create our custom Action for Rules:

    function custom_rules_action_info() {
      return array(
        'custom_update_table' => array(
          'label' => t('Update "custom_views_render" table'),
          'parameter' => array(
            'view' => array(
              'type' => 'custom_view_datatype',
              'label' => t('Rendered View'),
            ),
          ),
          'group' => t('Rules Custom')
          ),
      );
    }
  11. We also need to add a function that actually gets called by Rules when the action fires. The name of this function must match the value of the "base" attribute defined in hook_rules_action_info():

    /**
     * The database function that gets called by the Rules Action
     * The function name must match the value in the 'base' attribute
     * defined in hook_rules_action_info()
     */
    function custom_update_table($view) {
        if (!is_object($view)) {
          return FALSE;
        }
        $result = db_select('custom_views_render', 'c')
          ->fields('c')
          ->condition('view', $view->name .'_'. $view->current_display, '=')
          ->execute()
          ->fetchAssoc();
    
        if ($result) {
          $update = db_update('custom_views_render')
          ->expression('rendered', 'rendered + :one', array(':one' => 1))
          ->condition('view', $view->name .'_'. $view->current_display, '=')
          ->execute();
        }
        else {
          $insert = db_insert('custom_views_render')
          ->fields(array(
            'view' => $view->name .'_'. $view->current_display,
            'rendered' => 1
          ))
          ->execute();
        }
    }

    The last step is to create a new rule configuration, set the Event to Rules Custom | A view is rendered, add a Condition Rules Custom | View being rendered and set it to our latest content view, and add an Action Update "custom_views_render" table:

  12. Set the Event.

  13. Add the Condition.

  14. Set the view and display ID in the Condition, as shown in the following screenshot:

  15. Add our custom Action:

  16. Set the rendered view's Data selector value to the view object provided by our event:

How it works...

In this example, we're creating a custom workflow by providing a new Event, Condition, and Action. In this virtual example, we want to track how many times a given view has been rendered. First we create a new database table to store the data in. Then we define our custom Event (A view is rendered) and our Condition (View being rendered) where we can choose the view and display that's being rendered. In the last step, we define our Action (Update "custom_views_render" table) which takes care of the database operations. Then we go ahead and create the rule configuration using our new Event, Condition, and Action.

It is the best practice to add all Rules hooks to a custom *.rules.inc file. Rules will automatically detect this file and fire the hooks.

There's more...

The following sections provide more information on creating Events, Conditions and Actions, and clearing caches.

Events

To create new Events for Rules, we need to implement hook_rules_event_info(). In this hook we need to return an array of Events, with the keys becoming the machine readable names of the Events. We can define the label, group, and variables this event will use. We can then fire this event by using rules_invoke_event() or rules_invoke_event_by_args() in another function or hook.

Conditions

We can define new Conditions by implementing hook_rules_condition_info(). Again, we need to return an array of Conditions with the array keys becoming the machine readable names of the Conditions, and by default, Rules will look for a function with the same name which will be fired when the Condition is invoked. Therefore, we need to create a function using the same machine readable name.

We must also define the parameters used by the condition. These parameters will be used in the custom function that returns either TRUE or FALSE.

Actions

When defining new Actions, we need to implement hook_rules_action_info(). Actions have a similar structure to Conditions, the definition consists of an array with information about the Action and a callback function that gets fired. The main difference is that an Action may execute an operation or return additional data for Rules.

Clearing the caches

Rules and the Entity API uses a fair amount of caching in order to increase performance. Therefore these caches need to be cleared every time a new Event, Condition, or Action is defined.