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.moduleView 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
Constants
Name | Description |
---|---|
VIEWS_BULK_OPS_DEFAULT_OPERATION_ENABLE | |
VIEWS_BULK_OPS_MAX_CONFIRM_NODES | |
VIEWS_BULK_OPS_STEP_CONFIG | |
VIEWS_BULK_OPS_STEP_CONFIRM | |
VIEWS_BULK_OPS_STEP_SINGLE | |
VIEWS_BULK_OPS_STEP_VIEW | @file Allow bulk node operations directly within views. |