ran security updates on contrib modules

ctools, video_embed_field, migrate, views_bulk_operations
This commit is contained in:
Bachir Soussi Chiadmi 2015-09-17 14:44:25 +02:00
parent 5d30d9bcee
commit f7cd9c0858
94 changed files with 1242 additions and 413 deletions

View File

@ -6,9 +6,9 @@ package = Chaos tool suite
version = CTOOLS_MODULE_VERSION version = CTOOLS_MODULE_VERSION
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -9,9 +9,9 @@ files[] = includes/math-expr.inc
files[] = includes/stylizer.inc files[] = includes/stylizer.inc
files[] = tests/css_cache.test files[] = tests/css_cache.test
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -23,7 +23,7 @@ define('CTOOLS_API_VERSION', '2.0.8');
* ; Requires CTools v7.x-1.4 or newer. * ; Requires CTools v7.x-1.4 or newer.
* dependencies[] = ctools (>=1.4) * dependencies[] = ctools (>=1.4)
*/ */
define('CTOOLS_MODULE_VERSION', '7.x-1.7'); define('CTOOLS_MODULE_VERSION', '7.x-1.9');
/** /**
* Test the CTools API version. * Test the CTools API version.
@ -617,6 +617,27 @@ function ctools_registry_files_alter(&$files, $indexed_modules) {
return _ctools_registry_files_alter($files, $indexed_modules); return _ctools_registry_files_alter($files, $indexed_modules);
} }
// -----------------------------------------------------------------------
// FAPI hooks that must be in the .module file.
/**
* Alter the comment form to get a little more control over it.
*/
function ctools_form_comment_form_alter(&$form, &$form_state) {
if (!empty($form_state['ctools comment alter'])) {
// Force the form to post back to wherever we are.
$form['#action'] = url($_GET['q'], array('fragment' => 'comment-form'));
if (empty($form['#submit'])) {
$form['#submit'] = array('comment_form_submit');
}
$form['#submit'][] = 'ctools_node_comment_form_submit';
}
}
function ctools_node_comment_form_submit(&$form, &$form_state) {
$form_state['redirect'][0] = $_GET['q'];
}
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// CTools hook implementations. // CTools hook implementations.
@ -1018,3 +1039,50 @@ function ctools_ctools_entity_context_alter(&$plugin, &$entity, $plugin_id) {
} }
} }
} }
/**
* Implements hook_field_create_field().
*/
function ctools_field_create_field($field) {
ctools_flush_field_caches();
}
/**
* Implements hook_field_create_instance().
*/
function ctools_field_create_instance($instance) {
ctools_flush_field_caches();
}
/**
* Implements hook_field_delete_field().
*/
function ctools_field_delete_field($field) {
ctools_flush_field_caches();
}
/**
* Implements hook_field_delete_instance().
*/
function ctools_field_delete_instance($instance) {
ctools_flush_field_caches();
}
/**
* Implements hook_field_update_field().
*/
function ctools_field_update_field($field, $prior_field, $has_data) {
ctools_flush_field_caches();
}
/**
* Implements hook_field_update_instance().
*/
function ctools_field_update_instance($instance, $prior_instance) {
ctools_flush_field_caches();
}
/**
* Clear field related caches.
*/
function ctools_flush_field_caches() {
// Clear caches of 'Entity field' content type plugin.
cache_clear_all('ctools_entity_field_content_type_content_types', 'cache');
}

View File

