Drupal triggers are best understood using an appropriate definition.

For Drupal site administrators: A trigger is an individual "event that Drupal can fire action(s) on." Some examples of triggers are a post being updated, a user logging in or a comment being deleted. Typically you will encounter triggers in Drupal's configuration screens.

For developers: a trigger is an event that fires specified action(s). When the event occurs it fires all action(s) that are attached to it.

Can you give me an example of where a trigger might be useful?

Triggers are usually used to allow Drupal to configure the response to an event. For example, suppose a Drupal site administrator wants to be notified by email whenever any user logs in. She would configure a "Send email" action and use the trigger module to assign that action to execute when the "user log in" trigger fires.

How do I create a new trigger?

There are three steps.

  1. We must describe the action to Drupal using hook_hook_info().
  2. We must write hook_trigger_name() to call the appropriate actions.
  3. We must insert module_invoke_all() everywhere we want the trigger to fire.

Let's create two triggers for the On this date module used in the tutorial. (Block Viewed and Page Viewed)

1. Describing the triggers with hook_hook_info()
1.  /**
2.   * Implementation of hook_hook_info().
3.   */
4.  function onthisdate_hook_info() {
5.    return array(
6.      'onthisdate' => array(
7.        'onthisdate' => array(
8.          'block_viewed' => array(
9.            'runs when' => t('The "On this date" block has been viewed by a logged in user'),
10.         ),
11.         'page_viewed' => array(
12.           'runs when' => t('The "On this date" page has been viewed by a logged in user'),
13.         ),
14.       ),
15.     ),
16.   );
17. }

hook_hook_info() must return an array that is keyed as follows.

  • Line 6: Key is the tab under the trigger admin page that these triggers will appear on.
  • Line 7: Key is module name.
  • Lines 8 & 11: Key is trigger $op value.
  • Lines 9 & 12: Value is user friendly description of when trigger will be fired.

A few additional notes about this array. Once this array is in place the triggers will appear under the trigger admin page. Also, It is described in hook_hook_info() with one error. Line 7 supposedly "defines the hook being described" in fact it must be the module name. (At least until it is fixed).

2. Performing the appropriate actions when the hook_trigger_name() is called.
1.  /**
2.   * Implementation of hook_trigger_name().
3.   */
4.  function onthisdate_onthisdate($op, $user) {
5.    // We support a subset of operations.
6.    if (!in_array($op, array('block_viewed', 'page_viewed'))) {
7.      return;
8.    }
9.    $aids = _trigger_get_hook_aids('onthisdate', $op);
10.   $context = array(
11.     'hook' => 'onthisdate',
12.     'op' => $op,
13.     'user' => $user,
14.   );
15.   actions_do(array_keys($aids), $user, $context);
16. }

Now for an analysis of this function.

The trigger_name part of hook_trigger_name is defined in line 7 of step one (onthisdate in our case).

$op: One of the keys defined in lines 8 & 11 of step one.
$user: The global $user variable. It will be passed in by our trigger call.

Line 6: Ensures that we only fire the triggers we created.
Line 9: Collects the actions we need to fire by calling the private _trigger_get_hook_aids() function.
Lines 11-14: Creates the context information that the actions will need to be performed.
Line 15: Actually fires the actions using the actions_do() function.

3. Call the appropriate trigger at the appropriate time.

Since we have defined two triggers we now need to ensure that they fire at the appropriate times. Let's start with the block_viewed trigger.

Since the block is generated for viewing in the onthisdate_block() function the code will be added there.

1.   ...
2.    $block['content'] = $block_content;
3.  
4.    //Load the user object
5.    global $user;
6.  
7.    //Fire Block Viewed Trigger
8.    module_invoke_all('onthisdate', 'block_viewed', $user);
9.  
10.   return $block;
}

Line 5: Loads the global user object so that we can send it when we fire the trigger.
Line 8: Fires the trigger using the module_invoke_all() function

module_invoke_all() expects the following format. module_invoke_all('trigger_name', 'trigger_operation', [additional context information], [additional context information], ...)

Repeat this process for the page view and you now have two new fully functioning triggers. Now you just need to create some actions that can be attached to these triggers.

An advanced action override

Perhaps you want your fancy new trigger to be able to utilize some built in actions. Then this is for you. Essentially, you need to alter the existing actions and tell them that they work with your new trigger.

In this example we will tell the user & system actions that they can be attached to our new trigger. Doing this requires using the hook_action_info_alter() function.

