contrib modules security updates

This commit is contained in:
Bachir Soussi Chiadmi
2016-10-13 12:10:40 +02:00
parent ffd758abc9
commit 747127f643
732 changed files with 67976 additions and 23207 deletions

View File

@@ -18,6 +18,7 @@ function views_bulk_operations_archive_action_info() {
// "Create an advanced action" dropdown on admin/config/system/actions.
'configurable' => FALSE,
'vbo_configurable' => TRUE,
'behavior' => array('views_property'),
'triggers' => array('any'),
);
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* @file
* Implements actions for managing books (book.module).
*/
function views_bulk_operations_book_action_info() {
$actions = array();
if (module_exists('book')) {
$actions['views_bulk_operations_move_to_book_action'] = array(
'type' => 'node',
'label' => t('Move to book'),
'configurable' => TRUE,
'behavior' => array('changes_property'),
'triggers' => array('any'),
);
$actions['views_bulk_operations_remove_from_book_action'] = array(
'type' => 'node',
'label' => t('Remove from book'),
'configurable' => FALSE,
'triggers' => array('any'),
);
}
return $actions;
}
function views_bulk_operations_move_to_book_action_form($context) {
$form = array();
if (!isset($context['book'])) {
$context['book'] = '';
}
$options = array();
$books = book_get_books();
foreach ($books as $value) {
$options[$value['nid']] = $value['title'];
}
if (empty($options)) {
drupal_set_message(t('You have no books.'), 'error');
return array();
}
$form['book'] = array(
'#type' => 'select',
'#title' => t('Choose a parent book'),
'#options' => $options,
'#description' => t('Select the parent book page you wish to move the book page into'),
);
return $form;
}
function views_bulk_operations_move_to_book_action_submit($form, $form_state) {
return array('book' => $form_state['values']['book']);
}
function views_bulk_operations_move_to_book_action($node, $context = array()) {
if (isset($context['book'])) {
$book_node = node_load($context['book']);
$mlid = db_select('menu_links' , 'ml')
->condition('ml.link_path' , 'node/' . $node->nid)
->fields('ml' , array('mlid'))
->execute()
->fetchField();
$node->book['mlid'] = $mlid;
$node->book['bid'] = $book_node->nid;
$node->book['plid'] = $book_node->book['mlid'];
$node->book['module'] = 'book';
}
}
/**
* Adds the action 'Remove node from a parent book'
*/
function views_bulk_operations_remove_from_book_action($node, $context) {
$book = $node->book['mlid'];
book_node_delete($node);
}

View File

@@ -14,6 +14,13 @@ function views_bulk_operations_delete_action_info() {
'behavior' => array('deletes_property'),
'triggers' => array('any'),
),
'views_bulk_operations_delete_revision' => array(
'type' => 'entity',
'label' => t('Delete revision'),
'configurable' => FALSE,
'behavior' => array('deletes_property'),
'triggers' => array('any'),
),
);
}
@@ -23,3 +30,9 @@ function views_bulk_operations_delete_item($entity, $context) {
entity_delete($context['entity_type'], $entity_id);
}
function views_bulk_operations_delete_revision($entity, $context) {
$info = entity_get_info($context['entity_type']);
$revision_id = $entity->{$info['entity keys']['revision']};
entity_revision_delete($context['entity_type'], $revision_id);
}

View File