@ -5,9 +5,9 @@ package = Chaos tool suite
version = CTOOLS_MODULE_VERSION version = CTOOLS_MODULE_VERSION
dependencies[] = ctools dependencies[] = ctools
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -5,9 +5,9 @@ version = CTOOLS_MODULE_VERSION
dependencies[] = ctools dependencies[] = ctools
core = 7.x core = 7.x
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -524,7 +524,7 @@ function ctools_ajax_sample_wizard_next(&$form_state) {
} }
/** /**
* Handle the 'finish' click on teh add/edit pane form wizard. * Handle the 'finish' click on the add/edit pane form wizard.
* *
* All we need to do is set a flag so the return can handle adding * All we need to do is set a flag so the return can handle adding
* the pane. * the pane.

View File

@ -5,9 +5,9 @@ package = Chaos tool suite
version = CTOOLS_MODULE_VERSION version = CTOOLS_MODULE_VERSION
dependencies[] = ctools dependencies[] = ctools
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -8,9 +8,9 @@ dependencies[] = page_manager
dependencies[] = advanced_help dependencies[] = advanced_help
core = 7.x core = 7.x
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -341,7 +341,16 @@ function ctools_content_editable($type, $subtype, $conf) {
return FALSE; return FALSE;
} }
if ($function = ctools_plugin_get_function($subtype, 'check editable')) { $function = FALSE;
if (!empty($subtype['check editable'])) {
$function = ctools_plugin_get_function($subtype, 'check editable');
}
elseif (!empty($type['check editable'])) {
$function = ctools_plugin_get_function($type, 'check editable');
}
if ($function) {
return $function($type, $subtype, $conf); return $function($type, $subtype, $conf);
} }

View File

@ -736,6 +736,15 @@ function ctools_edit_context_form_defaults($form, &$form_state) {
'#default_value' => $conf['keyword'], '#default_value' => $conf['keyword'],
); );
if ($type_info['key'] == 'requiredcontexts') {
$form['optional'] = array(
'#type' => 'checkbox',
'#title' => t('Context is optional'),
'#default_value' => !empty($form_state['conf']['optional']),
'#description' => t('This context need not be present for the component to function.'),
);
}
$form['#submit'][] = 'ctools_edit_context_form_defaults_submit'; $form['#submit'][] = 'ctools_edit_context_form_defaults_submit';
return $form; return $form;
@ -752,6 +761,9 @@ function ctools_edit_context_form_defaults_submit(&$form, &$form_state) {
$form_state['conf']['default'] = $form_state['values']['default']; $form_state['conf']['default'] = $form_state['values']['default'];
$form_state['conf']['title'] = $form_state['values']['title']; $form_state['conf']['title'] = $form_state['values']['title'];
} }
if ($form_state['type info']['key'] == 'requiredcontexts') {
$form_state['conf']['optional'] = $form_state['values']['optional'];
}
$form_state['conf']['identifier'] = $form_state['values']['identifier']; $form_state['conf']['identifier'] = $form_state['values']['identifier'];
$form_state['conf']['keyword'] = $form_state['values']['keyword']; $form_state['conf']['keyword'] = $form_state['values']['keyword'];

View File

@ -216,7 +216,7 @@ class ctools_context_optional extends ctools_context_required {
$context = new ctools_context('any'); $context = new ctools_context('any');
$context->title = t('No context'); $context->title = t('No context');
$context->identifier = t('No context'); $context->identifier = t('No context');
$contexts = array_merge(array('empty' => $context), $contexts); $contexts['empty'] = $context;
} }
function filter($contexts) { function filter($contexts) {
@ -1505,7 +1505,7 @@ function ctools_access($settings, $contexts = array()) {
return TRUE; return TRUE;
} }
else if (!$pass && $settings['logic'] == 'and') { else if (!$pass && $settings['logic'] == 'and') {
// Fail if 'and' and htis rule failed. // Fail if 'and' and this rule failed.
return FALSE; return FALSE;
} }
} }

View File

@ -172,10 +172,12 @@ function ctools_css_cache($css, $filter = TRUE) {
// @todo Is this slow? Does it matter if it is? // @todo Is this slow? Does it matter if it is?
$filename = $path . '/' . md5($css) . '.css'; $filename = $path . '/' . md5($css) . '.css';
// This will do renames if the file already exists, ensuring we don't // Generally md5 is considered unique enough to sign file downloads.
// accidentally overwrite other files who share the same md5. Yes this // So this replaces already existing files based on the assumption that two
// is a very miniscule chance but it's safe. // files with the same hash are identical content wise.
$filename = file_unmanaged_save_data($css, $filename); // If we rename, the cache folder can potentially fill up with thousands of
// files with the same content.
$filename = file_unmanaged_save_data($css, $filename, FILE_EXISTS_REPLACE);
return $filename; return $filename;
} }

View File

@ -51,7 +51,7 @@ function ctools_jump_menu($form, &$form_state, $select, $options = array()) {
'hide' => TRUE, 'hide' => TRUE,
); );
ctools_add_js('jump-menu'); $form['#attached']['js'][] = ctools_attach_js('jump-menu');
if (!empty($options['choose'])) { if (!empty($options['choose'])) {
$select = array('' => $options['choose']) + $select; $select = array('' => $options['choose']) + $select;

View File

@ -271,7 +271,7 @@ class ctools_math_expr {
} elseif (in_array($op, $ops) and !$expecting_op) { } elseif (in_array($op, $ops) and !$expecting_op) {
return $this->trigger("unexpected operator '$op'"); return $this->trigger("unexpected operator '$op'");
} else { // I don't even want to know what you did to get here } else { // I don't even want to know what you did to get here
return $this->trigger("an unexpected error occured"); return $this->trigger("an unexpected error occurred");
} }
if ($index == strlen($expr)) { if ($index == strlen($expr)) {
if (in_array($op, $ops)) { // did we end with an operator? bad. if (in_array($op, $ops)) { // did we end with an operator? bad.

View File

@ -66,6 +66,7 @@ function ctools_modal_add_js() {
drupal_add_library('system', 'jquery.form'); drupal_add_library('system', 'jquery.form');
drupal_add_library('system', 'drupal.progress'); drupal_add_library('system', 'drupal.progress');
drupal_add_library('system', 'drupal.ajax'); drupal_add_library('system', 'drupal.ajax');
drupal_add_library('system', 'ui');
ctools_add_js('modal'); ctools_add_js('modal');
ctools_add_css('modal'); ctools_add_css('modal');

View File

@ -79,7 +79,9 @@ function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version
} }
// Only process if version is between minimum and current, inclusive. // Only process if version is between minimum and current, inclusive.
if (version_compare($version, $minimum_version, '>=') && version_compare($version, $current_version, '<=')) { if (($version == $minimum_version) || ($version == $current_version)
|| (version_compare($version, $minimum_version, '>=')
&& version_compare($version, $current_version, '<='))) {
if (!isset($info['path'])) { if (!isset($info['path'])) {
$info['path'] = drupal_get_path('module', $module); $info['path'] = drupal_get_path('module', $module);
} }
@ -110,7 +112,7 @@ function ctools_plugin_api_info($owner, $api, $minimum_version, $current_version
} }
// Allow other modules to hook in. // Allow other modules to hook in.
drupal_alter($hook, $cache[$owner][$api]); drupal_alter($hook, $cache[$owner][$api], $owner, $api);
} }
return $cache[$owner][$api]; return $cache[$owner][$api];
@ -213,7 +215,7 @@ function ctools_plugin_api_get_hook($owner, $api) {
*/ */
function ctools_get_plugins($module, $type, $id = NULL) { function ctools_get_plugins($module, $type, $id = NULL) {
// Store local caches of plugins and plugin info so we don't have to do full // Store local caches of plugins and plugin info so we don't have to do full
// lookups everytime. // lookups every time.
static $drupal_static_fast; static $drupal_static_fast;
if (!isset($drupal_static_fast)) { if (!isset($drupal_static_fast)) {
$drupal_static_fast['plugins'] = &drupal_static('ctools_plugins', array()); $drupal_static_fast['plugins'] = &drupal_static('ctools_plugins', array());

View File

@ -500,7 +500,7 @@ class ctools_stylizer_image_processor {
$palette[$luminosity_input]['green'] = $green; $palette[$luminosity_input]['green'] = $green;
$palette[$luminosity_input]['blue'] = $blue; $palette[$luminosity_input]['blue'] = $blue;
// Now we complete the palette, first we'll do it tothe black, and then to // Now we complete the palette, first we'll do it to the black, and then to
// the white. // the white.
// From input to black // From input to black

View File

@ -25,7 +25,8 @@ function _ctools_uuid_generate_com() {
* Generates an universally unique identifier using the PECL extension. * Generates an universally unique identifier using the PECL extension.
*/ */
function _ctools_uuid_generate_pecl() { function _ctools_uuid_generate_pecl() {
return uuid_create(UUID_TYPE_DEFAULT); $uuid_type = UUID_TYPE_DEFAULT;
return uuid_create($uuid_type);
} }
/** /**

View File

@ -48,7 +48,8 @@
modalOptions: { modalOptions: {
opacity: .55, opacity: .55,
background: '#fff' background: '#fff'
} },
modalClass: 'default'
}; };
var settings = {}; var settings = {};
@ -97,8 +98,8 @@
resize(); resize();
$('span.modal-title', Drupal.CTools.Modal.modal).html(Drupal.CTools.Modal.currentSettings.loadingText); $('span.modal-title', Drupal.CTools.Modal.modal).html(Drupal.CTools.Modal.currentSettings.loadingText);
Drupal.CTools.Modal.modalContent(Drupal.CTools.Modal.modal, settings.modalOptions, settings.animation, settings.animationSpeed); Drupal.CTools.Modal.modalContent(Drupal.CTools.Modal.modal, settings.modalOptions, settings.animation, settings.animationSpeed, settings.modalClass);
$('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme)); $('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme)).addClass('ctools-modal-loading');
// Position autocomplete results based on the scroll position of the modal. // Position autocomplete results based on the scroll position of the modal.
$('#modalContent .modal-content').delegate('input.form-autocomplete', 'keyup', function() { $('#modalContent .modal-content').delegate('input.form-autocomplete', 'keyup', function() {
@ -299,6 +300,17 @@
// Attach behaviors within a modal dialog. // Attach behaviors within a modal dialog.
var settings = response.settings || ajax.settings || Drupal.settings; var settings = response.settings || ajax.settings || Drupal.settings;
Drupal.attachBehaviors('#modalContent', settings); Drupal.attachBehaviors('#modalContent', settings);
if ($('#modal-content').hasClass('ctools-modal-loading')) {
$('#modal-content').removeClass('ctools-modal-loading');
}
else {
// If the modal was already shown, and we are simply replacing its
// content, then focus on the first focusable element in the modal.
// (When first showing the modal, focus will be placed on the close
// button by the show() function called above.)
$('#modal-content :focusable:first').focus();
}
} }
/** /**
@ -349,8 +361,9 @@
* @param css obj of css attributes * @param css obj of css attributes
* @param animation (fadeIn, slideDown, show) * @param animation (fadeIn, slideDown, show)
* @param speed (valid animation speeds slow, medium, fast or # in ms) * @param speed (valid animation speeds slow, medium, fast or # in ms)
* @param modalClass class added to div#modalContent
*/ */
Drupal.CTools.Modal.modalContent = function(content, css, animation, speed) { Drupal.CTools.Modal.modalContent = function(content, css, animation, speed, modalClass) {
// If our animation isn't set, make it just show/pop // If our animation isn't set, make it just show/pop
if (!animation) { if (!animation) {
animation = 'show'; animation = 'show';
@ -402,9 +415,56 @@
if( docHeight < winHeight ) docHeight = winHeight; if( docHeight < winHeight ) docHeight = winHeight;
// Create our divs // Create our divs
$('body').append('<div id="modalBackdrop" style="z-index: 1000; display: none;"></div><div id="modalContent" style="z-index: 1001; position: absolute;">' + $(content).html() + '</div>'); $('body').append('<div id="modalBackdrop" class="backdrop-' + modalClass + '" style="z-index: 1000; display: none;"></div><div id="modalContent" class="modal-' + modalClass + '" style="z-index: 1001; position: absolute;">' + $(content).html() + '</div>');
// Keyboard and focus event handler ensures focus stays on modal elements only // Get a list of the tabbable elements in the modal content.
var getTabbableElements = function () {
var tabbableElements = $('#modalContent :tabbable'),
radioButtons = tabbableElements.filter('input[type="radio"]');
// The list of tabbable elements from jQuery is *almost* right. The
// exception is with groups of radio buttons. The list from jQuery will
// include all radio buttons, when in fact, only the selected radio button
// is tabbable, and if no radio buttons in a group are selected, then only
// the first is tabbable.
if (radioButtons.length > 0) {
// First, build up an index of which groups have an item selected or not.
var anySelected = {};
radioButtons.each(function () {
var name = this.name;
if (typeof anySelected[name] === 'undefined') {
anySelected[name] = radioButtons.filter('input[name="' + name + '"]:checked').length !== 0;
}
});
// Next filter out the radio buttons that aren't really tabbable.
var found = {};
tabbableElements = tabbableElements.filter(function () {
var keep = true;
if (this.type == 'radio') {
if (anySelected[this.name]) {
// Only keep the selected one.
keep = this.checked;
}
else {
// Only keep the first one.
if (found[this.name]) {
keep = false;
}
found[this.name] = true;
}
}
return keep;
});
}
return tabbableElements.get();
};
// Keyboard and focus event handler ensures only modal elements gain focus.
modalEventHandler = function( event ) { modalEventHandler = function( event ) {
target = null; target = null;
if ( event ) { //Mozilla if ( event ) { //Mozilla
@ -428,7 +488,7 @@
return true; return true;
} }
else { else {
$('#modalContent').focus(); getTabbableElements()[0].focus();
} }
event.preventDefault(); event.preventDefault();
@ -436,6 +496,59 @@
$('body').bind( 'focus', modalEventHandler ); $('body').bind( 'focus', modalEventHandler );
$('body').bind( 'keypress', modalEventHandler ); $('body').bind( 'keypress', modalEventHandler );
// Keypress handler Ensures you can only TAB to elements within the modal.
// Based on the psuedo-code from WAI-ARIA 1.0 Authoring Practices section
// 3.3.1 "Trapping Focus".
modalTabTrapHandler = function (evt) {
// We only care about the TAB key.
if (evt.which != 9) {
return true;
}
var tabbableElements = getTabbableElements(),
firstTabbableElement = tabbableElements[0],
lastTabbableElement = tabbableElements[tabbableElements.length - 1],
singleTabbableElement = firstTabbableElement == lastTabbableElement,
node = evt.target;
// If this is the first element and the user wants to go backwards, then
// jump to the last element.
if (node == firstTabbableElement && evt.shiftKey) {
if (!singleTabbableElement) {
lastTabbableElement.focus();
}
return false;
}
// If this is the last element and the user wants to go forwards, then
// jump to the first element.
else if (node == lastTabbableElement && !evt.shiftKey) {
if (!singleTabbableElement) {
firstTabbableElement.focus();
}
return false;
}
// If this element isn't in the dialog at all, then jump to the first
// or last element to get the user into the game.
else if ($.inArray(node, tabbableElements) == -1) {
// Make sure the node isn't in another modal (ie. WYSIWYG modal).
var parents = $(node).parents().get();
for (var i = 0; i < parents.length; ++i) {
var position = $(parents[i]).css('position');
if (position == 'absolute' || position == 'fixed') {
return true;
}
}
if (evt.shiftKey) {
lastTabbableElement.focus();
}
else {
firstTabbableElement.focus();
}
}
};
$('body').bind('keydown', modalTabTrapHandler);
// Create our content div, get the dimensions, and hide it // Create our content div, get the dimensions, and hide it
var modalContent = $('#modalContent').css('top','-1000px'); var modalContent = $('#modalContent').css('top','-1000px');
var mdcTop = wt + ( winHeight / 2 ) - ( modalContent.outerHeight() / 2); var mdcTop = wt + ( winHeight / 2 ) - ( modalContent.outerHeight() / 2);
@ -457,12 +570,19 @@
$(document).bind('keydown', modalEventEscapeCloseHandler); $(document).bind('keydown', modalEventEscapeCloseHandler);
// Per WAI-ARIA 1.0 Authoring Practices, initial focus should be on the
// close button, but we should save the original focus to restore it after
// the dialog is closed.
var oldFocus = document.activeElement;
$('.close').focus();
// Close the open modal content and backdrop // Close the open modal content and backdrop
function close() { function close() {
// Unbind the events // Unbind the events
$(window).unbind('resize', modalContentResize); $(window).unbind('resize', modalContentResize);
$('body').unbind( 'focus', modalEventHandler); $('body').unbind( 'focus', modalEventHandler);
$('body').unbind( 'keypress', modalEventHandler ); $('body').unbind( 'keypress', modalEventHandler );
$('body').unbind( 'keydown', modalTabTrapHandler );
$('.close').unbind('click', modalContentClose); $('.close').unbind('click', modalContentClose);
$('body').unbind('keypress', modalEventEscapeCloseHandler); $('body').unbind('keypress', modalEventEscapeCloseHandler);
$(document).trigger('CToolsDetachBehaviors', $('#modalContent')); $(document).trigger('CToolsDetachBehaviors', $('#modalContent'));
@ -478,12 +598,19 @@
// Remove the content // Remove the content
$('#modalContent').remove(); $('#modalContent').remove();
$('#modalBackdrop').remove(); $('#modalBackdrop').remove();
// Restore focus to where it was before opening the dialog
$(oldFocus).focus();
}; };
// Move and resize the modalBackdrop and modalContent on resize of the window // Move and resize the modalBackdrop and modalContent on window resize.
modalContentResize = function(){ modalContentResize = function(){
// position code lifted from http://www.quirksmode.org/viewport/compatibility.html // Reset the backdrop height/width to get accurate document size.
$('#modalBackdrop').css('height', '').css('width', '');
// Position code lifted from:
// http://www.quirksmode.org/viewport/compatibility.html
if (self.pageYOffset) { // all except Explorer if (self.pageYOffset) { // all except Explorer
var wt = self.pageYOffset; var wt = self.pageYOffset;
} else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
@ -509,8 +636,6 @@
modalContent.css('top', mdcTop + 'px').css('left', mdcLeft + 'px').show(); modalContent.css('top', mdcTop + 'px').css('left', mdcLeft + 'px').show();
}; };
$(window).bind('resize', modalContentResize); $(window).bind('resize', modalContentResize);
$('#modalContent').focus();
}; };
/** /**
@ -533,7 +658,9 @@
$(window).unbind('resize', modalContentResize); $(window).unbind('resize', modalContentResize);
$('body').unbind('focus', modalEventHandler); $('body').unbind('focus', modalEventHandler);
$('body').unbind('keypress', modalEventHandler); $('body').unbind('keypress', modalEventHandler);
$('body').unbind( 'keydown', modalTabTrapHandler );
$('.close').unbind('click', modalContentClose); $('.close').unbind('click', modalContentClose);
$('body').unbind('keypress', modalEventEscapeCloseHandler);
$(document).trigger('CToolsDetachBehaviors', $('#modalContent')); $(document).trigger('CToolsDetachBehaviors', $('#modalContent'));
// jQuery magic loop through the instances and run the animations or removal. // jQuery magic loop through the instances and run the animations or removal.

View File

@ -0,0 +1,43 @@
/**
* @file
* Custom state for handling visibility
*/
/**
* Add a new state to Drupal #states. We use this to toggle element-invisible
* to show/hidden #states elements. This allows elements to be visible to
* screen readers.
*
* To use:
* $form['my_form_field'] = array(
* ..
* // Only show this field if 'some_other_field' is checked.
* '#states => array(
* 'show' => array(
* 'some-other-field' => array('checked' => TRUE),
* ),
* ),
* ..
* // Required to load the 'show' state handler.
* '#attached' => array(
* 'js' => array(ctools_attach_js('states-show')),
* ),
* );
*/
(function ($) {
'use strict';
Drupal.states.State.aliases.hidden = '!show';
// Show/hide form items by toggling the 'element-invisible' class. This is a
// more accessible option than the core 'visible' state.
$(document).bind('state:show', function(e) {
if (e.trigger) {
var element = $(e.target).closest('.form-item, .form-submit, .form-wrapper');
element.toggle(e.value);
e.value === true ? element.removeClass('element-invisible') : element.addClass('element-invisible');
}
});
})(jQuery);

View File

@ -5,9 +5,9 @@ dependencies[] = ctools
package = Chaos tool suite package = Chaos tool suite
version = CTOOLS_MODULE_VERSION version = CTOOLS_MODULE_VERSION
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -440,13 +440,18 @@ function page_manager_cache_load($task_name) {
*/ */
function page_manager_handler_get_name($task_name, $handlers, $handler) { function page_manager_handler_get_name($task_name, $handlers, $handler) {
$base = str_replace('-', '_', $task_name); $base = str_replace('-', '_', $task_name);
$name = '';
// Optional machine name. // Optional machine name.
if (!empty($handler->conf['name'])) { if (!empty($handler->conf['name'])) {
$name = $base . '__' . $handler->conf['name']; $name = $base . '__' . $handler->conf['name'];
if (count(ctools_export_load_object('page_manager_handlers', 'names', array($name)))) {
$name = '';
}
} }
// If no machine name was provided, generate a unique name. // If no machine name was provided or the name is in use, generate a unique name.
else { if (empty($name)) {
$base .= '__' . $handler->handler; $base .= '__' . $handler->handler;
// Use the ctools uuid generator to generate a unique id. // Use the ctools uuid generator to generate a unique id.
@ -472,6 +477,10 @@ function page_manager_handler_add_to_page(&$page, &$handler, $title = NULL) {
if ($title) { if ($title) {
$handler->conf['title'] = $title; $handler->conf['title'] = $title;
$handler->conf['name'] = trim(preg_replace('/[^a-z0-9_]+/', '-', strtolower($title)), '-');
}
else {
$handler->conf['name'] = '';
} }
$name = page_manager_handler_get_name($page->task_name, $page->handlers, $handler); $name = page_manager_handler_get_name($page->task_name, $page->handlers, $handler);

View File

@ -108,7 +108,7 @@ function page_manager_node_edit($node) {
* Callback to handle the process of adding a node. * Callback to handle the process of adding a node.
* *
* This creates a basic $node and passes that off to page_manager_node_edit(). * This creates a basic $node and passes that off to page_manager_node_edit().
* It is modeled after Drupal's node_add() function. * It is modelled after Drupal's node_add() function.
* *
* Unlike node_add() we do not need to check node_access because that was * Unlike node_add() we do not need to check node_access because that was
* already checked by the menu system. * already checked by the menu system.

View File

@ -78,10 +78,6 @@ function page_manager_node_view_menu_alter(&$items, $task) {
* node view, which is node_page_view(). * node view, which is node_page_view().
*/ */
function page_manager_node_view_page($node) { function page_manager_node_view_page($node) {
// Prep the node to be displayed so all of the regular hooks are triggered.
// Also save the output for later, in case it is needed.
$default_output = node_page_view($node);
// Load my task plugin // Load my task plugin
$task = page_manager_get_task('node_view'); $task = page_manager_get_task('node_view');
@ -107,6 +103,9 @@ function page_manager_node_view_page($node) {
} }
} }
// Prepare the node to be displayed so all of the regular hooks are triggered.
$default_output = node_page_view($node);
// Otherwise, fall back to the default output generated by node_page_view(). // Otherwise, fall back to the default output generated by node_page_view().
return $default_output; return $default_output;
} }

View File

@ -61,7 +61,7 @@ function page_manager_page_menu(&$items, $task) {
} }
$path = array(); $path = array();
$page_arguments = array($subtask_id); $page_arguments = array((string) $subtask_id);
$access_arguments = array($subtask->access); $access_arguments = array($subtask->access);
$load_arguments = array($subtask_id, '%index', '%map'); $load_arguments = array($subtask_id, '%index', '%map');
@ -566,7 +566,7 @@ function page_manager_page_form_basic_validate(&$form, &$form_state) {
if (strpos($path, '%') === FALSE) { if (strpos($path, '%') === FALSE) {
$alias = db_query('SELECT alias, source FROM {url_alias} WHERE alias = :path', array(':path' => $path))->fetchObject(); $alias = db_query('SELECT alias, source FROM {url_alias} WHERE alias = :path', array(':path' => $path))->fetchObject();
if ($alias) { if ($alias) {
form_error($form['path'], t('That path is currently assigned to be an alias for @alias. This system cannot override existing aliases.', array('@alias' => $alias->src))); form_error($form['path'], t('That path is currently assigned to be an alias for @alias. This system cannot override existing aliases.', array('@alias' => $alias->source)));
} }
} }
else { else {

View File

@ -0,0 +1,74 @@
<?php
/**
* Plugins are described by creating a $plugin array which will be used
* by the system that includes this file.
*/
$plugin = array(
'single' => TRUE,
'title' => t('Comment created date'),
'icon' => 'icon_comment.png',
'description' => t('The date the referenced comment was created.'),
'required context' => new ctools_context_required(t('Comment'), 'entity:comment'),
'category' => t('Comment'),
'defaults' => array(
'format' => 'small',
),
);
/**
* Render the custom content type.
*/
function ctools_comment_created_content_type_render($subtype, $conf, $panel_args, $context) {
if (empty($context) || empty($context->data)) {
return;
}
// Get a shortcut to the comment.
$comment = $context->data;
// Build the content type block.
$block = new stdClass();
$block->module = 'comment_created';
$block->title = t('Created date');
$block->content = format_date($comment->created, $conf['format']);
$block->delta = $comment->cid;
return $block;
}
/**
* Returns an edit form for custom type settings.
*/
function ctools_comment_created_content_type_edit_form($form, &$form_state) {
$conf = $form_state['conf'];
$date_types = array();
foreach (system_get_date_types() as $date_type => $definition) {
$date_types[$date_type] = format_date(REQUEST_TIME, $date_type);
}
$form['format'] = array(
'#title' => t('Date format'),
'#type' => 'select',
'#options' => $date_types,
'#default_value' => $conf['format'],
);
return $form;
}
/**
* Submit handler for the custom type settings form.
*/
function ctools_comment_created_content_type_edit_form_submit($form, &$form_state) {
// Copy everything from our defaults.
foreach (array_keys($form_state['plugin']['defaults']) as $key) {
$form_state['conf'][$key] = $form_state['values'][$key];
}
}
/**
* Returns the administrative title for a type.
*/
function ctools_comment_created_content_type_admin_title($subtype, $conf, $context) {
return t('"@s" created date', array('@s' => $context->identifier));
}

View File

@ -34,6 +34,14 @@ function ctools_entity_field_content_type_content_types() {
return $types; return $types;
} }
$cache_key = 'ctools_entity_field_content_type_content_types';
if ($cache = cache_get($cache_key)) {
$types = $cache->data;
if (!empty($types)) {
return $types;
}
}
// This will hold all the individual field content types. // This will hold all the individual field content types.
$context_types = array(); $context_types = array();
$entities = entity_get_info(); $entities = entity_get_info();
@ -82,6 +90,8 @@ function ctools_entity_field_content_type_content_types() {
unset($context_types[$key]['types']); unset($context_types[$key]['types']);
} }
cache_set($cache_key, $types);
return $types; return $types;
} }

View File

@ -57,6 +57,26 @@ function ctools_entity_form_field_content_type_content_types() {
} }
} }
if (module_exists('field_group')) {
foreach ($entities as $entity_type => $entity) {
foreach ($entity['bundles'] as $type => $bundle) {
if ($group_info = field_group_info_groups($entity_type, $type, "form")) {
foreach ($group_info as $group_name => $group) {
if (!isset($types[$entity_type . ':' . $group_name])) {
$types[$entity_type . ':' . $group_name] = array(
'category' => t('Form'),
'icon' => 'icon_field.png',
'title' => t('Group form: @widget_label', array('@widget_label' => $group->label)),
'description' => t('Field group on the referenced entity.'),
);
}
$content_types[$entity_type . ':' . $group_name]['types'][$type] = $bundle['label'];
}
}
}
}
}
// Create the required context for each field related to the bundle types. // Create the required context for each field related to the bundle types.
foreach ($types as $key => $field_content_type) { foreach ($types as $key => $field_content_type) {
list($entity_type, $field_name) = explode(':', $key, 2); list($entity_type, $field_name) = explode(':', $key, 2);
@ -85,16 +105,38 @@ function ctools_entity_form_field_content_type_render($subtype, $conf, $panel_ar
$ids = entity_extract_ids($entity_type, $entity); $ids = entity_extract_ids($entity_type, $entity);
$field = field_info_instance($entity_type, $field_name, $ids[2]); $field = field_info_instance($entity_type, $field_name, $ids[2]);
// Do not render if the entity type does not have this field. // Check for field groups.
if (empty($field)) { if (empty($field) && module_exists('field_group')) {
$groups = field_group_info_groups($entity_type, $entity->type, "form");
$group = !empty($groups[$field_name]) ? $groups[$field_name] : NULL;
}
// Do not render if the entity type does not have this field or group.
if (empty($field) && empty($group)) {
return; return;
} }
$block = new stdClass();
$block = new stdClass();
if (isset($context->form)) { if (isset($context->form)) {
$block->content = array(); $block->content = array();
$block->content[$field_name] = $context->form[$field_name]; if (!empty($field)) {
unset($context->form[$field_name]); $block->content[$field_name] = $context->form[$field_name];
unset($context->form[$field_name]);
}
else {
// Pre-render the form to populate field groups.
if (isset($context->form['#pre_render'])) {
foreach ($context->form['#pre_render'] as $function) {
if (function_exists($function)) {
$context->form = $function($context->form);
}
}
unset($context->form['#pre_render']);
}
$block->content[$field_name] = $context->form[$field_name];
unset($context->form[$field_name]);
}
} }
else { else {
$block->content = t('Entity info.'); $block->content = t('Entity info.');

View File

@ -77,20 +77,3 @@ function ctools_node_comment_form_content_type_edit_form_submit($form, &$form_st
} }
} }
/**
* Alter the comment form to get a little more control over it.
*/
function ctools_form_comment_form_alter(&$form, &$form_state) {
if (!empty($form_state['ctools comment alter'])) {
// Force the form to post back to wherever we are.
$form['#action'] = url($_GET['q'], array('fragment' => 'comment-form'));
if (empty($form['#submit'])) {
$form['#submit'] = array('comment_form_submit');
}
$form['#submit'][] = 'ctools_node_comment_form_submit';
}
}
function ctools_node_comment_form_submit(&$form, &$form_state) {
$form_state['redirect'][0] = $_GET['q'];
}

View File

@ -29,6 +29,9 @@ $plugin = array(
* Outputs the page title of the current page. * Outputs the page title of the current page.
*/ */
function ctools_page_title_content_type_render($subtype, $conf, $panel_args) { function ctools_page_title_content_type_render($subtype, $conf, $panel_args) {
if (!drupal_get_title()) {
return;
}
// TODO: This should have a setting or something for the markup. // TODO: This should have a setting or something for the markup.
if (empty($conf['markup'])) { if (empty($conf['markup'])) {
$conf['markup'] = 'h1'; $conf['markup'] = 'h1';

View File

@ -18,8 +18,8 @@ function ctools_term_description_content_type_render($subtype, $conf, $panel_arg
$block = new stdClass(); $block = new stdClass();
$block->module = 'node_type'; $block->module = 'node_type';
$block->title = $term->name; if (!empty($term)) {
if ($term) { $block->title = $term->name;
$block->content = check_markup($term->description, $term->format, '', TRUE); $block->content = check_markup($term->description, $term->format, '', TRUE);
$block->delta = $term->tid; $block->delta = $term->tid;
@ -33,6 +33,7 @@ function ctools_term_description_content_type_render($subtype, $conf, $panel_arg
} }
} }
else { else {
$block->title = '';
$block->content = t('Term description goes here.'); $block->content = t('Term description goes here.');
$block->delta = 'unknown'; $block->delta = 'unknown';
} }

View File

@ -43,7 +43,9 @@ function ctools_context_create_user($empty, $data = NULL, $conf = FALSE) {
if ($data['type'] == 'current') { if ($data['type'] == 'current') {
global $user; global $user;
$data = user_load($user->uid); $data = user_load($user->uid);
$data->logged_in_user = TRUE; if (user_is_logged_in()) {
$data->logged_in_user = TRUE;
}
} }
else { else {
$data = user_load($data['uid']); $data = user_load($data['uid']);

View File

@ -34,15 +34,15 @@ function ctools_context_create_user_edit_form($empty, $user = NULL, $conf = FALS
$category = !empty($conf['category']) ? $conf['category'] : FALSE; $category = !empty($conf['category']) ? $conf['category'] : FALSE;
unset($conf['category']); unset($conf['category']);
// If no category was specified, use the default 'account'.
if (!$category) {
$category = 'account';
}
// Return previously created contexts, per category. // Return previously created contexts, per category.
static $created = array(); static $created = array();
if (!empty($created[$category])) { if (!empty($created[$category])) {
return $created[$category]; return $created[$category];
} }
// If no category was specified, use the default 'account'.
if (!$category) {
$category = 'account';
}
$context = new ctools_context(array('form', 'user_edit', 'user_form', 'user_edit_form', 'user', 'entity:user')); $context = new ctools_context(array('form', 'user_edit', 'user_form', 'user_edit_form', 'user', 'entity:user'));
// Store this context for later. // Store this context for later.

View File

@ -724,7 +724,13 @@ class ctools_export_ui {
// Export the handler, which is a fantastic way to clean database IDs out of it. // Export the handler, which is a fantastic way to clean database IDs out of it.
$export = ctools_export_crud_export($this->plugin['schema'], $original); $export = ctools_export_crud_export($this->plugin['schema'], $original);
$item = ctools_export_crud_import($this->plugin['schema'], $export); $item = ctools_export_crud_import($this->plugin['schema'], $export);
$item->{$this->plugin['export']['key']} = 'clone_of_' . $item->{$this->plugin['export']['key']};
if (!empty($input[$this->plugin['export']['key']])) {
$item->{$this->plugin['export']['key']} = $input[$this->plugin['export']['key']];
}
else {
$item->{$this->plugin['export']['key']} = 'clone_of_' . $item->{$this->plugin['export']['key']};
}
} }
// Tabs and breadcrumb disappearing, this helps alleviate through cheating. // Tabs and breadcrumb disappearing, this helps alleviate through cheating.

View File

@ -185,7 +185,7 @@ function ctools_entity_from_field_context($context, $conf) {
$loaded_to_entity = array_shift($loaded_to_entity); $loaded_to_entity = array_shift($loaded_to_entity);
// Pass current user account and entity type to access callback. // Pass current user account and entity type to access callback.
if (function_exists($to_entity_info['access callback']) && !call_user_func($to_entity_info['access callback'], 'view', $loaded_to_entity, $account, $to_entity)) { if (isset($to_entity_info['access callback']) && function_exists($to_entity_info['access callback']) && !call_user_func($to_entity_info['access callback'], 'view', $loaded_to_entity)) {
return ctools_context_create_empty('entity:' . $to_entity, NULL); return ctools_context_create_empty('entity:' . $to_entity, NULL);
} }
else { else {

View File

@ -6,9 +6,9 @@ version = CTOOLS_MODULE_VERSION
dependencies[] = ctools dependencies[] = ctools
dependencies[] = color dependencies[] = color
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -5,9 +5,9 @@ dependencies[] = ctools
package = Chaos tool suite package = Chaos tool suite
version = CTOOLS_MODULE_VERSION version = CTOOLS_MODULE_VERSION
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -8,9 +8,9 @@ hidden = TRUE
files[] = ctools_export.test files[] = ctools_export.test
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -12,9 +12,9 @@ files[] = math_expression.test
files[] = math_expression_stack.test files[] = math_expression_stack.test
hidden = TRUE hidden = TRUE
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* @file * @file
* A cached plugin object that tests inheritence including. * A cached plugin object that tests inheritance including.
*/ */
class ctoolsCachedPluginArray {} class ctoolsCachedPluginArray {}

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* @file * @file
* A cached plugin object that tests inheritence including. * A cached plugin object that tests inheritance including.
*/ */
class ctoolsCachedPluginArray2 extends ctoolsCachedPluginArray {} class ctoolsCachedPluginArray2 extends ctoolsCachedPluginArray {}

View File

@ -1,7 +1,7 @@
<?php <?php
/** /**
* @file * @file
* A cached plugin object that tests inheritence including. * A cached plugin object that tests inheritance including.
*/ */
class ctoolsNotCachedPluginArray extends ctoolsNotCachedPluginArray2 {} class ctoolsNotCachedPluginArray extends ctoolsNotCachedPluginArray2 {}

View File

@ -10,9 +10,9 @@ files[] = plugins/views/views_content_plugin_display_ctools_context.inc
files[] = plugins/views/views_content_plugin_display_panel_pane.inc files[] = plugins/views/views_content_plugin_display_panel_pane.inc
files[] = plugins/views/views_content_plugin_style_ctools_context.inc files[] = plugins/views/views_content_plugin_style_ctools_context.inc
; Information added by Drupal.org packaging script on 2015-03-18 ; Information added by Drupal.org packaging script on 2015-08-19
version = "7.x-1.7" version = "7.x-1.9"
core = "7.x" core = "7.x"
project = "ctools" project = "ctools"
datestamp = "1426696183" datestamp = "1440020680"

View File

@ -13,7 +13,7 @@ $plugin = array(
'access' => 'administer video styles', 'access' => 'administer video styles',
// Define the menu item. // Define the menu item.
'menu' => array( 'menu' => array(
'menu prefix' => 'admin/config/media', 'menu prefix' => 'admin/config/media/vef',
'menu item' => 'vef_video_styles', 'menu item' => 'vef_video_styles',
'menu title' => 'Video Embed Styles', 'menu title' => 'Video Embed Styles',
'menu description' => 'Administer Video Embed Field\'s video styles.', 'menu description' => 'Administer Video Embed Field\'s video styles.',

View File

@ -6,9 +6,9 @@ configure = admin/config/media/vef_video_styles
dependencies[] = "video_embed_field" dependencies[] = "video_embed_field"
; Information added by Drupal.org packaging script on 2015-04-17 ; Information added by Drupal.org packaging script on 2015-09-07
version = "7.x-2.0-beta8+7-dev" version = "7.x-2.0-beta11"
core = "7.x" core = "7.x"
project = "video_embed_field" project = "video_embed_field"
datestamp = "1429278491" datestamp = "1441639440"

View File

@ -24,6 +24,7 @@ function video_embed_brightcove_video_embed_handler_info() {
'defaults' => array( 'defaults' => array(
'width' => 640, 'width' => 640,
'height' => 360, 'height' => 360,
'class' => '',
), ),
); );
@ -57,6 +58,13 @@ function video_embed_brightcove_form($defaults) {
'#default_value' => $defaults['height'], '#default_value' => $defaults['height'],
); );
$form['class'] = array(
'#type' => 'textfield',
'#title' => t('Player CSS class'),
'#description' => t('CSS class to add to the player'),
'#default_value' => $defaults['class'],
);
return $form; return $form;
} }
@ -83,7 +91,7 @@ function video_embed_brightcove_handle_video($url, $settings) {
if (isset($parameters['id']) && isset($parameters['key'])) { if (isset($parameters['id']) && isset($parameters['key'])) {
// Embed code. // Embed code.
$embed = '<object id="myExperience" class="BrightcoveExperience"> $embed = '<object class="@class" id="myExperience" class="BrightcoveExperience">
<param name="bgcolor" value="#FFFFFF" /> <param name="bgcolor" value="#FFFFFF" />
<param name="width" value="@width" /> <param name="width" value="@width" />
<param name="height" value="@height" /> <param name="height" value="@height" />
@ -100,6 +108,7 @@ function video_embed_brightcove_handle_video($url, $settings) {
'!key' => $parameters['key'], '!key' => $parameters['key'],
'@width' => $settings['width'], '@width' => $settings['width'],
'@height' => $settings['height'], '@height' => $settings['height'],
'@class' => $settings['class'],
'!videoplayer' => $parameters['player'], '!videoplayer' => $parameters['player'],
)); ));
@ -152,7 +161,7 @@ function _video_embed_brightcove_get_video_properties($url) {
$string = "/(.*){$component['start']}(.*){$component['finish']}/"; $string = "/(.*){$component['start']}(.*){$component['finish']}/";
preg_match($string, $url, $matches); preg_match($string, $url, $matches);
if ($matches && !empty($matches[2])) { if ($matches && !empty($matches[2])) {
$return[$key] = $matches[2]; $return[$key] = check_plain($matches[2]);
} }
} }
return $return; return $return;

View File

@ -5,9 +5,9 @@ package = Media
configure = admin/config/media/vef_video_styles configure = admin/config/media/vef_video_styles
dependencies[] = "video_embed_field" dependencies[] = "video_embed_field"
; Information added by Drupal.org packaging script on 2015-04-17 ; Information added by Drupal.org packaging script on 2015-09-07
version = "7.x-2.0-beta8+7-dev" version = "7.x-2.0-beta11"
core = "7.x" core = "7.x"
project = "video_embed_field" project = "video_embed_field"
datestamp = "1429278491" datestamp = "1441639440"

View File

@ -25,6 +25,7 @@ function video_embed_facebook_video_embed_handler_info() {
'defaults' => array( 'defaults' => array(
'width' => 640, 'width' => 640,
'height' => 360, 'height' => 360,
'class' => '',
), ),
); );
@ -57,6 +58,13 @@ function video_embed_facebook_form($defaults) {
'#default_value' => $defaults['height'], '#default_value' => $defaults['height'],
); );
$form['class'] = array(
'#type' => 'textfield',
'#title' => t('Player CSS class'),
'#description' => t('CSS class to add to the player'),
'#default_value' => $defaults['class'],
);
return $form; return $form;
} }
@ -90,12 +98,13 @@ function video_embed_facebook_handle_video($url, $settings) {
if ($id) { if ($id) {
// Our embed code. // Our embed code.
$embed='<iframe src="//www.facebook.com/video/embed?video_id=!id" width="!width" height="!height"></iframe> '; $embed='<iframe class="@class" src="//www.facebook.com/video/embed?video_id=!id" width="@width" height="@height"></iframe> ';
// Use format_string to replace our placeholders with the settings values. // Use format_string to replace our placeholders with the settings values.
$embed = format_string($embed, array( $embed = format_string($embed, array(
'!id' => $id, '!id' => $id,
'!width' => $settings['width'], '@width' => $settings['width'],
'!height' => $settings['height'], '@height' => $settings['height'],
'@class' => $settings['class'],
)); ));
$video = array( $video = array(
@ -137,10 +146,10 @@ function video_embed_facebook_handle_thumbnail($url) {
function _video_embed_facebook_get_video_id($url) { function _video_embed_facebook_get_video_id($url) {
// Parse_url is an easy way to break a url into its components. // Parse_url is an easy way to break a url into its components.
$matches = array(); $matches = array();
preg_match('/(.*)?[v|video_id]=([^&#]*)/', $url, $matches); preg_match('/(?:.*)(?:v=|video_id=|videos\/|videos\/v.\.\d+\/)(\d+).*/', $url, $matches);
// If the v or video_id get parameters are set, return it. // If the v or video_id get parameters are set, return it.
if ($matches && !empty($matches[2])) { if ($matches && !empty($matches[1])) {
return $matches[2]; return check_plain($matches[1]);
} }
// Otherwise return false. // Otherwise return false.
return FALSE; return FALSE;

View File

@ -32,3 +32,15 @@ function video_embed_field_video_style_form(&$form, &$form_state) {
return $form; return $form;
} }
/**
* VEF settings page form callback.
*/
function video_embed_field_settings_form($form, &$form_state) {
$form['video_embed_field_youtube_v3_api_key'] = array(
'#type' => 'textfield',
'#title' => t('Youtube v3 API key'),
'#default_value' => variable_get('video_embed_field_youtube_v3_api_key', ''),
);
return system_settings_form($form);
}

View File

@ -454,7 +454,7 @@ function video_embed_field_field_formatter_view($entity_type, $entity, $field, $
if (isset($item['description']) && $item['description'] && $settings['description'] && $instance['settings']['description_field']) { if (isset($item['description']) && $item['description'] && $settings['description'] && $instance['settings']['description_field']) {
$description = array( $description = array(
'#prefix' => '<div class="video-embed-description">', '#prefix' => '<div class="video-embed-description">',
'#markup' => $item['description'], '#markup' => check_plain($item['description']),
'#suffix' => '</div>', '#suffix' => '</div>',
); );
$alt = $item['description']; $alt = $item['description'];

View File

@ -38,6 +38,7 @@ function video_embed_field_video_embed_handler_info() {
'modestbranding' => 0, 'modestbranding' => 0,
'theme' => 'dark', 'theme' => 'dark',
'iv_load_policy' => 1, 'iv_load_policy' => 1,
'class' => '',
), ),
); );
@ -46,6 +47,7 @@ function video_embed_field_video_embed_handler_info() {
'function' => 'video_embed_field_handle_vimeo', 'function' => 'video_embed_field_handle_vimeo',
'thumbnail_function' => 'video_embed_field_handle_vimeo_thumbnail', 'thumbnail_function' => 'video_embed_field_handle_vimeo_thumbnail',
'thumbnail_default' => drupal_get_path('module', 'video_embed_field') . '/img/vimeo.jpg', 'thumbnail_default' => drupal_get_path('module', 'video_embed_field') . '/img/vimeo.jpg',
'data_function' => '_video_embed_field_get_vimeo_data',
'form' => 'video_embed_field_handler_vimeo_form', 'form' => 'video_embed_field_handler_vimeo_form',
'form_validate' => 'video_embed_field_handler_vimeo_form_validate', 'form_validate' => 'video_embed_field_handler_vimeo_form_validate',
'domains' => array( 'domains' => array(
@ -61,6 +63,7 @@ function video_embed_field_video_embed_handler_info() {
'autoplay' => 0, 'autoplay' => 0,
'loop' => 0, 'loop' => 0,
'froogaloop' => 0, 'froogaloop' => 0,
'class' => ''
), ),
); );
@ -146,7 +149,7 @@ function _video_embed_field_get_youtube_id($url) {
$id = substr($url, $pos); $id = substr($url, $pos);
} }
} }
return $id; return check_plain($id);
} }
/** /**
@ -163,16 +166,21 @@ function _video_embed_field_get_youtube_id($url) {
function video_embed_field_handle_youtube($url, $settings) { function video_embed_field_handle_youtube($url, $settings) {
$output = array(); $output = array();
// Grab the minutes and seconds, and just convert it down to seconds. if(preg_match('/#t=((?P<min>\d+)m)?((?P<sec>\d+)s)?((?P<tinsec>\d+))?/', $url, $matches)){
preg_match('/#t=((?P<min>\d+)m)?((?P<sec>\d+)s)?/', $url, $matches); if(isset($matches['tinsec'])){
$settings['start'] = $matches['tinsec']; // url already in form #t=125 for 2 minutes and 5 seconds
// Give it some default data in case there is no #t=... } else {
$matches += array( // url in form #t=2m5s or with other useless data, this is why we still keep adding the default data..
"min" => 0, // give it some default data in case there is no #t=...
"sec" => 0, $matches += array(
); "min" => 0,
$time = ($matches["min"] * 60) + $matches["sec"]; "sec" => 0,
$settings['start'] = $time; );
if ($time = ($matches["min"] * 60) + $matches["sec"]) {
$settings['start'] = $time;
}
}
}
$id = _video_embed_field_get_youtube_id($url); $id = _video_embed_field_get_youtube_id($url);
if (!$id) { if (!$id) {
@ -180,11 +188,16 @@ function video_embed_field_handle_youtube($url, $settings) {
$output['#markup'] = l($url, $url); $output['#markup'] = l($url, $url);
return $output; return $output;
} }
// Add class to variable to avoid adding it to URL param string.
$class = $settings['class'];
unset($settings['class']);
// Construct the embed code. // Construct the embed code.
$settings['wmode'] = 'opaque'; $settings['wmode'] = 'opaque';
$settings_str = _video_embed_code_get_settings_str($settings); $settings_str = urlencode(_video_embed_code_get_settings_str($settings));
$output['#markup'] = '<iframe width="' . check_plain($settings['width']) . '" height="' . check_plain($settings['height']) . '" src="//www.youtube.com/embed/' . $id . '?' . $settings_str . '" frameborder="0" allowfullscreen></iframe>'; $output['#markup'] = '<iframe class="' . check_plain($class) . '" width="' . check_plain($settings['width']) . '" height="' . check_plain($settings['height']) . '" src="//www.youtube.com/embed/' . $id . '?' . $settings_str . '" frameborder="0" allowfullscreen></iframe>';
return $output; return $output;
} }
@ -243,11 +256,17 @@ function video_embed_field_handle_youtube_data($url) {
$id = _video_embed_field_get_youtube_id($url); $id = _video_embed_field_get_youtube_id($url);
if ($id) { if ($id) {
$response = drupal_http_request('http://gdata.youtube.com/feeds/api/videos/' . $id . '?v=2&alt=json');
$options['v'] = 3;
$options['key'] = variable_get('video_embed_field_youtube_v3_api_key', '');
$options['part'] = 'snippet';
$options['id'] = $id;
$response = drupal_http_request(url('https://www.googleapis.com/youtube/v3/videos', array('query' => $options)));
if (!isset($response->error)) { if (!isset($response->error)) {
$data = json_decode($response->data); $data = json_decode($response->data);
$data = isset($data->entry) ? (array) $data->entry : (array) $data->feed; return _video_embed_field_clean_up_youtube_data($data->items);
return _video_embed_field_clean_up_youtube_data($data);
} }
} }
@ -401,6 +420,13 @@ function video_embed_field_handler_youtube_form($defaults) {
'#default_value' => $defaults['autohide'], '#default_value' => $defaults['autohide'],
); );
$form['class'] = array(
'#type' => 'textfield',
'#title' => t('Player CSS class'),
'#description' => t('CSS class to add to the player'),
'#default_value' => $defaults['class'],
);
return $form; return $form;
} }
@ -489,10 +515,14 @@ function video_embed_field_handle_vimeo($url, $settings) {
} }
unset($settings['froogaloop']); unset($settings['froogaloop']);
// Add class to variable to avoid adding it to URL param string.
$class = $settings['class'];
unset($settings['class']);
$settings_str = _video_embed_code_get_settings_str($settings); $settings_str = _video_embed_code_get_settings_str($settings);
return array( return array(
'#markup' => '<iframe id="' . $settings['player_id'] . '" width="' . check_plain($settings['width']) . '" height="' . check_plain($settings['height']) . '" src="//player.vimeo.com/video/' . $id . '#markup' => '<iframe class="' . check_plain($class) . '" id="' . $settings['player_id'] . '" width="' . check_plain($settings['width']) . '" height="' . check_plain($settings['height']) . '" src="//player.vimeo.com/video/' . $id .
'?' . $settings_str . '" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowfullscreen></iframe>', '?' . $settings_str . '" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowfullscreen></iframe>',
); );
} }
@ -616,6 +646,13 @@ function video_embed_field_handler_vimeo_form($defaults) {
'#default_value' => $defaults['loop'], '#default_value' => $defaults['loop'],
); );
$form['class'] = array(
'#type' => 'textfield',
'#title' => t('Player CSS class'),
'#description' => t('CSS class to add to the player'),
'#default_value' => $defaults['class'],
);
return $form; return $form;
} }

View File

@ -2,7 +2,7 @@ name = "Video Embed Field"
description = "Expose a field type for embedding videos from youtube or vimeo." description = "Expose a field type for embedding videos from youtube or vimeo."
core = 7.x core = 7.x
package = Media package = Media
configure = admin/config/media/vef_video_styles configure = admin/config/media/vef
files[] = video_embed_field.migrate.inc files[] = video_embed_field.migrate.inc
files[] = views/handlers/views_embed_field_views_handler_field_thumbnail_path.inc files[] = views/handlers/views_embed_field_views_handler_field_thumbnail_path.inc
@ -10,9 +10,9 @@ files[] = views/handlers/views_embed_field_views_handler_field_thumbnail_path.in
dependencies[] = ctools dependencies[] = ctools
dependencies[] = image dependencies[] = image
; Information added by Drupal.org packaging script on 2015-04-17 ; Information added by Drupal.org packaging script on 2015-09-07
version = "7.x-2.0-beta8+7-dev" version = "7.x-2.0-beta11"
core = "7.x" core = "7.x"
project = "video_embed_field" project = "video_embed_field"
datestamp = "1429278491" datestamp = "1441639440"

View File

@ -96,6 +96,13 @@ function video_embed_field_schema() {
return $schema; return $schema;
} }
/**
* Implements hook_uninstall().
*/
function video_embed_field_uninstall() {
variable_del('video_embed_field_youtube_api_key');
}
/** /**
* Adds an optional description form. * Adds an optional description form.
*/ */
@ -363,3 +370,22 @@ function video_embed_field_update_7009() {
return t('Updated default instance settings'); return t('Updated default instance settings');
} }
/**
* Update styles with empty class parameter.
*/
function video_embed_field_update_7010() {
drupal_get_schema('vef_video_styles', TRUE);
ctools_include('export');
$styles = ctools_export_load_object('vef_video_styles');
foreach ($styles as $style) {
foreach ($style->data as &$provider) {
if (!isset($provider['class'])) {
$provider['class'] = '';
}
}
ctools_export_crud_save('vef_video_styles', $style);
}
return 'Parameter class added to existing styles';
}

View File

@ -93,6 +93,26 @@ function video_embed_field_menu() {
'type' => MENU_CALLBACK, 'type' => MENU_CALLBACK,
); );
$items['admin/config/media/vef'] = array(
'title' => 'Video Embed Field',
'description' => 'Video Embed Field configuration',
'page callback' => 'system_admin_menu_block_page',
'access arguments' => array('administer video styles'),
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/config/media/vef/settings'] = array(
'title' => 'Settings',
'description' => 'Video Embed Field module settings',
'page callback' => 'drupal_get_form',
'page arguments' => array('video_embed_field_settings_form'),
'file' => 'video_embed_field.admin.inc',
'access arguments' => array('administer video styles'),
'type' => MENU_NORMAL_ITEM,
);
return $items; return $items;
} }
@ -577,15 +597,7 @@ function _video_embed_field_get_provider_domains() {
* An array containing the allowed video domains. * An array containing the allowed video domains.
*/ */
function _video_embed_field_get_instance_provider_domains($instance) { function _video_embed_field_get_instance_provider_domains($instance) {
$domains = _video_embed_field_get_provider_domains(); return array_intersect(_video_embed_field_get_provider_domains(), $instance['settings']['allowed_providers']);
foreach ($domains as $domain => $provider) {
if (empty($instance['settings']['allowed_providers'][$provider])) {
unset($domains[$domain]);
}
}
return $domains;
} }
/** /**

View File

@ -1,5 +0,0 @@
Conventions to make porting changes between Drupal 6 and Drupal 7 easier:
Try to always use specific DBTNG functions such as db_select() instead of the
more general db_query(), which needs to be renamed to dbtng_query() under
Drupal 6.

View File

@ -1,11 +1,31 @@
Migrate 2.7 Migrate 2.8
=========== ===========
Bug fixes Features and enhancements
- #2415597 - Make batching of SQL sources optional, and force map_joinable FALSE. - #2379289 - Better handle interaction of --update with highwater marks.
- #2403643 - Support an additional level of subfields.
- #2472045 - Add language subfields only if field is translatable.
- #2474809 - Provide better message for bad dependencies.
- #2397791 - Provide detailed field validation errors.
- #2309563 - Add support for running migrations via wildcard name.
- #2095841 - Abstract mail system disablement for more flexibility.
- #2419373 - Provide ability to cache map lookups.
- #2141687 - Provide detailed message on file copy error.
Migrate 2.7 Release Candidate 1 Bug fixes
=============================== Field sanitization added to prevent possibility of XSS - see security advisory
https://security.drupal.org/node/155268.
- #2447115 - Add xpath handling to the field mapping editor.
- #2497015 - Term reference handler would ignore all terms if one was NULL.
- #2488560 - MigrateSourceList/MigrateSourceMultiItems iterators prematurely
return.
- #2446105 - Keep coded DNM source field mappings from overriding UI mappings.
- #2415977 - Use temporary:// instead of /tmp for drush logging.
- #2475473 - Fix handling of --idlist when map not joined.
- #2465387 - Fix handling of --stop option on migrate-import.
Migrate 2.7
===========
Features and enhancements Features and enhancements
- #2296911 - Add a source handler for IBM DB2. - #2296911 - Add a source handler for IBM DB2.
@ -14,6 +34,7 @@ Features and enhancements
- #1751438 - Add spreadsheet source plugin. - #1751438 - Add spreadsheet source plugin.
Bug fixes Bug fixes
- #2415597 - Make batching of SQL sources optional, and force map_joinable FALSE.
- #2403593 - SQL batching messes up cases with altered queries, such as idlist. - #2403593 - SQL batching messes up cases with altered queries, such as idlist.
- #2298969 - Verify wizard validation function exists. - #2298969 - Verify wizard validation function exists.
- #2268863 - Fix drush --all option. - #2268863 - Fix drush --all option.

View File

@ -301,6 +301,11 @@ abstract class MigrationBase {
return $this->disableHooks; return $this->disableHooks;
} }
/**
* An array to track 'mail_system' variable if disabled.
*/
protected $mailSystem = array();
/** /**
* Have we already warned about obsolete constructor argumentss on this request? * Have we already warned about obsolete constructor argumentss on this request?
* *
@ -432,16 +437,11 @@ abstract class MigrationBase {
// Record the time limit // Record the time limit
$this->timeLimit = ini_get('max_execution_time'); $this->timeLimit = ini_get('max_execution_time');
// Prevent any emails from being sent out on migration // Save the current mail system, prior to disabling emails.
global $conf; $this->saveMailSystem();
if (!empty($conf['mail_system'])) {
foreach ($conf['mail_system'] as $system => $class) { // Prevent emails from being sent out during migrations.
$conf['mail_system'][$system] = 'MigrateMailIgnore'; $this->disableMailSystem();
}
}
else {
$conf['mail_system']['default-system'] = 'MigrateMailIgnore';
}
// Make sure we clear our semaphores in case of abrupt exit // Make sure we clear our semaphores in case of abrupt exit
drupal_register_shutdown_function(array($this, 'endProcess')); drupal_register_shutdown_function(array($this, 'endProcess'));
@ -1355,6 +1355,39 @@ abstract class MigrationBase {
} }
return $time; return $time;
} }
/**
* Saves the current mail system, or set a system default if there is none.
*/
protected function saveMailSystem() {
global $conf;
if (empty($conf['mail_system'])) {
$conf['mail_system']['default-system'] = 'MigrateMailIgnore';
}
else {
$this->mailSystem = $conf['mail_system'];
}
}
/**
* Disables mail system to prevent emails from being sent during migrations.
*/
public function disableMailSystem() {
global $conf;
if (!empty($conf['mail_system'])) {
foreach ($conf['mail_system'] as $system => $class) {
$conf['mail_system'][$system] = 'MigrateMailIgnore';
}
}
}
/**
* Restores the original saved mail system for migrations that require it.
*/
public function restoreMailSystem() {
global $conf;
$conf['mail_system'] = $this->mailSystem;
}
} }
// Make sure static members (in particular, $displayFunction) get // Make sure static members (in particular, $displayFunction) get

View File

@ -141,16 +141,26 @@ abstract class Migration extends MigrationBase {
// destination field, keep only the last (so the UI can override a source // destination field, keep only the last (so the UI can override a source
// field DNM that was defined in code). // field DNM that was defined in code).
$no_destination = array(); $no_destination = array();
// But also remove a mapping of a source field to nothing, if there is
// a mapping to something.
$mapped_source_fields = array();
/** @var MigrateFieldMapping $mapping */
foreach ($this->allFieldMappings as $destination_field => $mapping) { foreach ($this->allFieldMappings as $destination_field => $mapping) {
$source_field = $mapping->getSourceField();
// If the source field is not mapped to a destination field, the // If the source field is not mapped to a destination field, the
// array index is integer. // array index is integer.
if (is_int($destination_field)) { if (is_int($destination_field)) {
$source_field = $mapping->getSourceField();
if (isset($no_destination[$source_field])) { if (isset($no_destination[$source_field])) {
unset($this->allFieldMappings[$no_destination[$source_field]]); unset($this->allFieldMappings[$no_destination[$source_field]]);
unset($no_destination[$source_field]); unset($no_destination[$source_field]);
} }
$no_destination[$source_field] = $destination_field; $no_destination[$source_field] = $destination_field;
if (isset($mapped_source_fields[$source_field])) {
unset($this->allFieldMappings[$destination_field]);
}
}
else {
$mapped_source_fields[$source_field] = $source_field;
} }
} }
@ -1267,8 +1277,11 @@ abstract class Migration extends MigrationBase {
// Are we dealing with the primary value of the destination field, or a // Are we dealing with the primary value of the destination field, or a
// subfield? // subfield?
$destination = explode(':', $destination); $destination = explode(':', $destination);
// Count how many levels of fields are in the mapping. We'll use the
// last one.
$destination_count = count($destination);
$destination_field = $destination[0]; $destination_field = $destination[0];
if (isset($destination[1])) { if ($destination_count == 2) {
$subfield = $destination[1]; $subfield = $destination[1];
// We're processing the subfield before the primary value, initialize it // We're processing the subfield before the primary value, initialize it
if (!property_exists($this->destinationValues, $destination_field)) { if (!property_exists($this->destinationValues, $destination_field)) {
@ -1282,6 +1295,24 @@ abstract class Migration extends MigrationBase {
// Add the subfield value to the arguments array. // Add the subfield value to the arguments array.
$this->destinationValues->{$destination_field}['arguments'][$subfield] = $destination_values; $this->destinationValues->{$destination_field}['arguments'][$subfield] = $destination_values;
} }
elseif ($destination_count == 3) {
$subfield2 = $destination[2];
// We're processing the subfield before the primary value, initialize it
if (!property_exists($this->destinationValues, $destination_field)) {
$this->destinationValues->$destination_field = array();
}
// We have a value, and need to convert to an array so we can add
// arguments.
elseif (!is_array($this->destinationValues->$destination_field)) {
$this->destinationValues->$destination_field = array($this->destinationValues->$destination_field);
}
if (!is_array($this->destinationValues->{$destination_field}['arguments'][$destination[1]])) {
// Convert first subfield level to an array so we can add to it.
$this->destinationValues->{$destination_field}['arguments'][$destination[1]] = array( $this->destinationValues->{$destination_field}['arguments'][$destination[1]] );
}
// Add the subfield value to the arguments array.
$this->destinationValues->{$destination_field}['arguments'][$destination[1]]['arguments'][$subfield2] = $destination_values;
}
// Just the primary value, the first time through for this field, simply // Just the primary value, the first time through for this field, simply
// set it. // set it.
elseif (!property_exists($this->destinationValues, $destination_field)) { elseif (!property_exists($this->destinationValues, $destination_field)) {

View File

@ -303,9 +303,11 @@ abstract class MigrateSource implements Iterator {
// highwaters and map rows). // highwaters and map rows).
$prepared = FALSE; $prepared = FALSE;
if (!empty($this->idList)) { if (!empty($this->idList)) {
// Check first source key.
if (!in_array(reset($this->currentKey), $this->idList)) { if (!in_array(reset($this->currentKey), $this->idList)) {
// If this is a compound source key, check the full key.
$compoundKey = implode($this->multikeySeparator, $this->currentKey); $compoundKey = implode($this->multikeySeparator, $this->currentKey);
if (count($this->currentKey) > 1 && !in_array($compoundKey, $this->idList)) { if (count($this->currentKey) == 1 || !in_array($compoundKey, $this->idList)) {
// Could not find the key, skip. // Could not find the key, skip.
continue; continue;
} }

View File

@ -18,6 +18,7 @@ function migrate_drush_command() {
'force' => 'Force an operation to run, even if all dependencies are not satisfied', 'force' => 'Force an operation to run, even if all dependencies are not satisfied',
'group' => 'Name of the migration group to run', 'group' => 'Name of the migration group to run',
'notify' => 'Send email notification upon completion of operation', 'notify' => 'Send email notification upon completion of operation',
'wildcard' => 'Process migrations that match a certain pattern. For example, Content*.',
); );
$items['migrate-status'] = array( $items['migrate-status'] = array(
'description' => 'List all migrations with current status.', 'description' => 'List all migrations with current status.',
@ -161,6 +162,7 @@ function migrate_drush_command() {
'migrate-import Article' => 'Import new articles', 'migrate-import Article' => 'Import new articles',
'migrate-import Article --update' => 'Import new items, and also update previously-imported items', 'migrate-import Article --update' => 'Import new items, and also update previously-imported items',
'migrate-import Article --idlist=4,9' => 'Import two specific articles. The ids refer to the value of the primary key in base table', 'migrate-import Article --idlist=4,9' => 'Import two specific articles. The ids refer to the value of the primary key in base table',
'migrate-import Article --idlist=450:pasta,451' => 'Import two specific articles. A colon can be used to separate parts of compound keys; otherwise, compound keys match by the first key field.',
'migrate-import Article --limit="60 seconds" --stop --rollback' => 'migrate-import Article --limit="60 seconds" --stop --rollback' =>
'Import for up to 60 seconds after stopping and rolling back the Article migration.', 'Import for up to 60 seconds after stopping and rolling back the Article migration.',
'migrate-import Article --limit="100 items"' => 'migrate-import Article --limit="100 items"' =>
@ -824,15 +826,6 @@ function drush_migrate_rollback($args = NULL) {
// Capture non-informational output for mailing // Capture non-informational output for mailing
ob_start(); ob_start();
ob_implicit_flush(FALSE); ob_implicit_flush(FALSE);
// Save original mail setup, which Migrate will disable, so we can
// restore it later.
global $conf;
if (!empty($conf['mail_system'])) {
$mail_system = $conf['mail_system'];
}
else {
$mail_system = NULL;
}
} }
$migrations = drush_migrate_get_migrations($args); $migrations = drush_migrate_get_migrations($args);
@ -928,12 +921,6 @@ function drush_migrate_rollback($args = NULL) {
// Notify user // Notify user
if (drush_get_option('notify')) { if (drush_get_option('notify')) {
if (is_null($mail_system)) {
unset($conf['mail_system']);
}
else {
$conf['mail_system'] = $mail_system;
}
_drush_migrate_notify(); _drush_migrate_notify();
} }
} }
@ -985,6 +972,14 @@ function drush_migrate_get_migrations($args) {
} }
} }
} }
elseif ($wildcard = drush_get_option('wildcard')) {
foreach ($migration_objects as $name => $migration) {
if (!fnmatch(drupal_strtolower($wildcard), drupal_strtolower($name)) ||
!$migration->getEnabled()) {
unset($migration_objects[$name]);
}
}
}
else { else {
$named_migrations = array(); $named_migrations = array();
foreach (explode(',', $args) as $name) { foreach (explode(',', $args) as $name) {
@ -1057,18 +1052,23 @@ function drush_migrate_rollback_validate($args = NULL) {
function drush_migrate_validate_common($args) { function drush_migrate_validate_common($args) {
if (drush_get_option('all')) { if (drush_get_option('all')) {
if (!empty($args) || drush_get_option('group')) { if (!empty($args) || drush_get_option('group') || drush_get_option('wildcard')) {
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group')); return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group, or --wildcard'));
} }
} }
elseif (drush_get_option('group')) { elseif (drush_get_option('group')) {
if (!empty($args) || drush_get_option('all')) { if (!empty($args) || drush_get_option('all') || drush_get_option('wildcard')) {
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group')); return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group, or --wildcard'));
}
}
elseif (drush_get_option('wildcard')) {
if (!empty($args) || drush_get_option('all') || drush_get_option('group')) {
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group, or --wildcard'));
} }
} }
else { else {
if (empty($args)) { if (empty($args)) {
return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group')); return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group, or --wildcard'));
} }
$machine_names = explode(',', $args); $machine_names = explode(',', $args);
@ -1107,7 +1107,29 @@ function drush_migrate_validate_common($args) {
*/ */
function drush_migrate_pre_migrate_import($args = NULL) { function drush_migrate_pre_migrate_import($args = NULL) {
if (drush_get_option('stop')) { if (drush_get_option('stop')) {
drush_invoke('migrate-stop', $args); drush_unset_option('stop');
try {
/** @var Migration[] $migrations */
$migrations = drush_migrate_get_migrations($args);
foreach ($migrations as $migration) {
$status = $migration->getStatus();
if ($status == MigrationBase::STATUS_IMPORTING ||
$status == MigrationBase::STATUS_ROLLING_BACK) {
drush_log(dt("Stopping '!description' migration", array('!description' => $migration->getMachineName())));
$migration->stopProcess();
// Give the process a chance to stop.
$count = 0;
while ($migration->getStatus() != MigrationBase::STATUS_IDLE
&& $count++ < 5) {
sleep(1);
}
}
}
}
catch (MigrateException $e) {
drush_print($e->getMessage());
exit;
}
} }
if (drush_get_option('rollback')) { if (drush_get_option('rollback')) {
drush_unset_option('rollback'); drush_unset_option('rollback');
@ -1127,15 +1149,6 @@ function drush_migrate_import($args = NULL) {
// Capture non-informational output for mailing // Capture non-informational output for mailing
ob_start(); ob_start();
ob_implicit_flush(FALSE); ob_implicit_flush(FALSE);
// Save original mail setup, which Migrate will disable, so we can
// restore it later.
global $conf;
if (!empty($conf['mail_system'])) {
$mail_system = $conf['mail_system'];
}
else {
$mail_system = NULL;
}
} }
$migrations = drush_migrate_get_migrations($args); $migrations = drush_migrate_get_migrations($args);
$options = array(); $options = array();
@ -1294,12 +1307,6 @@ function drush_migrate_import($args = NULL) {
// Notify user // Notify user
if (drush_get_option('notify')) { if (drush_get_option('notify')) {
if (is_null($mail_system)) {
unset($conf['mail_system']);
}
else {
$conf['mail_system'] = $mail_system;
}
_drush_migrate_notify(); _drush_migrate_notify();
} }
} }

View File

@ -51,9 +51,9 @@ files[] = tests/plugins/destinations/term.test
files[] = tests/plugins/destinations/user.test files[] = tests/plugins/destinations/user.test
files[] = tests/plugins/sources/xml.test files[] = tests/plugins/sources/xml.test
; Information added by Drupal.org packaging script on 2015-02-09 ; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-2.7" version = "7.x-2.8"
core = "7.x" core = "7.x"
project = "migrate" project = "migrate"
datestamp = "1423521491" datestamp = "1435760949"

View File

@ -93,11 +93,18 @@ function migrate_migrations($reset = NULL) {
$final_migrations[$name] = array(); $final_migrations[$name] = array();
} }
// Fill in the grouped list // Fill in the grouped list.
foreach ($migrations as $machine_name => $migration) { foreach ($migrations as $machine_name => $migration) {
$final_migrations[$migration->getGroup()->getName()][$machine_name] = $migration; if (!method_exists($migration, 'getGroup')) {
MigrationBase::displayMessage(t('Migration !machine_name is not a valid Migration dependency.', array(
'!machine_name' => $machine_name,
)));
}
else {
$final_migrations[$migration->getGroup()->getName()][$machine_name] = $migration;
}
} }
// Then flatten the list // Flatten the grouped list.
$migrations = array(); $migrations = array();
foreach ($final_migrations as $group_name => $group_migrations) { foreach ($final_migrations as $group_name => $group_migrations) {
foreach ($group_migrations as $machine_name => $migration) { foreach ($group_migrations as $machine_name => $migration) {

View File

@ -424,15 +424,13 @@ class BeerNodeMigration extends BasicExampleMigration {
// subfields of the same field may be grouped on the same line), and indent // subfields of the same field may be grouped on the same line), and indent
// subfields to distinguish them from top-level fields. // subfields to distinguish them from top-level fields.
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:format', 'body:language', 'body:format',
'changed', 'changed',
'comment', 'comment',
'created', 'created',
'field_migrate_example_country:language',
'field_migrate_example_image:destination_dir', 'field_migrate_example_image:destination_dir',
'field_migrate_example_image:destination_file', 'field_migrate_example_image:destination_file',
'field_migrate_example_image:file_replace', 'field_migrate_example_image:file_replace',
'field_migrate_example_image:language',
'field_migrate_example_image:preserve_files', 'field_migrate_example_image:preserve_files',
'field_migrate_example_image:urlencode', 'field_migrate_example_image:urlencode',
'is_new', 'is_new',
@ -515,7 +513,7 @@ class BeerCommentMigration extends BasicExampleMigration {
// Unmapped destination fields // Unmapped destination fields
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'changed', 'changed',
'comment_body:format', 'comment_body:language', 'comment_body:format',
'created', 'created',
'homepage', 'homepage',
'hostname', 'hostname',

View File

@ -18,9 +18,9 @@ files[] = wine.inc
; For testing table_copy plugin. Since is infrequently used, we comment it out. ; For testing table_copy plugin. Since is infrequently used, we comment it out.
; files[] = example.table_copy.inc ; files[] = example.table_copy.inc
; Information added by Drupal.org packaging script on 2015-02-09 ; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-2.7" version = "7.x-2.8"
core = "7.x" core = "7.x"
project = "migrate" project = "migrate"
datestamp = "1423521491" datestamp = "1435760949"

View File

@ -11,9 +11,9 @@ name = "Migrate example - Oracle"
package = "Migration" package = "Migration"
project = "migrate_example_oracle" project = "migrate_example_oracle"
; Information added by Drupal.org packaging script on 2015-02-09 ; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-2.7" version = "7.x-2.8"
core = "7.x" core = "7.x"
project = "migrate" project = "migrate"
datestamp = "1423521491" datestamp = "1435760949"

View File

@ -449,7 +449,7 @@ class WineProducerMigration extends AdvancedExampleMigration {
// Unmapped destination fields // Unmapped destination fields
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:format', 'body:language', 'body:format',
'changed', 'changed',
'comment', 'comment',
'created', 'created',
@ -550,7 +550,7 @@ class WineProducerXMLMigration extends XMLMigration {
->xpath('/producer/description'); ->xpath('/producer/description');
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:summary', 'body:format', 'body:language', 'body:summary', 'body:format',
'changed', 'changed',
'comment', 'comment',
'created', 'created',
@ -663,7 +663,7 @@ class WineProducerNamespaceXMLMigration extends XMLMigration {
->xpath('/pr:producer/pr:description'); ->xpath('/pr:producer/pr:description');
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:summary', 'body:format', 'body:language', 'body:summary', 'body:format',
'changed', 'changed',
'comment', 'comment',
'created', 'created',
@ -784,7 +784,7 @@ class WineProducerMultiXMLMigration extends XMLMigration {
->xpath('description'); ->xpath('description');
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:summary', 'body:format', 'body:language', 'body:summary', 'body:format',
'changed', 'changed',
'comment', 'comment',
'created', 'created',
@ -907,7 +907,7 @@ class WineProducerMultiNamespaceXMLMigration extends XMLMigration {
->xpath('pr:description'); ->xpath('pr:description');
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:summary', 'body:format', 'body:language', 'body:summary', 'body:format',
'changed', 'changed',
'comment', 'comment',
'created', 'created',
@ -1012,7 +1012,7 @@ class WineProducerXMLPullMigration extends XMLMigration {
->xpath('description'); ->xpath('description');
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:summary', 'body:format', 'body:language', 'body:summary', 'body:format',
'changed', 'changed',
'comment', 'comment',
'created', 'created',
@ -1119,7 +1119,7 @@ class WineProducerNamespaceXMLPullMigration extends XMLMigration {
->xpath('pr:description'); ->xpath('pr:description');
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:summary', 'body:format', 'body:language', 'body:summary', 'body:format',
'changed', 'changed',
'comment', 'comment',
'created', 'created',
@ -1290,12 +1290,11 @@ class WineWineMigration extends AdvancedExampleMigration {
// Unmapped destination fields // Unmapped destination fields
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:format', 'body:language', 'body:format',
'comment', 'comment',
'field_migrate_example_image:destination_dir', 'field_migrate_example_image:destination_dir',
'field_migrate_example_image:destination_file', 'field_migrate_example_image:destination_file',
'field_migrate_example_image:file_class', 'field_migrate_example_image:file_class',
'field_migrate_example_image:language',
'field_migrate_example_image:preserve_files', 'field_migrate_example_image:preserve_files',
'field_migrate_example_image:source_dir', 'field_migrate_example_image:source_dir',
'field_migrate_example_image:urlencode', 'field_migrate_example_image:urlencode',
@ -1418,7 +1417,7 @@ class WineCommentMigration extends AdvancedExampleMigration {
// Unmapped destination fields // Unmapped destination fields
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'comment_body:format', 'comment_body:language', 'comment_body:format',
'language', 'language',
'thread', 'thread',
)); ));
@ -1551,14 +1550,13 @@ class WineUpdatesMigration extends AdvancedExampleMigration {
$this->addFieldMapping('created'); $this->addFieldMapping('created');
$this->addFieldMapping('changed'); $this->addFieldMapping('changed');
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'body:format', 'body:summary', 'body:language', 'body:format', 'body:summary',
'comment', 'comment',
'field_migrate_example_image:alt', 'field_migrate_example_image:alt',
'field_migrate_example_image:destination_dir', 'field_migrate_example_image:destination_dir',
'field_migrate_example_image:destination_file', 'field_migrate_example_image:destination_file',
'field_migrate_example_image:file_class', 'field_migrate_example_image:file_class',
'field_migrate_example_image:file_replace', 'field_migrate_example_image:file_replace',
'field_migrate_example_image:language',
'field_migrate_example_image:preserve_files', 'field_migrate_example_image:preserve_files',
'field_migrate_example_image:source_dir', 'field_migrate_example_image:source_dir',
'field_migrate_example_image:title', 'field_migrate_example_image:title',
@ -1623,7 +1621,7 @@ class WineCommentUpdatesMigration extends AdvancedExampleMigration {
// Unmapped destination fields // Unmapped destination fields
$this->addUnmigratedDestinations(array( $this->addUnmigratedDestinations(array(
'changed', 'changed',
'comment_body', 'comment_body:format', 'comment_body:language', 'comment_body', 'comment_body:format',
'created', 'created',
'homepage', 'homepage',
'hostname', 'hostname',

View File

@ -24,9 +24,9 @@ name = "migrate_example_baseball"
package = "Migration" package = "Migration"
php = "5.2.4" php = "5.2.4"
; Information added by Drupal.org packaging script on 2015-02-09 ; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-2.7" version = "7.x-2.8"
core = "7.x" core = "7.x"
project = "migrate" project = "migrate"
datestamp = "1423521491" datestamp = "1435760949"

View File

@ -6,9 +6,9 @@ core = 7.x
dependencies[] = migrate dependencies[] = migrate
files[] = migrate_ui.wizard.inc files[] = migrate_ui.wizard.inc
; Information added by Drupal.org packaging script on 2015-02-09 ; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-2.7" version = "7.x-2.8"
core = "7.x" core = "7.x"
project = "migrate" project = "migrate"
datestamp = "1423521491" datestamp = "1435760949"

View File

@ -14,7 +14,7 @@ function migrate_ui_migrate_dashboard($form, &$form_state) {
$build['overview'] = array( $build['overview'] = array(
'#prefix' => '<div>', '#prefix' => '<div>',
'#markup' => migrate_overview(), '#markup' => filter_xss_admin(migrate_overview()),
'#suffix' => '</div>', '#suffix' => '</div>',
); );
@ -100,7 +100,7 @@ function migrate_ui_migrate_dashboard($form, &$form_state) {
l($group_row->title, 'admin/content/migrate/groups/' . $group_row->name); l($group_row->title, 'admin/content/migrate/groups/' . $group_row->name);
$arguments = unserialize($group_row->arguments); $arguments = unserialize($group_row->arguments);
if (!empty($arguments['source_system'])) { if (!empty($arguments['source_system'])) {
$row['source_system'] = $arguments['source_system']; $row['source_system'] = filter_xss_admin($arguments['source_system']);
} }
else { else {
$row['source_system'] = ''; $row['source_system'] = '';
@ -212,9 +212,9 @@ function migrate_ui_migrate_group($form, &$form_state, $group_name) {
} }
$row['machinename'] = $row['machinename'] =
l($display_name, "admin/content/migrate/groups/$group_name/$machine_name"); l($display_name, "admin/content/migrate/groups/$group_name/$machine_name");
$row['importrows'] = $total; $row['importrows'] = (int) $total;
$row['imported'] = $imported; $row['imported'] = (int) $imported;
$row['unprocessed'] = $unprocessed; $row['unprocessed'] = (int) $unprocessed;
if (user_access(MIGRATE_ACCESS_ADVANCED)) { if (user_access(MIGRATE_ACCESS_ADVANCED)) {
if (is_subclass_of($migration, 'Migration')) { if (is_subclass_of($migration, 'Migration')) {
$num_messages = $migration->messageCount(); $num_messages = $migration->messageCount();
@ -231,13 +231,13 @@ function migrate_ui_migrate_group($form, &$form_state, $group_name) {
$row['lastthroughput'] = t('Unknown'); $row['lastthroughput'] = t('Unknown');
} }
else { else {
$row['lastthroughput'] = t('!rate/min', array('!rate' => $rate)); $row['lastthroughput'] = t('@rate/min', array('@rate' => $rate));
} }
} }
else { else {
$row['lastthroughput'] = t('N/A'); $row['lastthroughput'] = t('N/A');
} }
$row['lastimported'] = $migration->getLastImported(); $row['lastimported'] = check_plain($migration->getLastImported());
} }
$rows[$machine_name] = $row; $rows[$machine_name] = $row;
} }
@ -497,6 +497,19 @@ function migrate_ui_migrate_submit($form, &$form_state) {
} }
elseif (count($drush_arguments) > 0) { elseif (count($drush_arguments) > 0) {
$drush_path = trim(variable_get('migrate_drush_path', '')); $drush_path = trim(variable_get('migrate_drush_path', ''));
// Check that $drush_path works. See migrate_ui_configure_form().
if (!is_executable($drush_path)) {
$message = t('To enable running operations in the background with <a href="@drush">drush</a>, (which is <a href="@recommended">recommended</a>), some configuration must be done on the server. See the <a href="@config">documentation</a> on <a href="@dorg">drupal.org</a>.',
array(
'@drush' => 'http://drupal.org/project/drush',
'@recommended' => 'http://drupal.org/node/1806824',
'@config' => 'http://drupal.org/node/1958170',
'@dorg' => 'http://drupal.org/',
)
);
drupal_set_message($message);
return;
}
$uri = $GLOBALS['base_url']; $uri = $GLOBALS['base_url'];
$uid = $GLOBALS['user']->uid; $uid = $GLOBALS['user']->uid;
if ($operation == 'import_background') { if ($operation == 'import_background') {
@ -523,7 +536,7 @@ function migrate_ui_migrate_submit($form, &$form_state) {
$limit = $limit['value'] . ' ' . $limit['unit']; $limit = $limit['value'] . ' ' . $limit['unit'];
$drush_command .= " --limit=\"$limit\""; $drush_command .= " --limit=\"$limit\"";
} }
$log_file = '/tmp/' . $drush_arguments[0] . $log_suffix; $log_file = drupal_realpath('temporary://' . $drush_arguments[0] . $log_suffix);
$drush_command .= " >$log_file 2>&1 &"; $drush_command .= " >$log_file 2>&1 &";
exec($drush_command, $output, $status); exec($drush_command, $output, $status);
if (variable_get('migrate_drush_mail', 0)) { if (variable_get('migrate_drush_mail', 0)) {
@ -674,13 +687,24 @@ function migrate_ui_batch_finish($success, $results, $operations) {
} }
} }
/**
* Store a message to be displayed later.
*
* Ignore the message if $level is set to 'debug'.
*
* @param string $message
* the message to be displayed
* @param string $level
* the type of the message: 'debug', 'completed', 'failed', or a valid $type
* used by drupal_set_message()
*/
function migrate_ui_capture_message($message, $level) { function migrate_ui_capture_message($message, $level) {
if ($level != 'debug') { if ($level != 'debug') {
// Store each message as an array with keys 'message' and 'level'. // Store each message as an array with keys 'message' and 'level'.
global $_migrate_messages; global $_migrate_messages;
$_migrate_messages[] = array( $_migrate_messages[] = array(
'message' => $message, 'message' => filter_xss_admin($message),
'level' => $level, 'level' => check_plain($level),
); );
} }
} }
@ -738,8 +762,8 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
foreach ($team as $group => $list) { foreach ($team as $group => $list) {
$form['overview'][$group] = array( $form['overview'][$group] = array(
'#type' => 'item', '#type' => 'item',
'#title' => $group, '#title' => filter_xss_admin($group),
'#markup' => implode(', ', $list), '#markup' => filter_xss_admin(implode(', ', $list)),
); );
} }
@ -747,7 +771,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
if (count($dependencies) > 0) { if (count($dependencies) > 0) {
$form['overview']['dependencies'] = array( $form['overview']['dependencies'] = array(
'#title' => t('Dependencies') , '#title' => t('Dependencies') ,
'#markup' => implode(', ', $dependencies), '#markup' => filter_xss_admin(implode(', ', $dependencies)),
'#type' => 'item', '#type' => 'item',
); );
} }
@ -755,14 +779,14 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
if (count($soft_dependencies) > 0) { if (count($soft_dependencies) > 0) {
$form['overview']['soft_dependencies'] = array( $form['overview']['soft_dependencies'] = array(
'#title' => t('Soft Dependencies'), '#title' => t('Soft Dependencies'),
'#markup' => implode(', ', $soft_dependencies), '#markup' => filter_xss_admin(implode(', ', $soft_dependencies)),
'#type' => 'item', '#type' => 'item',
); );
} }
$form['overview']['group'] = array( $form['overview']['group'] = array(
'#title' => t('Group:'), '#title' => t('Group:'),
'#markup' => $migration->getGroup()->getTitle(), '#markup' => filter_xss_admin($migration->getGroup()->getTitle()),
'#type' => 'item', '#type' => 'item',
); );
@ -787,7 +811,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
$form['overview']['description'] = array( $form['overview']['description'] = array(
'#title' => t('Description:'), '#title' => t('Description:'),
'#markup' => $migration->getDescription(), '#markup' => filter_xss_admin($migration->getDescription()),
'#type' => 'item', '#type' => 'item',
); );
@ -807,7 +831,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
$form['destination']['type'] = array( $form['destination']['type'] = array(
'#type' => 'item', '#type' => 'item',
'#title' => t('Type'), '#title' => t('Type'),
'#markup' => (string)$destination, '#markup' => filter_xss_admin((string) $destination),
); );
$dest_key = $destination->getKeySchema(); $dest_key = $destination->getKeySchema();
$header = array(t('Machine name'), t('Description')); $header = array(t('Machine name'), t('Description'));
@ -822,7 +846,10 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
// Add class for mapped/unmapped. Used in summary. // Add class for mapped/unmapped. Used in summary.
$classes[] = !isset($destination_fields[$machine_name]) ? 'migrate-error' : ''; $classes[] = !isset($destination_fields[$machine_name]) ? 'migrate-error' : '';
} }
$rows[] = array(array('data' => $machine_name, 'class' => $classes), array('data' => $description, 'class' => $classes)); $rows[] = array(
array('data' => check_plain($machine_name), 'class' => $classes),
array('data' => filter_xss_admin($description), 'class' => $classes),
);
} }
$classes = array(); $classes = array();
@ -848,7 +875,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
$form['source']['query'] = array( $form['source']['query'] = array(
'#type' => 'item', '#type' => 'item',
'#title' => t('Query'), '#title' => t('Query'),
'#markup' => '<pre>' . $source . '</pre>', '#markup' => '<pre>' . filter_xss_admin($source) . '</pre>',
); );
$source_key = $migration->getMap()->getSourceKey(); $source_key = $migration->getMap()->getSourceKey();
$header = array(t('Machine name'), t('Description')); $header = array(t('Machine name'), t('Description'));
@ -862,7 +889,10 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
// Add class for mapped/unmapped. Used in summary. // Add class for mapped/unmapped. Used in summary.
$classes = !isset($source_fields[$machine_name]) ? 'migrate-error' : ''; $classes = !isset($source_fields[$machine_name]) ? 'migrate-error' : '';
} }
$rows[] = array(array('data' => $machine_name, 'class' => $classes), array('data' => $description, 'class' => $classes)); $rows[] = array(
array('data' => check_plain($machine_name), 'class' => $classes),
array('data' => filter_xss_admin($description), 'class' => $classes),
);
} }
$classes = array(); $classes = array();
@ -887,16 +917,16 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
if (!is_null($source_field) && !isset($source_fields[$source_field])) { if (!is_null($source_field) && !isset($source_fields[$source_field])) {
drupal_set_message(t('"!source" was used as source field in the drupal_set_message(t('"!source" was used as source field in the
"!destination" mapping but is not in list of source fields', array( "!destination" mapping but is not in list of source fields', array(
'!source' => $source_field, '!source' => filter_xss_admin($source_field),
'!destination' => $destination_field '!destination' => filter_xss_admin($destination_field),
)), )),
'warning'); 'warning');
} }
if (!is_null($destination_field) && !isset($destination_fields[$destination_field])) { if (!is_null($destination_field) && !isset($destination_fields[$destination_field])) {
drupal_set_message(t('"!destination" was used as destination field in drupal_set_message(t('"!destination" was used as destination field in
"!source" mapping but is not in list of destination fields', array( "!source" mapping but is not in list of destination fields', array(
'!source' => $source_field, '!source' => filter_xss_admin($source_field),
'!destination' => $destination_field)), '!destination' => filter_xss_admin($destination_field))),
'warning'); 'warning');
} }
$descriptions[$mapping->getIssueGroup()][] = $mapping; $descriptions[$mapping->getIssueGroup()][] = $mapping;
@ -906,7 +936,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
foreach ($descriptions as $group => $mappings) { foreach ($descriptions as $group => $mappings) {
$form[$group] = array( $form[$group] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Mapping: !group', array('!group' => $group)), '#title' => t('Mapping: !group', array('!group' => filter_xss_admin($group))),
'#group' => 'detail', '#group' => 'detail',
'#attributes' => array('class' => array('migrate-mapping')), '#attributes' => array('class' => array('migrate-mapping')),
); );
@ -918,7 +948,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
} }
$issue_priority = $mapping->getIssuePriority(); $issue_priority = $mapping->getIssuePriority();
if (!is_null($issue_priority)) { if (!is_null($issue_priority)) {
$classes[] = 'migrate-priority-' . $issue_priority; $classes[] = 'migrate-priority-' . drupal_html_class($issue_priority);
$priority = MigrateFieldMapping::$priorities[$issue_priority]; $priority = MigrateFieldMapping::$priorities[$issue_priority];
$issue_pattern = $migration->getIssuePattern(); $issue_pattern = $migration->getIssuePattern();
$issue_number = $mapping->getIssueNumber(); $issue_number = $mapping->getIssueNumber();
@ -942,11 +972,11 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
$source_field = "<em>$source_field</em>"; $source_field = "<em>$source_field</em>";
} }
$row = array( $row = array(
array('data' => $destination_field, 'class' => $classes), array('data' => filter_xss_admin($destination_field), 'class' => $classes),
array('data' => $source_field, 'class' => $classes), array('data' => filter_xss_admin($source_field), 'class' => $classes),
array('data' => $default, 'class' => $classes), array('data' => filter_xss_admin($default), 'class' => $classes),
array('data' => $mapping->getDescription(), 'class' => $classes), array('data' => filter_xss_admin($mapping->getDescription()), 'class' => $classes),
array('data' => $priority, 'class' => $classes), array('data' => filter_xss_admin($priority), 'class' => $classes),
); );
$rows[] = $row; $rows[] = $row;
$classes = array(); $classes = array();
@ -973,7 +1003,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
*/ */
function migrate_ui_edit_mappings($form, $form_state, $group_name, function migrate_ui_edit_mappings($form, $form_state, $group_name,
$migration_name) { $migration_name) {
drupal_set_title(t('Edit !migration', array('!migration' => $migration_name))); drupal_set_title(t('Edit !migration', array('!migration' => filter_xss_admin($migration_name))));
$form = array(); $form = array();
$form['#tree'] = TRUE; $form['#tree'] = TRUE;
@ -1014,6 +1044,9 @@ function migrate_ui_edit_mappings($form, $form_state, $group_name,
'#suffix' => '</div>', '#suffix' => '</div>',
); );
// So the theme function knows whether to include the xpath column.
$form['field_mappings']['#is_xml_migration'] = is_a($migration, 'XMLMigration');
$form['source_fields'] = array( $form['source_fields'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Source fields'), '#title' => t('Source fields'),
@ -1045,10 +1078,14 @@ function migrate_ui_edit_mappings($form, $form_state, $group_name,
else { else {
$label_format = '!description'; $label_format = '!description';
} }
$label = t($label_format, $label = t($label_format, array(
array('!source_field' => $name, '!description' => $description)); '!source_field' => filter_xss_admin($name),
$short_label = t($label_format, '!description' => filter_xss_admin($description),
array('!source_field' => $name, '!description' => $short_description)); ));
$short_label = t($label_format, array(
'!source_field' => filter_xss_admin($name),
'!description' => filter_xss_admin($short_description),
));
$dnm_value = 0; $dnm_value = 0;
@ -1171,6 +1208,16 @@ function migrate_ui_edit_mappings($form, $form_state, $group_name,
'#options' => $source_migration_options, '#options' => $source_migration_options,
'#default_value' => $source_migration, '#default_value' => $source_migration,
); );
if (is_a($mapping, 'MigrateXMLFieldMapping')) {
/** @var MigrateXMLFieldMapping $mapping */
$form['field_mappings'][$name]['xpath'] = array(
'#type' => 'textfield',
'#default_value' => $mapping->getXpath(),
'#size' => 20,
);
}
} }
} }
@ -1213,7 +1260,7 @@ function migrate_ui_edit_mappings($form, $form_state, $group_name,
} }
$form['dependencies'][$machine_name] = array( $form['dependencies'][$machine_name] = array(
'#type' => 'select', '#type' => 'select',
'#title' => $machine_name, '#title' => check_plain($machine_name),
'#default_value' => $default_value, '#default_value' => $default_value,
'#options' => $dependency_options, '#options' => $dependency_options,
); );
@ -1275,16 +1322,21 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
$field_mappings = array(); $field_mappings = array();
$default_values = array(); $default_values = array();
$issue_group_values = array(); $issue_group_values = array();
$xpaths = array();
$migration = Migration::getInstance($machine_name); $migration = Migration::getInstance($machine_name);
if (is_a($migration, 'Migration')) { if (is_a($migration, 'Migration')) {
$xml = is_a($migration, 'XMLMigration') ? TRUE : FALSE;
$existing_mappings = $migration->getFieldMappings(); $existing_mappings = $migration->getFieldMappings();
$coded_mappings = $migration->getCodedFieldMappings(); $coded_mappings = $migration->getCodedFieldMappings();
foreach ($form_state['values']['field_mappings'] as $destination_field => $info) { foreach ($form_state['values']['field_mappings'] as $destination_field => $info) {
// Treat an empty string for the default value as NULL. // Treat empty strings as NULL.
if ($info['default_value'] === '') { if ($info['default_value'] === '') {
$info['default_value'] = NULL; $info['default_value'] = NULL;
} }
if ($xml && $info['xpath'] === '') {
$info['xpath'] = NULL;
}
// If this mapping matches a coded mapping but not a stored mapping, remove // If this mapping matches a coded mapping but not a stored mapping, remove
// it entirely (don't store it in the database) so the coded mapping is not // it entirely (don't store it in the database) so the coded mapping is not
@ -1296,6 +1348,10 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
$coded_mappings[$destination_field]->getDefaultValue(); $coded_mappings[$destination_field]->getDefaultValue();
$coded_source_migration = $coded_source_migration =
$coded_mappings[$destination_field]->getSourceMigration(); $coded_mappings[$destination_field]->getSourceMigration();
if ($xml) {
$coded_xpath =
$coded_mappings[$destination_field]->getXpath();
}
if ($info['mapping'] == '-1') { if ($info['mapping'] == '-1') {
$info['mapping'] = NULL; $info['mapping'] = NULL;
} }
@ -1312,6 +1368,7 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
if ($info['mapping'] == $coded_source_field && if ($info['mapping'] == $coded_source_field &&
$info['default_value'] == $coded_default_value && $info['default_value'] == $coded_default_value &&
$info['source_migration'] == $coded_source_migration && $info['source_migration'] == $coded_source_migration &&
(!$xml || ($xml && ($info['xpath'] == $coded_xpath))) &&
$dnm_matches) { $dnm_matches) {
continue; continue;
} }
@ -1321,6 +1378,9 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
$default_values[$destination_field] = $info['default_value']; $default_values[$destination_field] = $info['default_value'];
$source_migrations[$destination_field] = $info['source_migration']; $source_migrations[$destination_field] = $info['source_migration'];
$issue_group_values[$destination_field] = $info['issue_group']; $issue_group_values[$destination_field] = $info['issue_group'];
if ($xml) {
$xpaths[$destination_field] = $info['xpath'];
}
} }
foreach ($field_mappings as $destination_field => $source_field) { foreach ($field_mappings as $destination_field => $source_field) {
@ -1342,10 +1402,12 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
$mapping = NULL; $mapping = NULL;
if (isset($existing_mappings[$destination_field]) && if (isset($existing_mappings[$destination_field]) &&
$issue_group_values[$destination_field] != 0) { $issue_group_values[$destination_field] != 0) {
/** @var MigrateFieldMapping $old_mapping */
$old_mapping = $existing_mappings[$destination_field]; $old_mapping = $existing_mappings[$destination_field];
if ($source_field == $old_mapping->getSourceField() && if ($source_field == $old_mapping->getSourceField() &&
$default_values[$destination_field] == $old_mapping->getDefaultValue() && $default_values[$destination_field] == $old_mapping->getDefaultValue() &&
$source_migrations[$destination_field] == $old_mapping->getSourceMigration()) { $source_migrations[$destination_field] == $old_mapping->getSourceMigration() &&
(!$xml || ($xml && ($xpaths[$destination_field] == $old_mapping->getXpath())))) {
// First, if this mapping matches a previously-stored mapping, we want to // First, if this mapping matches a previously-stored mapping, we want to
// preserve it as it was originally stored. // preserve it as it was originally stored.
if ($old_mapping->getMappingSource() == if ($old_mapping->getMappingSource() ==
@ -1362,8 +1424,11 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
// We're not skipping this mapping, or preserving an old one, so create the // We're not skipping this mapping, or preserving an old one, so create the
// new mapping. // new mapping.
if (!$mapping) { if (!$mapping) {
$mapping = new MigrateFieldMapping($destination_field, $source_field); $mapping = _migrate_ui_get_mapping_object($migration, $destination_field, $source_field);
$mapping->defaultValue($default); $mapping->defaultValue($default);
if ($xml && $xpaths[$destination_field]) {
$mapping->xpath($xpaths[$destination_field]);
}
} }
if ($issue_group_values[$destination_field]) { if ($issue_group_values[$destination_field]) {
@ -1394,7 +1459,7 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
// If it is marked DNM in the UI, but is not ignored in the code, // If it is marked DNM in the UI, but is not ignored in the code,
// generate a DNM mapping. // generate a DNM mapping.
if ($value && !$code_ignored) { if ($value && !$code_ignored) {
$mapping = new MigrateFieldMapping(NULL, $source_field); $mapping = _migrate_ui_get_mapping_object($migration, NULL, $source_field);
$mapping->issueGroup(t('DNM')); $mapping->issueGroup(t('DNM'));
$arguments['field_mappings'][] = $mapping; $arguments['field_mappings'][] = $mapping;
} }
@ -1410,7 +1475,7 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
} }
} }
if (!$mapping_found) { if (!$mapping_found) {
$mapping = new MigrateFieldMapping(NULL, $source_field); $mapping = _migrate_ui_get_mapping_object($migration, NULL, $source_field);
$arguments['field_mappings'][] = $mapping; $arguments['field_mappings'][] = $mapping;
} }
} }
@ -1436,6 +1501,22 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
"admin/content/migrate/groups/$group_name/$machine_name"; "admin/content/migrate/groups/$group_name/$machine_name";
} }
/**
* Create a field mapping object of the appropriate class.
*
* @param $migration
*
* @return MigrateFieldMapping
*/
function _migrate_ui_get_mapping_object($migration, $destination, $source) {
if (is_a($migration, 'XMLMigration')) {
return new MigrateXMLFieldMapping($destination, $source);
}
else {
return new MigrateFieldMapping($destination, $source);
}
}
/** /**
* Revert callback for the edit mappings form. Remove any field mappings that * Revert callback for the edit mappings form. Remove any field mappings that
* were defined through the UI. * were defined through the UI.
@ -1469,6 +1550,9 @@ function theme_migrate_ui_field_mapping_form($variables) {
if (!empty($elements)) { if (!empty($elements)) {
$header = array(t('DNM'), t('Destination field'), t('Source field'), $header = array(t('DNM'), t('Destination field'), t('Source field'),
t('Default value'), t('Source migration')); t('Default value'), t('Source migration'));
if (!empty($form['#is_xml_migration'])) {
$header[] = t('Xpath');
}
$rows = array(); $rows = array();
foreach ($elements as $mapping_key) { foreach ($elements as $mapping_key) {
$row = array(); $row = array();
@ -1479,6 +1563,9 @@ function theme_migrate_ui_field_mapping_form($variables) {
$row[] = drupal_render($form[$mapping_key]['mapping']); $row[] = drupal_render($form[$mapping_key]['mapping']);
$row[] = drupal_render($form[$mapping_key]['default_value']); $row[] = drupal_render($form[$mapping_key]['default_value']);
$row[] = drupal_render($form[$mapping_key]['source_migration']); $row[] = drupal_render($form[$mapping_key]['source_migration']);
if (!empty($form['#is_xml_migration'])) {
$row[] = drupal_render($form[$mapping_key]['xpath']);
}
$rows[] = $row; $rows[] = $row;
} }
$output .= theme('table', array('header' => $header, 'rows' => $rows)); $output .= theme('table', array('header' => $header, 'rows' => $rows));
@ -1536,7 +1623,11 @@ function migrate_ui_messages($group_name, $migration_name) {
$header = array(); $header = array();
// Add a table header for each source key in the migration's map. // Add a table header for each source key in the migration's map.
foreach ($source_key as $key => $map_info) { foreach ($source_key as $key => $map_info) {
$header[] = array('data' => $map_info['description'], 'field' => $source_key_map[$key], 'sort' => 'asc'); $header[] = array(
'data' => filter_xss_admin($map_info['description']),
'field' => $source_key_map[$key],
'sort' => 'asc',
);
} }
$header[] = array('data' => t('Level'), 'field' => 'level'); $header[] = array('data' => t('Level'), 'field' => 'level');
@ -1558,12 +1649,18 @@ function migrate_ui_messages($group_name, $migration_name) {
// Add a table column for each source key. // Add a table column for each source key.
foreach ($source_key_map_flipped as $source_key => $source_field) { foreach ($source_key_map_flipped as $source_key => $source_field) {
$row[] = array( $row[] = array(
'data' => $message->{$source_key}, 'data' => filter_xss_admin($message->{$source_key}),
'class' => $classes, 'class' => $classes,
); );
} }
$row[] = array('data' => $migration->getMessageLevelName($message->level), 'class' => $classes); $row[] = array(
$row[] = array('data' => $message->message, 'class' => $classes); 'data' => filter_xss_admin($migration->getMessageLevelName($message->level)),
'class' => $classes,
);
$row[] = array(
'data' => filter_xss_admin($message->message),
'class' => $classes,
);
$rows[] = $row; $rows[] = $row;
@ -1642,7 +1739,10 @@ function migrate_ui_configure_form($form, &$form_state) {
if (!class_exists($row->class_name)) { if (!class_exists($row->class_name)) {
$migrations[] = $row->machine_name; $migrations[] = $row->machine_name;
$migration_list .= '<li>' . t('!migration (class !class)', $migration_list .= '<li>' . t('!migration (class !class)',
array('!migration' => $row->machine_name, '!class' => $row->class_name)) . "</li>\n"; array(
'!migration' => filter_xss_admin($row->machine_name),
'!class' => filter_xss_admin($row->class_name),
)) . "</li>\n";
} }
} }
@ -1680,7 +1780,7 @@ function migrate_ui_configure_form($form, &$form_state) {
// Configure background imports if the drush command has been set. // Configure background imports if the drush command has been set.
$drush_path = trim(variable_get('migrate_drush_path', '')); $drush_path = trim(variable_get('migrate_drush_path', ''));
$drush_validated = FALSE; $drush_validated = FALSE;
if ($drush_path) { if ($drush_path && is_executable($drush_path)) {
// Try running a drush status command to verify it's properly configured. // Try running a drush status command to verify it's properly configured.
$uri = $GLOBALS['base_url']; $uri = $GLOBALS['base_url'];
$uid = $GLOBALS['user']->uid; $uid = $GLOBALS['user']->uid;
@ -1818,9 +1918,9 @@ function migrate_ui_configure_form($form, &$form_state) {
->condition('type', 'class') ->condition('type', 'class')
->execute() ->execute()
->fetchField(); ->fetchField();
$row['module'] = $module; $row['module'] = check_plain($module);
$row['class'] = $class_name; $row['class'] = check_plain($class_name);
$row['types'] = implode(', ', $handler->getTypesHandled()); $row['types'] = filter_xss_admin(implode(', ', $handler->getTypesHandled()));
$default_values[$class_name] = !in_array($class_name, $disabled); $default_values[$class_name] = !in_array($class_name, $disabled);
$rows[$class_name] = $row; $rows[$class_name] = $row;
} }
@ -1855,9 +1955,9 @@ function migrate_ui_configure_form($form, &$form_state) {
->condition('type', 'class') ->condition('type', 'class')
->execute() ->execute()
->fetchField(); ->fetchField();
$row['module'] = $module; $row['module'] = check_plain($module);
$row['class'] = $class_name; $row['class'] = check_plain($class_name);
$row['types'] = implode(', ', $handler->getTypesHandled()); $row['types'] = filter_xss_admin(implode(', ', $handler->getTypesHandled()));
$default_values[$class_name] = !in_array($class_name, $disabled); $default_values[$class_name] = !in_array($class_name, $disabled);
$rows[$class_name] = $row; $rows[$class_name] = $row;
} }

View File

@ -216,7 +216,7 @@ class MigrateDestinationComment extends MigrateDestinationEntity {
} }
// Validate field data prior to saving. // Validate field data prior to saving.
field_attach_validate('comment', $comment); MigrateDestinationEntity::fieldAttachValidate('comment', $comment);
migrate_instrument_start('comment_save'); migrate_instrument_start('comment_save');
comment_save($comment); comment_save($comment);

View File

@ -176,4 +176,34 @@ abstract class MigrateDestinationEntity extends MigrateDestination {
} }
} }
} }
/**
* Perform field validation against the field data in an entity. Wraps
* field_attach_validate to handle exceptions cleanly and provide maximum
* information for identifying the cause of validation errors.
*
* @param $entity_type
* The type of $entity; e.g. 'node' or 'user'.
* @param $entity
* The entity with fields to validate.
*/
static public function fieldAttachValidate($entity_type, $entity) {
try {
field_attach_validate($entity_type, $entity);
}
catch (FieldValidationException $e) {
$migration = Migration::currentMigration();
foreach ($e->errors as $field_name => $field_errors) {
foreach ($field_errors as $langcode => $errors) {
foreach ($errors as $delta => $error_list) {
foreach ($error_list as $index => $error) {
$message = $error['message'];
$migration->saveMessage(t('Field validation error for !field_name: !message',
array('!field_name' => $field_name, '!message' => $message)));
}
}
}
}
}
}
} }

View File

@ -342,8 +342,11 @@ class MigrateTextFieldHandler extends MigrateFieldHandler {
$fields['format'] = t('Subfield: <a href="@doc">Text format for the field</a>', $fields['format'] = t('Subfield: <a href="@doc">Text format for the field</a>',
array('@doc' => 'http://drupal.org/node/1224042#format')); array('@doc' => 'http://drupal.org/node/1224042#format'));
} }
$fields['language'] = t('Subfield: <a href="@doc">Language for the field</a>', $field = field_info_field($instance['field_name']);
array('@doc' => 'http://drupal.org/node/1224042#language')); if (field_is_translatable($instance['entity_type'], $field)) {
$fields['language'] = t('Subfield: <a href="@doc">Language for the field</a>',
array('@doc' => 'http://drupal.org/node/1224042#language'));
}
return $fields; return $fields;
} }
@ -469,7 +472,7 @@ class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
else { else {
$arguments = array(); $arguments = array();
} }
if (empty($values[0])) { if (count($values) == 1 && empty($values[0])) {
$values = array(); $values = array();
} }
@ -530,7 +533,7 @@ class MigrateTaxonomyTermReferenceFieldHandler extends MigrateFieldHandler {
// This term is being created with no fields, but we should still call // This term is being created with no fields, but we should still call
// field_attach_validate() before saving, as that invokes // field_attach_validate() before saving, as that invokes
// hook_field_attach_validate(). // hook_field_attach_validate().
field_attach_validate('taxonomy_term', $new_term); MigrateDestinationEntity::fieldAttachValidate('taxonomy_term', $new_term);
taxonomy_term_save($new_term); taxonomy_term_save($new_term);
$tids[] = $new_term->tid; $tids[] = $new_term->tid;
@ -588,9 +591,13 @@ abstract class MigrateFileFieldBaseHandler extends MigrateFieldHandler {
$fields = array( $fields = array(
'file_class' => t('Option: <a href="@doc">Implementation of MigrateFile to use</a>', 'file_class' => t('Option: <a href="@doc">Implementation of MigrateFile to use</a>',
array('@doc' => 'http://drupal.org/node/1540106#file_class')), array('@doc' => 'http://drupal.org/node/1540106#file_class')),
'language' => t('Subfield: Language for the field'),
); );
$field = field_info_field($instance['field_name']);
if (field_is_translatable($instance['entity_type'], $field)) {
$fields['language'] = t('Subfield: Language for the field');
}
// If we can identify the file class mapped to this field, pick up the // If we can identify the file class mapped to this field, pick up the
// subfields specific to that class. // subfields specific to that class.
if ($migration) { if ($migration) {

View File

@ -389,15 +389,16 @@ class MigrateFileUri extends MigrateFile {
// Perform the copy operation, with a cleaned-up path. // Perform the copy operation, with a cleaned-up path.
$this->sourcePath = self::urlencode($this->sourcePath); $this->sourcePath = self::urlencode($this->sourcePath);
} }
if (!@copy($this->sourcePath, $destination)) { try {
$migration = Migration::currentMigration(); copy($this->sourcePath, $destination);
$migration->saveMessage(t('The specified file %file could not be copied to %destination.',
array('%file' => $this->sourcePath, '%destination' => $destination)));
return FALSE;
}
else {
return TRUE; return TRUE;
} }
catch (Exception $e) {
$migration = Migration::currentMigration();
$migration->saveMessage(t('The specified file %file could not be copied to %destination: "%exception_msg"',
array('%file' => $this->sourcePath, '%destination' => $destination, '%exception_msg' => $e->getMessage())));
return FALSE;
}
} }
/** /**

View File

@ -263,7 +263,7 @@ class MigrateDestinationNode extends MigrateDestinationEntity {
} }
// Validate field data prior to saving. // Validate field data prior to saving.
field_attach_validate('node', $node); MigrateDestinationEntity::fieldAttachValidate('node', $node);
migrate_instrument_start('node_save'); migrate_instrument_start('node_save');
node_save($node); node_save($node);

View File

@ -258,7 +258,7 @@ class MigrateDestinationTerm extends MigrateDestinationEntity {
} }
// Validate field data prior to saving. // Validate field data prior to saving.
field_attach_validate('taxonomy_term', $term); MigrateDestinationEntity::fieldAttachValidate('taxonomy_term', $term);
migrate_instrument_start('taxonomy_term_save'); migrate_instrument_start('taxonomy_term_save');
$status = taxonomy_term_save($term); $status = taxonomy_term_save($term);

View File

@ -231,7 +231,7 @@ class MigrateDestinationUser extends MigrateDestinationEntity {
} }
// Validate field data prior to saving. // Validate field data prior to saving.
field_attach_validate('user', $account); MigrateDestinationEntity::fieldAttachValidate('user', $account);
migrate_instrument_start('user_save'); migrate_instrument_start('user_save');
$newaccount = user_save($old_account, (array)$account); $newaccount = user_save($old_account, (array)$account);

View File

@ -198,8 +198,8 @@ class MigrateSourceList extends MigrateSource {
list(, $id) = each($ids); list(, $id) = each($ids);
$row->$key_name = $id; $row->$key_name = $id;
} }
break;
} }
break;
} }
return $row; return $row;
} }

View File

@ -187,8 +187,8 @@ class MigrateSourceMultiItems extends MigrateSource {
$sourceKey = $this->activeMap->getSourceKey(); $sourceKey = $this->activeMap->getSourceKey();
$key_name = key($sourceKey); $key_name = key($sourceKey);
$row->$key_name = $id; $row->$key_name = $id;
break;
} }
break;
} }
return $row; return $row;
} }

View File

@ -376,14 +376,29 @@ class MigrateSourceSQL extends MigrateSource {
// 3. If we are using highwater marks, also include rows above the mark. // 3. If we are using highwater marks, also include rows above the mark.
// But, include all rows if the highwater mark is not set. // But, include all rows if the highwater mark is not set.
if (isset($this->highwaterField['name']) && $this->activeMigration->getHighwater() !== '') { if (isset($this->highwaterField['name']) && $this->activeMigration->getHighwater() !== '') {
if (isset($this->highwaterField['alias'])) { // But, if there are any existing items marked as needing update which
$highwater = $this->highwaterField['alias'] . '.' . $this->highwaterField['name']; // fall below the highwater mark, and map_joinable is FALSE, those
// items will be skipped. Thus, in that case do not add the highwater
// optimization to the query.
$add_highwater_condition = TRUE;
if (!$this->mapJoinable) {
$count_needs_update = db_query('SELECT COUNT(*) FROM {' .
$this->activeMap->getQualifiedMapTable() . '} WHERE needs_update = 1')
->fetchField();
if ($count_needs_update > 0) {
$add_highwater_condition = FALSE;
}
} }
else { if ($add_highwater_condition) {
$highwater = $this->highwaterField['name']; if (isset($this->highwaterField['alias'])) {
$highwater = $this->highwaterField['alias'] . '.' . $this->highwaterField['name'];
}
else {
$highwater = $this->highwaterField['name'];
}
$conditions->condition($highwater, $this->activeMigration->getHighwater(), '>');
$condition_added = TRUE;
} }
$conditions->condition($highwater, $this->activeMigration->getHighwater(), '>');
$condition_added = TRUE;
} }
if ($condition_added) { if ($condition_added) {
$this->query->condition($conditions); $this->query->condition($conditions);

View File

@ -69,6 +69,11 @@ class MigrateSQLMap extends MigrateMap {
*/ */
protected $ensured; protected $ensured;
/**
* Provide caching for Source or Desination Map Lookups.
*/
protected $cacheMapLookups;
/** /**
* Constructor. * Constructor.
* *
@ -92,6 +97,13 @@ class MigrateSQLMap extends MigrateMap {
$this->trackLastImported = TRUE; $this->trackLastImported = TRUE;
} }
if (isset($options['cache_map_lookups'])) {
$this->cacheMapLookups = $options['cache_map_lookups'];
}
else {
$this->cacheMapLookups = FALSE;
}
$this->connection = Database::getConnection('default', $connection_key); $this->connection = Database::getConnection('default', $connection_key);
// Default generated table names, limited to 63 characters // Default generated table names, limited to 63 characters
@ -302,6 +314,16 @@ class MigrateSQLMap extends MigrateMap {
*/ */
public function lookupSourceID(array $destination_id) { public function lookupSourceID(array $destination_id) {
migrate_instrument_start('lookupSourceID'); migrate_instrument_start('lookupSourceID');
// Try a cache lookup if enabled.
if ($this->cacheMapLookups) {
$cache = &drupal_static($this->mapTable . '_sourceIDCache');
$serialized = json_encode($destination_id);
if (isset($cache[$serialized])) {
migrate_instrument_stop('lookupSourceID');
return $cache[$serialized];
}
}
$query = $this->connection->select($this->mapTable, 'map') $query = $this->connection->select($this->mapTable, 'map')
->fields('map', $this->sourceKeyMap); ->fields('map', $this->sourceKeyMap);
foreach ($this->destinationKeyMap as $key_name) { foreach ($this->destinationKeyMap as $key_name) {
@ -309,6 +331,12 @@ class MigrateSQLMap extends MigrateMap {
} }
$result = $query->execute(); $result = $query->execute();
$source_id = $result->fetchAssoc(); $source_id = $result->fetchAssoc();
// Store the id in a cache if enabled.
if ($this->cacheMapLookups) {
$cache[$serialized] = $destination_id;
}
migrate_instrument_stop('lookupSourceID'); migrate_instrument_stop('lookupSourceID');
return $source_id; return $source_id;
} }
@ -324,6 +352,16 @@ class MigrateSQLMap extends MigrateMap {
*/ */
public function lookupDestinationID(array $source_id) { public function lookupDestinationID(array $source_id) {
migrate_instrument_start('lookupDestinationID'); migrate_instrument_start('lookupDestinationID');
// Try a cache lookup if enabled.
if ($this->cacheMapLookups) {
$cache = &drupal_static($this->mapTable . '_destinationIDCache');
$serialized = json_encode($source_id);
if (isset($cache[$serialized])) {
migrate_instrument_stop('lookupDestinationID');
return $cache[$serialized];
}
}
$query = $this->connection->select($this->mapTable, 'map') $query = $this->connection->select($this->mapTable, 'map')
->fields('map', $this->destinationKeyMap); ->fields('map', $this->destinationKeyMap);
foreach ($this->sourceKeyMap as $key_name) { foreach ($this->sourceKeyMap as $key_name) {
@ -331,10 +369,15 @@ class MigrateSQLMap extends MigrateMap {
} }
$result = $query->execute(); $result = $query->execute();
$destination_id = $result->fetchAssoc(); $destination_id = $result->fetchAssoc();
// Store the id in a cache if enabled.
if ($this->cacheMapLookups) {
$cache[$serialized] = $destination_id;
}
migrate_instrument_stop('lookupDestinationID'); migrate_instrument_stop('lookupDestinationID');
return $destination_id; return $destination_id;
} }
/** /**
* Called upon import of one record, we record a mapping from the source key * Called upon import of one record, we record a mapping from the source key
* to the destination key. Also may be called, setting the third parameter to * to the destination key. Also may be called, setting the third parameter to

View File

@ -40,17 +40,13 @@ class MigrateImportOptionsTest extends DrupalWebTestCase {
$result = $migration->processImport($options); $result = $migration->processImport($options);
$this->verbose(print_r($timers, 1));
$successes = $migration->importedCount(); $successes = $migration->importedCount();
$this->verbose("Total successes: {$successes}");
$assertion = format_plural($limit, 'The migration successfully processed 1 item.', $assertion = format_plural($limit, 'The migration successfully processed 1 item.',
'The migration successfully processed @count items.'); 'The migration successfully processed @count items.');
$this->assertEqual($limit, $successes, $assertion); $this->assertEqual($limit, $successes, $assertion);
$prepare_row_count = $timers['BeerTermMigration prepareRow']['count']; $prepare_row_count = $timers['BeerTermMigration prepareRow']['count'];
$this->verbose("prepareRow() count: {$prepare_row_count}");
$processed = $migration->processedCount(); $processed = $migration->processedCount();
$this->verbose("Total processed count: {$processed}");
$assertion = format_plural($processed, 'The migration executed processRow() on 1 item.', $assertion = format_plural($processed, 'The migration executed processRow() on 1 item.',
'The migration executed processRow() on @count items.'); 'The migration executed processRow() on @count items.');
$this->assertEqual($prepare_row_count, $processed, $assertion); $this->assertEqual($prepare_row_count, $processed, $assertion);

View File

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

View File

@ -13,11 +13,13 @@ function views_bulk_operations_book_action_info() {
'label' => t('Move to book'), 'label' => t('Move to book'),
'configurable' => TRUE, 'configurable' => TRUE,
'behavior' => array('changes_property'), 'behavior' => array('changes_property'),
'triggers' => array('any'),
); );
$actions['views_bulk_operations_remove_from_book_action'] = array( $actions['views_bulk_operations_remove_from_book_action'] = array(
'type' => 'node', 'type' => 'node',
'label' => t('Remove from book'), 'label' => t('Remove from book'),
'configurable' => FALSE, 'configurable' => FALSE,
'triggers' => array('any'),
); );
} }

View File

@ -19,6 +19,7 @@ function views_bulk_operations_delete_action_info() {
'label' => t('Delete revision'), 'label' => t('Delete revision'),
'configurable' => FALSE, 'configurable' => FALSE,
'behavior' => array('deletes_property'), 'behavior' => array('deletes_property'),
'triggers' => array('any'),
), ),
); );
} }

View File

@ -82,6 +82,11 @@ function views_bulk_operations_modify_action($entity, $context) {
// The wrapper will automatically modify $entity itself. // The wrapper will automatically modify $entity itself.
$wrapper = entity_metadata_wrapper($context['entity_type'], $entity); $wrapper = entity_metadata_wrapper($context['entity_type'], $entity);
foreach ($context['selected']['properties'] as $key) { foreach ($context['selected']['properties'] as $key) {
if (!$wrapper->$key->access('update')) {
// No access.
continue;
}
if (in_array($key, $context['append']['properties'])) { if (in_array($key, $context['append']['properties'])) {
$old_values = $wrapper->$key->value(); $old_values = $wrapper->$key->value();
$wrapper->$key->set($context['properties'][$key]); $wrapper->$key->set($context['properties'][$key]);
@ -134,7 +139,7 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
if (!empty($properties)) { if (!empty($properties)) {
$form['properties'] = array( $form['properties'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => 'Properties', '#title' => t('Properties'),
); );
$form['properties']['show_value'] = array( $form['properties']['show_value'] = array(
'#suffix' => '<div class="clearfix"></div>', '#suffix' => '<div class="clearfix"></div>',
@ -298,7 +303,7 @@ function views_bulk_operations_modify_action_form($context, &$form_state) {
$token_type = str_replace('_', '-', $entity_type); $token_type = str_replace('_', '-', $entity_type);
$form['tokens'] = array( $form['tokens'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => 'Available tokens', '#title' => t('Available tokens'),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => TRUE, '#collapsed' => TRUE,
'#weight' => 998, '#weight' => 998,

View File

@ -10,6 +10,7 @@ function views_bulk_operations_user_cancel_action_info() {
'label' => t('Cancel user account'), 'label' => t('Cancel user account'),
'configurable' => TRUE, 'configurable' => TRUE,
'behavior' => array('deletes_property'), 'behavior' => array('deletes_property'),
'triggers' => array('any'),
)); ));
} }

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) { function views_bulk_operations_user_roles_action($user, $context) {
$roles = $user->roles; $wrapper = entity_metadata_wrapper('user', $user);
$selected = (is_array($context['add_roles']) ? $context['add_roles'] : array()) + if (!$wrapper->roles->access("update")) {
(is_array($context['remove_roles']) ? $context['remove_roles'] : array()); // No access.
$result = db_query("SELECT rid, name FROM {role} WHERE rid IN (:selected)", array(':selected' => array_keys($selected))); return;
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;
}
} }
if (!empty($add_roles)) { $roles = $wrapper->roles->value();
$roles += $add_roles; if (is_array($context['add_roles'])) {
$roles = array_merge($roles, $context['add_roles']);
} }
if (!empty($remove_roles)) { if (is_array($context['remove_roles'])) {
$roles = array_diff($roles, $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 package = Administration
core = 7.x core = 7.x
; Information added by Drupal.org packaging script on 2013-12-23 ; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-3.2" version = "7.x-3.3"
core = "7.x" core = "7.x"
project = "views_bulk_operations" project = "views_bulk_operations"
datestamp = "1387798183" datestamp = "1435764542"

View File

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

View File

@ -20,7 +20,7 @@ class ViewsBulkOperationsAction extends ViewsBulkOperationsBaseOperation {
*/ */
public function getAccessMask() { public function getAccessMask() {
// Assume edit by default. // Assume edit by default.
if (!isset($this->operationInfo['behavior'])) { if (empty($this->operationInfo['behavior'])) {
$this->operationInfo['behavior'] = array('changes_property'); $this->operationInfo['behavior'] = array('changes_property');
} }

View File

@ -124,6 +124,8 @@ class ViewsBulkOperationsRulesComponent extends ViewsBulkOperationsBaseOperation
else { else {
$element = rules_action('component_' . $this->operationInfo['parameters']['component_key']); $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,6 +55,7 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
'contains' => array( 'contains' => array(
'display_type' => array('default' => 0), 'display_type' => array('default' => 0),
'enable_select_all_pages' => array('default' => TRUE), 'enable_select_all_pages' => array('default' => TRUE),
'row_clickable' => array('default' => TRUE),
'force_single' => array('default' => FALSE), 'force_single' => array('default' => FALSE),
'entity_load_capacity' => array('default' => 10), 'entity_load_capacity' => array('default' => 10),
'skip_batching' => array('default' => 0), 'skip_batching' => array('default' => 0),
@ -63,11 +64,26 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
$options['vbo_operations'] = array( $options['vbo_operations'] = array(
'default' => array(), 'default' => array(),
'unpack_translatable' => 'unpack_operations', 'unpack_translatable' => 'unpack_operations',
'export' => 'export_vbo_operations',
); );
return $options; 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) { function unpack_operations(&$translatable, $storage, $option, $definition, $parents, $keys) {
$translatable[] = array( $translatable[] = array(
'value' => t('- Choose an operation -'), 'value' => t('- Choose an operation -'),
@ -107,6 +123,12 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
'#default_value' => $this->options['vbo_settings']['enable_select_all_pages'], '#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.'), '#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( $form['vbo_settings']['force_single'] = array(
'#type' => 'checkbox', '#type' => 'checkbox',
'#title' => t('Force single'), '#title' => t('Force single'),

View File

@ -9,9 +9,9 @@ php = 5.2.9
files[] = plugins/operation_types/base.class.php files[] = plugins/operation_types/base.class.php
files[] = views/views_bulk_operations_handler_field_operations.inc files[] = views/views_bulk_operations_handler_field_operations.inc
; Information added by Drupal.org packaging script on 2013-12-23 ; Information added by Drupal.org packaging script on 2015-07-01
version = "7.x-3.2" version = "7.x-3.3"
core = "7.x" core = "7.x"
project = "views_bulk_operations" project = "views_bulk_operations"
datestamp = "1387798183" datestamp = "1435764542"

View File

@ -41,21 +41,20 @@ function views_bulk_operations_load_action_includes() {
// The list of VBO actions is fairly static, so it's hardcoded for better // The list of VBO actions is fairly static, so it's hardcoded for better
// performance (hitting the filesystem with file_scan_directory(), and then // performance (hitting the filesystem with file_scan_directory(), and then
// caching the result has its cost). // caching the result has its cost).
$path = drupal_get_path('module', 'views_bulk_operations') . '/actions/';
$files = array( $files = array(
'archive.action.inc', 'archive.action',
'argument_selector.action.inc', 'argument_selector.action',
'book.action.inc', 'book.action',
'delete.action.inc', 'delete.action',
'modify.action.inc', 'modify.action',
'script.action.inc', 'script.action',
'user_roles.action.inc', 'user_roles.action',
'user_cancel.action.inc', 'user_cancel.action',
); );
if (!$loaded) { if (!$loaded) {
foreach ($files as $file) { foreach ($files as $file) {
include_once $path . $file; module_load_include('inc', 'views_bulk_operations', 'actions/' . $file);
} }
$loaded = TRUE; $loaded = TRUE;
} }
@ -77,7 +76,7 @@ function views_bulk_operations_load_action_includes() {
function views_bulk_operations_cron() { function views_bulk_operations_cron() {
db_delete('queue') 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 - 864000, '<') ->condition('created', REQUEST_TIME - 86400, '<')
->execute(); ->execute();
} }
@ -358,7 +357,7 @@ function views_bulk_operations_form_alter(&$form, &$form_state, $form_id) {
*/ */
function views_bulk_operations_views_post_build(&$view) { function views_bulk_operations_views_post_build(&$view) {
$vbo = _views_bulk_operations_get_field($view); $vbo = _views_bulk_operations_get_field($view);
if ($vbo && $vbo->get_selected_operations() < 1) { if ($vbo && count($vbo->get_selected_operations()) < 1) {
$vbo->options['exclude'] = TRUE; $vbo->options['exclude'] = TRUE;
} }
} }
@ -420,7 +419,7 @@ function theme_views_bulk_operations_select_all($variables) {
if ($enable_select_all_pages) { if ($enable_select_all_pages) {
$form['select_all']['or'] = array( $form['select_all']['or'] = array(
'#type' => 'markup', '#type' => 'markup',
'#markup' => '<em>OR</em>', '#markup' => '<em>' . t('OR') . '</em>',
); );
$form['select_all']['all_pages'] = array( $form['select_all']['all_pages'] = array(
'#type' => 'checkbox', '#type' => 'checkbox',
@ -443,6 +442,13 @@ function theme_views_bulk_operations_select_all($variables) {
*/ */
function views_bulk_operations_form($form, &$form_state, $vbo) { 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'][] = 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['#attached']['css'][] = drupal_get_path('module', 'views_bulk_operations') . '/css/views_bulk_operations.css';
// Wrap the form in a div with specific classes for JS targeting and theming. // Wrap the form in a div with specific classes for JS targeting and theming.
$class = 'vbo-views-form'; $class = 'vbo-views-form';
@ -595,14 +601,23 @@ function views_bulk_operations_confirm_form($form, &$form_state, $view, $output)
$operation = $form_state['operation']; $operation = $form_state['operation'];
$rows = $form_state['selection']; $rows = $form_state['selection'];
$query = drupal_get_query_parameters($_GET, array('q')); $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, $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), array('path' => $view->get_url(), 'query' => $query),
theme('views_bulk_operations_confirmation', array('rows' => $rows, 'vbo' => $vbo, 'operation' => $operation, '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(). // Add VBO's submit handler to the Confirm button added by config_form().
$form['actions']['submit']['#submit'] = array('views_bulk_operations_form_submit'); $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; return $form;
} }
@ -618,7 +633,7 @@ function theme_views_bulk_operations_confirmation($variables) {
// Load the entities from the current page, and show their titles. // Load the entities from the current page, and show their titles.
$entities = _views_bulk_operations_entity_load($entity_type, array_values($rows), $vbo->revision); $entities = _views_bulk_operations_entity_load($entity_type, array_values($rows), $vbo->revision);
foreach ($entities as $entity) { 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. // All rows on all pages have been selected, so show a count of additional items.
if ($select_all_pages) { if ($select_all_pages) {
@ -631,6 +646,29 @@ function theme_views_bulk_operations_confirmation($variables) {
return $output; 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 * Goes through the submitted values, and returns
* an array of selected rows, in the form of * an array of selected rows, in the form of
@ -871,7 +909,7 @@ function views_bulk_operations_adjust_selection($queue_name, $operation, $option
'views_row' => array(), 'views_row' => array(),
'position' => array( 'position' => array(
'current' => ++$context['sandbox']['progress'], 'current' => ++$context['sandbox']['progress'],
'total' => $view->total_rows, 'total' => $context['sandbox']['max'],
), ),
); );
// Some operations require full selected rows. // Some operations require full selected rows.
@ -1042,7 +1080,7 @@ function views_bulk_operations_queue_item_process($queue_item_data, &$log = NULL
$arguments = array( $arguments = array(
'%operation' => $operation->label(), '%operation' => $operation->label(),
'@type' => $entity_type, '@type' => $entity_type,
'%title' => _views_bulk_operations_entity_label($entity_type, $entity), '%title' => entity_label($entity_type, $entity),
); );
if ($log) { if ($log) {
@ -1128,7 +1166,7 @@ function views_bulk_operations_direct_process($operation, $rows, $options) {
$context['results']['log'][] = t('Skipped %operation on @type %title due to insufficient permissions.', array( $context['results']['log'][] = t('Skipped %operation on @type %title due to insufficient permissions.', array(
'%operation' => $operation->label(), '%operation' => $operation->label(),
'@type' => $entity_type, '@type' => $entity_type,
'%title' => _views_bulk_operations_entity_label($entity_type, $entity), '%title' => entity_label($entity_type, $entity),
)); ));
unset($entities[$id]); unset($entities[$id]);
} }
@ -1236,29 +1274,6 @@ function _views_bulk_operations_entity_load($entity_type, $ids, $revision = FALS
return $entities; return $entities;
} }
/**
* Label function for entities.
* Core entities don't declare the "label" key, so entity_label() fails,
* and a fallback is needed. This function provides that fallback.
*/
function _views_bulk_operations_entity_label($entity_type, $entity) {
$label = entity_label($entity_type, $entity);
if (!$label) {
$entity_info = entity_get_info($entity_type);
$id_key = $entity_info['entity keys']['id'];
// 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. * Helper function to report an error.
*/ */