You are here

views_bulk_operations.module in Views Bulk Operations (VBO) 5

Allow bulk node operations directly within views.

BUGS:

  • settings for bulk ops views are currently stored in the variables table, and there's no way to hook into the view delete process, so settings stick around after a corresponding view has been deleted. If cruft becomes an issue we should switch to using our own table for storing this info and we can prune it using 'where vid not in (select vid from view... )' etc...

File

views_bulk_operations.module
View source
<?php

/**
 * @file
 * Allow bulk node operations directly within views.
 *
 * BUGS:
 *  - settings for bulk ops views are currently stored in the variables table,
 *    and there's no way to hook into the view delete process, so settings stick
 *    around after a corresponding view has been deleted. If cruft becomes an
 *    issue we should switch to using our own table for storing this info and we can
 *    prune it using 'where vid not in (select vid from view... )' etc...
 *
 */
define('VIEWS_BULK_OPS_STEP_VIEW', 1);
define('VIEWS_BULK_OPS_STEP_CONFIG', 2);
define('VIEWS_BULK_OPS_STEP_CONFIRM', 3);
define('VIEWS_BULK_OPS_STEP_SINGLE', 4);

// maximum number of node titles that will be displayed in operation
// confirmation page.
define('VIEWS_BULK_OPS_MAX_CONFIRM_NODES', 10);

// whether all operations are enabled by default, setting to 0 requires that
// the admin enable one or more actions before the bulk operations view will
// be usable. 0 is the safe setting.
define('VIEWS_BULK_OPS_DEFAULT_OPERATION_ENABLE', 0);

/**
 * Implementation of hook_help().
 */
