updated views_send views_bulk_update views_data_export

This commit is contained in:
Bachir Soussi Chiadmi 2017-05-24 19:09:09 +02:00
parent dbd7b88639
commit 542ac42fca
42 changed files with 2939 additions and 264 deletions

View File

@ -8,9 +8,9 @@ core = 7.x
files[] = views_send.rules.inc
files[] = views/views_send_handler_field_selector.inc
; Information added by Drupal.org packaging script on 2016-11-09
version = "7.x-1.3"
; Information added by Drupal.org packaging script on 2017-04-03
version = "7.x-1.6"
core = "7.x"
project = "views_send"
datestamp = "1478685242"
datestamp = "1491222486"

View File

@ -1,4 +1,11 @@
(function ($) {
// Polyfill for jQuery less than 1.6.
if (typeof $.fn.prop != 'function') {
jQuery.fn.extend({
prop: jQuery.fn.attr
});
}
Drupal.behaviors.viewsSend = {
attach: function(context) {
$('.views-send-selection-form', context).each(function() {
@ -14,7 +21,7 @@
// This is the "select all" checkbox in (each) table header.
$('.views-send-table-select-all', form).click(function() {
var table = $(this).closest('table')[0];
$('input[id^="edit-views-send"]:not(:disabled)', table).attr('checked', this.checked).change();
$('input[id^="edit-views-send"]:not(:disabled)', table).prop('checked', this.checked).change();
});
}
@ -23,21 +30,21 @@
$('.views-send-select-all-markup', form).show();
$('.views-send-select-this-page', form).click(function() {
$('input[id^="edit-views-send"]', form).attr('checked', this.checked).change();
$('input[id^="edit-views-send"]', form).prop('checked', this.checked).change();
// Toggle the "select all" checkbox in grouped tables (if any).
$('.views-send-table-select-all', form).attr('checked', this.checked).change();
$('.views-send-table-select-all', form).prop('checked', this.checked).change();
});
$('.views-send-select', form).click(function() {
// If a checkbox was deselected, uncheck any "select all" checkboxes.
if (!this.checked) {
$('.views-send-select-this-page', form).attr('checked', false).change();
$('.views-send-select-this-page', form).prop('checked', false).change();
var table = $(this).closest('table')[0];
if (table) {
// Uncheck the "select all" checkbox in the table header.
$('.views-send-table-select-all', table).attr('checked', false).change();
$('.views-send-table-select-all', table).prop('checked', false).change();
}
}
});

View File

@ -33,21 +33,20 @@ define('VIEWS_SEND_TOKEN_PREFIX', '[');
define('VIEWS_SEND_TOKEN_POSTFIX', ']');
/**
* Detect if there is MIME support (thorough modules like Mime Mail or Mandrill).
* Detect if there is MIME support (through modules like Mime Mail or Mandrill).
*/
define('VIEWS_SEND_MIMEMAIL', module_exists('mimemail') || module_exists('mandrill'));
switch (true) {
case (module_exists('htmlmail') && module_exists('mailmime')):
case module_exists('mailgun'):
case module_exists('mandrill'):
case module_exists('mimemail'):
case module_exists('sendgrid_integration'):
case module_exists('swiftmailer'):
define('VIEWS_SEND_MIMEMAIL', TRUE);
break;
/**
* Sets the mailsystem for Views Send (if not already set).
*/
function _views_send_mailsystem_set($key) {
$mailsystem = mailsystem_get();
$mailsystem_key = "views_send_$key";
if (empty($mailsystem[$mailsystem_key])) {
mailsystem_set(array(
$mailsystem_key => $mailsystem['default-system']
));
}
default:
define('VIEWS_SEND_MIMEMAIL', FALSE);
}
/**
@ -549,7 +548,7 @@ function views_send_confirm_form($form, &$form_state, $view, $output) {
);
if (VIEWS_SEND_MIMEMAIL && !empty($configuration['views_send_attachments']) && user_access('attachments with views_send')) {
foreach ($configuration['views_send_attachments'] as $attachment) {
$attachments[] = $attachment['filename'];
$attachments[] = check_plain($attachment['filename']);
}
$form['attachments'] = array(
'#type' => 'item',
@ -609,6 +608,7 @@ function views_send_form_submit($form, &$form_state) {
}
}
$form_state['configuration'] = $form_state['values'];
$form_state['configuration']['views_send_attachments'] = array();
// If a file was uploaded, process it.
if (VIEWS_SEND_MIMEMAIL && user_access('attachments with views_send') && isset($_FILES['files']) && is_uploaded_file($_FILES['files']['tmp_name']['views_send_attachments'])) {
@ -665,6 +665,8 @@ function views_send_form_back_submit($form, &$form_state) {
/**
* Assembles the email and queues it for sending.
*
* Also email sent directly using the Batch API is handled here.
*
* @param $params
* Data entered in the "config" step of the form.
* @param $selected_rows
@ -684,8 +686,6 @@ function views_send_queue_mail($params, $selected_rows, $view) {
return;
}
$formats = filter_formats();
// From: parts.
$from_mail = trim($params['views_send_from_mail']);
$from_name = $params['views_send_from_name'];
@ -699,6 +699,36 @@ function views_send_queue_mail($params, $selected_rows, $view) {
$to_name_key = false;
$to_name = '';
}
$subject = $params['views_send_subject'];
$body = $params['views_send_message']['value'];
$headers = _views_send_headers($params['views_send_receipt'], $params['views_send_priority'], $from_mail, $params['views_send_headers']);
$format = $params['views_send_message']['format'];
$attachments = $params['views_send_attachments'];
$formats = filter_formats();
if (!filter_access($formats[$format])) {
drupal_set_message(t('No mails sent since an illegale format is selected for the message.'));
return;
}
else {
$body = check_markup($body, $format);
}
if ($format == 'plain_text') {
$plain_format = TRUE;
}
else {
$plain_format = FALSE;
}
$message_base = array(
'uid' => $user->uid,
'from_name' => trim($from_name),
'from_mail' => trim($from_mail),
'headers' => $headers,
);
foreach ($selected_rows as $selected_rows_key => $row_id) {
// To: parts.
$to_mail = implode(',', _views_send_get_field_value_from_views_row($view, $row_id, $to_mail_key, 'mail'));
@ -706,18 +736,6 @@ function views_send_queue_mail($params, $selected_rows, $view) {
$to_name = _views_send_get_field_value_from_views_row($view, $row_id, $to_name_key, 'plain_text');
}
$subject = $params['views_send_subject'];
$body = $params['views_send_message']['value'];
$params['format'] = $params['views_send_message']['format'];
// This shouldn't happen, but better be 100% sure.
if (!filter_access($formats[$params['format']])) {
drupal_set_message(t('No mails sent since an illegale format is selected for the message.'));
return;
}
$body = check_markup($body, $params['format']);
// Populate row/context tokens.
$token_keys = $token_values = array();
foreach ($params['views_send_tokens'] as $field_key => $field_name) {
@ -726,8 +744,8 @@ function views_send_queue_mail($params, $selected_rows, $view) {
}
// Views Send specific token replacements
$subject = str_replace($token_keys, $token_values, $subject);
$body = str_replace($token_keys, $token_values, $body);
$subject_expanded = str_replace($token_keys, $token_values, $subject);
$body_expanded = str_replace($token_keys, $token_values, $body);
// Global token replacement, and node/user token replacements
// if a nid/uid is found in the views result row.
@ -738,36 +756,19 @@ function views_send_queue_mail($params, $selected_rows, $view) {
if (property_exists($view->result[$row_id], 'nid')) {
$data['node'] = node_load($view->result[$row_id]->nid);
}
$subject = token_replace($subject, $data);
$body = token_replace($body, $data);
$subject_expanded = token_replace($subject_expanded, $data);
$body_expanded = token_replace($body_expanded, $data);
if (!VIEWS_SEND_MIMEMAIL || (variable_get('mimemail_format', 'plain_text') == 'plain_text')) {
$body = drupal_html_to_text($body);
$body_expanded = drupal_html_to_text($body_expanded);
}
if ($params['format'] == 'plain_text') {
$plain_format = TRUE;
}
else {
$plain_format = FALSE;
}
// We transform receipt, priority in headers,
// merging them to the user defined headers.
$headers = _views_send_headers($params['views_send_receipt'], $params['views_send_priority'], $from_mail, $params['views_send_headers']);
$attachments = isset($params['views_send_attachments']) ? $params['views_send_attachments'] : array();
$message = array(
'uid' => $user->uid,
$message = $message_base + array(
'timestamp' => time(),
'from_name' => trim($from_name),
'from_mail' => trim($from_mail),
'to_name' => trim($to_name),
'to_mail' => trim($to_mail),
'subject' => strip_tags($subject),
'body' => $body,
'headers' => $headers,
'subject' => strip_tags($subject_expanded),
'body' => $body_expanded,
);
// Enable other modules to alter the actual message before queueing it
@ -782,6 +783,10 @@ function views_send_queue_mail($params, $selected_rows, $view) {
// Only queue the message if it hasn't been cancelled by another module.
if ($message['send']) {
unset($message['send']);
// Removing stuff add to work around Swifth Mailer issue 2841663
if (module_exists('swiftmailer')) {
unset($message['params']);
}
db_insert('views_send_spool')->fields($message)->execute();
if (module_exists('rules')) {
@ -1086,7 +1091,6 @@ function _views_send_prepare_mail(&$message, $plain_format=TRUE, $attachments=ar
$params['headers'] = $headers;
if (VIEWS_SEND_MIMEMAIL) {
_views_send_mailsystem_set($key);
$params['attachments'] = $attachments;
if ($plain_format) {
$params['plain'] = TRUE;
@ -1109,6 +1113,10 @@ function _views_send_prepare_mail(&$message, $plain_format=TRUE, $attachments=ar
$message['body'] = $mail['body'];
$message['send'] = $mail['send'];
$message['headers'] = serialize($mail['headers']);
// Working around a Swifth Mailer issue - see https://www.drupal.org/node/2841663
if (module_exists('swiftmailer')) {
$message['params'] = array('attachments' => $params['attachments']);
}
}
/**
@ -1135,9 +1143,9 @@ function views_send_deliver($message) {
'body' => $message->body,
'headers' => $headers,
);
if (VIEWS_SEND_MIMEMAIL) {
_views_send_mailsystem_set($key);
// Because of Swifth Mailer issue 2841663
if (module_exists('swiftmailer')) {
$mail['params'] = $message->params;
}
// Mime encode the subject before passing to the mail function
@ -1217,47 +1225,6 @@ function theme_views_send_token_help($fields) {
return $output;
}
if (module_exists('token')) {
/**
* Implements hook_token_info().
*
* These token are used by Rules and not in the Views form.
*/
function views_send_token_info() {
$data = array();
foreach (_views_send_email_message_property_info() as $key => $info) {
$data[$key] = array(
'name' => $info['label'],
'description' => ''
);
}
$type = array(
'name' => t('Views Send e-mail message'),
'description' => t('Tokens for Views Send e-mail message.'),
'needs-data' => 'views_send_email_message',
);
return array(
'types' => array('views_send_email_message' => $type),
'tokens' => array('views_send_email_message' => $data),
);
}
/**
* Implementation hook_tokens().
*
* These token replacements are used by Rules and not in the Views form.
*/
function views_send_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
if ($type == 'views_send_email_message' && !empty($data['views_send_email_message'])) {
foreach ($tokens as $name => $original) {
$replacements[$original] = $data['views_send_email_message']->{$name};
}
}
return $replacements;
}
}
/**
* Generates and returns fields and tokens.
*/

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Token integration for the Views Send module.
*/
/**
* Implements hook_token_info().
*
* These token are used by Rules and not in the Views form.
*/
function views_send_token_info() {
$data = array();
foreach (_views_send_email_message_property_info() as $key => $info) {
$data[$key] = array(
'name' => $info['label'],
'description' => ''
);
}
$type = array(
'name' => t('Views Send e-mail message'),
'description' => t('Tokens for Views Send e-mail message.'),
'needs-data' => 'views_send_email_message',
);
return array(
'types' => array('views_send_email_message' => $type),
'tokens' => array('views_send_email_message' => $data),
);
}
/**
* Implementation hook_tokens().
*
* These token replacements are used by Rules and not in the Views form.
*/
function views_send_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
if ($type == 'views_send_email_message' && !empty($data['views_send_email_message'])) {
foreach ($tokens as $name => $original) {
$replacements[$original] = $data['views_send_email_message']->{$name};
}
}
return $replacements;
}

View File

@ -3,10 +3,14 @@
/**
* @file
* Provides an action for creating a zip archive of selected files.
*
* An entry in the {file_managed} table is created for the newly created archive,
* and it is marked as permanent or temporary based on the operation settings.
*/
/**
* Implements hook_action_info().
*/
function views_bulk_operations_archive_action_info() {
$actions = array();
if (function_exists('zip_open')) {
@ -71,6 +75,10 @@ function views_bulk_operations_archive_action($file, $context) {
$archive_file->filemime = file_get_mimetype($destination);
$archive_file->uid = $user->uid;
$archive_file->status = $context['settings']['temporary'] ? FALSE : FILE_STATUS_PERMANENT;
// Clear filesize() cache to avoid private file system differences in
// filesize.
// @see https://www.drupal.org/node/2743999
clearstatcache();
file_save($archive_file);
$url = file_create_url($archive_file->uri);
@ -100,8 +108,11 @@ function views_bulk_operations_archive_action_form($context) {
}
/**
* Assembles a sanitized and unique URI for the archive, and returns it for
* usage by the action callback (views_bulk_operations_archive_action).
* Assembles a sanitized and unique URI for the archive.
*
* @returns array
* A URI array used by the action callback
* (views_bulk_operations_archive_action).
*/
function views_bulk_operations_archive_action_submit($form, $form_state) {
// Validate the scheme, fallback to public if it's somehow invalid.
@ -156,10 +167,12 @@ function views_bulk_operations_archive_action_views_bulk_operations_form($option
/**
* Create a sanitized and unique version of the provided filename.
*
* @param $filename
* String filename
* @param string $filename
* The filename to create.
* @param array $archive_list
* The list of files already in the archive.
*
* @return
* @return string
* The new filename.
*/
function _views_bulk_operations_archive_action_create_filename($filename, $archive_list) {
@ -167,7 +180,7 @@ function _views_bulk_operations_archive_action_create_filename($filename, $archi
// some filesystems, not many applications handle them well.
$filename = preg_replace('/[\x00-\x1F]/u', '_', $filename);
if (substr(PHP_OS, 0, 3) == 'WIN') {
// These characters are not allowed in Windows filenames
// These characters are not allowed in Windows filenames.
$filename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $filename);
}

View File

@ -0,0 +1,65 @@
<?php
/**
* @file
* Implements a generic entity change owner action.
*/
/**
* Implements hook_action_info().
*/
function views_bulk_operations_change_owner_action_info() {
return array(
'views_bulk_operations_change_owner_action' => array(
'type' => 'entity',
'label' => t('Change owner'),
'configurable' => TRUE,
'behavior' => array('changes_property'),
'triggers' => array('any'),
),
);
}
/**
* Action function.
*/
function views_bulk_operations_change_owner_action($entity, $context) {
$entity->uid = $context['owner_uid'];
}
/**
* Action form function.
*/
function views_bulk_operations_change_owner_action_form($context, &$form_state) {
$form['owner_username'] = array(
'#type' => 'textfield',
'#maxlength' => USERNAME_MAX_LENGTH,
'#title' => t('Owner'),
'#required' => TRUE,
'#description' => t('Choose the user you would like to set as the owner.'),
'#autocomplete_path' => 'user/autocomplete',
);
return $form;
}
/**
* Action form validate function.
*
* Checks that the submitted text is a valid username.
*/
function views_bulk_operations_change_owner_action_validate($form, $form_state) {
if (!user_load_by_name($form_state['values']['owner_username'])) {
form_set_error('owner_username', t('Valid username required.'));
}
}
/**
* Action form submit function.
*
* Pass submitted username back to views_bulk_operations_change_owner.
*/
function views_bulk_operations_change_owner_action_submit($form, $form_state) {
$user = user_load_by_name($form_state['values']['owner_username']);
return array('owner_uid' => $user->uid);
}

View File

@ -24,11 +24,46 @@ function views_bulk_operations_delete_action_info() {
);
}
function views_bulk_operations_delete_item_views_bulk_operations_form($settings) {
$form = array();
$form['log'] = array(
'#type' => 'checkbox',
'#title' => t('Log individual deletions'),
'#description' => t('Note: Deleting large amounts of entities will generate large amounts of log messages.'),
'#default_value' => !empty($settings['log']),
);
return $form;
}
function views_bulk_operations_delete_item($entity, $context) {
$info = entity_get_info($context['entity_type']);
$entity_id = $entity->{$info['entity keys']['id']};
entity_delete($context['entity_type'], $entity_id);
// Add a message to the watchdog if we've been configured to do so.
if (!empty($context['settings']['log'])) {
// Log an appropriate message for this entity type, using the format from
// the node, taxonomy and user module for their entity types.
switch ($context['entity_type']) {
case 'node':
watchdog('content', '@type: deleted %title.', array('@type' => $entity->type, '%title' => $entity->title));
break;
case 'taxonomy_term':
watchdog('taxonomy', 'Deleted term %name.', array('%name' => $entity->name), WATCHDOG_NOTICE);
break;
case 'user':
watchdog('user', 'Deleted user: %name %email.', array('%name' => $entity->name, '%email' => '<' . $entity->mail . '>'), WATCHDOG_NOTICE);
break;
default:
watchdog('entity', 'Deleted @type %label.', array('@type' => $context['entity_type'], '%label' => entity_label($context['entity_type'], $entity)));
break;
}
}
}
function views_bulk_operations_delete_revision($entity, $context) {

View File

@ -1,24 +1,30 @@
<?php
/**
* @file VBO action to modify entity values (properties and fields).
* @file
* VBO action to modify entity values (properties and fields).
*/
// Specifies that all available values should be shown to the user for editing.
define('VBO_MODIFY_ACTION_ALL', '_all_');
/**
* Implements hook_action_info().
*/
function views_bulk_operations_modify_action_info() {
return array('views_bulk_operations_modify_action' => array(
'type' => 'entity',
'label' => t('Modify entity values'),
'behavior' => array('changes_property'),
// This action only works when invoked through VBO. That's why it's
// declared as non-configurable to prevent it from being shown in the
// "Create an advanced action" dropdown on admin/config/system/actions.
'configurable' => FALSE,
'vbo_configurable' => TRUE,
'triggers' => array('any'),
));
return array(
'views_bulk_operations_modify_action' => array(
'type' => 'entity',
'label' => t('Modify entity values'),
'behavior' => array('changes_property'),
// This action only works when invoked through VBO. That's why it's
// declared as non-configurable to prevent it from being shown in the
// "Create an advanced action" dropdown on admin/config/system/actions.
'configurable' => FALSE,
'vbo_configurable' => TRUE,
'triggers' => array('any'),
),
);
}
/**
@ -28,7 +34,7 @@ function views_bulk_operations_modify_action_info() {
* replacing the existing values, or appending to them (based on user input).
*/
function views_bulk_operations_modify_action($entity, $context) {
list(,,$bundle_name) = entity_extract_ids($context['entity_type'], $entity);
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
@ -38,7 +44,7 @@ function views_bulk_operations_modify_action($entity, $context) {
// 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
// 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] as $delta => &$item) {
@ -58,9 +64,11 @@ function views_bulk_operations_modify_action($entity, $context) {
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.',
array('!field_count' => $field_count,
'!field_name' => $field_info['field_name'],
'!cardinality' => $field_info['cardinality']));
array(
'!field_count' => $field_count,
'!field_name' => $field_info['field_name'],
'!cardinality' => $field_info['cardinality'],
));
drupal_set_message($warning, 'warning', FALSE);
}
@ -76,11 +84,17 @@ function views_bulk_operations_modify_action($entity, $context) {
}
// Handle properties.
// Use the wrapper to set property values, since some properties need
// additional massaging by their setter callbacks.
// The wrapper will automatically modify $entity itself.
$wrapper = entity_metadata_wrapper($context['entity_type'], $entity);
// The default setting for 'revision' property (create new revisions) should
// be respected for nodes. This requires some special treatment.
if ($context['entity_type'] == 'node' && in_array('revision', variable_get('node_options_' . $bundle_name)) && !in_array('revision', $context['selected']['properties'])) {
$wrapper->revision->set(1);
}
if (!empty($context['selected']['properties'])) {
// Use the wrapper to set property values, since some properties need
// additional massaging by their setter callbacks.
// 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.
@ -113,7 +127,8 @@ function views_bulk_operations_modify_action($entity, $context) {
* entity bundle, as provided by field_attach_form().
*/
function views_bulk_operations_modify_action_form($context, &$form_state) {
// This action form uses admin-provided settings. If they were not set, pull the defaults now.
// This action form uses admin-provided settings. If they were not set, pull
// the defaults now.
if (!isset($context['settings'])) {
$context['settings'] = views_bulk_operations_modify_action_views_bulk_operations_form_options();
}
@ -126,7 +141,8 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
// and filled with form data.
// After submit, the pseudo-entities get passed to the actual action
// (views_bulk_operations_modify_action()) which copies the data from the
// relevant pseudo-entity constructed here to the actual entity being modified.
// relevant pseudo-entity constructed here to the actual entity being
// modified.
$form_state['entities'] = array();
$info = entity_get_info($entity_type);
@ -151,7 +167,16 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
'#title' => $property['label'],
);
$determined_type = ($property['type'] == 'boolean') ? 'checkbox' : 'textfield';
// According to _views_bulk_operations_modify_action_get_properties
// we have fixed list of supported types. Most of these types are string
// and only some of them has options list.
if (isset($property['options list'])) {
$determined_type = ($property['type'] == 'list') ? 'checkboxes' : 'select';
}
else {
$determined_type = ($property['type'] == 'boolean') ? 'checkbox' : 'textfield';
}
$form['properties'][$key] = array(
'#type' => $determined_type,
'#title' => $property['label'],
@ -189,7 +214,7 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
}
}
// Going to need this for multilingual nodes
// Going to need this for multilingual nodes.
global $language;
foreach ($bundles as $bundle_name => $bundle) {
$bundle_key = $info['entity keys']['bundle'];
@ -202,8 +227,8 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
$entity = entity_create($context['entity_type'], $default_values);
$form_state['entities'][$bundle_name] = $entity;
// Show the more detailed label only if the entity type has multiple bundles.
// Otherwise, it would just be confusing.
// Show the more detailed label only if the entity type has multiple
// bundles. Otherwise, it would just be confusing.
if (count($info['bundles']) > 1) {
$label = t('Fields for @bundle_key @label', array('@bundle_key' => $bundle_key, '@label' => $bundle['label']));
}
@ -417,9 +442,9 @@ function views_bulk_operations_modify_action_submit($form, $form_state) {
* Properties that can't be changed are entity keys, timestamps, and the ones
* without a setter callback.
*
* @param $entity_type
* @param string $entity_type
* The entity type whose properties will be fetched.
* @param $display_values
* @param array $display_values
* An optional, admin-provided list of properties and fields that should be
* displayed for editing, used to filter the returned list of properties.
*/
@ -435,8 +460,17 @@ 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');
$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.
@ -484,9 +518,9 @@ function _views_bulk_operations_modify_action_get_properties($entity_type, $disp
* (through the action settings) then only bundles that have at least one field
* selected are returned.
*
* @param $entity_type
* @param string $entity_type
* The entity type whose bundles will be fetched.
* @param $context
* @param array $context
* The VBO context variable.
*/
function _views_bulk_operations_modify_action_get_bundles($entity_type, $context) {
@ -594,8 +628,8 @@ function views_bulk_operations_modify_action_views_bulk_operations_form($options
}
foreach ($info['bundles'] as $bundle_name => $bundle) {
$bundle_key = $info['entity keys']['bundle'];
// Show the more detailed label only if the entity type has multiple bundles.
// Otherwise, it would just be confusing.
// Show the more detailed label only if the entity type has multiple
// bundles. Otherwise, it would just be confusing.
if (count($info['bundles']) > 1) {
$label = t('Fields for @bundle_key @label', array('@bundle_key' => $bundle_key, '@label' => $bundle['label']));
}

View File

@ -1,17 +1,19 @@
<?php
/**
* @file
* VBO action to cancel user accounts.
*/
* @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'),
));
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) {
@ -75,7 +77,17 @@ function views_bulk_operations_user_cancel_action($account, $context) {
if (!empty($context['user_cancel_notify'])) {
_user_mail_notify('status_canceled', $account);
}
user_delete($account->uid);
// In cases when nodes are to be reassigned to UID 0, the user_delete must
// not run until *after* the user_cancel has been invoked, otherwise the
// nodes are deleted before they can be reassigned. Adding the user delete
// to the batch queue ensures things happen in the correct sequence.
$batch = array(
'operations' => array(
array('user_delete', array($account->uid)),
),
'file' => drupal_get_path('module', 'node') . '/node.admin.inc',
);
batch_set($batch);
watchdog('user', 'Deleted user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
break;
}

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 2015-07-01
version = "7.x-3.3"
; Information added by Drupal.org packaging script on 2017-02-21
version = "7.x-3.4"
core = "7.x"
project = "views_bulk_operations"
datestamp = "1435764542"
datestamp = "1487698687"

View File

@ -1,4 +1,5 @@
.vbo-select-all-markup, .vbo-table-select-all-markup {
.vbo-select-all-markup,
.vbo-table-select-all-markup {
display: none;
}
@ -15,9 +16,10 @@
.views-table-row-select-all td {
text-align: center;
}
.vbo-table-select-all-pages, .vbo-table-select-this-page {
margin: 0 !important;
padding: 2px 5px !important;
.vbo-views-form .vbo-table-select-all-pages,
.vbo-views-form .vbo-table-select-this-page {
margin: 0;
padding: 2px 5px;
}
/* Generic "select all" */
@ -30,6 +32,6 @@
margin-bottom: 0;
}
.vbo-fieldset-select-all div {
padding: 0 !important;
margin: 0 !important;
padding: 0;
margin: 0;
}

View File

@ -1,9 +1,17 @@
(function ($) {
// Polyfill for jQuery less than 1.6.
if (typeof $.fn.prop != 'function') {
jQuery.fn.extend({
prop: jQuery.fn.attr
});
}
Drupal.behaviors.vbo = {
attach: function(context) {
$('.vbo-views-form', context).each(function() {
Drupal.vbo.initTableBehaviors(this);
Drupal.vbo.initGenericBehaviors(this);
Drupal.vbo.toggleButtonsState(this);
});
}
}
@ -32,7 +40,8 @@
// This is the "select all" checkbox in (each) table header.
$('.vbo-table-select-all', form).click(function() {
var table = $(this).closest('table')[0];
$('input[id^="edit-views-bulk-operations"]:not(:disabled)', table).attr('checked', this.checked);
$('input[id^="edit-views-bulk-operations"]:not(:disabled)', table).prop('checked', this.checked);
Drupal.vbo.toggleButtonsState(form);
// Toggle the visibility of the "select all" row (if any).
if (this.checked) {
@ -83,35 +92,43 @@
$('.vbo-select-all-markup', form).show();
$('.vbo-select-this-page', form).click(function() {
$('input[id^="edit-views-bulk-operations"]', form).attr('checked', this.checked);
$('.vbo-select-all-pages', form).attr('checked', false);
$('input[id^="edit-views-bulk-operations"]', form).prop('checked', this.checked);
Drupal.vbo.toggleButtonsState(form);
$('.vbo-select-all-pages', form).prop('checked', false);
// Toggle the "select all" checkbox in grouped tables (if any).
$('.vbo-table-select-all', form).attr('checked', this.checked);
$('.vbo-table-select-all', form).prop('checked', this.checked);
});
$('.vbo-select-all-pages', form).click(function() {
$('input[id^="edit-views-bulk-operations"]', form).attr('checked', this.checked);
$('.vbo-select-this-page', form).attr('checked', false);
$('input[id^="edit-views-bulk-operations"]', form).prop('checked', this.checked);
Drupal.vbo.toggleButtonsState(form);
$('.vbo-select-this-page', form).prop('checked', false);
// Toggle the "select all" checkbox in grouped tables (if any).
$('.vbo-table-select-all', form).attr('checked', this.checked);
$('.vbo-table-select-all', form).prop('checked', this.checked);
// Modify the value of the hidden form field.
$('.select-all-rows', form).val(this.checked);
});
// Toggle submit buttons' "disabled" states with the state of the operation
// selectbox.
$('select[name="operation"]', form).change(function () {
Drupal.vbo.toggleButtonsState(form);
});
$('.vbo-select', form).click(function() {
// If a checkbox was deselected, uncheck any "select all" checkboxes.
if (!this.checked) {
$('.vbo-select-this-page', form).attr('checked', false);
$('.vbo-select-all-pages', form).attr('checked', false);
$('.vbo-select-this-page', form).prop('checked', false);
$('.vbo-select-all-pages', form).prop('checked', false);
// Modify the value of the hidden form field.
$('.select-all-rows', form).val('0')
var table = $(this).closest('table')[0];
if (table) {
// Uncheck the "select all" checkbox in the table header.
$('.vbo-table-select-all', table).attr('checked', false);
$('.vbo-table-select-all', table).prop('checked', false);
// If there's a "select all" row, hide it.
if ($('.vbo-table-select-this-page', table).length) {
@ -121,7 +138,24 @@
}
}
}
Drupal.vbo.toggleButtonsState(form);
});
}
Drupal.vbo.toggleButtonsState = function(form) {
// If no rows are checked, disable any form submit actions.
var selectbox = $('select[name="operation"]', form);
var checkedCheckboxes = $('.vbo-select:checked', form);
var buttons = $('[id^="edit-select"] input[type="submit"]', form);
if (selectbox.length) {
var has_selection = checkedCheckboxes.length && selectbox.val() !== '0';
buttons.prop('disabled', !has_selection);
}
else {
buttons.prop('disabled', !checkedCheckboxes.length);
}
};
})(jQuery);

View File

@ -6,7 +6,8 @@
function views_bulk_operations_views_data_alter(&$data) {
foreach (entity_get_info() as $entity_type => $info) {
if (isset($info['base table']) && isset($data[$info['base table']]['table'])) {
$data[$info['base table']]['views_bulk_operations'] = array(
$data[$info['base table']]['views_bulk_operations']['moved to'] = array('views_entity_' . $entity_type, 'views_bulk_operations');
$data['views_entity_' . $entity_type]['views_bulk_operations'] = array(
'title' => $data[$info['base table']]['table']['group'],
'group' => t('Bulk operations'),
'help' => t('Provide a checkbox to select the row for bulk operations.'),

View File

@ -6,7 +6,7 @@
* Implements the Views Form API.
*/
class views_bulk_operations_handler_field_operations extends views_handler_field {
class views_bulk_operations_handler_field_operations extends views_handler_field_entity {
var $revision = FALSE;
function init(&$view, &$options) {
@ -46,6 +46,12 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
unset($operation_options['use_queue']);
}
}
// Check whether this is a revision.
$table_data = views_fetch_data($this->table);
if (!empty($table_data['table']['revision'])) {
$this->revision = TRUE;
}
}
function option_definition() {
@ -186,6 +192,14 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
$dom_id . '-selected' => array(1),
),
);
$form['vbo_operations'][$operation_id]['skip_permission_check'] = array(
'#type' => 'checkbox',
'#title' => t('Skip permission step'),
'#default_value' => !empty($operation_options['skip_permission_check']),
'#dependency' => array(
$dom_id . '-selected' => array(1),
),
);
$form['vbo_operations'][$operation_id] += $operation->adminOptionsForm($dom_id, $this);
}
@ -263,19 +277,20 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
// At this point, the query has already been run, so we can access the results
// in order to get the base key value (for example, nid for nodes).
foreach ($this->view->result as $row_index => $row) {
$entity_id = $this->get_value($row);
$this->view->row_index = $row_index;
$id = $this->get_value($row, $this->real_field);
if ($this->options['vbo_settings']['force_single']) {
$form[$this->options['id']][$row_index] = array(
'#type' => 'radio',
'#parents' => array($this->options['id']),
'#return_value' => $entity_id,
'#return_value' => $id,
);
}
else {
$form[$this->options['id']][$row_index] = array(
'#type' => 'checkbox',
'#return_value' => $entity_id,
'#return_value' => $id,
'#default_value' => FALSE,
'#attributes' => array('class' => array('vbo-select')),
);
@ -293,9 +308,12 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
if (empty($options['selected'])) {
continue;
}
$operation = views_bulk_operations_get_operation($operation_id, $entity_type, $options);
if (!$operation || !$operation->access($user)) {
if (!$operation) {
continue;
}
$skip_permission_check = $operation->getAdminOption('skip_permission_check', FALSE);
if (!$operation->access($user) && !$skip_permission_check) {
continue;
}
$selected[$operation_id] = $operation;
@ -318,29 +336,7 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
* the entity type that VBO is operating on.
*/
public function get_entity_type() {
$base_table = $this->view->base_table;
// If the current field is under a relationship you can't be sure that the
// base table of the view is the base table of the current field.
// For example a field from a node author on a node view does have users as base table.
if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') {
$relationships = $this->view->display_handler->get_option('relationships');
$options = $relationships[$this->options['relationship']];
$data = views_fetch_data($options['table']);
$base_table = $data[$options['field']]['relationship']['base'];
}
// The base table is now known, use it to determine the entity type.
foreach (entity_get_info() as $entity_type => $info) {
if (isset($info['base table']) && $info['base table'] == $base_table) {
return $entity_type;
}
elseif (isset($info['revision table']) && $info['revision table'] == $base_table) {
$this->revision = TRUE;
return $entity_type;
}
}
// This should never happen.
_views_bulk_operations_report_error("Could not determine the entity type for VBO field on views base table %table", array('%table' => $base_table));
return FALSE;
return $this->entity_type;
}
}

View File

@ -154,7 +154,7 @@ function views_bulk_operations_drush_execute($vid = NULL, $operation_id = NULL)
$current = 1;
foreach ($view->result as $row_index => $result) {
$rows[$row_index] = array(
'entity_id' => $vbo->get_value($result),
'entity_id' => $result->{$vbo->real_field},
'views_row' => array(),
'position' => array(
'current' => $current++,

View File

@ -1,7 +1,7 @@
name = Views Bulk Operations
description = Provides a way of selecting multiple rows and applying operations to them.
dependencies[] = entity
dependencies[] = views
dependencies[] = views (>=3.12)
package = Views
core = 7.x
php = 5.2.9
@ -9,9 +9,9 @@ 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 2015-07-01
version = "7.x-3.3"
; Information added by Drupal.org packaging script on 2017-02-21
version = "7.x-3.4"
core = "7.x"
project = "views_bulk_operations"
datestamp = "1435764542"
datestamp = "1487698687"

View File

@ -45,6 +45,7 @@ function views_bulk_operations_load_action_includes() {
'archive.action',
'argument_selector.action',
'book.action',
'change_owner.action',
'delete.action',
'modify.action',
'script.action',
@ -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('name', db_like('views_bulk_operations_active_queue_') . '%', 'LIKE')
->condition('created', REQUEST_TIME - 86400, '<')
->execute();
}
@ -214,7 +215,12 @@ function views_bulk_operations_get_operation_info($operation_id = NULL) {
function views_bulk_operations_get_operation($operation_id, $entity_type, $options) {
$operations = &drupal_static(__FUNCTION__);
if (!isset($operations[$operation_id])) {
// Create a unique hash of the options.
$cid = md5(serialize($options));
// See if there's a cached copy of the operation, including entity type and
// options.
if (!isset($operations[$operation_id][$entity_type][$cid])) {
// Intentionally not using views_bulk_operations_get_operation_info() here
// since it's an expensive function that loads all the operations on the
// system, despite the fact that we might only need a few.
@ -223,14 +229,14 @@ function views_bulk_operations_get_operation($operation_id, $entity_type, $optio
$operation_info = $plugin['list callback']($operation_id);
if ($operation_info) {
$operations[$operation_id] = new $plugin['handler']['class']($operation_id, $entity_type, $operation_info, $options);
$operations[$operation_id][$entity_type][$cid] = new $plugin['handler']['class']($operation_id, $entity_type, $operation_info, $options);
}
else {
$operations[$operation_id] = FALSE;
$operations[$operation_id][$entity_type][$cid] = FALSE;
}
}
return $operations[$operation_id];
return $operations[$operation_id][$entity_type][$cid];
}
/**
@ -638,11 +644,11 @@ function theme_views_bulk_operations_confirmation($variables) {
// All rows on all pages have been selected, so show a count of additional items.
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));
$items[] = t('...and %count 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))));
$output = theme('item_list', array('items' => $items, 'title' => t('You selected the following %count:', array('%count' => $count))));
return $output;
}
@ -902,10 +908,16 @@ function views_bulk_operations_adjust_selection($queue_name, $operation, $option
}
$vbo = _views_bulk_operations_get_field($view);
// Call views_handler_field_entity::pre_render() to get the entities.
$vbo->pre_render($view->result);
$rows = array();
foreach ($view->result as $row_index => $result) {
// Set the row index.
$view->row_index = $row_index;
$rows[$row_index] = array(
'entity_id' => $vbo->get_value($result),
'entity_id' => $vbo->get_value($result, $vbo->real_field),
'views_row' => array(),
'position' => array(
'current' => ++$context['sandbox']['progress'],
@ -1075,7 +1087,8 @@ function views_bulk_operations_queue_item_process($queue_item_data, &$log = NULL
}
// 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)) {
$skip_permission_check = $operation->getAdminOption('skip_permission_check');
if (!$skip_permission_check && !_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(),
@ -1127,12 +1140,22 @@ function views_bulk_operations_direct_adjust(&$selection, $vbo) {
if ($field_name != $vbo->options['id']) {
unset($view->field[$field_name]);
}
else {
// Get hold of the new VBO field.
$new_vbo = $view->field[$field_name];
}
}
$view->execute($vbo->view->current_display);
// Call views_handler_field_entity::pre_render() to get the entities.
$new_vbo->pre_render($view->result);
$results = array();
foreach ($view->result as $row_index => $result) {
$results[$row_index] = $vbo->get_value($result);
// Set the row index.
$view->row_index = $row_index;
$results[$row_index] = $new_vbo->get_value($result, $new_vbo->real_field);
}
$selection = $results;
}
@ -1160,9 +1183,10 @@ function views_bulk_operations_direct_process($operation, $rows, $options) {
}
$entities = _views_bulk_operations_entity_load($entity_type, $entity_ids, $options['revision']);
$skip_permission_check = $operation->getAdminOption('skip_permission_check');
// Filter out entities that can't be accessed.
foreach ($entities as $id => $entity) {
if (!_views_bulk_operations_entity_access($operation, $entity_type, $entity)) {
if (!$skip_permission_check && !_views_bulk_operations_entity_access($operation, $entity_type, $entity, $account)) {
$context['results']['log'][] = t('Skipped %operation on @type %title due to insufficient permissions.', array(
'%operation' => $operation->label(),
'@type' => $entity_type,

View File

@ -114,15 +114,25 @@ function views_bulk_operations_rules_action_info() {
function views_bulk_operations_views_list() {
$selectable_displays = array();
foreach (views_get_enabled_views() as $name => $base_view) {
$view = $base_view->clone_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) {
$selectable_displays[$view->name . '|' . $display_name] = check_plain($view->human_name) . ' | ' . check_plain($display->display_title);
if (!$view->set_display($display_name)) {
continue;
}
// Initialize the style plugin and only continue to initialize handlers
// if the style uses fields.
if (!$view->init_style() || !$view->style_plugin->uses_fields()) {
continue;
}
$view->init_handlers($display_name);
if (_views_bulk_operations_get_field($view)) {
$selectable_displays[$view->name . '|' . $display_name] = check_plain($view->human_name . ' | ' . $display->display_title);
}
}
}
return $selectable_displays;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 B

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 B

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 B

After

Width:  |  Height:  |  Size: 254 B

View File

@ -524,20 +524,30 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
// TODO: Handle external databases.
$result = db_query_range('SELECT * FROM {' . $this->index_tablename() . '} ORDER BY ' . $this->batched_execution_state->sandbox['weight_field_alias'] . ' ASC', 0, $this->get_option('segment_size'));
$this->view->result = array();
foreach ($result as $item_hashed) {
$item = new stdClass();
// We had to shorten some of the column names in the index, restore
// those now.
foreach ($item_hashed as $hash => $value) {
if (isset($this->batched_execution_state->sandbox['field_aliases'][$hash])) {
$item->{$this->batched_execution_state->sandbox['field_aliases'][$hash]} = $value;
}
else {
$item->{$hash} = $value;
$query_plugin = get_class($this->view->query);
if ($query_plugin == 'views_plugin_query_default') {
foreach ($result as $item_hashed) {
$item = new stdClass();
// We had to shorten some of the column names in the index, restore
// those now.
foreach ($item_hashed as $hash => $value) {
if (isset($this->batched_execution_state->sandbox['field_aliases'][$hash])) {
$item->{$this->batched_execution_state->sandbox['field_aliases'][$hash]} = $value;
}
else {
$item->{$hash} = $value;
}
}
// Push the restored $item in the views result array.
$this->view->result[] = $item;
}
}
elseif ($query_plugin == 'SearchApiViewsQuery') {
foreach ($result as $row) {
$item = unserialize($row->data);
$item->{$this->batched_execution_state->sandbox['weight_field_alias']} = $row->{$this->batched_execution_state->sandbox['weight_field_alias']};
$this->view->result[] = $item;
}
// Push the restored $item in the views result array.
$this->view->result[] = $item;
}
$this->view->_post_execute();
break;
@ -623,25 +633,31 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
// Get views to build the query.
$view->build();
// Change the query object to use our custom one.
switch ($this->_get_database_driver()) {
case 'pgsql':
$query_class = 'views_data_export_plugin_query_pgsql_batched';
break;
$query_plugin = get_class($view->query);
if ($query_plugin == 'views_plugin_query_default') {
// Change the query object to use our custom one.
switch ($this->_get_database_driver()) {
case 'pgsql':
$query_class = 'views_data_export_plugin_query_pgsql_batched';
break;
default:
$query_class = 'views_data_export_plugin_query_default_batched';
break;
}
$query = new $query_class();
// Copy the query over:
foreach ($view->query as $property => $value) {
$query->$property = $value;
}
// Replace the query object.
$view->query = $query;
default:
$query_class = 'views_data_export_plugin_query_default_batched';
break;
}
$query = new $query_class();
// Copy the query over:
foreach ($view->query as $property => $value) {
$query->$property = $value;
}
// Replace the query object.
$view->query = $query;
$view->execute();
$view->execute();
}
elseif ($query_plugin == 'SearchApiViewsQuery') {
$this->store_search_api_result(clone($view));
}
}
/**
@ -846,6 +862,55 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
$conn_info = Database::getConnectionInfo($name);
return $conn_info['default']['driver'];
}
/**
* Based on views_data_export_plugin_query_default_batched::execute().
*/
function store_search_api_result($view) {
$display_handler = &$view->display_handler;
$start = microtime(TRUE);
try {
// Get all the view results.
$view->query->set_limit(NULL);
$view->query->set_offset(0);
$view->query->execute($view);
$weight_alias = 'vde_weight';
$display_handler->batched_execution_state->sandbox['weight_field_alias'] = $weight_alias;
$schema = array(
'fields' => array(
$weight_alias => array('type' => 'int'),
'data' => array('type' => 'blob'),
));
db_create_table($display_handler->index_tablename(), $schema);
if (!empty($view->result)) {
$insert_query = db_insert($display_handler->index_tablename())->fields(array($weight_alias, 'data'));
$weight = 0;
foreach ($view->result as $item) {
$insert_query->values(array(
$weight_alias => $weight,
'data' => serialize($item),
));
$weight++;
}
$insert_query->execute();
}
$view->result = array();
// Now create an index for the weight field, otherwise the queries on the
// index will take a long time to execute.
db_add_unique_key($display_handler->index_tablename(), $weight_alias, array($weight_alias));
}
catch (Exception $e) {
$view->result = array();
debug('Exception: ' . $e->getMessage());
}
$view->execute_time = microtime(TRUE) - $start;
}
}
class views_data_export_plugin_query_default_batched extends views_plugin_query_default {

View File

@ -169,7 +169,9 @@ function drush_views_data_export($view_name, $display_id, $output_file) {
}
$arguments = drush_get_option('arguments', '');
$arguments = explode(',', $arguments);
if(!empty($arguments) && is_array($arguments)) {
$arguments = explode(',', $arguments);
}
if ($view->display_handler->is_batched()) {
// This is a batched export, and needs to be handled as such.

View File

@ -22,9 +22,9 @@ files[] = "tests/txt_export.test"
files[] = "tests/xls_export.test"
files[] = "tests/xml_export.test"
; Information added by Drupal.org packaging script on 2016-09-20
version = "7.x-3.1"
; Information added by Drupal.org packaging script on 2017-04-05
version = "7.x-3.2"
core = "7.x"
project = "views_data_export"
datestamp = "1474360174"
datestamp = "1491379387"

View File

@ -185,7 +185,12 @@ function views_data_export_requirements($phase) {
switch ($db_type) {
case 'mysql':
// Check the max allowed packet size.
$max_allowed_packet = db_query('SHOW VARIABLES WHERE variable_name = :name', array(':name' => 'max_allowed_packet'))->fetchField(1);
try {
$max_allowed_packet = db_query('SHOW VARIABLES WHERE variable_name = :name', array(':name' => 'max_allowed_packet'))->fetchField(1);
}
catch (Exception $e) {
$max_allowed_packet = NULL;
}
if (is_numeric($max_allowed_packet)) {
if ($max_allowed_packet < (16 * 1024 * 1024)) {
$requirements['views_data_export'] = array(

View File

@ -114,7 +114,7 @@ function views_data_export_cron() {
function views_data_export_garbage_collect($expires = NULL, $chunk = NULL) {
if (lock_acquire('views_data_export_gc')) {
if (!isset($expires)) {
$expires = variable_get('views_data_export_gc_expires', 604800); // one week
$expires = variable_get('views_data_export_gc_expires', DRUPAL_MAXIMUM_TEMP_FILE_AGE);
}
if (!isset($chunk)) {
$chunk = variable_get('views_data_export_gc_chunk', 30);

View File

@ -0,0 +1,43 @@
Views Send 7.x-1.2, 2016-03-29
------------------------------
#2237585 by hansfn: Make allowed file extensions for attachments configurable
#2368533 by hansfn: Bypassing views render layer breaks all sort of things (Also fixes #1833608 Views field rewriting)
#2693393 by hansfn: Mailsystem 3.x-dev requires "key" and "module" keys in the mail message
Views Send 7.x-1.1, 2014-06-21
------------------------------
#2225631 by hansfn: Using filter_fallback_format() instead of hard-coded value for message text format.
#2258947 by hansfn: Call to a member function get_value() on a non-object.
#2089409 by hansfn: Added support for Mandrill.
Views Send 7.x-1.0, 2014-04-15
------------------------------
#2202769 by hansfn: Subject not displaying correctly
#2122909 by hansfn: Unwanted conversion of quotes in fields used for recipient's e-mail and name
#2023977 by hansfn: Views Send overrides Mail System module configuration
#1963960 by hansfn: Add hooks.
#1957442 by hansfn: Token replacement doesn't work for repeated field.
#1939332 by hansfn: Token list doesn't update.
#1937548 by hansfn: Use rendered fields.
#1255194 by hansfn: Support multiple value email fields.
#1051564 by hansfn: Support recipient & sender altering.
#1894446 by hansfn: General token replacements doesn't work.
#1571228 by hansfn: Fatal error when recipient's name not selected.
#1497118 by hansfn: Queue processing with Batch API on D7.
By hansfn: Improved Rules integration - email message exposed as tokens.
#821530 by hansfn: Attachments to messages.
#1210758 by Sanjo: Incorrect detection of PHP maximum execution time almost exceeded.
#1513822 by hansfn: Rules integration.
#1462126 by hansfn: Indicate which selected rows contain invalid e-mail addresses.
#1477828 by bojanz: Stop relying on VBO, implement a custom Views Form handler instead.
#1462118 by cbergmann, hansfn: Preview not working.
#1454740 by hansfn: 'send mass mail' appears in VBO Operations drop-down even if not allowed.
#1466638 by cbergmann: Always fetch the view from the context.
#1468120 by cbergmann: views_send_debug has no effect.
#1462118 by Ben Coleman, hansfn: Trim all email addresses.
#1455820 by hansfn: Improve handling of formats
#1451594 by hansfn: Make field support more robust for the recipient's name/e-mail
#1449988 by hansfn: Fix token replacement for fields.
#1411976 by julien66, hansfn: Added carbon copy (to sender).
#1408756 by hansfn: Pass the selected views row(s) to the action.
#1052040 by hansfn: Port to Drupal 7.

View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,65 @@
Views Send allow mass mailing using Views.
The messages are queued in a spool table and delivered only on cron.
You can control how many messages will be send per cron run.
CONFIGURATION
General settings can be configured at: Site Configuration > Views Send
USAGE
1. Create a view and add at least one column containing e-mail addresses.
2. [Optional] Expose Views filters to let the user easily build list of
recipients using UI.
3. Add the "Global: Send e-mail" field to your view. This field provides the checkboxes
that allow the user to select multiple rows.
4. Save the view, load the page, use exposed filters to build the list, select
all or some rows and choose "Send e-mail".
5. Fill the message form to configure the e-mail. Use tokens to personalize
your e-mail.
6. Preview and send the message.
DEPENDENCIES & INTEGRATION
* Views Send depends on Views.
* The module integrates features from:
o Mime Mail. When the Mime Mail module is enabled, the user can choose to send
rich HTML messages and/or use attachments.
o Tokens. When the Tokens module is enabled, the user can insert context tokens
into the subject or body of the message. Note that row-based tokens are
available even if Tokens module is disabled.
o Rules. When the Rules module is enabled, the user can define actions
for when emails are sent and/or placed in the spool.
FOR DEVELOPERS / HOOKS
The module provides two hooks:
* hook_views_send_mail_queued($message, $view, $row_id)
Called just after each message is queued.
* hook_views_send_mail_alter(&$message)
Called just before each message is queued.
SIMILAR MODULES
You may want to try also:
* Views Mail | http://drupal.org/project/views_mail
See what's different: http://drupal.org/node/795782
* Simplenews | http://drupal.org/project/simplenews
Some pieces of code were inspired by Simplenews module.
HOW CAN YOU GET INVOLVED?
* Write a review for this module on http://drupalmodules.com/module/views-send
* Help translate this module at Drupal Localize Server
http://localize.drupal.org/translate/projects/views_send
MAINTAINERS & SPONSORS
* Module maintainer
Hans Fredrik Nordhaug (hansfn) | http://drupal.org/user/40521
* Module author of original Drupal 6 version
Claudiu Cristea (claudiu.cristea) | http://drupal.org/user/56348
* The Drupal 6 version of this module was sponsored by Grafit SRL,
now Webikon | http://www.webikon.com

View File

@ -0,0 +1,15 @@
<?php
/**
* Implements hook_views_data_alter().
*/
function views_send_views_data_alter(&$data) {
$data['views']['views_send'] = array(
'title' => t('Send e-mail'),
'help' => t('Provide a checkbox to select the row for e-mail sending.'),
'field' => array(
'handler' => 'views_send_handler_field_selector',
'click sortable' => FALSE,
),
);
}

View File

@ -0,0 +1,60 @@
<?php
/**
* @file
* Views field handler. Outputs a checkbox for selecting a row for email sending.
* Implements the Views Form API.
*/
class views_send_handler_field_selector extends views_handler_field {
/**
* If the view is using a table style, provide a
* placeholder for a "select all" checkbox.
*/
function label() {
if ($this->view->style_plugin instanceof views_plugin_style_table) {
return '<!--views-send-select-all-->';
}
else {
return parent::label();
}
}
function query() {
// Do nothing.
}
function render($values) {
return '<!--form-item-' . $this->options['id'] . '--' . $this->view->row_index . '-->';
}
/**
* The form which replaces the placeholder from render().
*/
function views_form(&$form, &$form_state) {
// The view is empty, abort.
if (empty($this->view->result)) {
return;
}
$form[$this->options['id']] = array(
'#tree' => TRUE,
);
foreach ($this->view->result as $row_index => $row) {
$form[$this->options['id']][$row_index] = array(
'#type' => 'checkbox',
'#default_value' => FALSE,
'#attributes' => array('class' => array('views-send-select')),
);
}
}
function views_form_validate($form, &$form_state) {
$field_name = $this->options['id'];
$selection = array_filter($form_state['values'][$field_name]);
if (empty($selection)) {
form_set_error($field_name, t('Please select at least one item.'));
}
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* @file
* Views Send administration page.
*
* @ingroup views_send
*/
/**
* Callback for admin/settings/views_send menu item.
*/
function views_send_settings() {
$form = array();
if (VIEWS_SEND_MIMEMAIL) {
$form['views_send_attachment_valid_extensions'] = array(
'#type' => 'textfield',
'#title' => t('Valid file extensions for attachments'),
'#default_value' => variable_get('views_send_attachment_valid_extensions', ''),
'#description' => t('A space separated list of allowed file extensions for attachments. Leave the list empty if you want to use the default list from file_save_upload().'),
);
}
$throttle = drupal_map_assoc(array(1, 10, 20, 30, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000));
$throttle[0] = t('Unlimited');
$form['views_send_throttle'] = array(
'#type' => 'select',
'#title' => t('Cron throttle'),
'#options' => $throttle,
'#default_value' => variable_get('views_send_throttle', 20),
'#description' => t('Sets the numbers of messages sent per cron run. Failure to send will also be counted. Cron execution must not exceed the PHP maximum execution time of %max seconds. You find the time spend to send e-mails in the !recent_logs.', array('%max' => ini_get('max_execution_time'), '!recent_logs' => l(t('Recent log entries'), 'admin/reports/dblog'))),
);
$form['views_send_spool_expire'] = array(
'#type' => 'select',
'#title' => t('Mail spool expiration'),
'#options' => array(0 => t('Immediate'), 1 => t('1 day'), 7 => t('1 week'), 14 => t('2 weeks')),
'#default_value' => variable_get('views_send_spool_expire', 0),
'#description' => t('E-mails are spooled. How long must messages be retained in the spool after successfull sending.'),
);
$form['views_send_debug'] = array(
'#type' => 'checkbox',
'#title' => t('Log e-mails'),
'#default_value' => variable_get('views_send_debug', FALSE),
'#description' => t('When checked all outgoing mesages are logged in the system log. A logged e-mail does not guarantee that it is send or will be delivered. It only indicates that a message is send to the PHP mail() function. No status information is available of delivery by the PHP mail() function.'),
);
return system_settings_form($form);
}

View File

@ -0,0 +1,77 @@
<?php
/**
* @file
* Views Send cron rotuines.
*
* @ingroup views_send
*/
/**
* Process the spool queue at cron run.
*/
function views_send_send_from_spool() {
$limit = variable_get('views_send_throttle', 20);
$ok = $fail = $check = 0;
// Get PHP maximum execution time. 30 seconds default.
$max_execution_time = ini_get('max_execution_time') ? ini_get('max_execution_time') : VIEWS_SEND_MAX_EXECUTION_TIME;
// Reset a Drupal timer.
timer_start('views_send');
// Retrieve messages to be send.
$query = "SELECT * FROM {views_send_spool} WHERE status = :status ORDER BY tentatives ASC, timestamp ASC";
$result = $limit ? db_query_range($query, 0, $limit, array(':status' => 0)) : db_query($query, array(':status' => 0));
foreach ($result as $message) {
// Send the message.
$status = views_send_deliver($message);
if ($status) {
// Update the spool status.
db_query("UPDATE {views_send_spool} SET status = :status WHERE eid = :eid", array(':status' => 1, ':eid' => $message->eid));
if (variable_get('views_send_debug', FALSE)) {
watchdog('views_send', 'Message sent to %mail.', array('%mail' => $message->to_mail));
}
if (module_exists('rules')) {
rules_invoke_event('views_send_email_sent', $message);
}
$ok++;
}
else {
// Increment tentatives so that next time this message
// will be scheduled with low priority.
db_query("UPDATE {views_send_spool} SET tentatives = tentatives + 1 WHERE eid = :eid", array(':eid' => $message->eid));
$fail++;
}
// Check the elapsed time and break if we've spent more than 80%.
// We check every 50 messages.
if (++$check >= 50) {
// Reset the counter.
$check = 0;
// Break if exceded.
if (timer_read('views_send') / 1000 > .8 * $max_execution_time) {
watchdog('views_send', 'PHP maximum execution time almost exceeded. Remaining e-mail messages will be sent during the next cron run. If this warning occurs regularly you should reduce the cron throttle setting.', NULL, WATCHDOG_WARNING);
break;
}
}
}
if ($ok + $fail > 0) {
// Log results and exit.
watchdog('views_send', '%ok messages sent in %sec seconds, %fail failed sending.',
array('%ok' => $ok, '%sec' => timer_read('views_send') / 1000, '%fail' => $fail)
);
}
}
/**
* Clear the expired items from spool.
*/
function views_send_clear_spool() {
// TODO: Drupal 7: replace time() with REQUEST_TIME.
$expiration_time = time() - variable_get('views_send_spool_expire', 0) * 86400;
db_query("DELETE FROM {views_send_spool} WHERE status = :status AND timestamp <= :expiry", array(':status' => 1, 'expiry' => $expiration_time));
}

View File

@ -0,0 +1,63 @@
.views-send-select-all-markup {
display: none;
}
/* "Select all" for table styles. */
.views-send-selection-form thead .form-type-checkbox {
margin: 0;
}
.views-send-table-select-all {
display: none;
}
.views-table-row-select-all {
display: none;
}
.views-table-row-select-all td {
text-align: center;
}
.views-send-table-select-this-page {
margin: 0 !important;
padding: 2px 5px !important;
}
/* Generic "select all" */
.views-send-fieldset-select-all {
text-align: center;
width: 210px;
padding: 0.6em 0;
}
.views-send-fieldset-select-all .form-item {
margin-bottom: 0;
}
.views-send-fieldset-select-all div {
padding: 0 !important;
margin: 0 !important;
}
form.views-send-preview label {
float: left;
clear: both;
padding: 2px;
}
form.views-send-preview .views-send-preview-value {
margin-left: 100px;
overflow: auto;
border: 1px solid #bbbbbb;
padding: 2px;
}
#views-send-preview-to {
height: 50px;
}
#views-send-preview-message {
height: 150px;
white-space:pre;
}
#views-send-preview-headers, #views-send-preview-attachments {
height: 80px;
font-family: monospace;
}

View File

@ -0,0 +1,16 @@
name = "Views Send"
description = "Provides a way to send e-mails from a list created with Views"
dependencies[] = views
configure = admin/config/system/views_send
package = Views
core = 7.x
files[] = views_send.rules.inc
files[] = views/views_send_handler_field_selector.inc
; Information added by Drupal.org packaging script on 2017-04-03
version = "7.x-1.6"
core = "7.x"
project = "views_send"
datestamp = "1491222486"

View File

@ -0,0 +1,151 @@
<?php
/**
* @file
* The install and update code for the Views Send module.
*
* @ingroup views_send.
*/
/**
* Implements hook_schema().
*/
function views_send_schema() {
$schema['views_send_spool'] = array(
'description' => 'Table holds e-mails that are being send on cron.',
'fields' => array(
'eid' => array(
'description' => 'The primary identifier for an e-mail.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE),
'uid' => array(
'description' => 'The user that has sent the message.',
'type' => 'int',
'not null' => TRUE,
'unsigned' => TRUE,
'default' => 0),
'timestamp' => array(
'description' => 'The Unix timestamp when the message was added to spool.',
'type' => 'int',
'not null' => TRUE,
'unsigned' => TRUE,
'default' => 0),
'status' => array(
'description' => 'Status: 0 = pending; 1 = sent.',
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'unsigned' => TRUE,
'default' => 0),
'tentatives' => array(
'description' => 'How many times we tried to send this message.',
'type' => 'int',
'not null' => TRUE,
'unsigned' => TRUE,
'default' => 0),
'from_name' => array(
'description' => 'The real name of the sender.',
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'default' => ''),
'from_mail' => array(
'description' => 'The sender e-mail address.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => ''),
'to_name' => array(
'description' => 'The real name of the recipient.',
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'default' => ''),
'to_mail' => array(
'description' => 'The recipient e-mail address.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => ''),
'subject' => array(
'description' => 'The e-mail subject.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => ''),
'body' => array(
'description' => 'The e-mail body.',
'type' => 'text',
'not null' => TRUE,
'size' => 'big',),
'headers' => array(
'description' => 'The e-mail additional headers.',
'type' => 'text',
'not null' => TRUE,
'size' => 'big',),
),
'indexes' => array(
'uid' => array('uid'),
'timestamp' => array('timestamp'),
),
'primary key' => array('eid'),
);
return $schema;
}
/**
* Implements hook_uninstall().
*/
function views_send_uninstall() {
db_query("DELETE FROM {variable} WHERE name LIKE 'views_send_%'");
db_query("DELETE FROM {cache} WHERE cid LIKE 'variables%'");
}
/**
* Remove unused variables.
*/
function views_send_update_6001() {
variable_del('views_send_format');
variable_del('views_send_from_mail');
variable_del('views_send_from_name');
variable_del('views_send_headers');
variable_del('views_send_message');
variable_del('views_send_priority');
variable_del('views_send_receipt');
variable_del('views_send_remember');
variable_del('views_send_subject');
variable_del('views_send_tokens');
variable_del('views_send_to_mail');
variable_del('views_send_to_name');
return array();
}
/**
* Remove views_send_format variables.
*/
function views_send_update_6002() {
$ret = array();
$ret[] = update_sql("DELETE FROM {variable} WHERE name LIKE 'views_send_format_%'");
return $ret;
}
/**
* Backend structure is altered for implementing http://drupal.org/node/808058
*/
function views_send_update_6003() {
$ret = array();
// Get updated schema.
$schema = views_send_schema();
// Rename body field from 'message' to 'body'.
db_change_field($ret, 'views_send_spool', 'message', 'body', $schema['views_send_spool']['fields']['body']);
// Drop other fields that are obsolete after spooling.
db_drop_field($ret, 'views_send_spool', 'format');
db_drop_field($ret, 'views_send_spool', 'priority');
db_drop_field($ret, 'views_send_spool', 'receipt');
return $ret;
}

View File

@ -0,0 +1,53 @@
(function ($) {
// Polyfill for jQuery less than 1.6.
if (typeof $.fn.prop != 'function') {
jQuery.fn.extend({
prop: jQuery.fn.attr
});
}
Drupal.behaviors.viewsSend = {
attach: function(context) {
$('.views-send-selection-form', context).each(function() {
Drupal.viewsSend.initTableBehaviors(this);
Drupal.viewsSend.initGenericBehaviors(this);
});
}
}
Drupal.viewsSend = Drupal.viewsSend || {};
Drupal.viewsSend.initTableBehaviors = function(form) {
$('.views-send-table-select-all', form).show();
// This is the "select all" checkbox in (each) table header.
$('.views-send-table-select-all', form).click(function() {
var table = $(this).closest('table')[0];
$('input[id^="edit-views-send"]:not(:disabled)', table).prop('checked', this.checked).change();
});
}
Drupal.viewsSend.initGenericBehaviors = function(form) {
// Show the "select all" fieldset.
$('.views-send-select-all-markup', form).show();
$('.views-send-select-this-page', form).click(function() {
$('input[id^="edit-views-send"]', form).prop('checked', this.checked).change();
// Toggle the "select all" checkbox in grouped tables (if any).
$('.views-send-table-select-all', form).prop('checked', this.checked).change();
});
$('.views-send-select', form).click(function() {
// If a checkbox was deselected, uncheck any "select all" checkboxes.
if (!this.checked) {
$('.views-send-select-this-page', form).prop('checked', false).change();
var table = $(this).closest('table')[0];
if (table) {
// Uncheck the "select all" checkbox in the table header.
$('.views-send-table-select-all', table).prop('checked', false).change();
}
}
});
}
})(jQuery);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
<?php
/**
* Implementation of hook_rules_event_info().
*/
function views_send_rules_event_info() {
$defaults = array(
'group' => t('Views Send'),
);
return array(
'views_send_email_sent' => $defaults + array(
'label' => t('After sending an individual email'),
'variables' => array(
'views_send_email_message' => array(
'type' => 'views_send_email_message',
'label' => t('e-mail message'),
'description' => t('All information about the message.')
),
),
),
'views_send_all_email_added_to_spool' => $defaults + array(
'label' => t('After adding all e-mails to the spool'),
'variables' => array(
'views_send_email_count' => array(
'type' => 'integer',
'label' => t('message count'),
'description' => t('The number of messages added to the spool.')
),
),
),
'views_send_email_added_to_spool' => $defaults + array(
'label' => t('After adding an individual e-mail to the spool'),
'variables' => array(
'views_send_email_message' => array(
'type' => 'views_send_email_message',
'label' => t('e-mail message'),
'description' => t('All information about the message.')
),
),
),
);
}
/**
* Implementation of hook_rules_data_info().
*/
function views_send_rules_data_info() {
return array(
'views_send_email_message' => array(
'label' => t('Views Send e-mail message'),
'group' => t('Views Send'),
'wrap' => TRUE,
'property info' => _views_send_email_message_property_info(),
),
);
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Token integration for the Views Send module.
*/
/**
* Implements hook_token_info().
*
* These token are used by Rules and not in the Views form.
*/
function views_send_token_info() {
$data = array();
foreach (_views_send_email_message_property_info() as $key => $info) {
$data[$key] = array(
'name' => $info['label'],
'description' => ''
);
}
$type = array(
'name' => t('Views Send e-mail message'),
'description' => t('Tokens for Views Send e-mail message.'),
'needs-data' => 'views_send_email_message',
);
return array(
'types' => array('views_send_email_message' => $type),
'tokens' => array('views_send_email_message' => $data),
);
}
/**
* Implementation hook_tokens().
*
* These token replacements are used by Rules and not in the Views form.
*/
function views_send_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
if ($type == 'views_send_email_message' && !empty($data['views_send_email_message'])) {
foreach ($tokens as $name => $original) {
$replacements[$original] = $data['views_send_email_message']->{$name};
}
}
return $replacements;
}