- <?php
-
- * @file
- * Allows operations to be performed on items selected in a view.
- */
-
- define('VBO_ACCESS_OP_VIEW', 0x01);
- define('VBO_ACCESS_OP_UPDATE', 0x02);
- define('VBO_ACCESS_OP_CREATE', 0x04);
- define('VBO_ACCESS_OP_DELETE', 0x08);
-
- * Implements hook_action_info().
- * Registers custom VBO actions as Drupal actions.
- */
- function views_bulk_operations_action_info() {
- $actions = array();
- $files = views_bulk_operations_load_action_includes();
- foreach ($files as $filename) {
- $action_info_fn = 'views_bulk_operations_'. str_replace('.', '_', basename($filename, '.inc')).'_info';
- $action_info = call_user_func($action_info_fn);
- if (is_array($action_info)) {
- $actions += $action_info;
- }
- }
-
- return $actions;
- }
-
- * Loads the VBO actions placed in their own include files (under actions/).
- *
- * @return
- * An array of containing filenames of the included actions.
- */
- function views_bulk_operations_load_action_includes() {
- static $loaded = FALSE;
-
-
-
-
- $path = drupal_get_path('module', 'views_bulk_operations') . '/actions/';
- $files = array(
- 'archive.action.inc',
- 'argument_selector.action.inc',
- 'delete.action.inc',
- 'modify.action.inc',
- 'script.action.inc',
- 'user_roles.action.inc',
- );
-
- if (!$loaded) {
- foreach ($files as $file) {
- include_once $path . $file;
- }
- $loaded = TRUE;
- }
-
- return $files;
- }
-
- * Implements hook_cron().
- *
- * Deletes queue items belonging to VBO active queues (used by VBO's batches)
- * that are older than a day (since they can only be a result of VBO crashing
- * or the execution being interrupted in some other way). This is the interval
- * used to cleanup batches in system_cron(), so it can't be increased.
- *
- * Note: This code is specific to SystemQueue. Other queue implementations will
- * need to do their own garbage collection.
- */
- function views_bulk_operations_cron() {
- db_delete('queue')
- ->condition('name', db_like('views_bulk_operations_active_queue_'), 'LIKE')
- ->condition('created', REQUEST_TIME - 864000, '<')
- ->execute();
- }
-
- * Implements of hook_cron_queue_info().
- */
- function views_bulk_operations_cron_queue_info() {
- return array(
- 'views_bulk_operations' => array(
- 'worker callback' => 'views_bulk_operations_queue_item_process',
- 'time' => 30,
- ),
- );
- }
-
- * Implements hook_views_api().
- */
- function views_bulk_operations_views_api() {
- return array(
- 'api' => 3,
- 'path' => drupal_get_path('module', 'views_bulk_operations') . '/views',
- );
- }
-
- * Implements hook_theme().
- */
- function views_bulk_operations_theme() {
- $themes = array(
- 'views_bulk_operations_select_all' => array(
- 'variables' => array('view' => NULL, 'enable_select_all_pages' => TRUE),
- ),
- 'views_bulk_operations_confirmation' => array(
- 'variables' => array('rows' => NULL, 'vbo' => NULL),
- ),
- );
- $files = views_bulk_operations_load_action_includes();
- foreach ($files as $filename) {
- $action_theme_fn = 'views_bulk_operations_'. str_replace('.', '_', basename($filename, '.inc')).'_theme';
- if (function_exists($action_theme_fn)) {
- $themes += call_user_func($action_theme_fn);
- }
- }
-
- return $themes;
- }
-
- * Implements hook_ctools_plugin_type().
- */
- function views_bulk_operations_ctools_plugin_type() {
- return array(
- 'operation_types' => array(
- 'classes' => array(
- 'handler',
- ),
- ),
- );
- }
-
- * Implements hook_ctools_plugin_directory().
- */
- function views_bulk_operations_ctools_plugin_directory($module, $plugin) {
- if ($module == 'views_bulk_operations') {
- return 'plugins/' . $plugin;
- }
- }
-
- * Fetch metadata for a specific operation type plugin.
- *
- * @param $operation_type
- * Name of the plugin.
- *
- * @return
- * An array with information about the requested operation type plugin.
- */
- function views_bulk_operations_get_operation_type($operation_type) {
- ctools_include('plugins');
- return ctools_get_plugins('views_bulk_operations', 'operation_types', $operation_type);
- }
-
- * Fetch metadata for all operation type plugins.
- *
- * @return
- * An array of arrays with information about all available operation types.
- */
- function views_bulk_operations_get_operation_types() {
- ctools_include('plugins');
- return ctools_get_plugins('views_bulk_operations', 'operation_types');
- }
-
- * Gets the info array of an operation from the provider plugin.
- *
- * @param $operation_id
- * The id of the operation for which the info shall be returned, or NULL
- * to return an array with info about all operations.
- */
- function views_bulk_operations_get_operation_info($operation_id = NULL) {
- $operations = &drupal_static(__FUNCTION__);
-
- if (!isset($operations)) {
- $operations = array();
- $plugins = views_bulk_operations_get_operation_types();
- foreach ($plugins as $plugin) {
- $operations += $plugin['list callback']();
- }
-
- uasort($operations, create_function('$a, $b', 'return strcasecmp($a["label"], $b["label"]);'));
- }
-
- if (!empty($operation_id)) {
- return $operations[$operation_id];
- }
- else {
- return $operations;
- }
- }
-
- * Returns an operation instance.
- *
- * @param $operation_id
- * The id of the operation to instantiate.
- * For example: action::node_publish_action.
- * @param $entity_type
- * The entity type on which the operation operates.
- * @param $options
- * Options for this operation (label, operation settings, etc.)
- */
- function views_bulk_operations_get_operation($operation_id, $entity_type, $options) {
- $operations = &drupal_static(__FUNCTION__);
-
- if (!isset($operations[$operation_id])) {
-
-
-
- $id_fragments = explode('::', $operation_id);
- $plugin = views_bulk_operations_get_operation_type($id_fragments[0]);
- $operation_info = $plugin['list callback']($operation_id);
-
- if ($operation_info) {
- $operations[$operation_id] = new $plugin['handler']['class']($operation_id, $entity_type, $operation_info, $options);
- }
- else {
- $operations[$operation_id] = FALSE;
- }
- }
-
- return $operations[$operation_id];
- }
-
- * Get all operations that match the current entity type.
- *
- * @param $entity_type
- * Entity type.
- * @param $options
- * An array of options for all operations, in the form of
- * $operation_id => $operation_options.
- */
- function views_bulk_operations_get_applicable_operations($entity_type, $options) {
- $operations = array();
- foreach (views_bulk_operations_get_operation_info() as $operation_id => $operation_info) {
- if ($operation_info['type'] == $entity_type || $operation_info['type'] == 'entity' || $operation_info['type'] == 'system') {
- $options[$operation_id] = !empty($options[$operation_id]) ? $options[$operation_id] : array();
- $operations[$operation_id] = views_bulk_operations_get_operation($operation_id, $entity_type, $options[$operation_id]);
- }
- }
-
- return $operations;
- }
-
- * Gets the VBO field if it exists on the passed-in view.
- *
- * @return
- * The field object if found. Otherwise, FALSE.
- */
- function _views_bulk_operations_get_field($view) {
- foreach ($view->field as $field_name => $field) {
- if ($field instanceof views_bulk_operations_handler_field_operations) {
-
- $field->view = $view;
- return $field;
- }
- }
- return FALSE;
- }
-
- * Implements hook_views_form_substitutions().
- */
- function views_bulk_operations_views_form_substitutions() {
-
-
- $select_all_placeholder = check_plain('<!--views-bulk-operations-select-all-->');
- $select_all = array(
- '#type' => 'checkbox',
- '#default_value' => FALSE,
- '#attributes' => array('class' => array('vbo-table-select-all')),
- );
-
- return array(
- $select_all_placeholder => drupal_render($select_all),
- );
- }
-
- * Implements hook_form_alter().
- */
- function views_bulk_operations_form_alter(&$form, &$form_state, $form_id) {
- if (strpos($form_id, 'views_form_') === 0) {
- $vbo = _views_bulk_operations_get_field($form_state['build_info']['args'][0]);
- }
-
- if (empty($vbo)) {
- return;
- }
-
-
-
-
-
- $form_state['cache'] = TRUE;
-
- if (empty($vbo->view->override_url)) {
-
-
-
- if (!empty($vbo->view->preview) || $vbo->view->display_handler instanceof views_plugin_display_block) {
- $vbo->view->override_url = $_GET['q'];
-
-
- $query = drupal_get_query_parameters($_GET, array('q'));
- $form['#action'] = url($_GET['q'], array('query' => $query));
- }
- }
-
-
- if ($form_state['step'] == 'views_form_views_form') {
- $form = views_bulk_operations_form($form, $form_state, $vbo);
- }
-
-
- drupal_alter('views_bulk_operations_form', $form, $form_state, $vbo);
- }
-
- * Returns the 'select all' div that gets inserted below the table header row
- * (for table style plugins with grouping disabled), or above the view results
- * (for non-table style plugins), providing a choice between selecting items
- * on the current page, and on all pages.
- *
- * The actual insertion is done by JS, matching the degradation behavior
- * of Drupal core (no JS - no select all).
- */
- function theme_views_bulk_operations_select_all($variables) {
- $view = $variables['view'];
- $enable_select_all_pages = $variables['enable_select_all_pages'];
- $form = array();
-
- if ($view->style_plugin instanceof views_plugin_style_table && empty($view->style_plugin->options['grouping'])) {
- if (!$enable_select_all_pages) {
- return '';
- }
-
- $wrapper_class = 'vbo-table-select-all-markup';
- $this_page_count = format_plural(count($view->result), '1 row', '@count rows');
- $this_page = t('Selected <strong>!row_count</strong> in this page.', array('!row_count' => $this_page_count));
- $all_pages_count = format_plural($view->total_rows, '1 row', '@count rows');
- $all_pages = t('Selected <strong>!row_count</strong> in this view.', array('!row_count' => $all_pages_count));
-
- $form['select_all_pages'] = array(
- '#type' => 'button',
- '#attributes' => array('class' => array('vbo-table-select-all-pages')),
- '#value' => t('Select all !row_count in this view.', array('!row_count' => $all_pages_count)),
- '#prefix' => '<span class="vbo-table-this-page">' . $this_page . ' ',
- '#suffix' => '</span>',
- );
- $form['select_this_page'] = array(
- '#type' => 'button',
- '#attributes' => array('class' => array('vbo-table-select-this-page')),
- '#value' => t('Select only !row_count in this page.', array('!row_count' => $this_page_count)),
- '#prefix' => '<span class="vbo-table-all-pages" style="display: none">' . $all_pages . ' ',
- '#suffix' => '</span>',
- );
- }
- else {
- $wrapper_class = 'vbo-select-all-markup';
-
- $form['select_all'] = array(
- '#type' => 'fieldset',
- '#attributes' => array('class' => array('vbo-fieldset-select-all')),
- );
- $form['select_all']['this_page'] = array(
- '#type' => 'checkbox',
- '#title' => t('Select all items on this page'),
- '#default_value' => '',
- '#attributes' => array('class' => array('vbo-select-this-page')),
- );
-
- if ($enable_select_all_pages) {
- $form['select_all']['or'] = array(
- '#type' => 'markup',
- '#markup' => '<em>OR</em>',
- );
- $form['select_all']['all_pages'] = array(
- '#type' => 'checkbox',
- '#title' => t('Select all items on all pages'),
- '#default_value' => '',
- '#attributes' => array('class' => array('vbo-select-all-pages')),
- );
- }
- }
-
- $output = '<div class="' . $wrapper_class . '">';
- $output .= drupal_render($form);
- $output .= '</div>';
-
- return $output;
- }
-
- * Extend the views_form multistep form with elements for executing an operation.
- */
- function views_bulk_operations_form($form, &$form_state, $vbo) {
- $form['#attached']['js'][] = drupal_get_path('module', 'views_bulk_operations') . '/js/views_bulk_operations.js';
- $form['#attached']['css'][] = drupal_get_path('module', 'views_bulk_operations') . '/css/views_bulk_operations.css';
- $form['#prefix'] = '<div class="vbo-views-form">';
- $form['#suffix'] = '</div>';
-
-
- if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('/msie/i', $_SERVER['HTTP_USER_AGENT'])) {
- drupal_add_http_header('Cache-Control', 'no-cache');
- }
- else {
- drupal_add_http_header('Cache-Control', 'no-store');
- }
-
-
- $form['select_all'] = array(
- '#type' => 'hidden',
- '#attributes' => array('class' => 'select-all-rows'),
- '#default_value' => FALSE,
- );
-
-
-
- $has_other_views_form_handlers = FALSE;
- foreach ($vbo->view->field as $field) {
- if (property_exists($field, 'views_form_callback') || method_exists($field, 'views_form')) {
- if (!($field instanceof views_bulk_operations_handler_field_operations)) {
- $has_other_views_form_handlers = TRUE;
- }
- }
- }
- if (!$has_other_views_form_handlers) {
- $form['actions']['submit']['#access'] = FALSE;
- }
-
- $form['select'] = array(
- '#type' => 'fieldset',
- '#title' => t('Operations'),
- '#collapsible' => TRUE,
- '#attributes' => array('class' => array('container-inline')),
- );
- if ($vbo->get_vbo_option('display_type') == 0) {
- $options = array(0 => t('- Choose an operation -'));
- foreach ($vbo->get_selected_operations() as $operation_id => $operation) {
- $options[$operation_id] = $operation->label();
- }
-
-
- $form['select']['operation'] = array(
- '#type' => 'select',
- '#options' => $options,
- );
- $form['select']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Execute'),
- '#validate' => array('views_bulk_operations_form_validate'),
- '#submit' => array('views_bulk_operations_form_submit'),
- );
- }
- else {
-
- foreach ($vbo->get_selected_operations() as $operation_id => $operation) {
- $form['select'][$operation_id] = array(
- '#type' => 'submit',
- '#value' => $operation->label(),
- '#validate' => array('views_bulk_operations_form_validate'),
- '#submit' => array('views_bulk_operations_form_submit'),
- '#operation_id' => $operation_id,
- );
- }
- }
-
-
-
-
-
- if (!empty($vbo->view->result) && !$vbo->get_vbo_option('force_single')) {
- $enable_select_all_pages = FALSE;
-
-
- if (count($vbo->view->result) != $vbo->view->total_rows && $vbo->get_vbo_option('enable_select_all_pages')) {
- $enable_select_all_pages = TRUE;
- }
- $form['select_all_markup'] = array(
- '#type' => 'markup',
- '#markup' => theme('views_bulk_operations_select_all', array('view' => $vbo->view, 'enable_select_all_pages' => $enable_select_all_pages)),
- );
- }
-
- return $form;
- }
-
- * Validation callback for the first step of the VBO form.
- */
- function views_bulk_operations_form_validate($form, &$form_state) {
- $vbo = _views_bulk_operations_get_field($form_state['build_info']['args'][0]);
-
- if (!empty($form_state['triggering_element']['#operation_id'])) {
- $form_state['values']['operation'] = $form_state['triggering_element']['#operation_id'];
- }
- if (!$form_state['values']['operation']) {
- form_set_error('operation', t('No operation selected. Please select an operation to perform.'));
- }
-
- $field_name = $vbo->options['id'];
- $selection = _views_bulk_operations_get_selection($vbo, $form_state);
- if (!$selection) {
- form_set_error($field_name, t('Please select at least one item.'));
- }
- }
-
- * Multistep form callback for the "configure" step.
- */
- function views_bulk_operations_config_form($form, &$form_state, $view, $output) {
- $vbo = _views_bulk_operations_get_field($view);
- $operation = $form_state['operation'];
- drupal_set_title(t('Set parameters for %operation', array('%operation' => $operation->label())), PASS_THROUGH);
-
- $context = array(
- 'entity_type' => $vbo->get_entity_type(),
-
-
-
-
- 'view' => $view,
- );
- $form += $operation->form($form, $form_state, $context);
-
- $query = drupal_get_query_parameters($_GET, array('q'));
- $form['actions'] = array(
- '#type' => 'container',
- '#attributes' => array('class' => array('form-actions')),
- '#weight' => 999,
- );
- $form['actions']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Next'),
- '#validate' => array('views_bulk_operations_config_form_validate'),
- '#submit' => array('views_bulk_operations_form_submit'),
- '#suffix' => l(t('Cancel'), $vbo->view->get_url(), array('query' => $query)),
- );
-
- return $form;
- }
-
- * Validation callback for the "configure" step.
- * Gives the operation a chance to validate its config form.
- */
- function views_bulk_operations_config_form_validate($form, &$form_state) {
- $operation = &$form_state['operation'];
- $operation->formValidate($form, $form_state);
- }
-
- * Multistep form callback for the "confirm" step.
- */
- function views_bulk_operations_confirm_form($form, &$form_state, $view, $output) {
- $vbo = _views_bulk_operations_get_field($view);
- $operation = $form_state['operation'];
- $rows = $form_state['selection'];
- $query = drupal_get_query_parameters($_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' => $view->get_url(), 'query' => $query),
- theme('views_bulk_operations_confirmation', array('rows' => $rows, 'vbo' => $vbo, 'select_all_pages' => $form_state['select_all_pages']))
- );
-
- $form['actions']['submit']['#submit'] = array('views_bulk_operations_form_submit');
-
- return $form;
- }
-
- * Theme function to show the confirmation page before executing the operation.
- */
- function theme_views_bulk_operations_confirmation($variables) {
- $select_all_pages = $variables['select_all_pages'];
- $vbo = $variables['vbo'];
- $entity_type = $vbo->get_entity_type();
- $rows = $variables['rows'];
- $items = array();
-
- $entities = _views_bulk_operations_entity_load($entity_type, array_values($rows), $vbo->revision);
- foreach ($entities as $entity) {
- $items[] = check_plain(_views_bulk_operations_entity_label($entity_type, $entity));
- }
-
- if ($select_all_pages) {
- $more_count = $vbo->view->total_rows - count($vbo->view->result);
- $items[] = t('...and <strong>!count</strong> more.', array('!count' => $more_count));
- }
-
- $count = format_plural(count($entities), 'item', '@count items');
- $output = theme('item_list', array('items' => $items, 'title' => t('You selected the following <strong>!count</strong>:', array('!count' => $count))));
- return $output;
- }
-
- * Goes through the submitted values, and returns
- * an array of selected rows, in the form of
- * $row_index => $entity_id.
- */
- function _views_bulk_operations_get_selection($vbo, $form_state) {
- $selection = array();
- $field_name = $vbo->options['id'];
-
- if (!empty($form_state['values'][$field_name])) {
-
- if (is_array($form_state['values'][$field_name])) {
- $selection = array_filter($form_state['values'][$field_name]);
- }
- else {
- $selection = array($form_state['values'][$field_name]);
- }
- }
-
- return $selection;
- }
-
- * Submit handler for all steps of the VBO multistep form.
- */
- function views_bulk_operations_form_submit($form, &$form_state) {
- $vbo = _views_bulk_operations_get_field($form_state['build_info']['args'][0]);
- $entity_type = $vbo->get_entity_type();
-
- switch ($form_state['step']) {
- case 'views_form_views_form':
- $form_state['selection'] = _views_bulk_operations_get_selection($vbo, $form_state);
- $form_state['select_all_pages'] = $form_state['values']['select_all'];
-
- $options = $vbo->get_operation_options($form_state['values']['operation']);
- $form_state['operation'] = $operation = views_bulk_operations_get_operation($form_state['values']['operation'], $entity_type, $options);
- if (!$operation->configurable() && $operation->getAdminOption('skip_confirmation')) {
- break;
- }
- $form_state['step'] = $operation->configurable() ? 'views_bulk_operations_config_form' : 'views_bulk_operations_confirm_form';
- $form_state['rebuild'] = TRUE;
- return;
-
- case 'views_bulk_operations_config_form':
- $form_state['step'] = 'views_bulk_operations_confirm_form';
- $operation = &$form_state['operation'];
- $operation->formSubmit($form, $form_state);
-
- if ($operation->getAdminOption('skip_confirmation')) {
- break;
- }
- $form_state['rebuild'] = TRUE;
- return;
-
- case 'views_bulk_operations_confirm_form':
- break;
- }
-
-
- views_bulk_operations_execute($vbo, $form_state['operation'], $form_state['selection'], $form_state['select_all_pages']);
-
-
- $query = drupal_get_query_parameters($_GET, array('q'));
- $form_state['redirect'] = array('path' => $vbo->view->get_url(), array('query' => $query));
- }
-
- * Entry point for executing the chosen operation upon selected rows.
- *
- * If the selected operation is an aggregate operation (requiring all selected
- * items to be passed at the same time), or the execution is being triggered
- * through Drush, the operation is executed directly.
- * This means that there is no batching & queueing, the PHP execution
- * time limit is ignored (if allowed), all selected entities are loaded and
- * processed.
- *
- * Otherwise, the selected entity ids are divided into groups not larger than
- * $entity_load_capacity, and enqueued for processing.
- * If all items on all pages should be processed, a batch job runs that
- * collects and enqueues the items from all pages of the view, page by page.
- *
- * Based on the "Enqueue the operation instead of executing it directly"
- * VBO field setting, the newly filled queue is either processed at cron
- * time by the VBO worker function, or right away in a new batch job.
- *
- * @param $vbo
- * The VBO field, containing a reference to the view in $vbo->view.
- * @param $operation
- * The operation object.
- * @param $selection
- * An array in the form of $row_index => $entity_id.
- * @param $select_all_pages
- * Whether all items on all pages should be selected.
- */
- function views_bulk_operations_execute($vbo, $operation, $selection, $select_all_pages = FALSE) {
- global $user;
-
-
-
- if ($operation->aggregate() && $select_all_pages) {
- views_bulk_operations_direct_adjust($selection, $vbo);
- }
-
-
- $options = array(
- 'revision' => $vbo->revision,
- 'entity_load_capacity' => $vbo->get_vbo_option('entity_load_capacity', 10),
- );
-
- $rows = array();
- $current = 1;
- foreach ($selection as $row_index => $entity_id) {
- $rows[$row_index] = array(
- 'entity_id' => $entity_id,
- 'views_row' => array(),
-
-
-
- 'position' => array(
- 'current' => $current++,
- 'total' => count($selection),
- ),
- );
-
- if ($operation->needsRows()) {
- $rows[$row_index]['views_row'] = $vbo->view->result[$row_index];
- }
- }
-
-
- if ($operation->aggregate()) {
- views_bulk_operations_direct_process($operation, $rows, $options);
- return;
- }
-
-
- if ($operation->getAdminOption('postpone_processing')) {
-
- $queue_name = 'views_bulk_operations';
- }
- else {
-
- $queue_name = 'views_bulk_operations_active_queue_' . db_next_id();
- }
-
- $batch = array(
- 'operations' => array(),
- 'finished' => 'views_bulk_operations_execute_finished',
- 'progress_message' => '',
- 'title' => t('Performing %operation on the selected items...', array('%operation' => $operation->label())),
- );
-
-
-
- if ($select_all_pages && $vbo->view->query->pager->has_more_records()) {
- $total_rows = $vbo->view->total_rows;
-
-
-
- $view_info = array(
- 'name' => $vbo->view->name,
- 'display' => $vbo->view->current_display,
- 'arguments' => $vbo->view->args,
- 'exposed_input' => $vbo->view->get_exposed_input(),
- 'entity_load_capacity' => $vbo->get_vbo_option('entity_load_capacity', 10),
- );
-
- $batch['operations'][] = array(
- 'views_bulk_operations_adjust_selection', array($view_info, $queue_name, $operation, $options),
- );
- }
- else {
- $total_rows = count($rows);
-
-
- views_bulk_operations_enqueue_rows($queue_name, $rows, $operation, $options);
-
-
-
- if ($operation->getAdminOption('postpone_processing')) {
- drupal_set_message(t('Enqueued the selected operation (%operation).', array(
- '%operation' => $operation->label(),
- )));
- }
- }
-
-
- if (!$operation->getAdminOption('postpone_processing')) {
- $batch['operations'][] = array(
- 'views_bulk_operations_active_queue_process', array($queue_name, $operation, $total_rows),
- );
- }
-
-
- if (count($batch['operations'])) {
- batch_set($batch);
- }
- }
-
- * Batch API callback: loads the view page by page and enqueues all items.
- *
- * @param $view_info
- * An array of information about the view, used to recreate and re-execute it.
- * @param $queue_name
- * The name of the queue to which the items should be added.
- * @param $operation
- * The operation object.
- * @param $options
- * An array of options that affect execution (revision, entity_load_capacity).
- * Passed along with each new queue item.
- */
- function views_bulk_operations_adjust_selection($view_info, $queue_name, $operation, $options, &$context) {
- if (!isset($context['sandbox']['progress'])) {
- $context['sandbox']['progress'] = 0;
- $context['sandbox']['max'] = 0;
- }
-
- $view = views_get_view($view_info['name']);
- $view->set_exposed_input($view_info['exposed_input']);
- $view->set_arguments($view_info['arguments']);
- $view->set_display($view_info['display']);
- $view->set_offset($context['sandbox']['progress']);
- $view->build();
- $view->execute($view_info['display']);
-
- if (empty($context['sandbox']['max'])) {
- $context['sandbox']['max'] = $view->total_rows;
- }
-
- $vbo = _views_bulk_operations_get_field($view);
- $rows = array();
- foreach ($view->result as $row_index => $result) {
- $rows[$row_index] = array(
- 'entity_id' => $vbo->get_value($result),
- 'views_row' => array(),
- 'position' => array(
- 'current' => ++$context['sandbox']['progress'],
- 'total' => $view->total_rows,
- ),
- );
-
- if ($operation->needsRows()) {
- $rows[$row_index]['views_row'] = $result;
- }
- }
-
-
- views_bulk_operations_enqueue_rows($queue_name, $rows, $operation, $options);
-
- if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
-
- $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
- $context['message'] = t('Prepared @current out of @total', array('@current' => $context['sandbox']['progress'], '@total' => $context['sandbox']['max']));
- }
- else {
-
- if ($operation->getAdminOption('postpone_processing')) {
- $context['results']['log'][] = t('Enqueued the selected operation (%operation).', array(
- '%operation' => $operation->label(),
- ));
- }
- }
- }
-
- * Divides the passed rows into groups and enqueues each group for processing
- *
- * @param $queue_name
- * The name of the queue.
- * @param $rows
- * The rows to be enqueued.
- * @param $operation
- * The object representing the current operation.
- * Passed along with each new queue item.
- * @param $options
- * An array of options that affect execution (revision, entity_load_capacity).
- * Passed along with each new queue item.
- */
- function views_bulk_operations_enqueue_rows($queue_name, $rows, $operation, $options) {
- global $user;
-
- $queue = DrupalQueue::get($queue_name, TRUE);
- $row_groups = array_chunk($rows, $options['entity_load_capacity'], TRUE);
-
- foreach ($row_groups as $row_group) {
- $entity_ids = array();
- foreach ($row_group as $row) {
- $entity_ids[] = $row['entity_id'];
- }
-
- $job = array(
- 'title' => t('Perform %operation on @type !entity_ids.', array(
- '%operation' => $operation->label(),
- '@type' => $operation->entityType,
- '!entity_ids' => implode(',', $entity_ids),
- )),
- 'uid' => $user->uid,
- 'arguments' => array($row_group, $operation, $options),
- );
- $queue->createItem($job);
- }
- }
-
- * Batch API callback: processes the active queue.
- *
- * @param $queue_name
- * The name of the queue to process.
- * @param $operation
- * The object representing the current operation.
- * @param $total_rows
- * The total number of processable items (across all queue items), used
- * to report progress.
- *
- * @see views_bulk_operations_queue_item_process()
- */
- function views_bulk_operations_active_queue_process($queue_name, $operation, $total_rows, &$context) {
- static $queue;
-
-
- @set_time_limit(0);
-
-
- if (!isset($context['sandbox']['progress'])) {
- $context['sandbox']['progress'] = 0;
- $context['sandbox']['max'] = $total_rows;
- $context['results']['log'] = array();
- }
-
- if (!isset($queue)) {
- $queue = DrupalQueue::get($queue_name, TRUE);
- }
-
-
- $queue_item = $queue->claimItem(3600);
- if ($queue_item) {
-
- views_bulk_operations_queue_item_process($queue_item->data, $context['results']['log']);
- $queue->deleteItem($queue_item);
-
-
- $context['sandbox']['progress'] += count($queue_item->data['arguments'][0]);
- $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
- $context['message'] = t('Processed @current out of @total', array('@current' => $context['sandbox']['progress'], '@total' => $context['sandbox']['max']));
- }
-
- if (!$queue_item || $context['finished'] === 1) {
-
- $context['results']['log'][] = t('Performed %operation on @items.', array(
- '%operation' => $operation->label(),
- '@items' => format_plural($context['sandbox']['progress'], '1 item', '@count items'),
- ));
- }
- }
-
- * Processes the provided queue item.
- *
- * Used as a worker callback defined by views_bulk_operations_cron_queue_info()
- * to process the site queue, as well as by
- * views_bulk_operations_active_queue_process() to process the active queue.
- *
- * @param $queue_item_arguments
- * The arguments of the queue item to process.
- * @param $log
- * An injected array of log messages, to be modified by reference.
- * If NULL, the function defaults to using watchdog.
- */
- function views_bulk_operations_queue_item_process($queue_item_data, &$log = NULL) {
- list($row_group, $operation, $options) = $queue_item_data['arguments'];
- $account = user_load($queue_item_data['uid']);
- $entity_type = $operation->entityType;
- $entity_ids = array();
- foreach ($row_group as $row_id => $row) {
- $entity_ids[] = $row['entity_id'];
- }
-
- $entities = _views_bulk_operations_entity_load($entity_type, $entity_ids, $options['revision']);
- foreach ($row_group as $row_id => $row) {
- $entity_id = $row['entity_id'];
-
- if (!isset($entities[$entity_id])) {
- continue;
- }
-
- $entity = $entities[$entity_id];
-
- if (!_views_bulk_operations_entity_access($operation, $entity_type, $entity, $account)) {
- $message = 'Skipped %operation on @type %title due to insufficient permissions.';
- $arguments = array(
- '%operation' => $operation->label(),
- '@type' => $entity_type,
- '%title' => _views_bulk_operations_entity_label($entity_type, $entity),
- );
-
- if ($log) {
- $log[] = t($message, $arguments);
- }
- else {
- watchdog('views bulk operations', $message, $arguments, WATCHDOG_ALERT);
- }
-
- continue;
- }
-
- $operation_context = array(
- 'progress' => $row['position'],
- 'rows' => $row['views_row'],
- );
- $operation->execute($entity, $operation_context);
-
- unset($row_group[$row_id]);
- }
- }
-
- * Adjusts the selection for the direct execution method.
- *
- * Just like the direct method itself, this is legacy code, used only for
- * aggregate actions.
- */
- function views_bulk_operations_direct_adjust(&$selection, $vbo) {
-
- $view = views_get_view($vbo->view->name);
- $view->set_exposed_input($vbo->view->get_exposed_input());
- $view->set_arguments($vbo->view->args);
- $view->set_display($vbo->view->current_display);
- $view->display_handler->set_option('pager', array('type' => 'none', 'options' => array()));
- $view->build();
-
-
-
- foreach ($view->field as $field_name => $field) {
- if ($field_name != $vbo->options['id']) {
- unset($view->field[$field_name]);
- }
- }
-
- $view->execute($vbo->view->current_display);
- $results = array();
- foreach ($view->result as $row_index => $result) {
- $results[$row_index] = $vbo->get_value($result);
- }
- $selection = $results;
- }
-
- * Processes the passed rows directly (without batching and queueing).
- *
- * This is a legacy function that is now only used for aggregate operations.
- */
- function views_bulk_operations_direct_process($operation, $rows, $options) {
- @set_time_limit(0);
-
-
-
- $context = array();
- $context['results']['progress'] = 0;
- $context['results']['log'] = array();
-
- $entity_type = $operation->entityType;
- $entity_ids = array();
- foreach ($rows as $row_index => $row) {
- $entity_ids[] = $row['entity_id'];
- }
- $entities = _views_bulk_operations_entity_load($entity_type, $entity_ids, $options['revision']);
-
- foreach ($entities as $id => $entity) {
- if (!_views_bulk_operations_entity_access($operation, $entity_type, $entity)) {
- $context['results']['log'][] = t('Skipped %operation on @type %title due to insufficient permissions.', array(
- '%operation' => $operation->label(),
- '@type' => $entity_type,
- '%title' => _views_bulk_operations_entity_label($entity_type, $entity),
- ));
- unset($entities[$id]);
- }
- }
- if (empty($entities)) {
- return;
- }
-
-
- $operation_context = array();
- if ($operation->needsRows()) {
- $operation_context['rows'] = array();
- foreach ($rows as $row_index => $row) {
- $operation_context['rows'][$row_index] = $row['views_row'];
- }
- }
- $operation->execute($entities, $operation_context);
-
- $context['results']['log'][] = t('Performed aggregate %operation on @items.', array(
- '%operation' => $operation->label(),
- '@items' => format_plural(count($entities), '1 item', '@count items'),
- ));
- $context['results']['progress'] += count($entities);
-
- views_bulk_operations_execute_finished(TRUE, $context['results'], array());
- }
-
- * Helper function that runs after the execution process is complete.
- */
- function views_bulk_operations_execute_finished($success, $results, $operations) {
- if ($success) {
- if (count($results['log']) > 1) {
- $message = theme('item_list', array('items' => $results['log']));
- }
- else {
- $message = reset($results['log']);
- }
- }
- else {
-
-
- $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)));
- }
-
- _views_bulk_operations_log($message);
- }
-
- * Helper function to verify access permission to operate on an entity.
- */
- function _views_bulk_operations_entity_access($operation, $entity_type, $entity, $account = NULL) {
- if (!entity_type_supports($entity_type, '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->getAccessMask() & $bit) {
- if (!entity_access($op, $entity_type, $entity, $account)) {
- return FALSE;
- }
- }
- }
-
- return TRUE;
- }
-
- * Loads multiple entities by their entity or revision ids, and returns them,
- * keyed by the id used for loading.
- */
- function _views_bulk_operations_entity_load($entity_type, $ids, $revision = FALSE) {
- if (!$revision) {
- $entities = entity_load($entity_type, $ids);
- }
- else {
-
- $info = entity_get_info($entity_type);
- $entities = array();
- foreach ($ids as $revision_id) {
- $loaded_entities = entity_load($entity_type, array(), array($info['entity keys']['revision'] => $revision_id));
- $entities[$revision_id] = reset($loaded_entities);
- }
- }
-
- return $entities;
- }
-
- * Label function for entities.
- * Core entities don't declare the "label" key, so entity_label() fails,
- * and a fallback is needed. This function provides that fallback.
- */
- function _views_bulk_operations_entity_label($entity_type, $entity) {
- $label = entity_label($entity_type, $entity);
- if (!$label) {
- $entity_info = entity_get_info($entity_type);
- $id_key = $entity_info['entity keys']['id'];
-
- if (isset($entity->name)) {
- $label = $entity->name;
- }
- elseif (isset($entity->{$id_key})) {
-
- $label = $entity->{$id_key};
- }
- }
-
- return $label;
- }
-
- * Helper function to report an error.
- */
- function _views_bulk_operations_report_error($msg, $arg) {
- watchdog('views bulk operations', $msg, $arg, WATCHDOG_ERROR);
- if (function_exists('drush_set_error')) {
- drush_set_error('VIEWS_BULK_OPERATIONS_EXECUTION_ERROR', strip_tags(dt($msg, $arg)));
- }
- }
-
- * Display a message to the user through the relevant function.
- */
- function _views_bulk_operations_log($msg) {
-
- if (function_exists('drush_log')) {
- drush_log(strip_tags($msg), 'ok');
- }
- else {
- drupal_set_message($msg);
- }
- }
-