views_bulk_operations.module in Views Bulk Operations (VBO) 6
Allows operations to be performed on items selected in a view.
Table of contents:
- API functions
- Drupal hooks and callbacks
- Helper functions
File
views_bulk_operations.moduleView source
<?php
/**
* @file
* Allows operations to be performed on items selected in a view.
*
* Table of contents:
* - API functions
* - Drupal hooks and callbacks
* - Helper functions
*/
/**
* API function to programmatically invoke a VBO.
*/
function views_bulk_operations_execute($vid, $operation_callback, $operation_arguments = array(), $view_exposed_input = array(), $view_arguments = array(), $respect_limit = FALSE) {
$view = views_get_view($vid);
if (!is_object($view)) {
watchdog('vbo', 'Could not find view %vid.', array(
'%vid' => $vid,
), WATCHDOG_ERROR);
return;
}
$vd = new views_bulk_operations_destructor($view);
// this will take care of calling $view->destroy() on exit.
// Find the view display that has the VBO style.
$found = FALSE;
foreach (array_keys($view->display) as $display) {
$display_options =& $view->display[$display]->display_options;
if (isset($display_options['style_plugin']) && $display_options['style_plugin'] == 'bulk') {
$view
->set_display($display);
$found = TRUE;
break;
}
}
if (!$found) {
watchdog('vbo', 'Could not find a VBO display in view %vid.', array(
'%vid' => $vid,
), WATCHDOG_ERROR);
return;
}
// Execute the view.
$view
->set_exposed_input($view_exposed_input);
$view
->set_arguments($view_arguments);
$view
->set_items_per_page($respect_limit ? $display_options['items_per_page'] : 0);
$view
->execute();
if (empty($view->result)) {
watchdog('vbo', 'No results for view %vid.', array(
'%vid' => $vid,
), WATCHDOG_WARNING);
return;
}
// Find the selected operation.
$plugin = $view->style_plugin;
$operations = $plugin
->get_selected_operations();
if (!isset($operations[$operation_callback])) {
watchdog('vbo', 'Could not find operation %operation in view %vid.', array(
'%operation' => $operation_callback,
'%vid' => $vid,
), WATCHDOG_ERROR);
return;
}
$operation = $plugin
->get_operation_info($operation_callback);
// Execute the operation on the view results.
$execution_type = $plugin->options['execution_type'];
if ($execution_type == VBO_EXECUTION_BATCH) {
$execution_type = VBO_EXECUTION_DIRECT;
// we don't yet support Batch API here
}
_views_bulk_operations_execute($view, $view->result, $operation, $operation_arguments, array(
'execution_type' => $execution_type,
'display_result' => $plugin->options['display_result'],
'max_performance' => $plugin->options['max_performance'],
'settings' => $operation['options']['settings'],
));
}
/**
* API function to add actions to a VBO.
*/
function views_bulk_operations_add_actions($vid, $actions) {
$view = views_get_view($vid);
if (!is_object($view)) {
watchdog('vbo', 'Could not find view %vid.', array(
'%vid' => $vid,
), WATCHDOG_ERROR);
return;
}
// Find the view display that has the VBO style.
$found = FALSE;
foreach (array_keys($view->display) as $display) {
$display_options =& $view->display[$display]->display_options;
if (isset($display_options['style_plugin']) && $display_options['style_plugin'] == 'bulk') {
$found = TRUE;
break;
}
}
if (!$found) {
watchdog('vbo', 'Could not find a VBO display in view %vid.', array(
'%vid' => $vid,
), WATCHDOG_ERROR);
return;
}
// Iterate on the desired actions.
$operations = $display_options['style_options']['operations'];
$ignored = $added = array();
if (!empty($actions)) {
foreach ($actions as $action) {
$modified = FALSE;
if (is_numeric($action)) {
// aid
$action_object = db_fetch_object(db_query("SELECT * FROM {actions} WHERE aid = %d", $action));
if (is_object($action_object)) {
$parameters = unserialize($action_object->parameters);
$key = $action_object->callback . (empty($parameters) ? '' : '-' . md5($action_object->parameters));
if (isset($operations[$key])) {
// available for this view
$display_options['style_options']['operations'][$key]['selected'] = TRUE;
$modified = TRUE;
}
}
}
else {
// callback or title
if (isset($operations[$action])) {
// callback and available for this view
$display_options['style_options']['operations'][$action]['selected'] = TRUE;
$modified = TRUE;
}
else {
// try the title
$action_object = db_fetch_object(db_query("SELECT * FROM {actions} WHERE description LIKE '%s'", db_escape_string($action)));
if (is_object($action_object)) {
$parameters = unserialize($action_object->parameters);
$key = $action_object->callback . (empty($parameters) ? '' : '-' . md5($action_object->parameters));
if (isset($operations[$key])) {
// available for this view
$display_options['style_options']['operations'][$key]['selected'] = TRUE;
$modified = TRUE;
}
}
}
}
if ($modified) {
$added[] = $action;
}
else {
$ignored[] = $action;
}
}
}
// Save the view if anything was changed.
if (!empty($added)) {
$view
->save();
views_object_cache_clear('view', $vid);
if (empty($ignored)) {
watchdog('vbo', 'View %vid was successfully modified. The following actions were added: %added.', array(
'%vid' => $vid,
'%added' => implode(', ', $added),
), WATCHDOG_INFO);
}
else {
watchdog('vbo', 'View %vid was modified. The following actions were added: %added. The following actions were ignored: %ignored.', array(
'%vid' => $vid,
'%added' => implode(', ', $added),
'%ignored' => implode(', ', $ignored),
), WATCHDOG_WARNING);
}
}
else {
watchdog('vbo', 'View %vid was NOT modified. The following actions were ignored: %ignored.', array(
'%vid' => $vid,
'%ignored' => implode(', ', $ignored),
), WATCHDOG_ERROR);
}
}
// Define the steps in the multistep form that executes operations.
define('VBO_STEP_VIEW', 1);
define('VBO_STEP_CONFIG', 2);
define('VBO_STEP_CONFIRM', 3);
define('VBO_STEP_SINGLE', 4);
// Types of bulk execution.
define('VBO_EXECUTION_DIRECT', 1);
define('VBO_EXECUTION_BATCH', 2);
define('VBO_EXECUTION_QUEUE', 3);
// Types of aggregate actions.
define('VBO_AGGREGATE_FORCED', 1);
define('VBO_AGGREGATE_FORBIDDEN', 0);
define('VBO_AGGREGATE_OPTIONAL', 2);
// Access operations.
define('VBO_ACCESS_OP_VIEW', 0x1);
define('VBO_ACCESS_OP_UPDATE', 0x2);
define('VBO_ACCESS_OP_CREATE', 0x4);
define('VBO_ACCESS_OP_DELETE', 0x8);
/**
* Implementation of hook_cron_queue_info().
*/
function views_bulk_operations_cron_queue_info() {
return array(
'views_bulk_operations' => array(
'worker callback' => '_views_bulk_operations_execute_queue',
'time' => 30,
),
);
}
/**
* Implementation of hook_views_api().
*/
function views_bulk_operations_views_api() {
return array(
'api' => 2.0,
);
}
/**
* Implementation of hook_elements().
*/
function views_bulk_operations_elements() {
$type['views_node_selector'] = array(
'#input' => TRUE,
'#view' => NULL,
'#process' => array(
'views_node_selector_process',
),
);
return $type;
}
/**
* Process function for views_node_selector element.
*
* @see views_bulk_operations_elements()
*/
function views_node_selector_process($element, $edit) {
$view = $element['#view'];
$view_id = _views_bulk_operations_view_id($view);
$view_name = $view->name;
// Gather options.
$result = $view->style_plugin->options['preserve_selection'] ? $_SESSION['vbo_values'][$view_name][$view_id]['result'] : $view->style_plugin->result;
$options = array();
foreach ($result as $k => $v) {
$options[$k] = '';
}
// Fix default value.
$element['#default_value'] += array(
'selection' => array(),
'selectall' => FALSE,
);
$element['#default_value']['selection'] = $element['#default_value']['selectall'] ? array_diff_key($options, array_filter($element['#default_value']['selection'], '_views_bulk_operations_filter_invert')) : array_intersect_key($options, array_filter($element['#default_value']['selection']));
// Create selection FAPI elements.
$element['#tree'] = TRUE;
$element['selection'] = array(
'#options' => $options,
'#value' => $element['#default_value']['selection'],
'#attributes' => array(
'class' => 'select',
),
);
$element['selection'] = expand_checkboxes($element['selection']);
$element['selectall'] = array(
'#type' => 'hidden',
'#default_value' => $element['#default_value']['selectall'],
);
return $element;
}
/**
* Implementation of hook_theme().
*/
function views_bulk_operations_theme() {
$themes = array(
'views_node_selector' => array(
'arguments' => array(
'element' => NULL,
),
),
'views_bulk_operations_confirmation' => array(
'arguments' => array(
'objects' => NULL,
'invert' => FALSE,
'view' => NULL,
),
),
'views_bulk_operations_select_all' => array(
'arguments' => array(
'colspan' => 0,
'selection' => 0,
'view' => NULL,
),
),
'views_bulk_operations_table' => array(
'arguments' => array(
'header' => array(),
'rows' => array(),
'attributes' => array(),
'title' => NULL,
'view' => NULL,
),
'pattern' => 'views_bulk_operations_table__',
),
);
// Load action theme files.
foreach (_views_bulk_operations_load_actions() as $file) {
$action_theme_fn = 'views_bulk_operations_' . $file . '_action_theme';
if (function_exists($action_theme_fn)) {
$themes += call_user_func($action_theme_fn);
}
}
return $themes;
}
/**
* Theme function for 'views_bulk_operations_table'.
*/
function theme_views_bulk_operations_table($header, $rows, $attributes, $title, $view) {
return theme('table', $header, $rows, $attributes, $title);
}
/**
* Template preprocessor for theme function 'views_bulk_operations_table'.
*/
function template_preprocess_views_bulk_operations_table(&$vars, $hook) {
$view = $vars['view'];
$options = $view->style_plugin->options;
$handler = $view->style_plugin;
$fields =& $view->field;
$columns = $handler
->sanitize_columns($options['columns'], $fields);
$active = !empty($handler->active) ? $handler->active : '';
foreach ($columns as $field => $column) {
$vars['fields'][$field] = views_css_safe($field);
if ($active == $field) {
$vars['fields'][$field] .= ' active';
}
}
$count = 0;
foreach ($vars['rows'] as $r => &$row) {
$vars['row_classes'][$r][] = $count++ % 2 == 0 ? 'odd' : 'even';
$cells = $row;
if (isset($row['class'])) {
$vars['row_classes'][$r][] = $row['class'];
}
if (isset($row['data'])) {
$cells = $row['data'];
}
foreach ($cells as $c => &$cell) {
if (is_array($cell) && isset($cell['data'])) {
$cell = $cell['data'];
}
}
$row = $cells;
}
$vars['row_classes'][0][] = 'views-row-first';
$vars['row_classes'][count($vars['row_classes']) - 1][] = 'views-row-last';
$vars['class'] = 'views-bulk-operations-table';
if ($view->style_plugin->options['sticky']) {
drupal_add_js('misc/tableheader.js');
$vars['class'] .= ' sticky-enabled';
}
$vars['class'] .= ' cols-' . count($vars['rows']);
$vars['class'] .= ' views-table';
}
/**
* Theme function for 'views_node_selector'.
*/
function theme_views_node_selector($element) {
module_load_include('inc', 'views', 'theme/theme');
$output = '';
$view = $element['#view'];
$sets = $element['#sets'];
$vars = array(
'view' => $view,
);
// Give each group its own headers row.
foreach ($sets as $title => $records) {
$headers = array();
// template_preprocess_views_view_table() expects the raw data in 'rows'.
$vars['rows'] = $records;
// Render the view as table. Use the hook from from views/theme/theme.inc
// and allow overrides using the same algorithm as the theme system will
// do calling the theme() function.
$hook = 'views_view_table';
$hooks = theme_get_registry();
if (!isset($hooks[$hook])) {
return '';
}
$args = array(
&$vars,
$hook,
);
foreach ($hooks[$hook]['preprocess functions'] as $func) {
if (function_exists($func)) {
call_user_func_array($func, $args);
}
}
// Add checkboxes to the header and the rows.
$rows = array();
if (empty($view->style_plugin->options['hide_selector'])) {
$headers[] = array(
'class' => 'vbo-select-all',
);
// Add extra status row if needed.
$items_per_page = method_exists($view, 'get_items_per_page') ? $view
->get_items_per_page() : (isset($view->pager) ? $view->pager['items_per_page'] : 0);
if ($items_per_page && $view->total_rows > $items_per_page) {
$row = theme('views_bulk_operations_select_all', count($vars['header']) + 1, _views_bulk_operations_get_selection_count($view->style_plugin, $element['#default_value']), $view);
$rows[] = $row;
}
}
else {
$headers[] = array(
'class' => 'no-select-all',
);
}
foreach ($vars['header'] as $field => $label) {
$headers[] = array(
'data' => $label,
'class' => "views-field views-field-{$vars['fields'][$field]}",
);
}
foreach ($records as $num => $object) {
$vars['row_classes'][$num][] = 'rowclick';
$row = array(
'class' => implode(' ', $vars['row_classes'][$num]),
'data' => array(),
);
$row['data'][] = theme('checkbox', $element['selection'][_views_bulk_operations_hash_object($object, $view)]);
foreach ($vars['rows'][$num] as $field => $content) {
// Support field classes in Views 3, but provide a fallback for Views 2.
if (views_api_version() == 2) {
$row['data'][] = array(
'data' => $content,
'class' => 'views-field views-field-' . $vars['fields'][$field],
);
}
else {
$row['data'][] = array(
'data' => $content,
'class' => $vars['field_classes'][$field][$num],
);
}
}
$rows[] = $row;
}
$theme_functions = views_theme_functions('views_bulk_operations_table', $view, $view->display[$view->current_display]);
$output .= theme($theme_functions, $headers, $rows, array(
'class' => $vars['class'],
), $title, $view);
$output .= theme('hidden', $element['selectall']);
}
return theme('form_element', $element, $output);
}
/**
* Theme function for 'views_bulk_operations_select_all'.
*/
function theme_views_bulk_operations_select_all($colspan, $selection, $view) {
$clear_selection = t('Clear selection');
$select_label = t('Select all items:');
$this_page = t('on this page only');
$all_pages = t('across all pages');
$this_page_checked = $selection['selectall'] ? '' : ' checked="checked"';
$all_pages_checked = $selection['selectall'] ? ' checked="checked"' : '';
$selection_count = t('<span class="selected">@selected</span> items selected.', array(
'@selected' => $selection['selected'],
));
$output = <<<EOF
{<span class="php-variable">$selection_count</span>}
<span class="select">
{<span class="php-variable">$select_label</span>}
<input type="radio" name="select-all" id="select-this-page" value="0"{<span class="php-variable">$this_page_checked</span>} /><label for="select-this-page">{<span class="php-variable">$this_page</span>}</label>
<input type="radio" name="select-all" id="select-all-pages" value="1"{<span class="php-variable">$all_pages_checked</span>} /><label for="select-all-pages">{<span class="php-variable">$all_pages</span>}</label>
</span>
<input type="button" id="clear-selection" value="{<span class="php-variable">$clear_selection</span>}" />
EOF;
return array(
array(
'data' => $output,
'class' => 'views-field views-field-select-all',
'colspan' => $colspan,
),
);
}
/**
* Form implementation for main VBO multistep form.
*/
function views_bulk_operations_form($form_state, $form_id, $plugin) {
// Erase the form parameters from $_REQUEST for a clean pager.
if (!empty($form_state['post'])) {
$_REQUEST = array_diff($_REQUEST, $form_state['post']);
}
// Force browser to reload the page if Back is hit.
if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('/msie/i', $_SERVER['HTTP_USER_AGENT'])) {
drupal_set_header("Cache-Control: no-cache");
// works for IE6+
}
else {
drupal_set_header("Cache-Control: no-store");
// works for Firefox and other browsers
}
// Which step is this?
if (empty($form_state['storage']['step'])) {
// If empty view, render the empty text.
if (empty($plugin->view->result)) {
$form['empty'] = array(
'#value' => $plugin->view->display_handler
->render_empty(),
);
return $form;
}
// If there's a session variable on this view, pre-load the old values.
$view_id = _views_bulk_operations_view_id($plugin->view);
$view_name = $plugin->view->name;
if (isset($_SESSION['vbo_values'][$view_name][$view_id]) && $plugin->options['preserve_selection']) {
// Avoid PHP warnings.
$_SESSION['vbo_values'][$view_name][$view_id] += array(
'selection' => array(),
'selectall' => FALSE,
'operation' => NULL,
);
$default_objects = array(
'selection' => $_SESSION['vbo_values'][$view_name][$view_id]['selection'],
'selectall' => $_SESSION['vbo_values'][$view_name][$view_id]['selectall'],
);
$default_operation = $_SESSION['vbo_values'][$view_name][$view_id]['operation'];
}
else {
$default_objects = array(
'selection' => array(),
'selectall' => FALSE,
);
$default_operation = NULL;
}
if (count($plugin
->get_selected_operations()) == 1 && $plugin->options['merge_single_action']) {
$step = VBO_STEP_SINGLE;
}
else {
$step = VBO_STEP_VIEW;
}
}
else {
_views_bulk_operations_strip_view($plugin->view);
switch ($form_state['storage']['step']) {
case VBO_STEP_VIEW:
$operation = $form_state['storage']['operation'];
if ($operation['configurable']) {
$step = VBO_STEP_CONFIG;
}
else {
$step = VBO_STEP_CONFIRM;
}
break;
case VBO_STEP_SINGLE:
case VBO_STEP_CONFIG:
$step = VBO_STEP_CONFIRM;
break;
}
}
$form['step'] = array(
'#type' => 'value',
'#value' => $step,
);
$form['#plugin'] = $plugin;
switch ($step) {
case VBO_STEP_VIEW:
$form['select'] = array(
'#type' => 'fieldset',
'#title' => t('Bulk operations'),
'#prefix' => '<div id="views-bulk-operations-select">',
'#suffix' => '</div>',
);
$form['objects'] = array(
'#type' => 'views_node_selector',
'#view' => $plugin->view,
'#sets' => $plugin->sets,
'#default_value' => $default_objects,
'#prefix' => '<div class="views-node-selector">',
'#suffix' => '</div>',
);
if ($plugin->options['display_type'] == 0) {
// Create dropdown and submit button.
$form['select']['operation'] = array(
'#type' => 'select',
'#options' => array(
0 => t('- Choose an operation -'),
) + $plugin
->get_selected_operations(),
'#default_value' => $default_operation,
'#prefix' => '<div id="views-bulk-operations-dropdown">',
'#suffix' => '</div>',
);
$form['select']['submit'] = array(
'#type' => 'submit',
'#value' => t('Execute'),
'#prefix' => '<div id="views-bulk-operations-submit">',
'#suffix' => '</div>',
);
}
else {
// Create buttons for actions.
foreach ($plugin
->get_selected_operations() as $md5 => $description) {
$form['select'][$md5] = array(
'#type' => 'submit',
'#value' => $description,
'#hash' => $md5,
);
}
}
break;
case VBO_STEP_SINGLE:
$operation_keys = array_keys($plugin
->get_selected_operations());
$operation = $plugin
->get_operation_info($operation_keys[0]);
$form['operation'] = array(
'#type' => 'value',
'#value' => $operation_keys[0],
);
if ($operation['configurable']) {
$form += _views_bulk_operations_action_form($operation, $plugin->view, $plugin->result, $operation['options']['settings']);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => $operation['label'],
'#prefix' => '<div id="views-bulk-operations-submit">',
'#suffix' => '</div>',
);
$form['objects'] = array(
'#type' => 'views_node_selector',
'#view' => $plugin->view,
'#sets' => $plugin->sets,
'#default_value' => $default_objects,
'#prefix' => '<div class="views-node-selector">',
'#suffix' => '</div>',
);
break;
case VBO_STEP_CONFIG:
$operation = $form_state['storage']['operation'];
$form += _views_bulk_operations_action_form($operation, $plugin->view, _views_bulk_operations_get_selection_full($plugin, $form_state), $operation['options']['settings']);
$form['execute'] = array(
'#type' => 'submit',
'#value' => t('Next'),
'#weight' => 98,
);
$query = drupal_query_string_encode($_GET, array(
'q',
));
$form['cancel'] = array(
'#type' => 'markup',
'#value' => l('Cancel', $_GET['q'], array(
'query' => $query,
)),
'#weight' => 99,
);
drupal_set_title(t('Set parameters for %operation', array(
'%operation' => $operation['label'],
)));
break;
case VBO_STEP_CONFIRM:
$operation = $form_state['storage']['operation'];
$query = drupal_query_string_encode($_GET, array(
'q',
));
$form = confirm_form($form, t('Are you sure you want to perform %operation on the selected items?', array(
'%operation' => $operation['label'],
)), array(
'path' => $_GET['q'],
'query' => $query,
), theme('views_bulk_operations_confirmation', $form_state['storage']['selection'], $form_state['storage']['selectall'], $plugin->view));
break;
}
// Use views_bulk_operations_form_submit() for form submit, regardless of form_id.
$form['#submit'][] = 'views_bulk_operations_form_submit';
$form['#validate'][] = 'views_bulk_operations_form_validate';
$form['#attributes']['class'] = 'views-bulk-operations-form views-bulk-operations-form-step-' . $step;
// A view with ajax enabled, and a space in the url, has to be decoded to work fine,
// @see http://drupal.org/node/1325632
$form['#action'] = urldecode(request_uri());
return $form;
}
/**
* Implementation of hook_form_alter().
*/
function views_bulk_operations_form_alter(&$form, &$form_state) {
// Get the form ID here to add the JS settings.
if (!empty($form['form_id']) && strpos($form['form_id']['#value'], 'views_bulk_operations_form') === 0 && !empty($form['#plugin'])) {
_views_bulk_operations_add_js($form['#plugin'], $form['#id'], $form['form_id']['#value']);
}
}
/**
* Form validate function for views_bulk_operations_form().
*/
function views_bulk_operations_form_validate($form, &$form_state) {
$form_id = $form_state['values']['form_id'];
$plugin = $form['#plugin'];
$view_id = _views_bulk_operations_view_id($plugin->view);
$view_name = $plugin->view->name;
switch ($form_state['values']['step']) {
case VBO_STEP_VIEW:
if (!array_filter($form_state['values']['objects']['selection']) && (empty($_SESSION['vbo_values'][$view_name][$view_id]) || !array_filter($_SESSION['vbo_values'][$view_name][$view_id]['selection']))) {
form_set_error('objects', t('No item selected. Please select one or more items.'));
}
if (!empty($form_state['clicked_button']['#hash'])) {
$form_state['values']['operation'] = $form_state['clicked_button']['#hash'];
}
if (!$form_state['values']['operation']) {
// No action selected
form_set_error('operation', t('No operation selected. Please select an operation to perform.'));
}
if (form_get_errors()) {
_views_bulk_operations_add_js($plugin, $form['#id'], $form_id);
}
break;
case VBO_STEP_SINGLE:
if (!array_filter($form_state['values']['objects']['selection']) && (empty($_SESSION['vbo_values'][$view_name][$view_id]) || !array_filter($_SESSION['vbo_values'][$view_name][$view_id]['selection']))) {
form_set_error('objects', t('No item selected. Please select one or more items.'));
}
$operation = $plugin
->get_operation_info($form_state['values']['operation']);
if ($operation['configurable']) {
_views_bulk_operations_action_validate($operation, $form, $form_state);
}
if (form_get_errors()) {
_views_bulk_operations_add_js($plugin, $form['#id'], $form_id);
}
break;
case VBO_STEP_CONFIG:
$operation = $form_state['storage']['operation'];
_views_bulk_operations_action_validate($operation, $form, $form_state);
// If the action validation fails, Form API will bring us back to this step.
// We need to strip the view here because the form function will not be called.
// Also, the $plugin variable above was carried over from last submission, so it
// does not represent the current instance of the plugin.
// That's why we had to store instances of the plugin in this global array.
if (form_get_errors()) {
global $vbo_plugins;
if (isset($vbo_plugins[$form_id])) {
_views_bulk_operations_strip_view($vbo_plugins[$form_id]->view);
}
}
break;
}
}
/**
* Form submit function for views_bulk_operations_form().
*/
function views_bulk_operations_form_submit($form, &$form_state) {
$form_id = $form_state['values']['form_id'];
$plugin = $form['#plugin'];
$view = $plugin->view;
$view_id = _views_bulk_operations_view_id($view);
$view_name = $view->name;
$form_state['storage']['step'] = $step = $form_state['values']['step'];
switch ($step) {
case VBO_STEP_VIEW:
$form_state['storage']['selection'] = _views_bulk_operations_get_selection($plugin, $form_state, $form_id);
$form_state['storage']['selectall'] = $form_state['values']['objects']['selectall'];
$form_state['storage']['operation'] = $operation = $plugin
->get_operation_info($form_state['values']['operation']);
$_SESSION['vbo_values'][$view_name][$view_id]['operation'] = $operation['key'];
if (!$operation['configurable'] && !empty($operation['options']['skip_confirmation'])) {
break;
// Go directly to execution
}
return;
case VBO_STEP_SINGLE:
$form_state['storage']['selection'] = _views_bulk_operations_get_selection($plugin, $form_state, $form_id);
$form_state['storage']['selectall'] = $form_state['values']['objects']['selectall'];
$form_state['storage']['operation'] = $operation = $plugin
->get_operation_info($form_state['values']['operation']);
$_SESSION['vbo_values'][$view_name][$view_id]['operation'] = $operation['key'];
if ($operation['configurable']) {
$form_state['storage']['operation_arguments'] = _views_bulk_operations_action_submit($operation, $form, $form_state);
}
if (!empty($operation['options']['skip_confirmation'])) {
break;
// Go directly to execution
}
return;
case VBO_STEP_CONFIG:
$operation = $form_state['storage']['operation'];
$form_state['storage']['operation_arguments'] = _views_bulk_operations_action_submit($operation, $form, $form_state);
if (!empty($operation['options']['skip_confirmation'])) {
break;
// Go directly to execution
}
return;
case VBO_STEP_CONFIRM:
break;
}
// Clean up unneeded SESSION variables.
unset($_SESSION['vbo_values'][$view->name]);
// Execute the VBO.
$objects = _views_bulk_operations_get_selection_full($plugin, $form_state);
$operation = $form_state['storage']['operation'];
$operation_arguments = array();
if ($operation['configurable']) {
$operation_arguments = $form_state['storage']['operation_arguments'];
}
_views_bulk_operations_execute($view, $objects, $operation, $operation_arguments, array(
'execution_type' => $plugin->options['execution_type'],
'display_result' => $plugin->options['display_result'],
'max_performance' => $plugin->options['max_performance'],
'settings' => $operation['options']['settings'],
));
// Clean up the form.
$query = drupal_query_string_encode($_GET, array(
'q',
));
$form_state['redirect'] = array(
'path' => $view
->get_url(),
'query' => $query,
);
unset($form_state['storage']);
}
/**
* Compute the selection based on the settings.
*/
function _views_bulk_operations_get_selection($plugin, $form_state, $form_id) {
$result = $plugin->result;
$selection = $form_state['values']['objects']['selection'];
if ($plugin->options['preserve_selection']) {
$view_id = _views_bulk_operations_view_id($plugin->view);
$view_name = $plugin->view->name;
$result = $_SESSION['vbo_values'][$view_name][$view_id]['result'];
$selection = $_SESSION['vbo_values'][$view_name][$view_id]['selection'];
}
$selection = $form_state['values']['objects']['selectall'] ? array_intersect_key($result, array_filter($selection, '_views_bulk_operations_filter_invert')) : array_intersect_key($result, array_filter($selection));
return $selection;
}
/**
* Compute the actual selected objects based on the settings.
*/
function _views_bulk_operations_get_selection_full($plugin, $form_state) {
// Get the objects from the view if selectall was chosen.
$view = $plugin->view;
if ($form_state['storage']['selectall']) {
$view_copy = views_get_view($view->name);
$view_copy
->set_exposed_input($view->exposed_input);
$view_copy
->set_arguments($view->args);
$view_copy
->set_items_per_page(0);
$view_copy->skip_render = TRUE;
// signal our plugin to skip the rendering
$view_copy
->render($view->current_display);
$objects = array();
foreach ($view_copy->result as $row) {
$objects[_views_bulk_operations_hash_object($row, $view_copy)] = $row;
}
$view_copy
->destroy();
$objects = array_diff_key($objects, $form_state['storage']['selection']);
}
else {
$objects = $form_state['storage']['selection'];
}
return $objects;
}
/**
* Compute the actual number of selected items.
*/
function _views_bulk_operations_get_selection_count($plugin, $selection) {
if ($plugin->options['preserve_selection']) {
$view_id = _views_bulk_operations_view_id($plugin->view);
$view_name = $plugin->view->name;
$selection = $_SESSION['vbo_values'][$view_name][$view_id];
}
return array(
'selectall' => $selection['selectall'],
'selected' => $selection['selectall'] ? $plugin->view->total_rows - count(array_filter($selection['selection'], '_views_bulk_operations_filter_invert')) : count(array_filter($selection['selection'])),
);
}
/**
* Theme function to show the confirmation page before executing the action.
*/
function theme_views_bulk_operations_confirmation($objects, $invert, $view) {
$selectall = $invert ? count($objects) == 0 : count($objects) == $view->total_rows;
if ($selectall) {
$output = format_plural($view->total_rows, 'You selected the only item in this view.', 'You selected all <strong>@count</strong> items in this view.');
}
else {
$object_info = _views_bulk_operations_object_info_for_view($view);
$items = array();
foreach ($objects as $row) {
$oid = $row->{$view->base_field};
if ($object = call_user_func($object_info['load'], $oid)) {
$items[] = check_plain((string) $object->{$object_info['title']});
}
}
$output = theme('item_list', $items, $invert ? format_plural(count($objects), 'You selected all ' . $view->total_rows . ' but the following item:', 'You selected all ' . $view->total_rows . ' but the following <strong>@count</strong> items:') : format_plural(count($objects), 'You selected the following item:', 'You selected the following <strong>@count</strong> items:'));
}
return $output;
}
/**
* Implementation of hook_forms().
*
* Force each instance of function to use the same callback.
*/
function views_bulk_operations_forms($form_id, $args) {
// Ensure we map a callback for our form and not something else.
$forms = array();
if (strpos($form_id, 'views_bulk_operations_form_') === 0) {
// Let the forms API know where to get the form data corresponding
// to this form id.
$forms[$form_id] = array(
'callback' => 'views_bulk_operations_form',
'callback arguments' => array(
$form_id,
),
);
}
return $forms;
}
/**
* Implementation of hook_views_bulk_operations_object_info()
*
* Hook used by VBO to be able to handle different objects as does Views 2 and the Drupal core action system.
*
* The array returned for each object type contains:
* 'type' (required) => the object type name, should be the same as 'type' field in hook_action_info().
* 'context' (optional) => the context name that should receive the object, defaults to the value of 'type' above.
* 'base_table' (required) => the Views 2 table name corresponding to that object type, should be the same as the $view->base_table attribute.
* 'oid' (currently unused) => an attribute on the object that returns the unique object identifier (should be the same as $view->base_field).
* 'load' (required) => a function($oid) that returns the corresponding object.
* 'title' (required) => an attribute on the object that returns a human-friendly identifier of the object.
* 'access' (optional) => a function($op, $node, $account = NULL) that behaves like node_access().
*
* The following attributes allow VBO to show actions on view types different than the action's type:
* 'hook' (optional) => the name of the hook supported by this object type, as defined in the 'hooks' attribute of hook_action_info().
* 'normalize' (optional) => a function($type, $object) that takes an object type and the object instance, returning additional context information for cross-type
*
* e.g., an action declaring hooks => array('user') while of type 'system' will be shown on user views, and VBO will call the user's 'normalize' function to
* prepare the action to fit the user context.
*/
function views_bulk_operations_views_bulk_operations_object_info() {
$object_info = array(
'node' => array(
'type' => 'node',
'base_table' => 'node',
'load' => '_views_bulk_operations_node_load',
'oid' => 'nid',
'title' => 'title',
'access' => 'node_access',
'hook' => 'nodeapi',
'normalize' => '_views_bulk_operations_normalize_node_context',
),
'user' => array(
'type' => 'user',
'base_table' => 'users',
'load' => 'user_load',
'oid' => 'uid',
'title' => 'name',
'context' => 'account',
'access' => '_views_bulk_operations_user_access',
'hook' => 'user',
'normalize' => '_views_bulk_operations_normalize_user_context',
),
'comment' => array(
'type' => 'comment',
'base_table' => 'comments',
'load' => '_comment_load',
'oid' => 'cid',
'title' => 'subject',
'access' => '_views_bulk_operations_comment_access',
'hook' => 'comment',
'normalize' => '_views_bulk_operations_normalize_comment_context',
),
'term' => array(
'type' => 'term',
'base_table' => 'term_data',
'load' => 'taxonomy_get_term',
'oid' => 'tid',
'title' => 'name',
'hook' => 'taxonomy',
),
'node_revision' => array(
'type' => 'node_revision',
'base_table' => 'node_revisions',
'load' => '_views_bulk_operations_node_revision_load',
'title' => 'name',
),
'file' => array(
'type' => 'file',
'base_table' => 'files',
'load' => '_views_bulk_operations_file_load',
'oid' => 'fid',
'title' => 'filename',
),
);
return $object_info;
}
/**
* Access function for objects of type 'user'.
*/
function _views_bulk_operations_user_access($op, $user, $account = NULL) {
return user_access('access user profiles', $account);
}
/**
* Access function for objects of type 'comments'.
*/
function _views_bulk_operations_comment_access($op, $comment, $account = NULL) {
return user_access('access comments', $account);
}
/**
* Load function for objects of type 'node'.
*/
function _views_bulk_operations_node_load($nid) {
return node_load($nid, NULL, TRUE);
}
/**
* Load function for objects of type 'file'.
*/
function _views_bulk_operations_file_load($fid) {
return db_fetch_object(db_query("SELECT * FROM {files} WHERE fid = %d", $fid));
}
/**
* Load function for node revisions.
*/
function _views_bulk_operations_node_revision_load($vid) {
$nid = db_result(db_query("SELECT nid FROM {node_revisions} WHERE vid = %d", $vid));
return node_load($nid, $vid, TRUE);
}
/**
* Normalize function for node context.
*
* @see _trigger_normalize_node_context()
*/
function _views_bulk_operations_normalize_node_context($type, $node) {
switch ($type) {
// If an action that works on comments is being called in a node context,
// the action is expecting a comment object. But we do not know which comment
// to give it. The first? The most recent? All of them? So comment actions
// in a node context are not supported.
// An action that works on users is being called in a node context.
// Load the user object of the node's author.
case 'user':
return user_load(array(
'uid' => $node->uid,
));
}
}
/**
* Normalize function for comment context.
*
* @see _trigger_normalize_comment_context()
*/
function _views_bulk_operations_normalize_comment_context($type, $comment) {
switch ($type) {
// An action that works with nodes is being called in a comment context.
case 'node':
return node_load(is_array($comment) ? $comment['nid'] : $comment->nid);
// An action that works on users is being called in a comment context.
case 'user':
return user_load(array(
'uid' => is_array($comment) ? $comment['uid'] : $comment->uid,
));
}
}
/**
* Normalize function for user context.
*
* @see _trigger_normalize_user_context()
*/
function _views_bulk_operations_normalize_user_context($type, $account) {
switch ($type) {
// If an action that works on comments is being called in a user context,
// the action is expecting a comment object. But we have no way of
// determining the appropriate comment object to pass. So comment
// actions in a user context are not supported.
// An action that works with nodes is being called in a user context.
// If a single node is being viewed, return the node.
case 'node':
// If we are viewing an individual node, return the node.
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == NULL) {
return node_load(array(
'nid' => arg(1),
));
}
}
}
/**
* Implementation of hook_init().
*/
function views_bulk_operations_init() {
// Make sure our actions are loaded.
_views_bulk_operations_load_actions();
}
/**
* Implementation of hook_action_info().
*/
function views_bulk_operations_action_info() {
$actions = array();
foreach (_views_bulk_operations_load_actions() as $file) {
$action_info_fn = 'views_bulk_operations_' . $file . '_action_info';
$action_info = call_user_func($action_info_fn);
if (is_array($action_info)) {
$actions += $action_info;
}
}
// Add VBO's own programmatic action.
$actions['views_bulk_operations_action'] = array(
'description' => t('Execute a VBO programmatically'),
'type' => 'system',
'configurable' => TRUE,
'rules_ignore' => TRUE,
);
return $actions;
}
/**
* Implementation of hook_menu().
*/
function views_bulk_operations_menu() {
$items['views-bulk-operations/js/action'] = array(
'title' => 'VBO action form',
'description' => 'AHAH callback to display action form on VBO action page.',
'page callback' => 'views_bulk_operations_form_ahah',
'page arguments' => array(
'views_bulk_operations_action_form_operation',
),
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
$items['views-bulk-operations/js/select'] = array(
'title' => 'VBO select handler',
'description' => 'AJAX callback to update selection.',
'page callback' => 'views_bulk_operations_select',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* AJAX callback to update selection.
*/
function views_bulk_operations_select() {
$view_id = $_REQUEST['view_id'];
$view_name = $_REQUEST['view_name'];
foreach (json_decode($_REQUEST['selection'], TRUE) as $selection => $value) {
switch ($selection) {
case 'operation':
$_SESSION['vbo_values'][$view_name][$view_id]['operation'] = $value;
break;
case 'selectall':
$_SESSION['vbo_values'][$view_name][$view_id]['selectall'] = $value > 0;
if ($value == -1) {
// -1 => reset selection
$_SESSION['vbo_values'][$view_name][$view_id]['selection'] = array();
}
break;
default:
$_SESSION['vbo_values'][$view_name][$view_id]['selection'][$selection] = $value > 0;
break;
}
}
drupal_json(array(
'selected' => count(array_filter($_SESSION['vbo_values'][$view_name][$view_id]['selection'])),
'unselected' => count(array_filter($_SESSION['vbo_values'][$view_name][$view_id]['selection'], '_views_bulk_operations_filter_invert')),
'selectall' => $_SESSION['vbo_values'][$view_name][$view_id]['selectall'],
));
exit;
}
/**
* Form function for views_bulk_operations_action action.
*/
function views_bulk_operations_action_form($context) {
// Some views choke on being rebuilt at this moment because of validation errors in the action form.
// So we save the error state, reset it, build the views, then reinstate the errors.
// Also unset the error messages because they'll be displayed again after the loop.
$errors = form_get_errors();
if (!empty($errors)) {
foreach ($errors as $message) {
unset($_SESSION['messages']['error'][array_search($message, $_SESSION['messages']['error'])]);
}
}
form_set_error(NULL, '', TRUE);
// Look for all views with VBO styles, and for each find the operations they use.
// Distinguish between overridden and default views to simplify export.
$views[0] = t('- Choose a view -');
$operations[0] = t('- Choose an operation -');
foreach (views_get_all_views() as $name => $view) {
foreach (array_keys($view->display) as $display) {
$display_options =& $view->display[$display]->display_options;
if (isset($display_options['style_plugin']) && $display_options['style_plugin'] == 'bulk') {
$vid = $view->name;
$views[$vid] = $view->name . (!empty($view->description) ? ': ' . $view->description : '');
$view_clone = clone $view;
$style_plugin = views_get_plugin('style', $display_options['style_plugin']);
$style_plugin
->init($view_clone, $view_clone->display[$display], $display_options['style_options']);
if (isset($context['view_vid']) && $vid == $context['view_vid']) {
$form['#plugin'] = $style_plugin;
}
unset($view_clone);
if (!empty($display_options['style_options']['operations'])) {
foreach ($display_options['style_options']['operations'] as $key => $options) {
if (empty($options['selected'])) {
continue;
}
$operations[$key] = $views_operations[$vid][$key] = $style_plugin->all_operations[$key]['label'];
if (isset($context['operation_key']) && isset($context['view_vid']) && $key == $context['operation_key'] && $vid == $context['view_vid']) {
$form['#operation'] = $style_plugin
->get_operation_info($key);
}
}
}
}
}
}
if (!empty($errors)) {
foreach ($errors as $name => $message) {
form_set_error($name, $message);
}
}
drupal_add_js(array(
'vbo' => array(
'action' => array(
'views_operations' => $views_operations,
),
),
), 'setting');
drupal_add_js(drupal_get_path('module', 'views_bulk_operations') . '/js/views_bulk_operations.action.js');
$form['view_vid'] = array(
'#type' => 'select',
'#title' => t('View'),
'#description' => t('Select the VBO to be executed.'),
'#options' => $views,
'#default_value' => @$context['view_vid'],
'#attributes' => array(
'onchange' => 'Drupal.vbo.action.updateOperations(this.options[this.selectedIndex].value, true);',
),
);
$form['operation_key'] = array(
'#type' => 'select',
'#title' => t('Operation'),
'#description' => t('Select the operation to be executed.'),
'#options' => $operations,
'#default_value' => @$context['operation_key'],
'#ahah' => array(
'path' => 'views-bulk-operations/js/action',
'wrapper' => 'operation-wrapper',
'method' => 'replace',
),
);
$form['operation_arguments'] = array(
'#type' => 'fieldset',
'#title' => t('Operation arguments'),
'#description' => t('If the selected action is configurable, this section will show the action\'s arguments form,
followed by a text field where a PHP script can be entered to programmatically assemble the arguments.
'),
);
$form['operation_arguments']['wrapper'] = array(
'#type' => 'markup',
'#value' => '',
'#prefix' => '<div id="operation-wrapper">',
'#suffix' => '</div>',
);
if (isset($form['#operation']) && $form['#operation']['configurable'] && isset($form['#plugin'])) {
$form['operation_arguments']['wrapper']['operation_form'] = _views_bulk_operations_action_form($form['#operation'], $form['#plugin']->view, NULL, $form['#operation']['options']['settings'], $context);
if (!empty($form['#operation']['form properties'])) {
foreach ($form['#operation']['form properties'] as $property) {
if (isset($form['operation_arguments']['wrapper']['operation_form'][$property])) {
$form[$property] = $form['operation_arguments']['wrapper']['operation_form'][$property];
}
}
}
$form['operation_arguments']['wrapper']['operation_arguments'] = array(
'#type' => 'textarea',
'#title' => t('Operation arguments'),
'#description' => t('Enter PHP script that will assemble the operation arguments (and will override the arguments above).
These arguments should be of the form: <code>return array(\'argument1\' => \'value1\', ...);</code>
and they should correspond to the values returned by the action\'s form submit function.
The variables <code>&$object</code> and <code>$context</code> are available to this script.
'),
'#default_value' => @$context['operation_arguments'],
);
}
else {
$form['operation_arguments']['wrapper']['operation_form'] = array(
'#type' => 'markup',
'#value' => t('This operation is not configurable.'),
);
$form['operation_arguments']['wrapper']['operation_arguments'] = array(
'#type' => 'value',
'#value' => '',
);
}
$form['view_exposed_input'] = array(
'#type' => 'textarea',
'#title' => t('View exposed input'),
'#description' => t('Enter PHP script that will assemble the view exposed input (if the view accepts exposed input).
These inputs should be of the form: <code>return array(\'input1\' => \'value1\', ...);</code>
and they should correspond to the query values used on the view URL when exposed filters are applied.
The variables <code>&$object</code> and <code>$context</code> are available to this script.
'),
'#default_value' => @$context['view_exposed_input'],
);
$form['view_arguments'] = array(
'#type' => 'textarea',
'#title' => t('View arguments'),
'#description' => t('Enter PHP script that will assemble the view arguments (if the view accepts arguments).
These arguments should be of the form: <code>return array(\'value1\', ...);</code>
and they should correspond to the arguments defined in the view.
The variables <code>&$object</code> and <code>$context</code> are available to this script.
'),
'#default_value' => @$context['view_arguments'],
);
$form['respect_limit'] = array(
'#type' => 'checkbox',
'#title' => t('Respect the view\'s item limit'),
'#default_value' => @$context['respect_limit'],
);
return $form;
}
/**
* Generic AHAH callback to manipulate a form.
*/
function views_bulk_operations_form_ahah($callback) {
$form_state = array(
'submitted' => FALSE,
);
$form_build_id = $_POST['form_build_id'];
// Add the new element to the stored form. Without adding the element to the
// form, Drupal is not aware of this new elements existence and will not
// process it. We retreive the cached form, add the element, and resave.
$form = form_get_cache($form_build_id, $form_state);
// Invoke the callback that will populate the form.
$render =& $callback($form, array(
'values' => $_POST,
));
form_set_cache($form_build_id, $form, $form_state);
$form += array(
'#post' => $_POST,
'#programmed' => FALSE,
);
// Rebuild the form.
$form = form_builder($_POST['form_id'], $form, $form_state);
// Render the new output.
$output_html = drupal_render($render);
$output_js = drupal_get_js();
print drupal_to_js(array(
'data' => theme('status_messages') . $output_html . $output_js,
'status' => TRUE,
));
exit;
}
/**
* Form callback to update an action form when a new action is selected in views_bulk_operations_action form.
*/
function &views_bulk_operations_action_form_operation(&$form, $form_state) {
// TODO: Replace this with autoloading of style plugin and view definitions to use $form['#plugin'].
$view = views_get_view($form_state['values']['view_vid']);
$vd = new views_bulk_operations_destructor($view);
// this will take care of calling $view->destroy() on exit.
foreach (array_keys($view->display) as $display) {
$display_options =& $view->display[$display]->display_options;
if (isset($display_options['style_plugin']) && $display_options['style_plugin'] == 'bulk') {
$plugin = views_get_plugin('style', $display_options['style_plugin']);
$plugin
->init($view, $view->display[$display], $display_options['style_options']);
break;
}
}
$form['#operation'] = $plugin
->get_operation_info($form_state['values']['operation_key']);
if ($form['#operation']['configurable']) {
$form['operation_arguments']['wrapper'] = array(
'#type' => 'markup',
'#value' => '',
'#prefix' => '<div id="operation-wrapper">',
'#suffix' => '</div>',
);
$form['operation_arguments']['wrapper']['operation_form'] = _views_bulk_operations_action_form($form['#operation'], $plugin->view, NULL, $form['#operation']['options']['settings']);
if (!empty($form['#operation']['form properties'])) {
foreach ($form['#operation']['form properties'] as $property) {
if (isset($form['operation_arguments']['wrapper']['operation_form'][$property])) {
$form[$property] = $form['operation_arguments']['wrapper']['operation_form'][$property];
}
}
}
$form['operation_arguments']['wrapper']['operation_arguments'] = array(
'#type' => 'textarea',
'#title' => t('Operation arguments'),
'#description' => t('Enter PHP script that will assemble the operation arguments (and will override the operation form above).
These arguments should be of the form: <code>return array(\'argument1\' => \'value1\', ...);</code>
and they should correspond to the values returned by the action\'s form submit function.
The variables <code>&$object</code> and <code>$context</code> are available to this script.
'),
);
}
else {
$form['operation_arguments']['wrapper']['operation_form'] = array(
'#type' => 'markup',
'#value' => t('This operation is not configurable.'),
);
$form['operation_arguments']['wrapper']['operation_arguments'] = array(
'#type' => 'value',
'#value' => '',
);
}
return $form['operation_arguments']['wrapper'];
}
/**
* Form validate function for views_bulk_operations_action action.
*/
function views_bulk_operations_action_validate($form, $form_state) {
if (empty($form_state['values']['view_vid'])) {
form_set_error('view_vid', t('You must choose a view to be executed.'));
}
if (empty($form_state['values']['operation_key'])) {
form_set_error('operation_callback', t('You must choose an operation to be executed.'));
}
if ($form['#operation']) {
module_invoke_all('action_info');
// some validate functions are created dynamically...
_views_bulk_operations_action_validate($form['#operation'], $form, $form_state);
}
}
/**
* Form submit function for views_bulk_operations_action action.
*/
function views_bulk_operations_action_submit($form, $form_state) {
$submit = array(
'view_vid' => $form_state['values']['view_vid'],
'operation_key' => $form_state['values']['operation_key'],
'operation_arguments' => $form_state['values']['operation_arguments'],
'view_exposed_input' => $form_state['values']['view_exposed_input'],
'view_arguments' => $form_state['values']['view_arguments'],
'respect_limit' => $form_state['values']['respect_limit'],
);
if ($form['#operation'] && function_exists($form['#operation']['callback'] . '_submit')) {
$submit = array_merge($submit, _views_bulk_operations_action_submit($form['#operation'], $form, $form_state));
}
return $submit;
}
/**
* Execution function for views_bulk_operations_action action.
*/
function views_bulk_operations_action(&$object, $context) {
$view_exposed_input = array();
if (!empty($context['view_exposed_input'])) {
$view_exposed_input = eval($context['view_exposed_input']);
}
$view_arguments = array();
if (!empty($context['view_arguments'])) {
$view_arguments = eval($context['view_arguments']);
}
if (!empty($context['operation_arguments'])) {
$operation_arguments = eval($context['operation_arguments']);
}
else {
$operation_arguments = $context;
foreach (array(
'operation_key',
'operation_arguments',
'views_vid',
'view_exposed_input',
'view_arguments',
) as $key) {
unset($operation_arguments[$key]);
}
}
views_bulk_operations_execute($context['view_vid'], $context['operation_key'], $operation_arguments, $view_exposed_input, $view_arguments, $context['respect_limit']);
}
/**
* Helper function to execute the chosen action upon selected objects.
*/
function _views_bulk_operations_execute($view, $objects, $operation, $operation_arguments, $options) {
global $user;
// Get the object info we're dealing with.
$object_info = _views_bulk_operations_object_info_for_view($view);
if (!$object_info) {
return;
}
// Add action arguments.
$params = array();
if ($operation['configurable'] && is_array($operation_arguments)) {
$params += $operation_arguments;
}
// Add static callback arguments. Note that in the case of actions, static arguments
// are picked up from the database in actions_do().
if (isset($operation['callback arguments'])) {
$params += $operation['callback arguments'];
}
// Add this view as parameter.
$params['view'] = array(
'vid' => !empty($view->vid) ? $view->vid : $view->name,
'exposed_input' => $view
->get_exposed_input(),
'arguments' => $view->args,
);
// Add static settings to the params.
if (!empty($options['settings'])) {
$params['settings'] = $options['settings'];
}
// Add object info to the params.
$params['object_info'] = $object_info;
if ($operation['aggregate'] != VBO_AGGREGATE_FORCED && $options['execution_type'] == VBO_EXECUTION_BATCH) {
// Save the options in the session because Batch API doesn't give a way to
// send a parameter to the finished callback.
$_SESSION['vbo_options']['display_result'] = $options['display_result'];
$_SESSION['vbo_options']['operation'] = $operation;
$_SESSION['vbo_options']['params'] = $params;
$_SESSION['vbo_options']['object_info'] = $object_info;
$batch = array(
'title' => t('Performing %operation on selected items...', array(
'%operation' => $operation['label'],
)),
'finished' => '_views_bulk_operations_execute_finished',
);
// If they have max performance checked, use the high performant batch process.
if ($options['max_performance']) {
$batch += array(
'operations' => array(
array(
'_views_bulk_operations_execute_multiple',
array(
$view->base_field,
$operation,
$objects,
$params,
$object_info,
TRUE,
),
),
),
);
}
else {
$operations = array();
foreach ($objects as $num => $row) {
$oid = $row->{$view->base_field};
$operations[] = array(
'_views_bulk_operations_execute_single',
array(
$oid,
$row,
),
);
}
$batch += array(
'operations' => $operations,
);
}
batch_set($batch);
}
else {
if ($operation['aggregate'] != VBO_AGGREGATE_FORCED && module_exists('drupal_queue') && $options['execution_type'] == VBO_EXECUTION_QUEUE) {
drupal_queue_include();
foreach ($objects as $row) {
$oid = $row->{$view->base_field};
$job = array(
'description' => t('Perform %operation on @type %oid.', array(
'%operation' => $operation['label'],
'@type' => t($object_info['type']),
'%oid' => $oid,
)),
'arguments' => array(
$oid,
$row,
$operation,
$params,
$user->uid,
$options['display_result'],
$object_info,
),
);
$queue = DrupalQueue::get('views_bulk_operations');
$queue
->createItem($job);
$oids[] = $oid;
}
if ($options['display_result']) {
drupal_set_message(t('Enqueued %operation on @types %oid.', array(
'%operation' => $operation['label'],
'@types' => format_plural(count($objects), $object_info['type'], $object_info['type'] . 's'),
'%oid' => implode(', ', $oids),
)));
}
}
else {
/*if ($options['execution_type'] == VBO_EXECUTION_DIRECT)*/
@set_time_limit(0);
$context['results']['rows'] = 0;
$context['results']['time'] = microtime(TRUE);
_views_bulk_operations_execute_multiple($view->base_field, $operation, $objects, $params, $object_info, FALSE, $context);
_views_bulk_operations_execute_finished(TRUE, $context['results'], array(), $options + array(
'operation' => $operation,
'params' => $params,
));
}
}
}
/**
* Helper function to handle Drupal Queue operations.
*/
function _views_bulk_operations_execute_queue($data) {
module_load_include('inc', 'node', 'node.admin');
list($oid, $row, $operation, $params, $uid, $display_result, $object_info) = $data['arguments'];
$object = call_user_func($object_info['load'], $oid);
if (!$object) {
watchdog('vbo', 'Skipped %operation on @type id %oid because it was not found.', array(
'%operation' => $operation['label'],
'@type' => t($operation['type']),
'%oid' => $oid,
), WATCHDOG_ALERT);
return;
}
$account = user_load(array(
'uid' => $uid,
));
if (!_views_bulk_operations_object_permission($operation, $object, $object_info, $account)) {
watchdog('vbo', 'Skipped %operation on @type %title due to insufficient permissions.', array(
'%operation' => $operation['label'],
'@type' => t($object_info['type']),
'%title' => $object->{$object_info['title']},
), WATCHDOG_ALERT);
return;
}
_views_bulk_operations_action_do($operation, $oid, $object, $row, $params, $object_info, $account);
if ($display_result) {
watchdog('vbo', 'Performed %operation on @type %title.', array(
'%operation' => $operation['label'],
'@type' => t($object_info['type']),
'%title' => $object->{$object_info['title']},
), WATCHDOG_INFO);
}
}
/**
* Helper function to handle Batch API operations.
*/
function _views_bulk_operations_execute_single($oid, $row, &$context) {
module_load_include('inc', 'node', 'node.admin');
$operation = $_SESSION['vbo_options']['operation'];
$params = $_SESSION['vbo_options']['params'];
$object_info = $_SESSION['vbo_options']['object_info'];
if (!isset($context['results']['time'])) {
$context['results']['time'] = microtime(TRUE);
$context['results']['rows'] = 0;
}
$object = call_user_func($object_info['load'], $oid);
if (!$object) {
$context['results']['log'][] = t('Skipped %operation on @type id %oid because it was not found.', array(
'%operation' => $operation['label'],
'@type' => t($operation['type']),
'%oid' => $oid,
));
return;
}
if (!_views_bulk_operations_object_permission($operation, $object, $object_info)) {
$context['results']['log'][] = t('Skipped %operation on @type %title due to insufficient permissions.', array(
'%operation' => $operation['label'],
'@type' => t($object_info['type']),
'%title' => $object->{$object_info['title']},
));
return;
}
_views_bulk_operations_action_do($operation, $oid, $object, $row, $params, $object_info);
$context['results']['log'][] = $context['message'] = t('Performed %operation on @type %title.', array(
'%operation' => $operation['label'],
'@type' => t($object_info['type']),
'%title' => $object->{$object_info['title']},
));
$context['results']['rows'] += 1;
}
/**
* Gets the next item in the loop taking into consideration server limits for high performance batching.
*
* @return - The next object in the objects array.
*/
function _views_bulk_operations_execute_next($index, $objects, $batch) {
static $loop = 0, $last_mem = -1, $last_time = 0, $memory_limit = 0, $time_limit = 0;
// Early return if we're done.
if ($index >= count($objects)) {
return FALSE;
}
// Get the array keys.
$keys = array_keys($objects);
if ($batch) {
// Keep track of how many loops we have taken.
$loop++;
// Memory limit in bytes.
$memory_limit = $memory_limit ? $memory_limit : (int) preg_replace('/[^\\d\\s]/', '', ini_get('memory_limit')) * 1048576;
// Max time execution limit.
$time_limit = $time_limit ? $time_limit : (int) ini_get('max_execution_time');
// Current execution time in seconds.
$current_time = time() - $_SERVER['REQUEST_TIME'];
$time_left = $time_limit - $current_time;
if ($loop == 1) {
$last_time = $current_time;
// Never break the first loop.
return $objects[$keys[$index]];
}
// Break when current free memory past threshold. Default to 32 MB.
if ($memory_limit - memory_get_usage() < variable_get('batch_free_memory_threshold', 33554432)) {
return FALSE;
}
// Break when peak free memory past threshold. Default to 8 MB.
if ($memory_limit - memory_get_peak_usage() < variable_get('batch_peak_free_memory_threshold', 8388608)) {
return $objects[$keys[$index]];
}
// Break when execution time remaining past threshold. Default to 15 sec.
if ($time_limit - $current_time < variable_get('batch_time_remaining_threshold', 15)) {
return FALSE;
}
$last_time = $current_time;
return $objects[$keys[$index]];
}
else {
return $objects[$keys[$index]];
}
}
/**
* Helper function for multiple execution operations.
*/
function _views_bulk_operations_execute_multiple($base_field, $operation, $objects, $params, $object_info, $batch, &$context) {
// Setup our batch process.
if (empty($context['sandbox'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = count($objects);
}
if (empty($context['results']['time'])) {
$context['results']['time'] = microtime(TRUE);
$context['results']['rows'] = 0;
}
if ($operation['aggregate'] != VBO_AGGREGATE_FORBIDDEN) {
$oids = array();
while ($row = _views_bulk_operations_execute_next($context['sandbox']['progress'], $objects, $batch)) {
$context['sandbox']['progress']++;
$oid = $row->{$base_field};
if (isset($object_info['access'])) {
$object = call_user_func($object_info['load'], $oid);
if (!$object) {
unset($objects[$num]);
$context['results']['log'][] = t('Skipped %operation on @type %oid because it was not found.', array(
'%operation' => $operation['label'],
'@type' => t($operation['type']),
'%oid' => $oid,
));
continue;
}
if (!_views_bulk_operations_object_permission($operation, $object, $object_info)) {
unset($objects[$num]);
$context['results']['log'][] = t('Skipped %operation on @type %title due to insufficient permissions.', array(
'%operation' => $operation['label'],
'@type' => t($object_info['type']),
'%title' => $object->{$object_info['title']},
));
continue;
}
}
$oids[] = $oid;
}
if (!empty($objects)) {
_views_bulk_operations_action_aggregate_do($operation, $oids, $objects, $params, $object_info);
$context['results']['log'][] = t('Performed aggregate %operation on @types %oids.', array(
'%operation' => $operation['label'],
'@types' => format_plural(count($objects), $object_info['type'], $object_info['type'] . 's'),
'%oids' => implode(',', $oids),
));
$context['message'] = t('Performed aggregate %operation on !count @types.', array(
'%operation' => $operation['label'],
'!count' => count($objects),
'@types' => format_plural(count($objects), $object_info['type'], $object_info['type'] . 's'),
));
$context['results']['rows'] += count($objects);
}
}
else {
$oids = array();
while ($row = _views_bulk_operations_execute_next($context['sandbox']['progress'], $objects, $batch)) {
$context['sandbox']['progress']++;
$oid = $row->{$base_field};
$object = call_user_func($object_info['load'], $oid);
if (!$object) {
$context['results']['log'][] = t('Skipped %operation on @type id %oid because it was not found.', array(
'%operation' => $operation['label'],
'@type' => t($operation['type']),
'%oid' => $oid,
));
continue;
}
if (!_views_bulk_operations_object_permission($operation, $object, $object_info)) {
$context['results']['log'][] = t('Skipped %operation on @type %title due to insufficient permissions.', array(
'%operation' => $operation['label'],
'@type' => t($object_info['type']),
'%title' => $object->{$object_info['title']},
));
continue;
}
_views_bulk_operations_action_do($operation, $oid, $object, $row, $params, $object_info);
$context['results']['log'][] = t('Performed %operation on @type %title.', array(
'%operation' => $operation['label'],
'@type' => t($object_info['type']),
'%title' => $object->{$object_info['title']},
));
$context['results']['rows'] += 1;
$oids[] = $oid;
}
$context['message'] = t('Performed %operation on !count @types.', array(
'%operation' => $operation['label'],
'!count' => count($oids),
'@types' => format_plural(count($oids), $object_info['type'], $object_info['type'] . 's'),
));
}
// Update batch progress.
$context['finished'] = empty($context['sandbox']['max']) ? 1 : $context['sandbox']['progress'] / $context['sandbox']['max'];
}
/**
* Helper function to cleanup operations.
*/
function _views_bulk_operations_execute_finished($success, $results, $operations, $options = NULL) {
if ($success) {
if ($results['rows'] > 0) {
$message = t('!results items processed in about !time ms:', array(
'!results' => $results['rows'],
'!time' => round((microtime(TRUE) - $results['time']) * 1000),
));
}
else {
$message = t('No items were processed:');
}
$message .= "\n" . theme('item_list', $results['log']);
}
else {
// An error occurred.
// $operations contains the operations that remained unprocessed.
$error_operation = reset($operations);
$message = t('An error occurred while processing @operation with arguments: @arguments', array(
'@operation' => $error_operation[0],
'@arguments' => print_r($error_operation[0], TRUE),
));
}
if (empty($options)) {
$options = $_SESSION['vbo_options'];
}
// Inform other modules that VBO has finished executing.
module_invoke_all('views_bulk_operations_finish', $options['operation'], $options['params'], array(
'results' => $results,
));
if (!empty($options['display_result'])) {
drupal_set_message($message);
}
unset($_SESSION['vbo_options']);
// unset the options which were used for just one invocation
}
/**
* Helper function to execute one operation.
*/
function _views_bulk_operations_action_do($operation, $oid, $object, $row, $params, $object_info, $account = NULL) {
_views_bulk_operations_action_permission($operation, $account);
// Add the object to the context.
if (!empty($object_info['context'])) {
$params[$object_info['context']] = $object;
}
else {
$params[$object_info['type']] = $object;
}
// If the operation type is different from the view type, normalize the context first.
$actual_object = $object;
if ($object_info['type'] != $operation['type']) {
if (isset($object_info['normalize']) && function_exists($object_info['normalize'])) {
$actual_object = call_user_func($object_info['normalize'], $operation['type'], $object);
}
$params['hook'] = $object_info['hook'];
}
if (is_null($actual_object)) {
// Normalize function can return NULL: we don't want that
$actual_object = $object;
}
$params['row'] = $row;
// Expose the original view row to the action
if ($operation['source'] == 'action') {
actions_do($operation['callback'], $actual_object, $params);
if ($operation['type'] == 'node' && $operation['access op'] & VBO_ACCESS_OP_UPDATE) {
// Save nodes explicitly if needed
$node_options = variable_get('node_options_' . $actual_object->type, array(
'status',
'promote',
));
if (in_array('revision', $node_options) && !isset($actual_object->revision)) {
$actual_object->revision = TRUE;
$actual_object->log = '';
}
node_save($actual_object);
}
}
else {
// source == 'operation'
$args = array_merge(array(
array(
$oid,
),
), $params);
call_user_func_array($operation['callback'], $args);
}
}
/**
* Helper function to execute an aggregate operation.
*/
function _views_bulk_operations_action_aggregate_do($operation, $oids, $objects, $params, $object_info) {
_views_bulk_operations_action_permission($operation);
$params[$operation['type']] = $objects;
if ($operation['source'] == 'action') {
actions_do($operation['callback'], $oids, $params);
}
else {
$args = array_merge(array(
$oids,
), $params);
call_user_func_array($operation['callback'], $args);
}
}
/**
* Helper function to verify access permission to execute operation.
*/
function _views_bulk_operations_action_permission($operation, $account = NULL) {
if (module_exists('actions_permissions')) {
$perm = actions_permissions_get_perm($operation['perm label'], $operation['callback']);
if (!user_access($perm, $account)) {
global $user;
watchdog('vbo', 'An attempt by user %user to !perm was blocked due to insufficient permissions.', array(
'!perm' => $perm,
'%user' => isset($account) ? $account->name : $user->name,
), WATCHDOG_ALERT);
drupal_access_denied();
exit;
}
}
// Check against additional permissions.
if (!empty($operation['permissions'])) {
foreach ($operation['permissions'] as $perm) {
if (!user_access($perm, $account)) {
global $user;
watchdog('vbo', 'An attempt by user %user to !perm was blocked due to insufficient permissions.', array(
'!perm' => $perm,
'%user' => isset($account) ? $account->name : $user->name,
), WATCHDOG_ALERT);
drupal_access_denied();
exit;
}
}
}
}
/**
* Helper function to verify access permission to operate on object.
*/
function _views_bulk_operations_object_permission($operation, $object, $object_info, $account = NULL) {
// Check against object access permissions.
if (!isset($object_info['access'])) {
return TRUE;
}
$access_ops = array(
VBO_ACCESS_OP_VIEW => 'view',
VBO_ACCESS_OP_UPDATE => 'update',
VBO_ACCESS_OP_CREATE => 'create',
VBO_ACCESS_OP_DELETE => 'delete',
);
foreach ($access_ops as $bit => $op) {
if ($operation['access op'] & $bit) {
if (!call_user_func($object_info['access'], $op, $object, $account)) {
return FALSE;
}
}
}
return TRUE;
}
/**
* Helper function to let the configurable action provide its configuration form.
*/
function _views_bulk_operations_action_form($action, $view, $selection, $settings, $context = array()) {
$action_form = $action['callback'] . '_form';
$context = array_merge($context, array(
'view' => $view,
'selection' => $selection,
'settings' => $settings,
'object_info' => _views_bulk_operations_object_info_for_view($view),
));
if (isset($action['callback arguments'])) {
$context = array_merge($context, $action['callback arguments']);
}
$form = call_user_func($action_form, $context);
return is_array($form) ? $form : array();
}
/**
* Helper function to let the configurable action validate the form if it provides a validator.
*/
function _views_bulk_operations_action_validate($action, $form, $form_values) {
$action_validate = $action['callback'] . '_validate';
if (function_exists($action_validate)) {
call_user_func($action_validate, $form, $form_values);
}
}
/**
* Helper function to let the configurable action process the configuration form.
*/
function _views_bulk_operations_action_submit($action, $form, &$form_state) {
$action_submit = $action['callback'] . '_submit';
return call_user_func($action_submit, $form, $form_state);
}
/**
* Helper function to return all object info.
*/
function _views_bulk_operations_get_object_info($reset = FALSE) {
static $object_info = array();
if ($reset || empty($object_info)) {
$object_info = module_invoke_all('views_bulk_operations_object_info');
}
drupal_alter('views_bulk_operations_object_info', $object_info);
return $object_info;
}
/**
* Helper function to return object info for a given view.
*/
function _views_bulk_operations_object_info_for_view($view) {
foreach (_views_bulk_operations_get_object_info() as $object_info) {
if ($object_info['base_table'] == $view->base_table) {
return $object_info + array(
'context' => '',
'oid' => '',
'access' => NULL,
'hook' => '',
'normalize' => NULL,
);
}
}
watchdog('vbo', 'Could not find object info for view table @table.', array(
'@table' => $view->base_table,
), WATCHDOG_ERROR);
return NULL;
}
/**
* Helper to include all action files.
*/
function _views_bulk_operations_load_actions() {
static $files = NULL;
if (!empty($files)) {
return $files;
}
$files = cache_get('views_bulk_operations_actions');
if (empty($files) || empty($files->data)) {
$files = array();
foreach (file_scan_directory(drupal_get_path('module', 'views_bulk_operations') . '/actions', '\\.action\\.inc$') as $file) {
list($files[], ) = explode('.', $file->name);
}
cache_set('views_bulk_operations_actions', $files);
}
else {
$files = $files->data;
}
foreach ($files as $file) {
module_load_include('inc', 'views_bulk_operations', "actions/{$file}.action");
}
return $files;
}
/**
* Helper callback for array_walk().
*/
function _views_bulk_operations_get_oid($row, $base_field) {
return $row->{$base_field};
}
/**
* Helper callback for array_filter().
*/
function _views_bulk_operations_filter_invert($item) {
return empty($item);
}
/**
* Helper to add needed JavaScript files to VBO.
*/
function _views_bulk_operations_add_js($plugin, $form_dom_id, $form_id) {
static $views = NULL;
if (!isset($views[$form_id])) {
drupal_add_js(drupal_get_path('module', 'views_bulk_operations') . '/js/views_bulk_operations.js');
drupal_add_js(drupal_get_path('module', 'views_bulk_operations') . '/js/json2.js');
drupal_add_css(drupal_get_path('module', 'views_bulk_operations') . '/js/views_bulk_operations.css', 'module');
drupal_add_js(array(
'vbo' => array(
$form_dom_id => array(
'form_id' => $form_id,
'view_name' => $plugin->view->name,
'view_id' => _views_bulk_operations_view_id($plugin->view),
'options' => $plugin->options,
'ajax_select' => url('views-bulk-operations/js/select'),
'view_path' => url($plugin->view
->get_path()),
'total_rows' => $plugin->view->total_rows,
),
),
), 'setting');
$views[$form_id] = TRUE;
}
}
/**
* Implement hook_ajax_data_alter().
*/
function views_bulk_operations_ajax_data_alter(&$object, $type, $view) {
if ($type == 'views' && $view->display_handler
->get_option('style_plugin') == 'bulk') {
$object->vbo = array(
'view_id' => _views_bulk_operations_view_id($view),
'form_id' => $view->style_plugin->form_id,
);
$object->__callbacks[] = 'Drupal.vbo.ajaxViewResponse';
}
}
/**
* Helper function to calculate hash of an object.
*
* The default "hashing" is to use the object's primary/unique id. This would fail for VBOs that return many rows with
* the same primary key (e.g. a *node* view returning all node *comments*). Because we don't know in advance what kind of
* hashing is needed, we allow for a module to implement its own hashing via
*
* hook_views_bulk_operations_object_hash_alter(&$hash, $object, $view).
*/
function _views_bulk_operations_hash_object($object, $view) {
$hash = $object->{$view->base_field};
drupal_alter('views_bulk_operations_object_hash', $hash, $object, $view);
return $hash;
}
/**
* Helper function to strip of a view of all decorations.
*/
function _views_bulk_operations_strip_view($view) {
if (isset($view->query->pager)) {
$view->query->pager = NULL;
}
else {
$view
->set_use_pager(FALSE);
}
$view->exposed_widgets = NULL;
$view->display_handler
->set_option('header', '');
$view->display_handler
->set_option('footer', '');
$view->display_handler
->set_option('use_pager', FALSE);
$view->attachment_before = '';
$view->attachment_after = '';
$view->feed_icon = NULL;
}
/**
* Helper function to get a unique ID for a view, taking arguments and exposed filters into consideration.
*/
function _views_bulk_operations_view_id($view) {
// Normalize exposed input.
$exposed_input = array();
foreach ($view->filter as $filter) {
if (!empty($filter->options['exposed']) && isset($view->exposed_input[$filter->options['expose']['identifier']])) {
$exposed_input[$filter->options['expose']['identifier']] = $view->exposed_input[$filter->options['expose']['identifier']];
}
}
$exposed_input = array_filter($exposed_input);
$view_id = md5(serialize(array(
$view->name,
$view->args,
$exposed_input,
)));
return $view_id;
}
/**
* Helper function to identify VBO displays for a view.
*/
function _views_bulk_operations_displays($view) {
$displays = array();
foreach ($view->display as $display_id => $display) {
if ($display
->get_option('style_plugin') == 'bulk') {
$displays[] = $display_id;
}
}
return $displays;
}
/**
* Functor to destroy view on exit.
*/
class views_bulk_operations_destructor {
function __construct($view) {
$this->view = $view;
}
function __destruct() {
$this->view
->destroy();
}
private $view;
}
Functions
Name | Description |
---|---|
template_preprocess_views_bulk_operations_table | Template preprocessor for theme function 'views_bulk_operations_table'. |
theme_views_bulk_operations_confirmation | Theme function to show the confirmation page before executing the action. |
theme_views_bulk_operations_select_all | Theme function for 'views_bulk_operations_select_all'. |
theme_views_bulk_operations_table | Theme function for 'views_bulk_operations_table'. |
theme_views_node_selector | Theme function for 'views_node_selector'. |
views_bulk_operations_action | Execution function for views_bulk_operations_action action. |
views_bulk_operations_action_form | Form function for views_bulk_operations_action action. |
views_bulk_operations_action_form_operation | Form callback to update an action form when a new action is selected in views_bulk_operations_action form. |
views_bulk_operations_action_info | Implementation of hook_action_info(). |
views_bulk_operations_action_submit | Form submit function for views_bulk_operations_action action. |
views_bulk_operations_action_validate | Form validate function for views_bulk_operations_action action. |
views_bulk_operations_add_actions | API function to add actions to a VBO. |
views_bulk_operations_ajax_data_alter | Implement hook_ajax_data_alter(). |
views_bulk_operations_cron_queue_info | Implementation of hook_cron_queue_info(). |
views_bulk_operations_elements | Implementation of hook_elements(). |
views_bulk_operations_execute | API function to programmatically invoke a VBO. |
views_bulk_operations_form | Form implementation for main VBO multistep form. |
views_bulk_operations_forms | Implementation of hook_forms(). |
views_bulk_operations_form_ahah | Generic AHAH callback to manipulate a form. |
views_bulk_operations_form_alter | Implementation of hook_form_alter(). |
views_bulk_operations_form_submit | Form submit function for views_bulk_operations_form(). |
views_bulk_operations_form_validate | Form validate function for views_bulk_operations_form(). |
views_bulk_operations_init | Implementation of hook_init(). |
views_bulk_operations_menu | Implementation of hook_menu(). |
views_bulk_operations_select | AJAX callback to update selection. |
views_bulk_operations_theme | Implementation of hook_theme(). |
views_bulk_operations_views_api | Implementation of hook_views_api(). |
views_bulk_operations_views_bulk_operations_object_info | Implementation of hook_views_bulk_operations_object_info() |
views_node_selector_process | Process function for views_node_selector element. |
_views_bulk_operations_action_aggregate_do | Helper function to execute an aggregate operation. |
_views_bulk_operations_action_do | Helper function to execute one operation. |
_views_bulk_operations_action_form | Helper function to let the configurable action provide its configuration form. |
_views_bulk_operations_action_permission | Helper function to verify access permission to execute operation. |
_views_bulk_operations_action_submit | Helper function to let the configurable action process the configuration form. |
_views_bulk_operations_action_validate | Helper function to let the configurable action validate the form if it provides a validator. |
_views_bulk_operations_add_js | Helper to add needed JavaScript files to VBO. |
_views_bulk_operations_comment_access | Access function for objects of type 'comments'. |
_views_bulk_operations_displays | Helper function to identify VBO displays for a view. |
_views_bulk_operations_execute | Helper function to execute the chosen action upon selected objects. |
_views_bulk_operations_execute_finished | Helper function to cleanup operations. |
_views_bulk_operations_execute_multiple | Helper function for multiple execution operations. |
_views_bulk_operations_execute_next | Gets the next item in the loop taking into consideration server limits for high performance batching. |
_views_bulk_operations_execute_queue | Helper function to handle Drupal Queue operations. |
_views_bulk_operations_execute_single | Helper function to handle Batch API operations. |
_views_bulk_operations_file_load | Load function for objects of type 'file'. |
_views_bulk_operations_filter_invert | Helper callback for array_filter(). |
_views_bulk_operations_get_object_info | Helper function to return all object info. |
_views_bulk_operations_get_oid | Helper callback for array_walk(). |
_views_bulk_operations_get_selection | Compute the selection based on the settings. |
_views_bulk_operations_get_selection_count | Compute the actual number of selected items. |
_views_bulk_operations_get_selection_full | Compute the actual selected objects based on the settings. |
_views_bulk_operations_hash_object | Helper function to calculate hash of an object. |
_views_bulk_operations_load_actions | Helper to include all action files. |
_views_bulk_operations_node_load | Load function for objects of type 'node'. |
_views_bulk_operations_node_revision_load | Load function for node revisions. |
_views_bulk_operations_normalize_comment_context | Normalize function for comment context. |
_views_bulk_operations_normalize_node_context | Normalize function for node context. |
_views_bulk_operations_normalize_user_context | Normalize function for user context. |
_views_bulk_operations_object_info_for_view | Helper function to return object info for a given view. |
_views_bulk_operations_object_permission | Helper function to verify access permission to operate on object. |
_views_bulk_operations_strip_view | Helper function to strip of a view of all decorations. |
_views_bulk_operations_user_access | Access function for objects of type 'user'. |
_views_bulk_operations_view_id | Helper function to get a unique ID for a view, taking arguments and exposed filters into consideration. |
Constants
Classes
Name | Description |
---|---|
views_bulk_operations_destructor | Functor to destroy view on exit. |