1.  /**
2.   * Implementation of hook_action_info_alter().
3.   */
4.  function onthisdate_action_info_alter(&$info) {
5.    foreach ($info as $type => $data) {
6.      if (stripos($type, "user_") === 0 || stripos($type, "system_") === 0) {
7.        if (isset($info[$type]['hooks']['application'])) {
8.          $info[$type]['hooks']['onthisdate'] = array_merge($info[$type]['hooks']['onthisdate'], array('block_viewed', 'page_viewed'));
9.        }
10.       else {
11.         $info[$type]['hooks']['onthisdate'] = array('block_viewed', 'page_viewed',);
12.       }
13.     }
14.   }
15. }

Essentially, you loop through each available action and add your new trigger to the approved list.

Line 5: Starts looping through each action.
Line 6: Checks if the action is one you want to add your trigger to. (If you are still running PHP 4 you will need to replace stripos with strpos.)
Line 7: Ensures that we don't remove any other triggers that have already been added to the approved list.
Line 8: Adds our triggers to the the existing approved list.
Line 11: Creates the approved list by adding our triggers.

Note 1: In lines 8 & 11 you are essentially adding the following array to $info[$type]['hooks'].

  array(
    'trigger_name' => array(
      'op 1',
      'op 2',
      ...
    ),
  );

Note 2: In most instances for line 6 you will have a much more limited if statement. For instance:

  if ($type == "user_block_ip_action") {

For further information on writing actions see the Writing actions (Drupal 6.x) page.

Comments

bux’s picture

This material seems quite helpful, but I am not sure where I should put my code. Also, it would be good to have some references to which values I can access to build conditions for a trigger.

futuresoon’s picture

Typically, the context of "code that you don't know where to put it" is inside the modulename.module file (remember to make a modulename.info file too). Sometimes the context (typically theming is the template.php file for advanced theming php).

As for some variables to work with, try downloading the devel module, making a template.php file and making an experimental module and doing print_r()'s.

Best of luck

antgiant’s picture

As futuresoon mentioned the code should go into your modulename.module file (onthisdate.module in this example as it is built off the tutorial http://drupal.org/node/206754).

For your second question, you may be confusing actions with triggers. Essentially a trigger is code you tell your module to fire. So the trigger has access to everything your module does. Now if your question is what values does your module have access to perhaps this would be helpful http://blog.anselmbradford.com/2009/03/14/2-invaluable-drupal-developmen...

kenorb’s picture

In 6.x you have to set up permission using hook_menu_alter, otherwise triggers provided by other modules will be not accessible.
See:
#324183: Better Access Control in Custom Triggers
#563790: No 'AJAX Trigger' tab in Trigger settings page

moritzz’s picture

You need to grant access rights to get your custom triggers show up for users with . For the example above you need to add the following function to your module:

/**
 * Implementation of hook_menu_alter().
 *
 * Work around loss of menu local task inheritance.
 */
function onthisdate_menu_alter(&$callbacks) {
  if (module_exists('trigger') & isset($callbacks['admin/build/trigger/onthisdate'])) {
    $callbacks['admin/build/trigger/onthisdate']['access callback'] = 'trigger_access_check';
  }
}

(Taken from http://drupal.org/project/workflow. Issue documented at #324183: Better Access Control in Custom Triggers.)

sebagr’s picture

Hi!

When discussing hook_action_info_alter() above, line 8 reads:

array_merge($info[$type]['hooks']['onthisdate'], array('block_viewed', 'page_viewed'));

I believe this should be more like this, because array_merge() won't modify the existing arrays:

$info[$type]['hooks']['onthisdate'] = array_merge($info[$type]['hooks']['onthisdate'], array('block_viewed', 'page_viewed'));
jvdurme’s picture

I get this error:

Fatal error: Call to undefined function _trigger_get_hook_aids() in /var/www/develworld/sites/all/modules/onthisdate/onthisdate.module on line 218

Any idea why I'm seeing this? Thanks!

Wakken!

Niklas Fiekas’s picture

diwant’s picture

Thanks this was very helpful! I think in the code above it should say 'onthisdate' instead of 'application' so the correct code is:

if (isset($info[$type]['hooks']['onthisdate'])) {
  $info[$type]['hooks']['onthisdate'] = array_merge($info[$type]['hooks']['onthisdate'], array('block_viewed', 'page_viewed'));
} else {
  $info[$type]['hooks']['onthisdate'] = array('block_viewed', 'page_viewed',);
}
anup.tilak’s picture

Can we implement triggers on Views??