function views_bulk_operations_help($section) {
  switch ($section) {
    case 'admin/help#views_bulk_operations':
      return t('
        Views Bulk Operations adds a new views type which enables operations to
        be performed on multiple nodes directly from within a view. <p>To create
        a view of this type, simply select "Views Bulk Operations" as the "view
        type" for your view (which must be a page view).</p>
      ');
      break;
    case 'admin/modules#description':
      return t('Allow bulk node operations directly within views.');
      break;
  }
}

/**
 * Implementation of hook_menu().
 */
function views_bulk_operations_menu($may_cache) {
  $items = array();
  if ($may_cache) {

    // no static menu entries
  }
  else {
    if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'views') {
      $view_name = arg(3);
      $path = 'admin/build/views/' . $view_name . '/operations';
    }
    else {
      $urls = views_menu(false);
      $strlenview = strlen('/view');
      if ($urls) {
        foreach ($urls as $url) {
          if (strrpos($url['path'], '/view') == strlen($url['path']) - $strlenview) {
            $view_name = $url['callback arguments'][0];
            $path = substr($url['path'], 0, -$strlenview) . '/operations';
          }
        }
      }
    }
    $view = views_get_view($view_name);
    if ($view && isset($view->page_type) && $view->page_type == 'bulk') {
      $items[] = array(
        'path' => $path,
        'title' => t('Settings'),
        'description' => t('Select the operations to be displayed in the view and other settings.'),
        'access' => user_access('administer views'),
        'callback' => 'drupal_get_form',
        'callback arguments' => array(
          'views_bulk_operations_view_settings',
          $view,
        ),
        'type' => MENU_LOCAL_TASK,
        'weight' => 10,
      );
    }
  }
  return $items;
}
function views_bulk_operations_views_tabs($op) {
  switch ($op) {
    case 'names':
      return array(
        'operations',
      );
  }
}
function views_bulk_operations_form($view, $nodes, $type, $form_values = NULL) {
  drupal_add_css(drupal_get_path('module', 'views_bulk_operations') . '/views_bulk_operations.css', 'module');
  $enabled_operations = _views_bulk_operations_get_operations('enabled', isset($view->vid) ? $view->vid : $view->name);
  if (empty($enabled_operations)) {

    // No ops found: bail out
    $error_message = t('No operations are enabled. You must enable one or more
      operations in order to use Views Bulk Operations.');
    drupal_set_message($error_message, 'error');
    drupal_goto($view->url . '/operations');
  }
  elseif (count($enabled_operations) == 1) {

    // One op found: special case
    $form['#multistep'] = FALSE;
    $form['#redirect'] = FALSE;
    $step = VIEWS_BULK_OPS_STEP_SINGLE;
  }
  else {

    // Many ops found: calculate the next step
    $form['#multistep'] = TRUE;
    $step = isset($form_values) ? (int) $form_values['step'] + 1 : VIEWS_BULK_OPS_STEP_VIEW;
    if ($step == VIEWS_BULK_OPS_STEP_CONFIG) {
      $action = $form_values['action'];
      if (!isset($enabled_operations[$action]) || !$enabled_operations[$action]['configurable']) {
        $step = VIEWS_BULK_OPS_STEP_CONFIRM;
      }
    }
  }

  // Get the operations and based on them many things are decided.
  $form['step'] = array(
    '#type' => 'hidden',
    '#value' => $step,
  );
  $form['vid'] = array(
    '#type' => 'hidden',
    '#value' => isset($view->vid) ? $view->vid : $view->name,
  );
  switch ($step) {
    case VIEWS_BULK_OPS_STEP_SINGLE:

      // Show the full thing for a single action
      $keys = array_keys($enabled_operations);
      $action = $keys[0];
      $form['action'] = array(
        '#type' => 'hidden',
        '#value' => $action,
      );
      if ($enabled_operations[$action]['configurable']) {
        $form_action = _views_bulk_operations_action_form($enabled_operations[$action]);
        $form['arguments'] = array(
          '#type' => 'fieldset',
          '#title' => t($enabled_operations[$action]['label']),
          '#collapsible' => FALSE,
        );
        $form['arguments'] = array_merge($form['arguments'], $form_action);
      }
      $form['nodes'] = array(
        '#title' => '',
        '#type' => 'views_node_selector',
        '#view' => $view,
        '#view_nodes' => $nodes,
        '#multiple' => TRUE,
        '#prefix' => '<div class="views-node-selector">',
        '#suffix' => '</div>',
      );
      $form['execute'] = array(
        '#type' => 'submit',
        '#value' => t($enabled_operations[$action]['label']),
      );
      break;
    case VIEWS_BULK_OPS_STEP_VIEW:

      // Show the view
      $options[0] = t('- Choose an operation -');
      foreach ($enabled_operations as $operation => $values) {
        $options[$operation] = t($values['label']);
      }
      $form['select'] = array(
        '#type' => 'fieldset',
        '#title' => t('Bulk operations'),
        '#prefix' => '<div id="views-bulk-operations-select">',
        '#suffix' => '</div>',
      );
      $form['select']['action'] = array(
        '#type' => 'select',
        '#title' => '',
        '#options' => $options,
        '#prefix' => '<div id="views-bulk-operations-dropdown">',
        '#suffix' => '</div>',
        '#default_value' => count($enabled_operations) == 1 ? $operation : 0,
      );
      $form['select']['execute'] = array(
        '#type' => 'submit',
        '#value' => t('Execute'),
        '#prefix' => '<div id="views-bulk-operations-submit">',
        '#suffix' => '</div>',
      );
      $form['nodes'] = array(
        '#title' => '',
        '#type' => 'views_node_selector',
        '#view' => $view,
        '#view_nodes' => $nodes,
        '#multiple' => TRUE,
        '#prefix' => '<div class="views-node-selector">',
        '#suffix' => '</div>',
      );
      $form['#redirect'] = FALSE;
      break;
    case VIEWS_BULK_OPS_STEP_CONFIG:

      // Create the action form to allow configuring parameters
      $view->use_pager = FALSE;
      $action = $form_values['action'];
      $form['action'] = array(
        '#type' => 'hidden',
        '#value' => $action,
      );
      $form['nodes'] = array(
        '#type' => 'value',
        '#value' => serialize($form_values['nodes']),
      );
      $form_action = _views_bulk_operations_action_form($enabled_operations[$action]);
      $form = array_merge($form, $form_action);

      // 'action_elements' is where we store the key values of the configurable
      // action form elements so that we can extract just those form values
      // later and serialize them in $preserved_action_form_elements for hiding
      // in the confirmation page
      $form['action_elements'] = array(
        '#type' => 'hidden',
        '#value' => serialize(array_keys($form_action)),
      );
      $form['execute'] = array(
        '#type' => 'submit',
        '#value' => t('Next'),
      );
      $form['#redirect'] = FALSE;
      drupal_set_title(t('Set parameters for \'%action\'', array(
        '%action' => $enabled_operations[$action]['label'],
      )));
      break;
    case VIEWS_BULK_OPS_STEP_CONFIRM:

      // Display the final confirmation
      $view->use_pager = FALSE;
      $action = $form_values['action'];
      $configurable = isset($enabled_operations[$action]) && $enabled_operations[$action]['configurable'];
      $form['action'] = array(
        '#type' => 'hidden',
        '#value' => $action,
      );
      if ($configurable) {
        $preserved_action_form_elements = array();

        // If this action is configurable, we came from a VIEWS_BULK_OPS_STEP_CONFIG.
        // $form_values['nodes'] is already serialized.
        $form['nodes'] = array(
          '#type' => 'value',
          '#value' => $form_values['nodes'],
        );

        // Gather the form values for the action form elements and preserve them
        // in the confirmation page form as a serialized value in a hidden
        // element to be picked up by the submit function.
        $action_elements = unserialize($form_values['action_elements']);
        foreach ($action_elements as $key) {
          if (isset($form_values[$key])) {
            $preserved_action_form_elements[$key] = $form_values[$key];
          }
        }
        $form['preserved_action_form_elements'] = array(
          '#type' => 'hidden',
          '#value' => serialize($preserved_action_form_elements),
        );
        $selected = unserialize($form_values['nodes']);

        // We'll use that in the confirm_form
      }
      else {

        // If this action is not configurable, we skipped VIEWS_BULK_OPS_STEP_CONFIG.
        // $form_values['nodes'] still needs to be serialized.
        $form['nodes'] = array(
          '#type' => 'value',
          '#value' => serialize($form_values['nodes']),
        );
        $selected = $form_values['nodes'];

        // We'll use that in the confirm_form
      }
      if ($selected['select_all']) {

        // User selected all nodes?
        $items = views_build_view('items', $view);
        $selected = array();
        foreach ($items['items'] as $node) {
          $selected[$node->nid] = $node->nid;
        }
      }
      else {
        unset($selected['select_all']);
      }
      $query = drupal_query_string_encode($_GET, array(
        'q',
      ));
      $form = confirm_form($form, t('Are you sure you want to perform \'%action\' on selected nodes?', array(
        '%action' => $enabled_operations[$action]['label'],
      )), $query ? array(
        'path' => $_GET['q'],
        'query' => $query,
      ) : array(
        'path' => $_GET['q'],
      ), theme('views_bulk_operations_confirmation', $selected));
      break;
  }

  // Use views_bulk_operations_form_submit() for form submit, regardless of form_id
  $form['#submit'] = array(
    'views_bulk_operations_form_submit' => 1,
  );
  $form['#validate'] = array(
    'views_bulk_operations_form_validate' => array(),
  );
  return $form;
}
function views_bulk_operations_form_alter($form_id, &$form) {
  if ($form_id == 'views_filters') {

    // We only display the filter form on the first page.
    if (strpos($_POST['form_id'], 'views_bulk_operations_form') === 0 && isset($_POST['step']) && isset($_POST['nodes'])) {
      $form['#prefix'] = '<div style="display: none">';
      $form['#suffix'] = '</div>';
    }
  }
}
function views_bulk_operations_form_validate($form_id, $form_values) {
  if ($form_values['step'] == VIEWS_BULK_OPS_STEP_VIEW || $form_values['step'] == VIEWS_BULK_OPS_STEP_SINGLE) {
    if (!array_sum($form_values['nodes'])) {

      // If all 0, no node selected
      form_set_error('nodes', t('No nodes selected. Please select one or more nodes.'));
    }
  }
  if ($form_values['step'] == VIEWS_BULK_OPS_STEP_VIEW) {
    if (!$form_values['action']) {

      // No action selected
      form_set_error('action', t('No action selected. Please select an action to perform.'));
    }
  }
  if ($form_values['step'] == VIEWS_BULK_OPS_STEP_CONFIG || $form_values['step'] == VIEWS_BULK_OPS_STEP_SINGLE) {
    $action = $form_values['action'];
    $operations = _views_bulk_operations_get_operations('enabled', $form_values['vid']);
    if ($operations[$action]['configurable']) {
      _views_bulk_operations_action_validate($operations[$action], $form_id, $form_values);
    }
  }
  if ($form_values['step'] == VIEWS_BULK_OPS_STEP_CONFIG || $form_values['step'] == VIEWS_BULK_OPS_STEP_CONFIRM) {
    $view = module_invoke('views', 'get_view', $form_values['vid']);
    $view->use_pager = FALSE;
  }
}
function views_bulk_operations_form_submit($form_id, $form_values) {
  switch ($form_values['step']) {
    case VIEWS_BULK_OPS_STEP_CONFIRM:
      $nodes = unserialize($form_values['nodes']);
      $preserved_action_form_elements = unserialize($form_values['preserved_action_form_elements']);
      if (!is_array($nodes)) {
        return;
      }
      break;
    case VIEWS_BULK_OPS_STEP_SINGLE:
      $nodes = $form_values['nodes'];
      $preserved_action_form_elements = $form_values;
      break;
    default:
      return;
  }
  $operations = _views_bulk_operations_get_operations('enabled', $form_values['vid']);
  $operation = $operations[$form_values['action']];

  // Check if the user selected all nodes.
  if ($nodes['select_all']) {
    $view = module_invoke('views', 'get_view', $form_values['vid']);
    $items = views_build_view('items', $view);
    $nodes = array();
    foreach ($items['items'] as $node) {
      $nodes[$node->nid] = $node->nid;
    }
  }
  else {
    unset($nodes['select_all']);
  }
  $nodes = array_filter($nodes);

  // Set parameters.
  $params = array();
  if ($operation['configurable']) {
    $params += _views_bulk_operations_action_submit($operation, $form_id, $preserved_action_form_elements);
  }
  else {
    if (is_array($preserved_action_form_elements)) {
      $params += $preserved_action_form_elements;
    }
  }
  if ($operation['type'] == 'node' && isset($operation['callback arguments'])) {
    $params += $operation['callback arguments'];
  }

  // FIXME Hack to force actions_do() to process any number of invocations.
  // Check http://drupal.org/node/290282 to understand more.
  variable_set('actions_max_stack', 10000000);
  if (module_exists('job_queue') && variable_get('vbo_queue_' . $form_values['vid'], 0)) {

    // Deferred execution
    foreach ($nodes as $nid) {
      $node = node_load($nid);
      if (!$node) {
        continue;
      }
      global $user;
      job_queue_add('_views_bulk_operations_queue_process', t('Perform %action on node %title.', array(
        '%action' => $operation['label'],
        '%title' => $node->title,
      )), array(
        $operation,
        $node,
        $params,
        $user->uid,
      ));
    }
    drupal_set_message(t('Enqueued %action on nodes %nid. Check the <a href="@queue">queued jobs page</a>.', array(
      '%action' => $operation['label'],
      '%nid' => implode(', ', $nodes),
      '@queue' => url('admin/logs/job_queue'),
    )));
  }
  else {

    // Direct execution
    set_time_limit(0);
    if ($operation['type'] == 'node') {

      // Add node_access() check.
      foreach ($nodes as $i => $nid) {
        $node = node_load(array(
          'nid' => $nid,
        ));
        if (!node_access('update', $node)) {
          unset($nodes[$i]);
          drupal_set_message(t('Skipped %action on node %title due to insufficient permissions.', array(
            '%action' => $operation['label'],
            '%title' => $node->title,
          )));
        }
      }
      if (!empty($nodes)) {
        $args = array_merge(array(
          $nodes,
        ), $params);
        call_user_func_array($operation['callback'], $args);
        cache_clear_all();
        drupal_set_message(t('Performed %action on nodes %nid.', array(
          '%action' => $operation['label'],
          '%nid' => implode(', ', $nodes),
        )));
      }
    }
    else {
      if ($operation['type'] == 'action') {
        foreach ($nodes as $nid) {
          $node = node_load(array(
            'nid' => $nid,
          ));
          if (!$node) {
            continue;
          }

          // Add node_access() check.
          if (!node_access('update', $node)) {
            drupal_set_message(t('Skipped %action on node %title due to insufficient permissions.', array(
              '%action' => $operation['label'],
              '%title' => $node->title,
            )));
            continue;
          }
          _views_bulk_operations_action_do($operation, $node, $params);
          drupal_set_message(t('Performed %action on node %title.', array(
            '%action' => $operation['label'],
            '%title' => $node->title,
          )));
        }
      }
    }
  }
  drupal_goto($_GET['q'], drupal_query_string_encode($_GET, array(
    'q',
  )));
}
function views_bulk_operations_job_queue_functions() {
  $functions['_views_bulk_operations_queue_process'] = array(
    'title' => t('Execute operation'),
  );
  return $functions;
}
function _views_bulk_operations_queue_process($operation, $node, $params, $uid) {

  // Impersonate other user: http://drupal.org/node/218104
  global $user;
  $original_user = $user;
  session_save_session(FALSE);
  $user = user_load(array(
    'uid' => $uid,
  ));

  // Add node_access() check.
  if (!node_access('update', $node)) {
    drupal_set_message(t('Skipped %action on node %title due to insufficient permissions.', array(
      '%action' => $operation['label'],
      '%title' => $node->title,
    )));
  }
  else {
    if ($operation['type'] == 'action') {
      _views_bulk_operations_action_do($operation, $node, $params);
    }
    else {
      $args = array_merge(array(
        array(
          $node->nid,
        ),
      ), $params);
      call_user_func_array($operation['callback'], $args);
      cache_clear_all();
    }
    drupal_set_message(t('Performed %action on node %title.', array(
      '%action' => $operation['label'],
      '%title' => $node->title,
    )));
  }
  $user = $original_user;
  session_save_session(TRUE);
}
function _views_bulk_operations_action_form($action) {
  $action_form = $action['callback'] . '_form';
  if (function_exists($action_form)) {

    // Drupal-6-style actions
    return call_user_func($action_form, array());
  }
  else {
    return call_user_func($action['callback'], 'form', array(), array());
  }
}
function _views_bulk_operations_action_validate($action, $form_id, $form_values) {
  $action_validate = $action['callback'] . '_validate';
  if (function_exists($action_validate)) {

    // Drupal-6-style actions
    call_user_func($action_validate, $form_id, $form_values);
  }
  else {
    call_user_func($action['callback'], 'validate', $form_values, array());
  }
}
function _views_bulk_operations_action_submit($action, $form_id, $form_values) {
  $action_submit = $action['callback'] . '_submit';
  if (function_exists($action_submit)) {

    // Drupal-6-style actions
    return call_user_func($action_submit, $form_id, $form_values);
  }
  else {
    return call_user_func($action['callback'], 'submit', $form_values, array());
  }
}
function _views_bulk_operations_is_actions_6() {
  $info = _module_parse_info_file(drupal_get_path('module', 'actions') . '/actions.info');
  return (bool) preg_match('/^5.[x1-9]-2.[x1-9]/', $info['version']);
}
function _views_bulk_operations_action_do($action, $node, $context) {
  $action_do = $action['callback'];
  if (_views_bulk_operations_is_actions_6()) {

    // Drupal-6-style actions
    actions_do($action['callback'], $node, $context);
    node_save($node);
  }
  else {
    if (is_numeric($action_do)) {

      // Custom action
      actions_do($action_do, $node);
    }
    else {
      call_user_func($action_do, 'do', $context, $node);
    }
  }
}

/**
 * Bulk operations config page for a given view.
 */
function views_bulk_operations_view_settings($view) {
  $form['#tree'] = TRUE;
  $form['#submit'] = array(
    'views_bulk_operations_view_settings_submit' => array(),
  );
  $form['#validate'] = array(
    'views_bulk_operations_view_settings_validate' => array(),
  );

  // Static views don't have a vid
  $form['vid'] = array(
    '#type' => 'hidden',
    '#value' => isset($view->vid) ? $view->vid : $view->name,
  );

  // Other settings first.
  if (module_exists('job_queue')) {
    $form['job_queue'] = array(
      '#type' => 'checkbox',
      '#default_value' => variable_get('vbo_queue_' . $form['vid']['#value'], 0),
      '#title' => t('Background operations'),
      '#description' => t('Check this box to enqueue the operations on selected nodes to be executed in the background, using the <a href="@jobqueue">Job queue module</a>.', array(
        '@jobqueue' => url('http://drupal.org/project/job_queue'),
      )),
    );
  }
  drupal_set_title(t('Bulk operations settings for view %view', array(
    '%view' => $view->name,
  )));
  $all_operations = _views_bulk_operations_get_operations('refresh', $form['vid']['#value']);
  $enabled_operations = _views_bulk_operations_get_operations('enabled', $form['vid']['#value']);

  // Operations checkboxes
  $form['operations'] = array(
    '#type' => 'fieldset',
    '#title' => t('Operations'),
  );
  foreach ($all_operations as $operation => $values) {
    $form['operations'][$operation] = array(
      '#type' => 'checkbox',
      '#title' => $values['label'] . ' <em>(' . $values['callback'] . ')</em>',
      '#default_value' => $values['enabled'],
      '#collapsible' => FALSE,
    );
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  return $form;
}
function theme_views_bulk_operations_view_settings($form) {
  $output = t('
    <p>These settings determine which actions will be available from within a
    bulk operations view form. Operations selected here will be available for
    execution on the form. If only one action is selected, no dropdown will
    be provided and users will be able to perform or configure the single
    operation directly from the view form.</p>
  ');
  if (module_exists('actions')) {
    $output .= t('
      <p><em>Note:</em> Some of the operations provided by the Actions module are
      identical to native Drupal node operations. In most cases, you should
      disable at least one of these operations.</p>
    ');
  }
  else {
    $output .= t('
      <p><em>Note:</em> Install the !actions_module to enable additional
      operations.</p>
    ', array(
      '!actions_module' => l('Actions module', 'http://drupal.org/project/actions'),
    ));
  }
  $output .= drupal_render($form);
  $output .= '<p class="cvs-version">$Id$</p>';
  return $output;
}
function views_bulk_operations_view_settings_validate($form_id, $form_values) {
  if (array_sum($form_values['operations']) == 0) {
    form_set_error('operations', t('No operations are enabled. You must enable one or
      more operations in order to use Views Bulk Operations.'));
  }
}
function views_bulk_operations_view_settings_submit($form_id, $form_values) {
  $view = module_invoke('views', 'get_view', $form_values['vid']);
  $all_operations = _views_bulk_operations_get_operations('all', $form_values['vid']);

  // set the enabled/disabled status, but leave other values alone
  foreach ($all_operations as $operation => $values) {
    $all_operations[$operation]['enabled'] = $form_values['operations'][$operation];
  }

  // store serialized data
  _views_bulk_operations_set($form_values['vid'], $all_operations);
  variable_set('vbo_queue_' . $form_values['vid'], $form_values['job_queue']);

  // return to view
  if (isset($view->url)) {
    return $view->url;
  }
}
function views_bulk_operations_field_node_status($fieldinfo, $fielddata, $value, $data) {
  $node = node_load($data->nid);
  if ($node->status) {
    $status[] = t('Published');
  }
  if ($node->promote) {
    $status[] = t('Promoted');
  }
  if ($node->sticky) {
    $status[] = t('Sticky');
  }
  $output = '';
  if (is_array($status)) {
    $output = implode(', ', $status);
  }
  return $output;
}

/**
 * Implementation of hook_views_style_plugins()
 */
function views_bulk_operations_views_style_plugins() {
  return array(
    'bulk' => array(
      'name' => t('Bulk Operations View'),
      'theme' => 'views_bulk_operations_view',
      'needs_fields' => true,
      'needs_table_header' => true,
      'weight' => -10,
    ),
  );
}
function views_bulk_operations_views_default_views() {
  $view = new stdClass();
  $view->name = 'admin_content';
  $view->description = 'View, edit and delete your site\'s content.';
  $view->access = array();
  $view->view_args_php = '';
  $view->page = TRUE;
  $view->page_title = 'Content';
  $view->page_header = '';
  $view->page_header_format = '1';
  $view->page_footer = '';
  $view->page_footer_format = '1';
  $view->page_empty = 'Could not find nodes matching the criteria.';
  $view->page_empty_format = '1';
  $view->page_type = 'bulk';
  $view->url = 'admin/content/node2';
  $view->use_pager = TRUE;
  $view->nodes_per_page = '10';
  $view->sort = array(
    array(
      'tablename' => 'node',
      'field' => 'changed',
      'sortorder' => 'DESC',
      'options' => 'normal',
    ),
  );
  $view->argument = array();
  $view->field = array(
    array(
      'tablename' => 'node',
      'field' => 'title',
      'label' => 'Title',
      'handler' => 'views_handler_field_nodelink_with_mark',
      'options' => 'link',
    ),
    array(
      'tablename' => 'node',
      'field' => 'type',
      'label' => 'Type',
    ),
    array(
      'tablename' => 'users',
      'field' => 'name',
      'label' => 'Author',
    ),
    array(
      'tablename' => 'views_bulk_operations',
      'field' => 'views_bulk_operations_field_node_status',
      'label' => 'Status',
    ),
    array(
      'tablename' => 'node',
      'field' => 'edit',
      'label' => 'Edit',
      'handler' => 'views_handler_node_edit_destination',
    ),
  );
  $view->filter = array(
    array(
      'tablename' => 'node',
      'field' => 'type',
      'operator' => 'OR',
      'options' => '',
      'value' => array(),
    ),
    array(
      'tablename' => 'node',
      'field' => 'status',
      'operator' => '=',
      'options' => '',
      'value' => '1',
    ),
    array(
      'tablename' => 'node',
      'field' => 'promote',
      'operator' => '=',
      'options' => '',
      'value' => '1',
    ),
    array(
      'tablename' => 'node',
      'field' => 'sticky',
      'operator' => '=',
      'options' => '',
      'value' => '1',
    ),
  );
  $view->exposed_filter = array(
    array(
      'tablename' => 'node',
      'field' => 'type',
      'label' => 'Type',
      'optional' => '1',
      'is_default' => '0',
      'operator' => '0',
      'single' => '0',
    ),
    array(
      'tablename' => 'node',
      'field' => 'status',
      'label' => 'Published',
      'optional' => '1',
      'is_default' => '0',
      'operator' => '0',
      'single' => '0',
    ),
    array(
      'tablename' => 'node',
      'field' => 'promote',
      'label' => 'Promoted',
      'optional' => '1',
      'is_default' => '0',
      'operator' => '0',
      'single' => '0',
    ),
    array(
      'tablename' => 'node',
      'field' => 'sticky',
      'label' => 'Sticky',
      'optional' => '1',
      'is_default' => '0',
      'operator' => '0',
      'single' => '0',
    ),
  );
  $view->requires = array(
    node,
    users,
    views_bulk_operations,
  );
  $views[$view->name] = $view;
  return $views;
}
function views_bulk_operations_views_tables() {
  $tables['views_bulk_operations'] = array(
    'name' => 'views_bulk_operations',
    'join' => array(
      'left' => array(
        'table' => 'node',
        'field' => 'nid',
      ),
      'right' => array(
        'field' => 'nid',
      ),
    ),
    'fields' => array(
      'views_bulk_operations_field_node_status' => array(
        'name' => t('Node: Status'),
        'notafield' => true,
        'help' => t('Displays the node status (published/promoted/sticky).'),
        'handler' => 'views_bulk_operations_field_node_status',
      ),
    ),
  );
  return $tables;
}
function theme_views_bulk_operations_view($view, $nodes, $type) {
  static $form_suffix;
  if (isset($form_suffix)) {
    $form_suffix++;
  }
  else {
    $form_suffix = 1;
  }
  $output = drupal_get_form('views_bulk_operations_form_' . $form_suffix, $view, $nodes, $type);
  return $output;
}
function theme_views_bulk_operations_confirmation($nodes) {
  $count = 0;
  $output = t('You selected the following !count nodes:', array(
    '!count' => count($nodes),
  )) . '<br /><ul>';
  foreach ($nodes as $nid) {

    // Number of titles to display before we say "...and more"
    if (VIEWS_BULK_OPS_MAX_CONFIRM_NODES > 0 && $count >= VIEWS_BULK_OPS_MAX_CONFIRM_NODES) {
      $output .= '<li>' . t('...and !remaining more.', array(
        '!remaining' => count($nodes) - $count,
      )) . '</li>';
      break;
    }
    if (is_numeric($nid) && $nid > 0) {
      $node = node_load($nid);
      $output .= '<li>' . check_plain($node->title) . '</li>';
      $count++;
    }
  }
  $output .= '</ul>';
  return $output;
}

/**
 * Description
 * Fetch available operations.
 *
 * @param (string) $filter
 *  'refresh' - build operations data from scratch
 *  'all' - return stored data for all available operations
 *  'enabled' - return stored data for operations that are enabled only
 *  'disabled' - return stored data for operations that are disabled only
 *
 * @param (string) $vid
 *   The ID of the view whose operations we are selecting
 *
 * @return (array)
 *   Array whose keys contain the name of the callback and values contain
 *   additional information about that callback. Format of array is as follows:
 *
 *   array(
 *     'callback' => array(
 *       label,              // human readable name of operation
 *       callback arguments, // node operations callbacks
 *       enabled,            // whether we show this operation in our views
 *       configurable,       // actions operations only
 *       type                // action or node operation
 *     )
 *   )
 */
function _views_bulk_operations_get_operations($filter, $vid) {
  if (module_exists('actions')) {
    views_bulk_operations_action_info();

    // make sure our actions are loaded
  }
  $stored_operations = _views_bulk_operations_get($vid, array());
  switch ($filter) {
    case 'all':
      if (empty($store_operations)) {
        return _views_bulk_operations_get_operations('refresh', $vid);
      }
      return $stored_operations;
    case 'refresh':
      $new_operations = array();

      // Core 'node operations'
      $node_operations = module_invoke_all('node_operations');
      foreach ($node_operations as $operation) {

        // some node operations do not provide a valid callback
        // (such as the 'delete' operation)
        if (empty($operation['callback'])) {
          continue;
        }
        $key = md5($operation['callback'] . $operation['label']);
        $enabled = isset($stored_operations[$key]['enabled']) ? $stored_operations[$key]['enabled'] : VIEWS_BULK_OPS_DEFAULT_OPERATION_ENABLE;
        $new_operations[$key] = array(
          'label' => $operation['label'],
          'callback' => $operation['callback'],
          'callback arguments' => $operation['callback arguments'],
          'enabled' => $enabled,
          'configurable' => false,
          'type' => 'node',
        );
      }

      // Actions Operations (if module available)
      if (module_exists('actions')) {
        $action_operations = actions_list() + _views_bulk_operations_get_custom_actions();
        foreach ($action_operations as $callback => $values) {

          // we do not want to include ALL actions as some are not 'node
          // operations'-like. TODO: Depending on how actions work, we may want
          // to allow admin to turn on/off action types. Don't know enough about
          // actions to determine that right now, so limiting to types I know we
          // want.
          foreach (array(
            'node',
            'workflow',
            'email',
          ) as $type) {
            if (0 != strcasecmp($type, $values['type'])) {
              continue;
            }
            $key = md5($callback . $values['description']);
            $enabled = isset($stored_operations[$key]['enabled']) ? $stored_operations[$key]['enabled'] : VIEWS_BULK_OPS_DEFAULT_OPERATION_ENABLE;
            $new_operations[$key] = array(
              'label' => $values['description'],
              'callback' => $callback,
              'callback arguments' => $values['parameters'],
              'enabled' => $enabled,
              'configurable' => $values['configurable'],
              'type' => 'action',
            );
            break;

            // out of inner, type-searching loop
          }
        }
      }
      _views_bulk_operations_set($vid, $new_operations);
      return $new_operations;
    case 'enabled':
    case 'disabled':
      $filtered_operations = array();
      $state = $filter == 'enabled' ? 1 : 0;
      foreach ($stored_operations as $operation => $values) {
        if ($values['enabled'] == $state) {
          $filtered_operations[$operation] = $values;
        }
      }
      return $filtered_operations;
  }
}
function _views_bulk_operations_get_custom_actions() {
  $actions = array();
  if (_views_bulk_operations_is_actions_6()) {
    $result = db_query("SELECT * FROM {actions} WHERE parameters > ''");
  }
  else {
    $result = db_query("SELECT *, params AS parameters FROM {actions} WHERE params > ''");
  }
  while ($action = db_fetch_object($result)) {
    $actions[$action->aid] = array(
      'description' => $action->description,
      'type' => $action->type,
      'configurable' => FALSE,
      'parameters' => $actions->parameters,
    );
  }
  return $actions;
}
function _views_bulk_operations_get($name, $default) {
  $value = db_result(db_query("SELECT value FROM {vbo_settings} WHERE name='%s'", $name));
  return $value === FALSE ? $default : unserialize($value);
}
function _views_bulk_operations_set($name, $value) {
  db_query("DELETE FROM {vbo_settings} WHERE name='%s'", $name);
  db_query("INSERT INTO {vbo_settings} (name, value) VALUES ('%s', '%s')", $name, serialize($value));
}
function views_bulk_operations_views_links($type, $view) {
  return array(
    array(
      'title' => t('Operations'),
      'href' => "admin/build/views/{$view->name}/operations",
    ),
  );
}

/**
* Implementation of hook_elements();
*
* Here we define a views_node_selector form element that can be used to
* get back 1 or more nids using a view.
*
* We use the #multiple field to indicate if this element is to be used for
* selecting a single item using radios or multiple items using checkboxes.
*/
function views_bulk_operations_elements() {
  $type['views_node_selector'] = array(
    '#input' => TRUE,
    '#process' => array(
      'expand_views_node_selector' => array(),
    ),
    '#view_nodes' => array(),
    '#view' => '',
    '#multiple' => FALSE,
    '#disabled_nodes' => array(),
  );
  return $type;
}

/**
 * A place holder for the element processing function, located in views_form.inc
 */
function expand_views_node_selector($element) {
  include_once drupal_get_path('module', 'views_bulk_operations') . '/views_form.inc';
  return _expand_views_node_selector($element);
}

/**
 * hook_forms() implementation
 * force each instance of function to use the
 * same callback
 */
function views_bulk_operations_forms() {

  // Get the form ID.
  $args = func_get_args();
  $form_id = $args[0][0];

  // 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',
    );
  }
  return $forms;
}

/**
 * Implementation of hook_action_info().
 */
function views_bulk_operations_action_info() {
  $actions = array();
  $files = file_scan_directory(drupal_get_path('module', 'views_bulk_operations'), '(.*).action.inc$');
  if ($files) {
    foreach ($files as $file) {
      require_once $file->filename;
      $action_info_fn = str_replace('.', '_', basename($file->filename, '.inc')) . '_info';
      $action_info = call_user_func($action_info_fn);
      if (is_array($action_info)) {
        $actions += $action_info;
      }
    }
  }
  return $actions;
}

Functions

Namesort descending Description
expand_views_node_selector A place holder for the element processing function, located in views_form.inc
theme_views_bulk_operations_confirmation
theme_views_bulk_operations_view
theme_views_bulk_operations_view_settings
views_bulk_operations_action_info Implementation of hook_action_info().
views_bulk_operations_elements Implementation of hook_elements();
views_bulk_operations_field_node_status
views_bulk_operations_form
views_bulk_operations_forms hook_forms() implementation force each instance of function to use the same callback
views_bulk_operations_form_alter
views_bulk_operations_form_submit
views_bulk_operations_form_validate
views_bulk_operations_help Implementation of hook_help().
views_bulk_operations_job_queue_functions
views_bulk_operations_menu Implementation of hook_menu().
views_bulk_operations_views_default_views
views_bulk_operations_views_links
views_bulk_operations_views_style_plugins Implementation of hook_views_style_plugins()
views_bulk_operations_views_tables
views_bulk_operations_views_tabs
views_bulk_operations_view_settings Bulk operations config page for a given view.
views_bulk_operations_view_settings_submit
views_bulk_operations_view_settings_validate
_views_bulk_operations_action_do
_views_bulk_operations_action_form
_views_bulk_operations_action_submit
_views_bulk_operations_action_validate
_views_bulk_operations_get
_views_bulk_operations_get_custom_actions
_views_bulk_operations_get_operations Description Fetch available operations.
_views_bulk_operations_is_actions_6
_views_bulk_operations_queue_process
_views_bulk_operations_set

Constants