Last updated October 28, 2014. Created on July 27, 2013.
Edited by Ayesh, oadaeh, Kazanir, Artusamak. Log in to edit this page.

This guide applies to Drupal 7, Views 3 and Views Bulk Operations 7.3-x branch. You can find the Drupal 6 version of this guide here.

VBO reuses and extends Drupal core Action system. Most of the Drupal core actions can be used as a VBO-compatible action. This guide focuses on some VBO-specific functionality. If you are looking for Drupal core actions, please check the Examples module.

(To get the syntax highlighting, this guide will have both opening and closing PHP tags. They are not necessary in your module - you still need the top opening PHP tag)

Define the actions

First, we need to implement hook_action_info().

<?php
function MYMODULE_action_info() {
  return array(
   
'MYMODULE_my_custom_action' => array(
     
'type' => 'node',
     
'label' => t('Search and replace text in a field'),
     
'behavior' => array('changes_property'),
     
'configurable' => FALSE,
     
'vbo_configurable' => TRUE,
     
'triggers' => array('any'),
    ),
  );
}
?>

An associative array of action descriptions. The keys of the array are the names of the action functions, and each corresponding value is an associative array with the following key-value pairs:

  • 'type': The type of object this action acts upon. Core actions have types 'node', 'user', 'comment', and 'system'.
  • 'label': The human-readable name of the action, which should be passed through the t() function for translation.
  • 'configurable': If FALSE, then the action doesn't require any extra configuration. If TRUE, then your module must define a form function with the same name as the action function with '_form' appended (e.g., the form for 'node_assign_owner_action' is 'node_assign_owner_action_form'.) This function takes $context as its only parameter, and is paired with a _submit function, and possibly a _validate function.
  • 'triggers': An array of the events (that is, hooks) that can trigger this action. For example: array('node_insert', 'user_update'). You can also declare support for any trigger by returning array('any') for this value.
  • 'behavior': (optional) A machine-readable array of behaviors of this action, used to signal additionally required actions and permission requirements that may need to be triggered.
  • Currently recognized behaviors by Trigger module:

    'changes_property': If an action with this behavior is assigned to a trigger other than a "presave" hook, any save actions also assigned to this trigger are moved later in the list. If no save action is present, one will be added. Modules that are processing actions (like Trigger module) should take special care for the "presave" hook, in which case a dependent "save" action should NOT be invoked.

VBO-specific settings:

  • type: You can use "entity" to make the action available to all entities - nodes, taxonomy terms, users, etc.
  • vbo_configurable: Set this to TRUE if you want to make the VBO configurable (so there will be one more step before executing the action that user can choose extra options).
  • pass rows: Set this to TRUE if you want to pass row information to action $context
  • permissions: An array of permissions user must have (all of them) in order to execute the action. See more about access permissions.

Add a VBO configuration form

We can add extra settings to the VBO configuration form in the Views UI.

<?php
 
function mymodule_my_custom_action_views_bulk_operations_form($options) {
 
$form = array();
 
$form['hero'] = array(
   
'#type' => 'select',
   
'#title' => t('Choose your super hero'),
   
'#options' => array(
     
'Iron Man' => t('Iron Man'),
     
'Bat Man' => t('Bat Man'),
    ),
   
'#default_value' => !empty($options['hero']) ? $options['hero'] : '',
  );
 
  return
$form;
}
?>

$options will be an array of previous settings. When adding this action for the first, it won't be available.




Add a per-bulk configuration form as well

If you need to make your action flexible, that there could be a global View-wide setting as well as a per-bulk setting, we can do that as well! Observe that the $settings variable contains the information from the Views configuration form, while $form_state is passed by reference and contains the normal form state information in case you need to modify the form state (for example, to attach field widgets) during the setup of the per-bulk form. This function should return the $form variable for the form you want the per-bulk settings page to use.

<?php
function mymodule_my_custom_action_form($settings, &$form_state) {
 
$form = array();
 
$form['hero'] = array(
   
'#type' => 'select',
   
'#title' => t('Choose your super hero'),
   
'#options' => array(
     
'Iron Man' => t('Iron Man'),
     
'Bat Man' => t('Bat Man'),
    ),
   
'#required' => TRUE,
   
'#default_value' => isset($settings['settings']['hero']) ? $settings['settings']['hero'] : '',
  );
  return
$form;
}

function
mymodule_my_custom_action_submit($form, $form_state) {
 
$return = array();
 
$return['hero'] = $form_state['values']['hero'];
  return
$return;
}
?>

This form will be presented everytime we are about to execute some action on a set of chosen items.




$settings will contain the View-wide configuration we added with mymodule_my_custom_action_views_bulk_operations_form, as well as the view object itself and the current entity type.
In the submit function, we should return data to be passed to the actual action function so we can make the action "flexible".

ACTION!

It's time to create our action. So far, we have a View-wide setting form added, and there will be a form everytime to reselect the configuration for each bulk. The function name below comes from the hook_action_info() array that was built in the first steps of this article.

<?php
function mymodule_my_custom_action(&$node, $context) {
 
$message = t('Node title is %title. Sincerely, %hero', array(
   
'%title' => $node->title,
   
'%hero' => $context['hero'],
  ));
 
drupal_set_message($message);
}
?>

$node is the node object. If you are executing the action on a user entity, this obviously will be the user object.
$context variable will contain our per-bulk setting and the view-wide setting. This variable is also useful to determine the batch's position (current item, total number of items), and it contains the entity type.
rows in the $context array will be empty because we do not have aggregation in this action (I will add an example soon).




