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.
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.
Create a new custom module with the following structure:
{modules_folder}/custom/ custom.info custom.module custom.rules.inc custom.install
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
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; }
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') ) ) ) ); }
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 ourhook_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') ), ); }
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 userules_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)); }
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') ) ); }
In the
options list
attribute, we define a custom functioncustom_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; }
The array key
custom_views_condition
, defined in ourcustom_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; }
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') ), ); }
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:
Set the Event.
Add the Condition.
Set the view and display ID in the Condition, as shown in the following screenshot:
Add our custom Action:
Set the rendered view's Data selector value to the view object provided by our event:
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.
The following sections provide more information on creating Events, Conditions and Actions, and clearing caches.
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.
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
.
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.