@@ -29,15 +29,19 @@ function views_bulk_operations_modify_action_info() {
*/
function views_bulk_operations_modify_action($entity, $context) {
list(,,$bundle_name) = entity_extract_ids($context['entity_type'], $entity);
// Handle Field API fields.
if (!empty($context['selected']['bundle_' . $bundle_name])) {
// The pseudo entity is cloned so that changes to it don't get carried
// over to the next execution.
$pseudo_entity = clone $context['entities'][$bundle_name];
foreach ($context['selected']['bundle_' . $bundle_name] as $key) {
// Get this field's language. We can just pull it from the pseudo entity
// as it was created using field_attach_form and entity_language so it's
// already been figured out if this field is translatable or not and
// applied the appropriate language code to the field
$language = key($pseudo_entity->{$key});
// Replace any tokens that might exist in the field columns.
foreach ($pseudo_entity->{$key}[LANGUAGE_NONE] as $delta => &$item) {
foreach ($pseudo_entity->{$key}[$language] as $delta => &$item) {
foreach ($item as $column => $value) {
if (is_string($value)) {
$item[$column] = token_replace($value, array($context['entity_type'] => $entity), array('sanitize' => FALSE));
@@ -46,11 +50,11 @@ function views_bulk_operations_modify_action($entity, $context) {
}
if (in_array($key, $context['append']['bundle_' . $bundle_name]) && !empty($entity->$key)) {
$entity->{$key}[LANGUAGE_NONE] = array_merge($entity->{$key}[LANGUAGE_NONE], $pseudo_entity->{$key}[LANGUAGE_NONE]);
$entity->{$key}[$language] = array_merge($entity->{$key}[$language], $pseudo_entity->{$key}[$language]);
// Check if we breached cardinality, and notify the user.
$field_info = field_info_field($key);
$field_count = count($entity->{$key}[LANGUAGE_NONE]);
$field_count = count($entity->{$key}[$language]);
if ($field_info['cardinality'] != FIELD_CARDINALITY_UNLIMITED && $field_count > $field_info['cardinality']) {
$entity_label = entity_label($context['entity_type'], $entity);
$warning = t('Tried to set !field_count values for field !field_name that supports a maximum of !cardinality.',
@@ -59,9 +63,14 @@ function views_bulk_operations_modify_action($entity, $context) {
'!cardinality' => $field_info['cardinality']));
drupal_set_message($warning, 'warning', FALSE);
}
// Prevent storing duplicate references.
if (strpos($field_info['type'], 'reference') !== FALSE) {
$entity->{$key}[$language] = array_unique($entity->{$key}[LANGUAGE_NONE], SORT_REGULAR);
}
}
else {
$entity->$key = $pseudo_entity->$key;
$entity->{$key}[$language] = $pseudo_entity->{$key}[$language];
}
}
}
@@ -73,6 +82,11 @@ function views_bulk_operations_modify_action($entity, $context) {
// The wrapper will automatically modify $entity itself.
$wrapper = entity_metadata_wrapper($context['entity_type'], $entity);
foreach ($context['selected']['properties'] as $key) {
if (!$wrapper->$key->access('update')) {
// No access.
continue;
}
if (in_array($key, $context['append']['properties'])) {
$old_values = $wrapper->$key->value();
$wrapper->$key->set($context['properties'][$key]);
@@ -125,7 +139,7 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
if (!empty($properties)) {
$form['properties'] = array(
'#type' => 'fieldset',
'#title' => 'Properties',
'#title' => t('Properties'),
);
$form['properties']['show_value'] = array(
'#suffix' => '<div class="clearfix"></div>',
@@ -148,6 +162,11 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
),
),
);
// The default #maxlength for textfields is 128, while most varchar
// columns hold 255 characters, which makes it a saner default here.
if ($determined_type == 'textfield') {
$form['properties'][$key]['#maxlength'] = 255;
}
if (!empty($property['options list'])) {
$form['properties'][$key]['#type'] = 'select';
@@ -170,6 +189,8 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
}
}
// Going to need this for multilingual nodes
global $language;
foreach ($bundles as $bundle_name => $bundle) {
$bundle_key = $info['entity keys']['bundle'];
$default_values = array();
@@ -177,6 +198,7 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
if (!empty($bundle_key)) {
$default_values[$bundle_key] = $bundle_name;
}
$default_values['language'] = $language->language;
$entity = entity_create($context['entity_type'], $default_values);
$form_state['entities'][$bundle_name] = $entity;
@@ -195,7 +217,7 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
'#title' => $label,
'#parents' => array($form_key),
);
field_attach_form($context['entity_type'], $entity, $form[$form_key], $form_state, LANGUAGE_NONE);
field_attach_form($context['entity_type'], $entity, $form[$form_key], $form_state, entity_language($context['entity_type'], $entity));
// Now that all the widgets have been added, sort them by #weight.
// This ensures that they will stay in the correct order when they get
// assigned new weights.
@@ -206,8 +228,10 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
$weight = 0;
foreach (element_get_visible_children($form[$form_key]) as $field_name) {
// For our use case it makes no sense for any field widget to be required.
$language = $form[$form_key][$field_name]['#language'];
_views_bulk_operations_modify_action_unset_required($form[$form_key][$field_name][$language]);
if (isset($form[$form_key][$field_name]['#language'])) {
$field_language = $form[$form_key][$field_name]['#language'];
_views_bulk_operations_modify_action_unset_required($form[$form_key][$field_name][$field_language]);
}
// The admin has specified which fields to display, but this field didn't
// make the cut. Hide it with #access => FALSE and move on.
@@ -216,32 +240,34 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
continue;
}
$field = $instances[$field_name];
$form[$form_key]['show_value'][$field_name] = array(
'#type' => 'checkbox',
'#title' => $field['label'],
);
$form[$form_key][$field_name]['#states'] = array(
'visible' => array(
'#edit-bundle-' . str_replace('_', '-', $bundle_name) . '-show-value-' . str_replace('_', '-', $field_name) => array('checked' => TRUE),
),
);
// All field widgets get reassigned weights so that additional elements
// added between them (such as "_append") can be properly ordered.
$form[$form_key][$field_name]['#weight'] = $weight++;
$field_info = field_info_field($field_name);
if ($field_info['cardinality'] != 1) {
$form[$form_key]['_append::' . $field_name] = array(
if (isset($instances[$field_name])) {
$field = $instances[$field_name];
$form[$form_key]['show_value'][$field_name] = array(
'#type' => 'checkbox',
'#title' => t('Add new value(s) to %label, instead of overwriting the existing values.', array('%label' => $field['label'])),
'#states' => array(
'visible' => array(
'#edit-bundle-' . str_replace('_', '-', $bundle_name) . '-show-value-' . str_replace('_', '-', $field_name) => array('checked' => TRUE),
),
),
'#weight' => $weight++,
'#title' => $field['label'],
);
$form[$form_key][$field_name]['#states'] = array(
'visible' => array(
'#edit-bundle-' . str_replace('_', '-', $bundle_name) . '-show-value-' . str_replace('_', '-', $field_name) => array('checked' => TRUE),
),
);
// All field widgets get reassigned weights so that additional elements
// added between them (such as "_append") can be properly ordered.
$form[$form_key][$field_name]['#weight'] = $weight++;
$field_info = field_info_field($field_name);
if ($field_info['cardinality'] != 1) {
$form[$form_key]['_append::' . $field_name] = array(
'#type' => 'checkbox',
'#title' => t('Add new value(s) to %label, instead of overwriting the existing values.', array('%label' => $field['label'])),
'#states' => array(
'visible' => array(
'#edit-bundle-' . str_replace('_', '-', $bundle_name) . '-show-value-' . str_replace('_', '-', $field_name) => array('checked' => TRUE),
),
),
'#weight' => $weight++,
);
}
}
}
@@ -277,7 +303,7 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
$token_type = str_replace('_', '-', $entity_type);
$form['tokens'] = array(
'#type' => 'fieldset',
'#title' => 'Available tokens',
'#title' => t('Available tokens'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 998,
@@ -411,8 +437,12 @@ function _views_bulk_operations_modify_action_get_properties($entity_type, $disp
// List of supported types.
$supported_types = array('text', 'token', 'integer', 'decimal', 'date', 'duration',
'boolean', 'uri', 'list');
$property_info = entity_get_property_info($entity_type);
if (empty($property_info['properties'])) {
// Stop here if no properties were found.
return array();
}
foreach ($property_info['properties'] as $key => $property) {
if (in_array($key, $disabled_properties)) {
continue;
@@ -463,27 +493,38 @@ function _views_bulk_operations_modify_action_get_bundles($entity_type, $context
$bundles = array();
$view = $context['view'];
$vbo = _views_bulk_operations_get_field($view);
$display_values = $context['settings']['display_values'];
$info = entity_get_info($entity_type);
$bundle_key = $info['entity keys']['bundle'];
// Check if this View has a filter on the bundle key and assemble a list
// of allowed bundles according to the filter.
$filtered_bundles = array();
if (!empty($bundle_key) && isset($view->filter[$bundle_key]) && !empty($view->filter[$bundle_key]->value)) {
$operator = $view->filter[$bundle_key]->operator;
if ($operator == 'in') {
$filtered_bundles = $view->filter[$bundle_key]->value;
}
elseif ($operator == 'not in') {
$bundle_names = array_keys($info['bundles']);
$filtered_bundles = array_diff($bundle_names, $view->filter[$bundle_key]->value);
$filtered_bundles = array_keys($info['bundles']);
// Go over all the filters and find any relevant ones.
foreach ($view->filter as $key => $filter) {
// Check it's the right field on the right table.
if ($filter->table == $vbo->table && $filter->field == $bundle_key) {
// Exposed filters may have no bundles, so check that there is a value.
if (empty($filter->value)) {
continue;
}
$operator = $filter->operator;
if ($operator == 'in') {
$filtered_bundles = array_intersect($filtered_bundles, $filter->value);
}
elseif ($operator == 'not in') {
$filtered_bundles = array_diff($filtered_bundles, $filter->value);
}
}
}
foreach ($info['bundles'] as $bundle_name => $bundle) {
// The view is limited to specific bundles, but this bundle isn't one of
// them. Ignore it.
if (!empty($filtered_bundles) && !in_array($bundle_name, $filtered_bundles)) {
if (!in_array($bundle_name, $filtered_bundles)) {
continue;
}
@@ -575,6 +616,7 @@ function views_bulk_operations_modify_action_views_bulk_operations_form($options
'#multiple' => TRUE,
'#description' => t('Select which values the action form should present to the user.'),
'#default_value' => $options['display_values'],
'#size' => 10,
);
return $form;
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* @file
* VBO action to cancel user accounts.
*/
function views_bulk_operations_user_cancel_action_info() {
return array('views_bulk_operations_user_cancel_action' => array(
'type' => 'user',
'label' => t('Cancel user account'),
'configurable' => TRUE,
'behavior' => array('deletes_property'),
'triggers' => array('any'),
));
}
function views_bulk_operations_user_cancel_action_form($context) {
module_load_include('inc', 'user', 'user.pages');
$form['user_cancel_method'] = array(
'#type' => 'item',
'#title' => t('When cancelling these accounts'),
);
$form['user_cancel_method'] += user_cancel_methods();
// Remove method descriptions.
foreach (element_children($form['user_cancel_method']) as $element) {
unset($form['user_cancel_method'][$element]['#description']);
}
$admin_access = user_access('administer users');
$default_notify = variable_get('user_mail_status_canceled_notify', FALSE);
$form['user_cancel_notify'] = array(
'#type' => 'checkbox',
'#title' => t('Notify user when account is canceled.'),
'#default_value' => ($admin_access ? FALSE : $default_notify),
'#access' => $admin_access && $default_notify,
'#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
);
return $form;
}
function views_bulk_operations_user_cancel_action_submit($form, $form_state) {
return array(
'user_cancel_method' => $form_state['values']['user_cancel_method'],
'user_cancel_notify' => $form_state['values']['user_cancel_notify'],
);
}
function views_bulk_operations_user_cancel_action($account, $context) {
global $user;
// Prevent the user from cancelling itself.
if ($account->uid == $user->uid) {
return;
}
// Allow other modules to react on the cancellation.
if ($context['user_cancel_method'] != 'user_cancel_delete') {
module_invoke_all('user_cancel', array(), $account, $context['user_cancel_method']);
}
switch ($context['user_cancel_method']) {
case 'user_cancel_block':
case 'user_cancel_block_unpublish':
default:
// Send account blocked notification if option was checked.
if (!empty($context['user_cancel_notify'])) {
_user_mail_notify('status_blocked', $account);
}
user_save($account, array('status' => 0));
watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
break;
case 'user_cancel_reassign':
case 'user_cancel_delete':
// Send account canceled notification if option was checked.
if (!empty($context['user_cancel_notify'])) {
_user_mail_notify('status_canceled', $account);
}
user_delete($account->uid);
watchdog('user', 'Deleted user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
break;
}
}

View File

@@ -45,24 +45,19 @@ function views_bulk_operations_user_roles_action_submit($form, $form_state) {
);
}
function views_bulk_operations_user_roles_action(&$user, $context) {
$roles = $user->roles;
$selected = (is_array($context['add_roles']) ? $context['add_roles'] : array()) +
(is_array($context['remove_roles']) ? $context['remove_roles'] : array());
$result = db_query("SELECT rid, name FROM {role} WHERE rid IN (:selected)", array(':selected' => array_keys($selected)));
foreach ($result as $role) {
if (isset($context['add_roles'][$role->rid])) {
$add_roles[$role->rid] = $role->name;
}
if (isset($context['remove_roles'][$role->rid])) {
$remove_roles[$role->rid] = $role->name;
}
function views_bulk_operations_user_roles_action($user, $context) {
$wrapper = entity_metadata_wrapper('user', $user);
if (!$wrapper->roles->access("update")) {
// No access.
return;
}
if (!empty($add_roles)) {
$roles += $add_roles;
$roles = $wrapper->roles->value();
if (is_array($context['add_roles'])) {
$roles = array_merge($roles, $context['add_roles']);
}
if (!empty($remove_roles)) {
$roles = array_diff($roles, $remove_roles);
if (is_array($context['remove_roles'])) {
$roles = array_diff($roles, $context['remove_roles']);
}
user_save($user, array('roles' => $roles));
$wrapper->roles->set($roles);
$wrapper->save();
}

View File

@@ -3,9 +3,9 @@ description = Provides permission-based access control for actions. Used by View
package = Administration
core = 7.x
; Information added by drupal.org packaging script on 2012-12-03
version = "7.x-3.1"
; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-3.3"
core = "7.x"
project = "views_bulk_operations"
datestamp = "1354500015"
datestamp = "1435764542"

View File

@@ -46,21 +46,23 @@
});
// Set up the ability to click anywhere on the row to select it.
$('.views-table tbody tr', form).click(function(event) {
if (event.target.tagName.toLowerCase() != 'input' && event.target.tagName.toLowerCase() != 'a') {
$('input[id^="edit-views-bulk-operations"]:not(:disabled)', this).each(function() {
var checked = this.checked;
// trigger() toggles the checkmark *after* the event is set,
// whereas manually clicking the checkbox toggles it *beforehand*.
// that's why we manually set the checkmark first, then trigger the
// event (so that listeners get notified), then re-set the checkmark
// which the trigger will have toggled. yuck!
this.checked = !checked;
$(this).trigger('click');
this.checked = !checked;
});
}
});
if (Drupal.settings.vbo.row_clickable) {
$('.views-table tbody tr', form).click(function(event) {
if (event.target.tagName.toLowerCase() != 'input' && event.target.tagName.toLowerCase() != 'a') {
$('input[id^="edit-views-bulk-operations"]:not(:disabled)', this).each(function() {
var checked = this.checked;
// trigger() toggles the checkmark *after* the event is set,
// whereas manually clicking the checkbox toggles it *beforehand*.
// that's why we manually set the checkmark first, then trigger the
// event (so that listeners get notified), then re-set the checkmark
// which the trigger will have toggled. yuck!
this.checked = !checked;
$(this).trigger('click');
this.checked = !checked;
});
}
});
}
}
Drupal.vbo.tableSelectAllPages = function(form) {

View File

@@ -20,7 +20,7 @@ class ViewsBulkOperationsAction extends ViewsBulkOperationsBaseOperation {
*/
public function getAccessMask() {
// Assume edit by default.
if (!isset($this->operationInfo['behavior'])) {
if (empty($this->operationInfo['behavior'])) {
$this->operationInfo['behavior'] = array('changes_property');
}
@@ -141,9 +141,11 @@ class ViewsBulkOperationsAction extends ViewsBulkOperationsBaseOperation {
* @param $dom_id
* The dom path to the level where the admin options form is embedded.
* Needed for #dependency.
* @param $field_handler
* The Views field handler object for the VBO field.
*/
public function adminOptionsForm($dom_id) {
$form = parent::adminOptionsForm($dom_id);
public function adminOptionsForm($dom_id, $field_handler) {
$form = parent::adminOptionsForm($dom_id, $field_handler);
$settings_form_callback = $this->operationInfo['callback'] . '_views_bulk_operations_form';
if (function_exists($settings_form_callback)) {

View File

@@ -173,8 +173,10 @@ abstract class ViewsBulkOperationsBaseOperation {
* @param $dom_id
* The dom path to the level where the admin options form is embedded.
* Needed for #dependency.
* @param $field_handler
* The Views field handler object for the VBO field.
*/
public function adminOptionsForm($dom_id) {
public function adminOptionsForm($dom_id, $field_handler) {
$label = $this->getAdminOption('label', '');
$form = array();

View File

@@ -124,6 +124,8 @@ class ViewsBulkOperationsRulesComponent extends ViewsBulkOperationsBaseOperation
else {
$element = rules_action('component_' . $this->operationInfo['parameters']['component_key']);
}
$element->execute($data);
$wrapper_type = is_array($data) ? "list<{$this->entityType}>" : $this->entityType;
$wrapper = entity_metadata_wrapper($wrapper_type, $data);
$element->execute($wrapper);
}
}

View File

@@ -55,17 +55,50 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
'contains' => array(
'display_type' => array('default' => 0),
'enable_select_all_pages' => array('default' => TRUE),
'row_clickable' => array('default' => TRUE),
'force_single' => array('default' => FALSE),
'entity_load_capacity' => array('default' => 10),
'skip_batching' => array('default' => 0),
),
);
$options['vbo_operations'] = array(
'default' => array(),
'unpack_translatable' => 'unpack_operations',
'export' => 'export_vbo_operations',
);
return $options;
}
function export_vbo_operations($indent, $prefix, $storage, $option, $definition, $parents) {
// Anti-recursion, since we use the parent export helper.
unset($definition['export']);
// Find and remove all unselected/disabled operations.
foreach ($storage['vbo_operations'] as $operation_id => $operation) {
if (empty($operation['selected'])) {
unset($storage['vbo_operations'][$operation_id]);
}
}
return parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
}
function unpack_operations(&$translatable, $storage, $option, $definition, $parents, $keys) {
$translatable[] = array(
'value' => t('- Choose an operation -'),
'keys' => array_merge($keys, array('noop')),
);
foreach ($storage[$option] as $key => $operation) {
if (!empty($operation['override_label']) && !empty($operation['label'])) {
$translatable[] = array(
'value' => $operation['label'],
'keys' => array_merge($keys, array($key)),
);
}
}
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
@@ -90,6 +123,12 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
'#default_value' => $this->options['vbo_settings']['enable_select_all_pages'],
'#description' => t('Check this box to enable the ability to select all items on all pages.'),
);
$form['vbo_settings']['row_clickable'] = array(
'#type' => 'checkbox',
'#title' => t('Make the whole row clickable'),
'#default_value' => $this->options['vbo_settings']['row_clickable'],
'#description' => t('Check this box to select an item when its row has been clicked. Requires JavaScript.'),
);
$form['vbo_settings']['force_single'] = array(
'#type' => 'checkbox',
'#title' => t('Force single'),
@@ -102,6 +141,12 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
'#description' => t("Improve execution performance at the cost of memory usage. Set to '1' if you're having problems."),
'#default_value' => $this->options['vbo_settings']['entity_load_capacity'],
);
$form['vbo_settings']['skip_batching'] = array(
'#type' => 'checkbox',
'#title' => t('Skip batching'),
'#default_value' => $this->options['vbo_settings']['skip_batching'],
'#description' => '<b>' . t('Warning:') . '</b> ' . t('This will cause timeouts for larger amounts of selected items.'),
);
// Display operations and their settings.
$form['vbo_operations'] = array(
@@ -142,7 +187,7 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
),
);
$form['vbo_operations'][$operation_id] += $operation->adminOptionsForm($dom_id);
$form['vbo_operations'][$operation_id] += $operation->adminOptionsForm($dom_id, $this);
}
}

View File

@@ -4,13 +4,14 @@ dependencies[] = entity
dependencies[] = views
package = Views
core = 7.x
php = 5.2.9
files[] = plugins/operation_types/base.class.php
files[] = views/views_bulk_operations_handler_field_operations.inc
; Information added by drupal.org packaging script on 2012-12-03
version = "7.x-3.1"
; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-3.3"
core = "7.x"
project = "views_bulk_operations"
datestamp = "1354500015"
datestamp = "1435764542"

View File

@@ -41,19 +41,20 @@ function views_bulk_operations_load_action_includes() {
// The list of VBO actions is fairly static, so it's hardcoded for better
// performance (hitting the filesystem with file_scan_directory(), and then
// caching the result has its cost).
$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',
'archive.action',
'argument_selector.action',
'book.action',
'delete.action',
'modify.action',
'script.action',
'user_roles.action',
'user_cancel.action',
);
if (!$loaded) {
foreach ($files as $file) {
include_once $path . $file;
module_load_include('inc', 'views_bulk_operations', 'actions/' . $file);
}
$loaded = TRUE;
}
@@ -75,7 +76,7 @@ function views_bulk_operations_load_action_includes() {
function views_bulk_operations_cron() {
db_delete('queue')
->condition('name', db_like('views_bulk_operations_active_queue_'), 'LIKE')
->condition('created', REQUEST_TIME - 864000, '<')
->condition('created', REQUEST_TIME - 86400, '<')
->execute();
}
@@ -110,7 +111,7 @@ function views_bulk_operations_theme() {
'variables' => array('view' => NULL, 'enable_select_all_pages' => TRUE),
),
'views_bulk_operations_confirmation' => array(
'variables' => array('rows' => NULL, 'vbo' => NULL),
'variables' => array('rows' => NULL, 'vbo' => NULL, 'operation' => NULL, 'select_all_pages' => FALSE),
),
);
$files = views_bulk_operations_load_action_includes();
@@ -300,6 +301,29 @@ function views_bulk_operations_form_alter(&$form, &$form_state, $form_id) {
return;
}
// Add basic VBO functionality.
if ($form_state['step'] == 'views_form_views_form') {
// The submit button added by Views Form API might be used by a non-VBO Views
// Form handler. If there's no such handler on the view, hide the button.
$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']['#access'] = FALSE;
}
// The VBO field is excluded from display, stop here.
if (!empty($vbo->options['exclude'])) {
return;
}
$form = views_bulk_operations_form($form, $form_state, $vbo);
}
// Cache the built form to prevent it from being rebuilt prior to validation
// and submission, which could lead to data being processed incorrectly,
// because the views rows (and thus, the form elements as well) have changed
@@ -319,15 +343,25 @@ function views_bulk_operations_form_alter(&$form, &$form_state, $form_id) {
}
}
// Add basic VBO functionality.
if ($form_state['step'] == 'views_form_views_form') {
$form = views_bulk_operations_form($form, $form_state, $vbo);
}
// Give other modules a chance to alter the form.
drupal_alter('views_bulk_operations_form', $form, $form_state, $vbo);
}
/**
* Implements hook_views_post_build().
*
* Hides the VBO field if no operations are available.
* This causes the entire VBO form to be hidden.
*
* @see views_bulk_operations_form_alter().
*/
function views_bulk_operations_views_post_build(&$view) {
$vbo = _views_bulk_operations_get_field($view);
if ($vbo && count($vbo->get_selected_operations()) < 1) {
$vbo->options['exclude'] = TRUE;
}
}
/**
* 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
@@ -385,7 +419,7 @@ function theme_views_bulk_operations_select_all($variables) {
if ($enable_select_all_pages) {
$form['select_all']['or'] = array(
'#type' => 'markup',
'#markup' => '<em>OR</em>',
'#markup' => '<em>' . t('OR') . '</em>',
);
$form['select_all']['all_pages'] = array(
'#type' => 'checkbox',
@@ -408,8 +442,20 @@ function theme_views_bulk_operations_select_all($variables) {
*/
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']['js'][] = array(
'data' => array('vbo' => array(
'row_clickable' => $vbo->get_vbo_option('row_clickable'),
)),
'type' => 'setting',
);
$form['#attached']['css'][] = drupal_get_path('module', 'views_bulk_operations') . '/css/views_bulk_operations.css';
$form['#prefix'] = '<div class="vbo-views-form">';
// Wrap the form in a div with specific classes for JS targeting and theming.
$class = 'vbo-views-form';
if (empty($vbo->view->result)) {
$class .= ' vbo-views-form-empty';
}
$form['#prefix'] = '<div class="' . $class . '">';
$form['#suffix'] = '</div>';
// Force browser to reload the page if Back is hit.
@@ -426,25 +472,10 @@ function views_bulk_operations_form($form, &$form_state, $vbo) {
'#attributes' => array('class' => 'select-all-rows'),
'#default_value' => FALSE,
);
// The submit button added by Views Form API might be used by a non-VBO Views
// Form handler. If there's no such handler on the view, hide the button.
$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,
'#collapsible' => FALSE,
'#attributes' => array('class' => array('container-inline')),
);
if ($vbo->get_vbo_option('display_type') == 0) {
@@ -570,14 +601,23 @@ function views_bulk_operations_confirm_form($form, &$form_state, $view, $output)
$operation = $form_state['operation'];
$rows = $form_state['selection'];
$query = drupal_get_query_parameters($_GET, array('q'));
$title = t('Are you sure you want to perform %operation on the selected items?', array('%operation' => $operation->label()));
$form = confirm_form($form,
t('Are you sure you want to perform %operation on the selected items?', array('%operation' => $operation->label())),
$title,
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']))
theme('views_bulk_operations_confirmation', array('rows' => $rows, 'vbo' => $vbo, 'operation' => $operation, 'select_all_pages' => $form_state['select_all_pages']))
);
// Add VBO's submit handler to the Confirm button added by config_form().
$form['actions']['submit']['#submit'] = array('views_bulk_operations_form_submit');
// We can't set the View title here as $view is just a copy of the original,
// and our settings changes won't "stick" for the first page load of the
// confirmation form. We also can't just call drupal_set_title() directly
// because our title will be clobbered by the actual View title later. So
// let's tuck the title away in the form for use later.
// @see views_bulk_operations_preprocess_views_view()
$form['#vbo_confirm_form_title'] = $title;
return $form;
}
@@ -593,7 +633,7 @@ function theme_views_bulk_operations_confirmation($variables) {
// Load the entities from the current page, and show their titles.
$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));
$items[] = check_plain(entity_label($entity_type, $entity));
}
// All rows on all pages have been selected, so show a count of additional items.
if ($select_all_pages) {
@@ -606,6 +646,29 @@ function theme_views_bulk_operations_confirmation($variables) {
return $output;
}
/**
* Implements hook_preprocess_page().
*
* Hide action links on the configure and confirm pages.
*/
function views_bulk_operations_preprocess_page(&$variables) {
if (isset($_POST['select_all'], $_POST['operation'])) {
$variables['action_links'] = array();
}
}
/**
* Implements hook_preprocess_views_view().
*/
function views_bulk_operations_preprocess_views_view($variables) {
// If we've stored a title for the confirmation form, retrieve it here and
// retitle the View.
// @see views_bulk_operations_confirm_form()
if (array_key_exists('rows', $variables) && is_array($variables['rows']) && array_key_exists('#vbo_confirm_form_title', $variables['rows'])) {
$variables['view']->set_title($variables['rows']['#vbo_confirm_form_title']);
}
}
/**
* Goes through the submitted values, and returns
* an array of selected rows, in the form of
@@ -676,8 +739,8 @@ function views_bulk_operations_form_submit($form, &$form_state) {
* 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.
* items to be passed at the same time), restricted to a single value, or has
* the skip_batching option set, 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.
@@ -703,9 +766,13 @@ function views_bulk_operations_form_submit($form, &$form_state) {
function views_bulk_operations_execute($vbo, $operation, $selection, $select_all_pages = FALSE) {
global $user;
// This is an aggregate operation, and it requires all rows to be selected.
// Try to load them without a batch.
if ($operation->aggregate() && $select_all_pages) {
// Determine if the operation needs to be executed directly.
$aggregate = $operation->aggregate();
$skip_batching = $vbo->get_vbo_option('skip_batching');
$force_single = $vbo->get_vbo_option('force_single');
$execute_directly = ($aggregate || $skip_batching || $force_single);
// Try to load all rows without a batch if needed.
if ($execute_directly && $select_all_pages) {
views_bulk_operations_direct_adjust($selection, $vbo);
}
@@ -713,6 +780,15 @@ function views_bulk_operations_execute($vbo, $operation, $selection, $select_all
$options = array(
'revision' => $vbo->revision,
'entity_load_capacity' => $vbo->get_vbo_option('entity_load_capacity', 10),
// The information needed to recreate the view, to avoid serializing the
// whole object. Passed to the executed operation. Also used by
// views_bulk_operations_adjust_selection().
'view_info' => array(
'name' => $vbo->view->name,
'display' => $vbo->view->current_display,
'arguments' => $vbo->view->args,
'exposed_input' => $vbo->view->get_exposed_input(),
),
);
// Create an array of rows in the needed format.
$rows = array();
@@ -735,8 +811,8 @@ function views_bulk_operations_execute($vbo, $operation, $selection, $select_all
}
}
// Aggregate operations can only be executed directly.
if ($operation->aggregate()) {
if ($execute_directly) {
// Execute the operation directly and stop here.
views_bulk_operations_direct_process($operation, $rows, $options);
return;
}
@@ -763,18 +839,8 @@ function views_bulk_operations_execute($vbo, $operation, $selection, $select_all
if ($select_all_pages && $vbo->view->query->pager->has_more_records()) {
$total_rows = $vbo->view->total_rows;
// Pass information needed to recreate the view inside a batch,
// to avoid having to serialize the current object (which is expensive).
$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),
'views_bulk_operations_adjust_selection', array($queue_name, $operation, $options),
);
}
else {
@@ -808,22 +874,21 @@ function views_bulk_operations_execute($vbo, $operation, $selection, $select_all
/**
* 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.
* An array of options that affect execution (revision, entity_load_capacity,
* view_info). Passed along with each new queue item.
*/
function views_bulk_operations_adjust_selection($view_info, $queue_name, $operation, $options, &$context) {
function views_bulk_operations_adjust_selection($queue_name, $operation, $options, &$context) {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = 0;
}
$view_info = $options['view_info'];
$view = views_get_view($view_info['name']);
$view->set_exposed_input($view_info['exposed_input']);
$view->set_arguments($view_info['arguments']);
@@ -844,7 +909,7 @@ function views_bulk_operations_adjust_selection($view_info, $queue_name, $operat
'views_row' => array(),
'position' => array(
'current' => ++$context['sandbox']['progress'],
'total' => $view->total_rows,
'total' => $context['sandbox']['max'],
),
);
// Some operations require full selected rows.
@@ -927,7 +992,7 @@ function views_bulk_operations_active_queue_process($queue_name, $operation, $to
static $queue;
// It is still possible to hit the time limit.
@set_time_limit(0);
drupal_set_time_limit(0);
// Prepare the sandbox.
if (!isset($context['sandbox']['progress'])) {
@@ -980,26 +1045,42 @@ function views_bulk_operations_queue_item_process($queue_item_data, &$log = NULL
$account = user_load($queue_item_data['uid']);
$entity_type = $operation->entityType;
$entity_ids = array();
foreach ($row_group as $row_id => $row) {
foreach ($row_group as $row_index => $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) {
foreach ($row_group as $row_index => $row) {
$entity_id = $row['entity_id'];
// A matching entity couldn't be loaded. Skip this item.
if (!isset($entities[$entity_id])) {
continue;
}
$entity = $entities[$entity_id];
if ($options['revision']) {
// Don't reload revisions for now, they are not statically cached and
// usually don't run into the edge case described below.
$entity = $entities[$entity_id];
}
else {
// A previous action might have resulted in the entity being resaved
// (e.g. node synchronization from a prior node in this batch), so try
// to reload it. If no change occurred, the entity will be retrieved
// from the static cache, resulting in no performance penalty.
$entity = entity_load_single($entity_type, $entity_id);
if (empty($entity)) {
// The entity is no longer valid.
continue;
}
}
// If the current entity can't be accessed, skip it and log a notice.
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),
'%title' => entity_label($entity_type, $entity),
);
if ($log) {
@@ -1014,11 +1095,14 @@ function views_bulk_operations_queue_item_process($queue_item_data, &$log = NULL
$operation_context = array(
'progress' => $row['position'],
'rows' => $row['views_row'],
'view_info' => $options['view_info'],
);
if ($operation->needsRows()) {
$operation_context['rows'] = array($row_index => $row['views_row']);
}
$operation->execute($entity, $operation_context);
unset($row_group[$row_id]);
unset($row_group[$row_index]);
}
}
@@ -1055,11 +1139,11 @@ function views_bulk_operations_direct_adjust(&$selection, $vbo) {
/**
* 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);
global $user;
drupal_set_time_limit(0);
// Prepare an array of status information. Imitates the Batch API naming
// for consistency. Passed to views_bulk_operations_execute_finished().
@@ -1067,42 +1151,56 @@ function views_bulk_operations_direct_process($operation, $rows, $options) {
$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;
}
// Pass the selected rows to the operation if needed.
$operation_context = array();
if ($operation->needsRows()) {
$operation_context['rows'] = array();
if ($operation->aggregate()) {
// Load all entities.
$entity_type = $operation->entityType;
$entity_ids = array();
foreach ($rows as $row_index => $row) {
$operation_context['rows'][$row_index] = $row['views_row'];
$entity_ids[] = $row['entity_id'];
}
$entities = _views_bulk_operations_entity_load($entity_type, $entity_ids, $options['revision']);
// Filter out entities that can't be accessed.
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' => entity_label($entity_type, $entity),
));
unset($entities[$id]);
}
}
// If there are any entities left, execute the operation on them.
if ($entities) {
$operation_context = array(
'view_info' => $options['view_info'],
);
// Pass the selected rows to the operation if needed.
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);
}
}
$operation->execute($entities, $operation_context);
else {
// Imitate a queue and process the entities one by one.
$queue_item_data = array(
'uid' => $user->uid,
'arguments' => array($rows, $operation, $options),
);
views_bulk_operations_queue_item_process($queue_item_data, $context['results']['log']);
}
$context['results']['log'][] = t('Performed aggregate %operation on @items.', array(
$context['results']['progress'] += count($rows);
$context['results']['log'][] = t('Performed %operation on @items.', array(
'%operation' => $operation->label(),
'@items' => format_plural(count($entities), '1 item', '@count items'),
'@items' => format_plural(count($rows), '1 item', '@count items'),
));
$context['results']['progress'] += count($entities);
views_bulk_operations_execute_finished(TRUE, $context['results'], array());
}
@@ -1176,29 +1274,6 @@ function _views_bulk_operations_entity_load($entity_type, $ids, $revision = FALS
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'];
// Many entity types (e.g. "user") have a name which fits the label perfectly.
if (isset($entity->name)) {
$label = $entity->name;
}
elseif (isset($entity->{$id_key})) {
// Fallback to the id key.
$label = $entity->{$id_key};
}
}
return $label;
}
/**
* Helper function to report an error.
*/

View File

@@ -113,8 +113,9 @@ function views_bulk_operations_rules_action_info() {
*/
function views_bulk_operations_views_list() {
$selectable_displays = array();
foreach (views_get_enabled_views() as $name => $view) {
foreach ($view->display as $display_name => $display) {
foreach (views_get_enabled_views() as $name => $base_view) {
foreach ($base_view->display as $display_name => $display) {
$view = $base_view->clone_view();
$view->build($display_name);
$vbo = _views_bulk_operations_get_field($view);
if ($vbo) {
@@ -158,13 +159,12 @@ function views_bulk_operations_condition_result_count($view, $args, $minimum) {
function views_bulk_operations_action_load_list($view, $args) {
$vbo = _views_bulk_operations_rules_get_field($view, $args);
// Get all entity ids.
$ids = array();
// Get all entities, pass ids to the wrapper for lazy loading.
$entity_type = $vbo->get_entity_type();
$entities = entity_metadata_wrapper("list<$entity_type>", array());
foreach ($vbo->view->result as $row_index => $result) {
$ids[] = $vbo->get_value($result);
$entities[] = entity_metadata_wrapper($entity_type, $vbo->get_value($result));
}
// And load the entity objects.
$entities = entity_load($vbo->get_entity_type(), $ids);
return array('entity_list' => $entities);
}