Permissions for actions

In some cases, you need to make sure users who execute the actions have the permissions to perform them. There are many layers that you can enforce permissions.

If the user has access to the View, and to perform an operation on the entity (Create, Read, Update, Delete), there will be no further checks and VBO will execute the action on the entity.

See the behavior property in the initial action definition. Following behaviors are used in VBO:

  • views_property
  • changes_property
  • creates_property
  • deletes_property

Entity access will then be checked using entity_access function.

You can, however, define specific permissions for each action using the permissions key in the initial action definition.

<?php
function MYMODULE_action_info() {
  return array(
   
'MYMODULE_my_custom_action' => array(
     
'type' => 'node',
     
'label' => t('Search and replace text in a field'),
     
'permissions' => array('access content', 'administer site configuration'),
      ...
    ),
  );
}
?>

Note that permissions must be an array of permissions. User executing the actions must have all the defined permissions in order to execute the action.

There is a sub module, action permissions that defines a permission for each action, so you can configure it in the permissions page. Permissions you define in the action definition are strictly requirements only; they will not be added as a permission. Use hook_permission for that.

That's it!

You defined your custom action, added a configuration form, added a modification form, and created the actual action function!

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

Comments

Renrhaf’s picture

Really useful documentation, thanks a lot !

m23reo32’s picture

I have added custom action via the documentation here, but it seems that on my end, it has added a new custom bulk operation, but code inside function is not being executed.

The code is shown as follows:

function mymodule_custom_action_info(){
  return array(
    'mymodule_custom_action' => array(
  'type' => 'entity',
          'label' => t('Custom Action'),
          'configurable' => FALSE,
  'triggers' => array('any'),
  'behavior' => array('changes_property'),
  'vbo_configurable' => FALSE,
  'pass rows' => TRUE,
    ),
  );
}

function mymodule_custom_action( &$entity, $context ){
  drupal_set_message('Test');
  watchdog('rewardco_send_points', 'Test');
}

PascalAnimateur’s picture

I'm trying to implement a video conversion action and have its settings configured through VBO only with the following :

function video_conversion_action_info_alter(&$actions) {
  $actions['convert_video_action'] = array(
    'type' => 'file',
    'label' => t('Convert video'),
    'configurable' => FALSE,
    'vbo_configurable' => TRUE,
  );
}

The VBO configuration goes fine through convert_video_action_views_bulk_operations_form($context), but how am I supposed to prevent VBO from presenting the user with the configuration form defined in convert_video_action_form($context) ? (I get an error when the latter is undefined)

I tried defining placeholder form elements of type 'value' and populating them with $context passed from VBO configuration, but VBO still asks the user to set parameters for my action, although the form is empty!

Is it possible to have configurable = FALSE while having vbo_configurable = TRUE ?

W.M.’s picture

Thanks very much for this valuable information. I have two questions:

a. Is it possible to create an action and make it available for a particular role only?

b. How it is possible to set a time interval of 2 seconds between each execution as VBO moves from selected node to another? i.e.

Let's say I want to perform some action on nodes 1, 2 and 3 .. Is it possible to achieve this:

Execute action on node 1 ====> Wait 2 seconds ====> Execute action on node 2 ====> Wait 2 seconds ====> Execute action on node 3.

Thanks.

Ayesh’s picture

I updated the document with more information about permissions handling.
For delaying, while I'm not sure why you'd need to do this, you can simply call sleep(2); to halt the process.

// Ayesh
(Latest blog post: Drupal bad practices)

W.M.’s picture

@Ayesh

Thank you very much for the valuable information. I still have one question: Is it possible to get the number of entities (nodes) that had been selected by the user and sent further to handling by VBO before action execution?

(I have found the answer for the above question)..

Still wondering :) Can "Aggregate = TRUE " lead to memory or PHP errors when passing let's say 1000 nodes ?

Thanks.

W.M.’s picture

How to cancel form submission if certain condition is not met at this particular stage:

function mymodule_my_custom_action_submit($form, $form_state) {
  $return = array();
  $return['hero'] = $form_state['values']['hero'];
  return $return;
}

I want my users to be redirected back to the custom per-bulk configuration form if certain condition is not met at this point (based on a user entered value from the custom per-bulk configuration form itself). How to do that? Thanks.

Ayesh’s picture

You can validate the results in a mymodule_my_custom_action_validate hook.
Please post in forum or Drupal Answers if you have further questions.

// Ayesh
(Latest blog post: Drupal bad practices)

super_romeo’s picture

And how to output result message, for example "Sum of node nids = 123"?
Have VBO or actions such hook?

adamtyoung’s picture

I am trying to pass a single NID from a page to a VBO action using views_bulk_operations_execute(). Does anyone have any idea how to do this? The documentation is non-existent. My VBO action has a 'title' and 'body' field that I need to post to the final action as arguments. Cheers!

Adam Young
Vancouver, BC
http://www.adamtyoung.ca

nithinkolekar’s picture

If I need to disable next button and instead show message "no required data found to perform operation" if some select field on config form cannot be populated from db by passing contextual filter.

Whats the difference between mymodule_my_custom_action_views_bulk_operations_form and mymodule_my_custom_action_views_bulk_operations_form_alter? In which function it should be done?

Thanks n Regards
Nithin