Last updated 17 September 2013. Created on 10 August 2010.
Edited by fago, gollyg, drupalshrek, mitchell. Log in to edit this page.

Should I provide a new condition/action?

Before providing a new condition or action, make sure you have provided metadata for the data properties involved: then Rules is able to deal with that data with its generic conditions/actions. However, you should consider adding a separate condition or action if:
* the same cannot be achieved with the generic conditions/actions or
* the condition/action changes the site functionality

e.g. changing user roles is supported with the generic action, but may significantly modify site functionality for that particular user - thus a separate action (and so a condition) makes more sense. In contrast, a separate action is unnecessary when changing the node body, as the generic "Modify data" action fits very well for that case.

How to provide a condition/action

Rules implements its own API for providing actions, which is different to the core API for actions (see hook_action_info()). This is necessary in order to support all the features provided by the Rules module. Unlike Rules 1.x, the 2.x version never automatically supports core style actions - instead each action has to be defined with hook_rules_action_info() in order to be supported in Rules. However, to add support for an existing core style action specifying the right action info usually suffices.

For providing new conditions and actions to the system, implement hook_rules_condition_info() or hook_rules_action_info() respectively. Providing conditions works exactly the same way as providing actions, with the only difference that conditions have to return a boolean value and may not provide new variables. In the following example, we'll consider providing actions only.

/**
 * Implements hook_rules_action_info() on behalf of the user module.
 */
function user_rules_action_info() {
  $defaults = array(
   'parameter' => array(
      'account' => array(
        'type' => 'user',
        'label' => t('User'),
        'save' => TRUE,
      ),
    ),
    'group' => t('User'),
    'access callback' => 'rules_user_integration_access',
  );
  $actions['user_block'] = $defaults + array(
    'label' => t('Block a user'),
    'base' => 'rules_action_user_block',
  );
  $actions['user_unblock'] = $defaults + array(
    'label' => t('Unblock a user'),
    'base' => 'rules_action_user_unblock',
  );
  return $actions;
}

/**
 * Action: Unblock a user.
 */
function rules_action_user_unblock($account) {
  $account->status = 1;
}

/**
 * User integration access callback.
 */
function rules_user_integration_access($type, $name) {
  if ($type == 'event' || $type == 'condition') {
    return entity_metadata_entity_access('view', 'user');
  }
  // Else return admin access.
  return user_access('administer users');
}

The shown code snippet provides two actions for the user module. When the action gets executed, Rules will execute the function 'rules_action_user_(un)block() and pass the defined parameter $account to the function. Note that the key save has been enabled for this parameter, so Rules will permanently save the changes to $account after the action has been executed - but only if it does not return FALSE. That way Rules aggregates the changes of multiple actions into one save operation.

Defining parameters

You'll have to define all parameters passed to the action execution callback that way. Other (but not all) types known by Rules are text, token, integer, decimal, date, duration, uri all entities as well as lists of those types; e.g. list<text> - which are passed as arrays.

Updating variables

Note that for modifying Rules' variables an action may return an array of parameters to update, e.g. the above action would have to use return array('account' => $account);. However this is not necessary for this example due to the way PHP5 handles objects.

File inclusion

Note that you may put your hook_rules_action_info() implementation, as well as all specified callbacks, in your module's MODULE.rules.inc file - which gets automatically included. As the action information is cached, the rules.inc file doesn't need to be included when rules are evaluated.

When rules are evaluated and your action is to be executed, Rules cares about including the file in which your action execution implementation resides. For that purpose you may define further include files using hook_rules_file_info(), or just put the action execution callback into your main module.

Access

As for events and conditions the access callback is used to determine whether a user may configure the action in question.

More action callbacks

But in addition to that there are a bunch of additional callbacks one may implement for an action - check the RulesPluginHandlerInterface for an description of further callbacks. To implement one of those callbacks specify the function rules_action_user_unblock_METHOD() or define a custom callback function using the callbacks key.

Refer to the docs of hook_rules_action_info() for further details.

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

shakeri’s picture

i think your function name must be "user_rules_action_info()" not "rules_user_action_info()"

fago’s picture

If the user.module would implement that code yes, it would have to be user_rules_action_info(). However as the Rules module "implements the hook" for the user.module, it makes use of its own functions for that and finally uses rules_rules_action_info() to return the info.

steveoliver’s picture

I had the same question, and started to follow your reply @fago, but it doesn't make sense. I'm sure we're missing something (and the idea of implementing a hook on behalf of another module is quite something interesting I've never seen before). I think I would understand if the function names were user_rules_condition_info() and user_rules_action_info(). Can you provide any more clarification? I'm interested.

--
Steve Oliver
drupalsteve.com

steveoliver’s picture

This idea of implementing hooks on behalf of other modules seems a clever way to provide implementations for modules only when they exist -- a powerful idea I have never thought of or encountered. Is that how it works? Is there more info on this? Or is this just some real l33t drupal magick?

--
Steve Oliver
drupalsteve.com

fago’s picture

Usually, you shouldn't do so. However there is a common exception: if you are inventing a new hook, you may want to implement this hook for core modules as obviously they won't do so theirselves. Anyway, I've fixed the example above to use the regular function name to avoid any unnecessary confusion.

mexicoder’s picture

Just to add a couple of extra resources I found helpful NodeOne Rules action tutorial and Drupal Contrib rules action ref