Browse Source

ran security updates on contrib modules
ctools, video_embed_field, migrate, views_bulk_operations

Bachir Soussi Chiadmi 8 years ago
parent
commit
f7cd9c0858
94 changed files with 1240 additions and 411 deletions
  1. 3 3
      sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.info
  2. 3 3
      sites/all/modules/contrib/dev/ctools/ctools.info
  3. 69 1
      sites/all/modules/contrib/dev/ctools/ctools.module
  4. 3 3
      sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.info
  5. 3 3
      sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.info
  6. 1 1
      sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.module
  7. 3 3
      sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.info
  8. 3 3
      sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.info
  9. 10 1
      sites/all/modules/contrib/dev/ctools/includes/content.inc
  10. 12 0
      sites/all/modules/contrib/dev/ctools/includes/context-admin.inc
  11. 2 2
      sites/all/modules/contrib/dev/ctools/includes/context.inc
  12. 6 4
      sites/all/modules/contrib/dev/ctools/includes/css.inc
  13. 1 1
      sites/all/modules/contrib/dev/ctools/includes/jump-menu.inc
  14. 1 1
      sites/all/modules/contrib/dev/ctools/includes/math-expr.inc
  15. 1 0
      sites/all/modules/contrib/dev/ctools/includes/modal.inc
  16. 5 3
      sites/all/modules/contrib/dev/ctools/includes/plugins.inc
  17. 1 1
      sites/all/modules/contrib/dev/ctools/includes/stylizer.inc
  18. 2 1
      sites/all/modules/contrib/dev/ctools/includes/uuid.inc
  19. 139 12
      sites/all/modules/contrib/dev/ctools/js/modal.js
  20. 43 0
      sites/all/modules/contrib/dev/ctools/js/states-show.js
  21. 3 3
      sites/all/modules/contrib/dev/ctools/page_manager/page_manager.info
  22. 11 2
      sites/all/modules/contrib/dev/ctools/page_manager/page_manager.module
  23. 1 1
      sites/all/modules/contrib/dev/ctools/page_manager/plugins/tasks/node_edit.inc
  24. 3 4
      sites/all/modules/contrib/dev/ctools/page_manager/plugins/tasks/node_view.inc
  25. 2 2
      sites/all/modules/contrib/dev/ctools/page_manager/plugins/tasks/page.admin.inc
  26. 74 0
      sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_created.inc
  27. 10 0
      sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field.inc
  28. 47 5
      sites/all/modules/contrib/dev/ctools/plugins/content_types/form/entity_form_field.inc
  29. 0 17
      sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_form.inc
  30. 3 0
      sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_title.inc
  31. 3 2
      sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_description.inc
  32. 3 1
      sites/all/modules/contrib/dev/ctools/plugins/contexts/user.inc
  33. 4 4
      sites/all/modules/contrib/dev/ctools/plugins/contexts/user_edit_form.inc
  34. 7 1
      sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.class.php
  35. 1 1
      sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_field.inc
  36. 3 3
      sites/all/modules/contrib/dev/ctools/stylizer/stylizer.info
  37. 3 3
      sites/all/modules/contrib/dev/ctools/term_depth/term_depth.info
  38. 3 3
      sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.info
  39. 3 3
      sites/all/modules/contrib/dev/ctools/tests/ctools_plugin_test.info
  40. 1 1
      sites/all/modules/contrib/dev/ctools/tests/plugins/cached/ctoolsCachedPluginArray.class.php
  41. 1 1
      sites/all/modules/contrib/dev/ctools/tests/plugins/cached/ctoolsCachedPluginArray2.class.php
  42. 1 1
      sites/all/modules/contrib/dev/ctools/tests/plugins/not_cached/ctoolsNotCachedPluginArray.class.php
  43. 3 3
      sites/all/modules/contrib/dev/ctools/views_content/views_content.info
  44. 1 1
      sites/all/modules/contrib/fields/video_embed_field/plugins/export_ui/video_embed_field_export_ui.inc
  45. 3 3
      sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.info
  46. 11 2
      sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.module
  47. 3 3
      sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.info
  48. 15 6
      sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.module
  49. 12 0
      sites/all/modules/contrib/fields/video_embed_field/video_embed_field.admin.inc
  50. 1 1
      sites/all/modules/contrib/fields/video_embed_field/video_embed_field.field.inc
  51. 54 17
      sites/all/modules/contrib/fields/video_embed_field/video_embed_field.handlers.inc
  52. 4 4
      sites/all/modules/contrib/fields/video_embed_field/video_embed_field.info
  53. 26 0
      sites/all/modules/contrib/fields/video_embed_field/video_embed_field.install
  54. 21 9
      sites/all/modules/contrib/fields/video_embed_field/video_embed_field.module
  55. 0 5
      sites/all/modules/contrib/migrate/migrate/BACKPORT.txt
  56. 25 4
      sites/all/modules/contrib/migrate/migrate/CHANGELOG.txt
  57. 43 10
      sites/all/modules/contrib/migrate/migrate/includes/base.inc
  58. 33 2
      sites/all/modules/contrib/migrate/migrate/includes/migration.inc
  59. 3 1
      sites/all/modules/contrib/migrate/migrate/includes/source.inc
  60. 43 36
      sites/all/modules/contrib/migrate/migrate/migrate.drush.inc
  61. 3 3
      sites/all/modules/contrib/migrate/migrate/migrate.info
  62. 10 3
      sites/all/modules/contrib/migrate/migrate/migrate.module
  63. 2 4
      sites/all/modules/contrib/migrate/migrate/migrate_example/beer.inc
  64. 3 3
      sites/all/modules/contrib/migrate/migrate/migrate_example/migrate_example.info
  65. 3 3
      sites/all/modules/contrib/migrate/migrate/migrate_example/migrate_example_oracle/migrate_example_oracle.info
  66. 11 13
      sites/all/modules/contrib/migrate/migrate/migrate_example/wine.inc
  67. 3 3
      sites/all/modules/contrib/migrate/migrate/migrate_example_baseball/migrate_example_baseball.info
  68. 3 3
      sites/all/modules/contrib/migrate/migrate/migrate_ui/migrate_ui.info
  69. 154 54
      sites/all/modules/contrib/migrate/migrate/migrate_ui/migrate_ui.pages.inc
  70. 1 1
      sites/all/modules/contrib/migrate/migrate/plugins/destinations/comment.inc
  71. 30 0
      sites/all/modules/contrib/migrate/migrate/plugins/destinations/entity.inc
  72. 12 5
      sites/all/modules/contrib/migrate/migrate/plugins/destinations/fields.inc
  73. 7 6
      sites/all/modules/contrib/migrate/migrate/plugins/destinations/file.inc
  74. 1 1
      sites/all/modules/contrib/migrate/migrate/plugins/destinations/node.inc
  75. 1 1
      sites/all/modules/contrib/migrate/migrate/plugins/destinations/term.inc
  76. 1 1
      sites/all/modules/contrib/migrate/migrate/plugins/destinations/user.inc
  77. 1 1
      sites/all/modules/contrib/migrate/migrate/plugins/sources/list.inc
  78. 1 1
      sites/all/modules/contrib/migrate/migrate/plugins/sources/multiitems.inc
  79. 21 6
      sites/all/modules/contrib/migrate/migrate/plugins/sources/sql.inc
  80. 44 1
      sites/all/modules/contrib/migrate/migrate/plugins/sources/sqlmap.inc
  81. 0 4
      sites/all/modules/contrib/migrate/migrate/tests/import/options.test
  82. 1 0
      sites/all/modules/contrib/views/views_bulk_operations/actions/archive.action.inc
  83. 2 0
      sites/all/modules/contrib/views/views_bulk_operations/actions/book.action.inc
  84. 1 0
      sites/all/modules/contrib/views/views_bulk_operations/actions/delete.action.inc
  85. 7 2
      sites/all/modules/contrib/views/views_bulk_operations/actions/modify.action.inc
  86. 1 0
      sites/all/modules/contrib/views/views_bulk_operations/actions/user_cancel.action.inc
  87. 12 17
      sites/all/modules/contrib/views/views_bulk_operations/actions/user_roles.action.inc
  88. 3 3
      sites/all/modules/contrib/views/views_bulk_operations/actions_permissions.info
  89. 17 15
      sites/all/modules/contrib/views/views_bulk_operations/js/views_bulk_operations.js
  90. 1 1
      sites/all/modules/contrib/views/views_bulk_operations/plugins/operation_types/action.class.php
  91. 3 1
      sites/all/modules/contrib/views/views_bulk_operations/plugins/operation_types/rules_component.class.php
  92. 22 0
      sites/all/modules/contrib/views/views_bulk_operations/views/views_bulk_operations_handler_field_operations.inc
  93. 3 3
      sites/all/modules/contrib/views/views_bulk_operations/views_bulk_operations.info
  94. 56 41
      sites/all/modules/contrib/views/views_bulk_operations/views_bulk_operations.module

+ 3 - 3
sites/all/modules/contrib/dev/ctools/bulk_export/bulk_export.info

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

+ 3 - 3
sites/all/modules/contrib/dev/ctools/ctools.info

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

+ 69 - 1
sites/all/modules/contrib/dev/ctools/ctools.module

@@ -23,7 +23,7 @@ define('CTOOLS_API_VERSION', '2.0.8');
  *   ; Requires CTools v7.x-1.4 or newer.
  *   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.
@@ -617,6 +617,27 @@ function 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.
 
@@ -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');
+}

+ 3 - 3
sites/all/modules/contrib/dev/ctools/ctools_access_ruleset/ctools_access_ruleset.info

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

+ 3 - 3
sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.info

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

+ 1 - 1
sites/all/modules/contrib/dev/ctools/ctools_ajax_sample/ctools_ajax_sample.module

@@ -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
  * the pane.

+ 3 - 3
sites/all/modules/contrib/dev/ctools/ctools_custom_content/ctools_custom_content.info

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

+ 3 - 3
sites/all/modules/contrib/dev/ctools/ctools_plugin_example/ctools_plugin_example.info

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

+ 10 - 1
sites/all/modules/contrib/dev/ctools/includes/content.inc

@@ -341,7 +341,16 @@ function ctools_content_editable($type, $subtype, $conf) {
     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);
   }
 

+ 12 - 0
sites/all/modules/contrib/dev/ctools/includes/context-admin.inc

@@ -736,6 +736,15 @@ function ctools_edit_context_form_defaults($form, &$form_state) {
     '#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';
 
   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']['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']['keyword'] = $form_state['values']['keyword'];

+ 2 - 2
sites/all/modules/contrib/dev/ctools/includes/context.inc

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

+ 6 - 4
sites/all/modules/contrib/dev/ctools/includes/css.inc

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

+ 1 - 1
sites/all/modules/contrib/dev/ctools/includes/jump-menu.inc

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

+ 1 - 1
sites/all/modules/contrib/dev/ctools/includes/math-expr.inc

@@ -271,7 +271,7 @@ class ctools_math_expr {
             } elseif (in_array($op, $ops) and !$expecting_op) {
                 return $this->trigger("unexpected operator '$op'");
             } 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 (in_array($op, $ops)) { // did we end with an operator? bad.

+ 1 - 0
sites/all/modules/contrib/dev/ctools/includes/modal.inc

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

+ 5 - 3
sites/all/modules/contrib/dev/ctools/includes/plugins.inc

@@ -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.
-      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'])) {
           $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.
-    drupal_alter($hook, $cache[$owner][$api]);
+    drupal_alter($hook, $cache[$owner][$api], $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) {
   // 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;
   if (!isset($drupal_static_fast)) {
     $drupal_static_fast['plugins'] = &drupal_static('ctools_plugins', array());

+ 1 - 1
sites/all/modules/contrib/dev/ctools/includes/stylizer.inc

@@ -500,7 +500,7 @@ class ctools_stylizer_image_processor {
     $palette[$luminosity_input]['green'] = $green;
     $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.
 
     // From input to black

+ 2 - 1
sites/all/modules/contrib/dev/ctools/includes/uuid.inc

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

+ 139 - 12
sites/all/modules/contrib/dev/ctools/js/modal.js

@@ -48,7 +48,8 @@
       modalOptions: {
         opacity: .55,
         background: '#fff'
-      }
+      },
+      modalClass: 'default'
     };
 
     var settings = {};
@@ -97,8 +98,8 @@
     resize();
 
     $('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);
-    $('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme));
+    Drupal.CTools.Modal.modalContent(Drupal.CTools.Modal.modal, settings.modalOptions, settings.animation, settings.animationSpeed, settings.modalClass);
+    $('#modalContent .modal-content').html(Drupal.theme(settings.throbberTheme)).addClass('ctools-modal-loading');
 
     // Position autocomplete results based on the scroll position of the modal.
     $('#modalContent .modal-content').delegate('input.form-autocomplete', 'keyup', function() {
@@ -299,6 +300,17 @@
     // Attach behaviors within a modal dialog.
     var settings = response.settings || ajax.settings || Drupal.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 animation (fadeIn, slideDown, show)
    * @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 (!animation) {
       animation = 'show';
@@ -402,9 +415,56 @@
     if( docHeight < winHeight ) docHeight = winHeight;
 
     // 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>');
+
+    // 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;
+            }
+          }
 
-    // Keyboard and focus event handler ensures focus stays on modal elements only
+          return keep;
+        });
+      }
+
+      return tabbableElements.get();
+    };
+
+    // Keyboard and focus event handler ensures only modal elements gain focus.
     modalEventHandler = function( event ) {
       target = null;
       if ( event ) { //Mozilla
@@ -428,7 +488,7 @@
         return true;
       }
       else {
-        $('#modalContent').focus();
+        getTabbableElements()[0].focus();
       }
 
       event.preventDefault();
@@ -436,6 +496,59 @@
     $('body').bind( 'focus', 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
     var modalContent = $('#modalContent').css('top','-1000px');
     var mdcTop = wt + ( winHeight / 2 ) - (  modalContent.outerHeight() / 2);
@@ -457,12 +570,19 @@
 
     $(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
     function close() {
       // Unbind the events
       $(window).unbind('resize',  modalContentResize);
       $('body').unbind( 'focus', modalEventHandler);
       $('body').unbind( 'keypress', modalEventHandler );
+      $('body').unbind( 'keydown', modalTabTrapHandler );
       $('.close').unbind('click', modalContentClose);
       $('body').unbind('keypress', modalEventEscapeCloseHandler);
       $(document).trigger('CToolsDetachBehaviors', $('#modalContent'));
@@ -478,12 +598,19 @@
       // Remove the content
       $('#modalContent').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
-     modalContentResize = function(){
+    // Move and resize the modalBackdrop and modalContent on window resize.
+    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
       var wt = self.pageYOffset;
       } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
@@ -509,8 +636,6 @@
       modalContent.css('top', mdcTop + 'px').css('left', mdcLeft + 'px').show();
     };
     $(window).bind('resize', modalContentResize);
-
-    $('#modalContent').focus();
   };
 
   /**
@@ -533,7 +658,9 @@
     $(window).unbind('resize', modalContentResize);
     $('body').unbind('focus', modalEventHandler);
     $('body').unbind('keypress', modalEventHandler);
+    $('body').unbind( 'keydown', modalTabTrapHandler );
     $('.close').unbind('click', modalContentClose);
+    $('body').unbind('keypress', modalEventEscapeCloseHandler);
     $(document).trigger('CToolsDetachBehaviors', $('#modalContent'));
 
     // jQuery magic loop through the instances and run the animations or removal.

+ 43 - 0
sites/all/modules/contrib/dev/ctools/js/states-show.js

@@ -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);

+ 3 - 3
sites/all/modules/contrib/dev/ctools/page_manager/page_manager.info

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

+ 11 - 2
sites/all/modules/contrib/dev/ctools/page_manager/page_manager.module

@@ -440,13 +440,18 @@ function page_manager_cache_load($task_name) {
  */
 function page_manager_handler_get_name($task_name, $handlers, $handler) {
   $base = str_replace('-', '_', $task_name);
+  $name = '';
+
   // Optional machine name.
   if (!empty($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.
-  else {
+  // If no machine name was provided or the name is in use, generate a unique name.
+  if (empty($name)) {
     $base .= '__' . $handler->handler;
 
     // 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) {
     $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);

+ 1 - 1
sites/all/modules/contrib/dev/ctools/page_manager/plugins/tasks/node_edit.inc

@@ -108,7 +108,7 @@ function page_manager_node_edit($node) {
  * Callback to handle the process of adding a node.
  *
  * 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
  * already checked by the menu system.

+ 3 - 4
sites/all/modules/contrib/dev/ctools/page_manager/plugins/tasks/node_view.inc

@@ -78,10 +78,6 @@ function page_manager_node_view_menu_alter(&$items, $task) {
  * node view, which is node_page_view().
  */
 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
   $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().
   return $default_output;
 }

+ 2 - 2
sites/all/modules/contrib/dev/ctools/page_manager/plugins/tasks/page.admin.inc

@@ -61,7 +61,7 @@ function page_manager_page_menu(&$items, $task) {
     }
 
     $path             = array();
-    $page_arguments   = array($subtask_id);
+    $page_arguments   = array((string) $subtask_id);
     $access_arguments = array($subtask->access);
     $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) {
     $alias = db_query('SELECT alias, source FROM {url_alias} WHERE alias = :path', array(':path' => $path))->fetchObject();
     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 {

+ 74 - 0
sites/all/modules/contrib/dev/ctools/plugins/content_types/comment/comment_created.inc

@@ -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));
+}

+ 10 - 0
sites/all/modules/contrib/dev/ctools/plugins/content_types/entity_context/entity_field.inc

@@ -34,6 +34,14 @@ function ctools_entity_field_content_type_content_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.
   $context_types = array();
   $entities = entity_get_info();
@@ -82,6 +90,8 @@ function ctools_entity_field_content_type_content_types() {
     unset($context_types[$key]['types']);
   }
 
+  cache_set($cache_key, $types);
+
   return $types;
 }
 

+ 47 - 5
sites/all/modules/contrib/dev/ctools/plugins/content_types/form/entity_form_field.inc

@@ -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.
   foreach ($types as $key => $field_content_type) {
     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);
   $field = field_info_instance($entity_type, $field_name, $ids[2]);
 
-  // Do not render if the entity type does not have this field.
-  if (empty($field)) {
+  // Check for field groups.
+  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;
   }
-  $block = new stdClass();
 
+  $block = new stdClass();
   if (isset($context->form)) {
     $block->content = array();
-    $block->content[$field_name] = $context->form[$field_name];
-    unset($context->form[$field_name]);
+    if (!empty($field)) {
+      $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 {
     $block->content = t('Entity info.');

+ 0 - 17
sites/all/modules/contrib/dev/ctools/plugins/content_types/node_context/node_comment_form.inc

@@ -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'];
-}

+ 3 - 0
sites/all/modules/contrib/dev/ctools/plugins/content_types/page/page_title.inc

@@ -29,6 +29,9 @@ $plugin = array(
  * Outputs the page title of the current page.
  */
 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.
   if (empty($conf['markup'])) {
     $conf['markup'] = 'h1';

+ 3 - 2
sites/all/modules/contrib/dev/ctools/plugins/content_types/term_context/term_description.inc

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

+ 3 - 1
sites/all/modules/contrib/dev/ctools/plugins/contexts/user.inc

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

+ 4 - 4
sites/all/modules/contrib/dev/ctools/plugins/contexts/user_edit_form.inc

@@ -34,15 +34,15 @@ function ctools_context_create_user_edit_form($empty, $user = NULL, $conf = FALS
   $category = !empty($conf['category']) ? $conf['category'] : FALSE;
   unset($conf['category']);
 
+  // If no category was specified, use the default 'account'.
+  if (!$category) {
+    $category = 'account';
+  }
   // Return previously created contexts, per category.
   static $created = array();
   if (!empty($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'));
   // Store this context for later.

+ 7 - 1
sites/all/modules/contrib/dev/ctools/plugins/export_ui/ctools_export_ui.class.php

@@ -724,7 +724,13 @@ class ctools_export_ui {
       // Export the handler, which is a fantastic way to clean database IDs out of it.
       $export = ctools_export_crud_export($this->plugin['schema'], $original);
       $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.

+ 1 - 1
sites/all/modules/contrib/dev/ctools/plugins/relationships/entity_from_field.inc

@@ -185,7 +185,7 @@ function ctools_entity_from_field_context($context, $conf) {
         $loaded_to_entity = array_shift($loaded_to_entity);
 
         // 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);
         }
         else {

+ 3 - 3
sites/all/modules/contrib/dev/ctools/stylizer/stylizer.info

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

+ 3 - 3
sites/all/modules/contrib/dev/ctools/term_depth/term_depth.info

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

+ 3 - 3
sites/all/modules/contrib/dev/ctools/tests/ctools_export_test/ctools_export_test.info

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

+ 3 - 3
sites/all/modules/contrib/dev/ctools/tests/ctools_plugin_test.info

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

+ 1 - 1
sites/all/modules/contrib/dev/ctools/tests/plugins/cached/ctoolsCachedPluginArray.class.php

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

+ 1 - 1
sites/all/modules/contrib/dev/ctools/tests/plugins/cached/ctoolsCachedPluginArray2.class.php

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

+ 1 - 1
sites/all/modules/contrib/dev/ctools/tests/plugins/not_cached/ctoolsNotCachedPluginArray.class.php

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

+ 3 - 3
sites/all/modules/contrib/dev/ctools/views_content/views_content.info

@@ -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_style_ctools_context.inc
 
-; Information added by Drupal.org packaging script on 2015-03-18
-version = "7.x-1.7"
+; Information added by Drupal.org packaging script on 2015-08-19
+version = "7.x-1.9"
 core = "7.x"
 project = "ctools"
-datestamp = "1426696183"
+datestamp = "1440020680"
 

+ 1 - 1
sites/all/modules/contrib/fields/video_embed_field/plugins/export_ui/video_embed_field_export_ui.inc

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

+ 3 - 3
sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.info

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

+ 11 - 2
sites/all/modules/contrib/fields/video_embed_field/video_embed_brightcove/video_embed_brightcove.module

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

+ 3 - 3
sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.info

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

+ 15 - 6
sites/all/modules/contrib/fields/video_embed_field/video_embed_facebook/video_embed_facebook.module

@@ -25,6 +25,7 @@ function video_embed_facebook_video_embed_handler_info() {
     'defaults' => array(
       'width' => 640,
       'height' => 360,
+      'class' => '',
     ),
   );
 
@@ -57,6 +58,13 @@ function video_embed_facebook_form($defaults) {
     '#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;
 }
 
@@ -90,12 +98,13 @@ function video_embed_facebook_handle_video($url, $settings) {
 
   if ($id) {
     // 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.
     $embed = format_string($embed, array(
       '!id' => $id,
-      '!width' => $settings['width'],
-      '!height' => $settings['height'],
+      '@width' => $settings['width'],
+      '@height' => $settings['height'],
+      '@class' => $settings['class'],
     ));
 
     $video = array(
@@ -137,10 +146,10 @@ function video_embed_facebook_handle_thumbnail($url) {
 function _video_embed_facebook_get_video_id($url) {
   // Parse_url is an easy way to break a url into its components.
   $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 ($matches && !empty($matches[2])) {
-    return $matches[2];
+  if ($matches && !empty($matches[1])) {
+    return check_plain($matches[1]);
   }
   // Otherwise return false.
   return FALSE;

+ 12 - 0
sites/all/modules/contrib/fields/video_embed_field/video_embed_field.admin.inc

@@ -32,3 +32,15 @@ function video_embed_field_video_style_form(&$form, &$form_state) {
 
   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);
+}

+ 1 - 1
sites/all/modules/contrib/fields/video_embed_field/video_embed_field.field.inc

@@ -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']) {
       $description = array(
         '#prefix' => '<div class="video-embed-description">',
-        '#markup' => $item['description'],
+        '#markup' => check_plain($item['description']),
         '#suffix' => '</div>',
       );
       $alt = $item['description'];

+ 54 - 17
sites/all/modules/contrib/fields/video_embed_field/video_embed_field.handlers.inc

@@ -38,6 +38,7 @@ function video_embed_field_video_embed_handler_info() {
       'modestbranding' => 0,
       'theme' => 'dark',
       'iv_load_policy' => 1,
+      'class' => '',
     ),
   );
 
@@ -46,6 +47,7 @@ function video_embed_field_video_embed_handler_info() {
     'function' => 'video_embed_field_handle_vimeo',
     'thumbnail_function' => 'video_embed_field_handle_vimeo_thumbnail',
     '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_validate' => 'video_embed_field_handler_vimeo_form_validate',
     'domains' => array(
@@ -61,6 +63,7 @@ function video_embed_field_video_embed_handler_info() {
       'autoplay' => 0,
       'loop' => 0,
       'froogaloop' => 0,
+      'class' => ''
     ),
   );
 
@@ -146,7 +149,7 @@ function _video_embed_field_get_youtube_id($url) {
       $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) {
   $output = array();
 
-  // Grab the minutes and seconds, and just convert it down to seconds.
-  preg_match('/#t=((?P<min>\d+)m)?((?P<sec>\d+)s)?/', $url, $matches);
-
-  // Give it some default data in case there is no #t=...
-  $matches += array(
-    "min" => 0,
-    "sec" => 0,
-  );
-  $time = ($matches["min"] * 60) + $matches["sec"];
-  $settings['start'] = $time;
+  if(preg_match('/#t=((?P<min>\d+)m)?((?P<sec>\d+)s)?((?P<tinsec>\d+))?/', $url, $matches)){
+    if(isset($matches['tinsec'])){
+      $settings['start'] = $matches['tinsec']; // url already in form #t=125 for 2 minutes and 5 seconds
+    } else {
+      // url in form #t=2m5s or with other useless data, this is why we still keep adding the default data..
+      // give it some default data in case there is no #t=...
+      $matches += array(
+        "min" => 0,
+        "sec" => 0,
+      );
+      if ($time = ($matches["min"] * 60) + $matches["sec"]) {
+        $settings['start'] = $time;
+      }
+    }
+  }
 
   $id = _video_embed_field_get_youtube_id($url);
   if (!$id) {
@@ -180,11 +188,16 @@ function video_embed_field_handle_youtube($url, $settings) {
     $output['#markup'] = l($url, $url);
     return $output;
   }
+
+  // Add class to variable to avoid adding it to URL param string.
+  $class = $settings['class'];
+  unset($settings['class']);
+
   // Construct the embed code.
   $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;
 }
@@ -243,11 +256,17 @@ function video_embed_field_handle_youtube_data($url) {
   $id = _video_embed_field_get_youtube_id($url);
 
   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)) {
       $data = json_decode($response->data);
-      $data = isset($data->entry) ? (array) $data->entry : (array) $data->feed;
-      return _video_embed_field_clean_up_youtube_data($data);
+      return _video_embed_field_clean_up_youtube_data($data->items);
     }
   }
 
@@ -401,6 +420,13 @@ function video_embed_field_handler_youtube_form($defaults) {
     '#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;
 }
 
@@ -489,10 +515,14 @@ function video_embed_field_handle_vimeo($url, $settings) {
   }
   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);
 
   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>',
   );
 }
@@ -616,6 +646,13 @@ function video_embed_field_handler_vimeo_form($defaults) {
     '#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;
 }
 

+ 4 - 4
sites/all/modules/contrib/fields/video_embed_field/video_embed_field.info

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

+ 26 - 0
sites/all/modules/contrib/fields/video_embed_field/video_embed_field.install

@@ -96,6 +96,13 @@ function video_embed_field_schema() {
   return $schema;
 }
 
+/**
+ * Implements hook_uninstall().
+ */
+function video_embed_field_uninstall() {
+  variable_del('video_embed_field_youtube_api_key');
+}
+
 /**
  * Adds an optional description form.
  */
@@ -363,3 +370,22 @@ function video_embed_field_update_7009() {
 
   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';
+}

+ 21 - 9
sites/all/modules/contrib/fields/video_embed_field/video_embed_field.module

@@ -93,6 +93,26 @@ function video_embed_field_menu() {
     '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;
 }
 
@@ -577,15 +597,7 @@ function _video_embed_field_get_provider_domains() {
  *   An array containing the allowed video domains.
  */
 function _video_embed_field_get_instance_provider_domains($instance) {
-  $domains = _video_embed_field_get_provider_domains();
-
-  foreach ($domains as $domain => $provider) {
-    if (empty($instance['settings']['allowed_providers'][$provider])) {
-      unset($domains[$domain]);
-    }
-  }
-
-  return $domains;
+  return array_intersect(_video_embed_field_get_provider_domains(), $instance['settings']['allowed_providers']);
 }
 
 /**

+ 0 - 5
sites/all/modules/contrib/migrate/migrate/BACKPORT.txt

@@ -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.

+ 25 - 4
sites/all/modules/contrib/migrate/migrate/CHANGELOG.txt

@@ -1,11 +1,31 @@
-Migrate 2.7
+Migrate 2.8
 ===========
 
+Features and enhancements
+- #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.
+
 Bug fixes
-- #2415597 - Make batching of SQL sources optional, and force map_joinable FALSE.
+  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 Release Candidate 1
-===============================
+Migrate 2.7
+===========
 
 Features and enhancements
 - #2296911 - Add a source handler for IBM DB2.
@@ -14,6 +34,7 @@ Features and enhancements
 - #1751438 - Add spreadsheet source plugin.
 
 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.
 - #2298969 - Verify wizard validation function exists.
 - #2268863 - Fix drush --all option.

+ 43 - 10
sites/all/modules/contrib/migrate/migrate/includes/base.inc

@@ -301,6 +301,11 @@ abstract class MigrationBase {
     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?
    *
@@ -432,16 +437,11 @@ abstract class MigrationBase {
     // Record the time limit
     $this->timeLimit = ini_get('max_execution_time');
 
-    // Prevent any emails from being sent out on migration
-    global $conf;
-    if (!empty($conf['mail_system'])) {
-      foreach ($conf['mail_system'] as $system => $class) {
-        $conf['mail_system'][$system] = 'MigrateMailIgnore';
-      }
-    }
-    else {
-      $conf['mail_system']['default-system'] = 'MigrateMailIgnore';
-    }
+    // Save the current mail system, prior to disabling emails.
+    $this->saveMailSystem();
+
+    // Prevent emails from being sent out during migrations.
+    $this->disableMailSystem();
 
     // Make sure we clear our semaphores in case of abrupt exit
     drupal_register_shutdown_function(array($this, 'endProcess'));
@@ -1355,6 +1355,39 @@ abstract class MigrationBase {
     }
     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

+ 33 - 2
sites/all/modules/contrib/migrate/migrate/includes/migration.inc

@@ -141,16 +141,26 @@ abstract class Migration extends MigrationBase {
       // destination field, keep only the last (so the UI can override a source
       // field DNM that was defined in code).
       $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) {
+        $source_field = $mapping->getSourceField();
         // If the source field is not mapped to a destination field, the
         // array index is integer.
         if (is_int($destination_field)) {
-          $source_field = $mapping->getSourceField();
           if (isset($no_destination[$source_field])) {
             unset($this->allFieldMappings[$no_destination[$source_field]]);
             unset($no_destination[$source_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
         // subfield?
         $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];
-        if (isset($destination[1])) {
+	      if ($destination_count == 2) {
           $subfield = $destination[1];
           // We're processing the subfield before the primary value, initialize it
           if (!property_exists($this->destinationValues, $destination_field)) {
@@ -1282,6 +1295,24 @@ abstract class Migration extends MigrationBase {
           // Add the subfield value to the arguments array.
           $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
         // set it.
         elseif (!property_exists($this->destinationValues, $destination_field)) {

+ 3 - 1
sites/all/modules/contrib/migrate/migrate/includes/source.inc

@@ -303,9 +303,11 @@ abstract class MigrateSource implements Iterator {
       //    highwaters and map rows).
       $prepared = FALSE;
       if (!empty($this->idList)) {
+        // Check first source key.
         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);
-          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.
             continue;
           }

+ 43 - 36
sites/all/modules/contrib/migrate/migrate/migrate.drush.inc

@@ -18,6 +18,7 @@ function migrate_drush_command() {
     'force' => 'Force an operation to run, even if all dependencies are not satisfied',
     'group' => 'Name of the migration group to run',
     'notify' => 'Send email notification upon completion of operation',
+    'wildcard' => 'Process migrations that match a certain pattern. For example, Content*.',
   );
   $items['migrate-status'] = array(
     'description' => 'List all migrations with current status.',
@@ -161,6 +162,7 @@ function migrate_drush_command() {
       'migrate-import Article' => 'Import new articles',
       '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=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' =>
         'Import for up to 60 seconds after stopping and rolling back the Article migration.',
       'migrate-import Article --limit="100 items"' =>
@@ -824,15 +826,6 @@ function drush_migrate_rollback($args = NULL) {
       // Capture non-informational output for mailing
       ob_start();
       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);
@@ -928,12 +921,6 @@ function drush_migrate_rollback($args = NULL) {
 
   // Notify user
   if (drush_get_option('notify')) {
-    if (is_null($mail_system)) {
-      unset($conf['mail_system']);
-    }
-    else {
-      $conf['mail_system'] = $mail_system;
-    }
     _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 {
     $named_migrations = array();
     foreach (explode(',', $args) as $name) {
@@ -1057,18 +1052,23 @@ function drush_migrate_rollback_validate($args = NULL) {
 
 function drush_migrate_validate_common($args) {
   if (drush_get_option('all')) {
-    if (!empty($args) || drush_get_option('group')) {
-      return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --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, or --wildcard'));
     }
   }
   elseif (drush_get_option('group')) {
-    if (!empty($args) || drush_get_option('all')) {
-      return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
+    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, 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 {
     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);
 
@@ -1107,7 +1107,29 @@ function drush_migrate_validate_common($args) {
  */
 function drush_migrate_pre_migrate_import($args = NULL) {
   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')) {
     drush_unset_option('rollback');
@@ -1127,15 +1149,6 @@ function drush_migrate_import($args = NULL) {
       // Capture non-informational output for mailing
       ob_start();
       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);
     $options = array();
@@ -1294,12 +1307,6 @@ function drush_migrate_import($args = NULL) {
 
   // Notify user
   if (drush_get_option('notify')) {
-    if (is_null($mail_system)) {
-      unset($conf['mail_system']);
-    }
-    else {
-      $conf['mail_system'] = $mail_system;
-    }
     _drush_migrate_notify();
   }
 }

+ 3 - 3
sites/all/modules/contrib/migrate/migrate/migrate.info

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

+ 10 - 3
sites/all/modules/contrib/migrate/migrate/migrate.module

@@ -93,11 +93,18 @@ function migrate_migrations($reset = NULL) {
     $final_migrations[$name] = array();
   }
 
-  // Fill in the grouped list
+  // Fill in the grouped list.
   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();
   foreach ($final_migrations as $group_name => $group_migrations) {
     foreach ($group_migrations as $machine_name => $migration) {

+ 2 - 4
sites/all/modules/contrib/migrate/migrate/migrate_example/beer.inc

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

+ 3 - 3
sites/all/modules/contrib/migrate/migrate/migrate_example/migrate_example.info

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

+ 3 - 3
sites/all/modules/contrib/migrate/migrate/migrate_example/migrate_example_oracle/migrate_example_oracle.info

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

+ 11 - 13
sites/all/modules/contrib/migrate/migrate/migrate_example/wine.inc

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

+ 3 - 3
sites/all/modules/contrib/migrate/migrate/migrate_example_baseball/migrate_example_baseball.info

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

+ 3 - 3
sites/all/modules/contrib/migrate/migrate/migrate_ui/migrate_ui.info

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

+ 154 - 54
sites/all/modules/contrib/migrate/migrate/migrate_ui/migrate_ui.pages.inc

@@ -14,7 +14,7 @@ function migrate_ui_migrate_dashboard($form, &$form_state) {
 
   $build['overview'] = array(
     '#prefix' => '<div>',
-    '#markup' => migrate_overview(),
+    '#markup' => filter_xss_admin(migrate_overview()),
     '#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);
     $arguments = unserialize($group_row->arguments);
     if (!empty($arguments['source_system'])) {
-      $row['source_system'] = $arguments['source_system'];
+      $row['source_system'] = filter_xss_admin($arguments['source_system']);
     }
     else {
       $row['source_system'] = '';
@@ -212,9 +212,9 @@ function migrate_ui_migrate_group($form, &$form_state, $group_name) {
     }
     $row['machinename'] =
       l($display_name, "admin/content/migrate/groups/$group_name/$machine_name");
-    $row['importrows'] = $total;
-    $row['imported'] = $imported;
-    $row['unprocessed'] = $unprocessed;
+    $row['importrows'] = (int) $total;
+    $row['imported'] = (int) $imported;
+    $row['unprocessed'] = (int) $unprocessed;
     if (user_access(MIGRATE_ACCESS_ADVANCED)) {
       if (is_subclass_of($migration, 'Migration')) {
         $num_messages = $migration->messageCount();
@@ -231,13 +231,13 @@ function migrate_ui_migrate_group($form, &$form_state, $group_name) {
           $row['lastthroughput'] = t('Unknown');
         }
         else {
-          $row['lastthroughput'] = t('!rate/min', array('!rate' => $rate));
+          $row['lastthroughput'] = t('@rate/min', array('@rate' => $rate));
         }
       }
       else {
         $row['lastthroughput'] = t('N/A');
       }
-      $row['lastimported'] = $migration->getLastImported();
+      $row['lastimported'] = check_plain($migration->getLastImported());
     }
     $rows[$machine_name] = $row;
   }
@@ -497,6 +497,19 @@ function migrate_ui_migrate_submit($form, &$form_state) {
   }
   elseif (count($drush_arguments) > 0) {
     $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'];
     $uid = $GLOBALS['user']->uid;
     if ($operation == 'import_background') {
@@ -523,7 +536,7 @@ function migrate_ui_migrate_submit($form, &$form_state) {
       $limit = $limit['value'] . ' ' . $limit['unit'];
       $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 &";
     exec($drush_command, $output, $status);
     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) {
   if ($level != 'debug') {
     // Store each message as an array with keys 'message' and 'level'.
     global $_migrate_messages;
     $_migrate_messages[] = array(
-      'message' => $message,
-      'level' => $level,
+      'message' => filter_xss_admin($message),
+      'level' => check_plain($level),
     );
   }
 }
@@ -738,8 +762,8 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
   foreach ($team as $group => $list) {
     $form['overview'][$group] = array(
       '#type' => 'item',
-      '#title' => $group,
-      '#markup' => implode(', ', $list),
+      '#title' => filter_xss_admin($group),
+      '#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) {
     $form['overview']['dependencies'] = array(
       '#title' => t('Dependencies') ,
-      '#markup' => implode(', ', $dependencies),
+      '#markup' => filter_xss_admin(implode(', ', $dependencies)),
       '#type' => 'item',
     );
   }
@@ -755,14 +779,14 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
   if (count($soft_dependencies) > 0) {
     $form['overview']['soft_dependencies'] = array(
       '#title' => t('Soft Dependencies'),
-      '#markup' => implode(', ', $soft_dependencies),
+      '#markup' => filter_xss_admin(implode(', ', $soft_dependencies)),
       '#type' => 'item',
     );
   }
 
   $form['overview']['group'] = array(
     '#title' => t('Group:'),
-    '#markup' => $migration->getGroup()->getTitle(),
+    '#markup' => filter_xss_admin($migration->getGroup()->getTitle()),
     '#type' => 'item',
   );
 
@@ -787,7 +811,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
 
   $form['overview']['description'] = array(
     '#title' => t('Description:'),
-    '#markup' => $migration->getDescription(),
+    '#markup' => filter_xss_admin($migration->getDescription()),
     '#type' => 'item',
   );
 
@@ -807,7 +831,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
     $form['destination']['type'] = array(
       '#type' => 'item',
       '#title' => t('Type'),
-      '#markup' => (string)$destination,
+      '#markup' => filter_xss_admin((string) $destination),
     );
     $dest_key = $destination->getKeySchema();
     $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.
         $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();
 
@@ -848,7 +875,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
     $form['source']['query'] = array(
       '#type' => 'item',
       '#title' => t('Query'),
-      '#markup' => '<pre>' . $source . '</pre>',
+      '#markup' => '<pre>' . filter_xss_admin($source) . '</pre>',
     );
     $source_key = $migration->getMap()->getSourceKey();
     $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.
         $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();
 
@@ -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])) {
         drupal_set_message(t('"!source" was used as source field in the
           "!destination" mapping but is not in list of source fields', array(
-            '!source' => $source_field,
-            '!destination' => $destination_field
+            '!source' => filter_xss_admin($source_field),
+            '!destination' => filter_xss_admin($destination_field),
           )),
         'warning');
       }
       if (!is_null($destination_field) && !isset($destination_fields[$destination_field])) {
         drupal_set_message(t('"!destination" was used as destination field in
           "!source" mapping but is not in list of destination fields', array(
-            '!source' => $source_field,
-            '!destination' => $destination_field)),
+            '!source' => filter_xss_admin($source_field),
+            '!destination' => filter_xss_admin($destination_field))),
         'warning');
       }
       $descriptions[$mapping->getIssueGroup()][] = $mapping;
@@ -906,7 +936,7 @@ function migrate_migration_info($form, $form_state, $group_name, $migration_name
     foreach ($descriptions as $group => $mappings) {
       $form[$group] = array(
         '#type' => 'fieldset',
-        '#title' => t('Mapping: !group', array('!group' => $group)),
+        '#title' => t('Mapping: !group', array('!group' => filter_xss_admin($group))),
         '#group' => 'detail',
         '#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();
         if (!is_null($issue_priority)) {
-          $classes[] = 'migrate-priority-' . $issue_priority;
+          $classes[] = 'migrate-priority-' . drupal_html_class($issue_priority);
           $priority = MigrateFieldMapping::$priorities[$issue_priority];
           $issue_pattern = $migration->getIssuePattern();
           $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>";
         }
         $row = array(
-          array('data' => $destination_field, 'class' => $classes),
-          array('data' => $source_field, 'class' => $classes),
-          array('data' => $default, 'class' => $classes),
-          array('data' => $mapping->getDescription(),  'class' => $classes),
-          array('data' => $priority, 'class' => $classes),
+          array('data' => filter_xss_admin($destination_field), 'class' => $classes),
+          array('data' => filter_xss_admin($source_field), 'class' => $classes),
+          array('data' => filter_xss_admin($default), 'class' => $classes),
+          array('data' => filter_xss_admin($mapping->getDescription()), 'class' => $classes),
+          array('data' => filter_xss_admin($priority), 'class' => $classes),
         );
         $rows[] = $row;
         $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,
                                   $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['#tree'] = TRUE;
@@ -1014,6 +1044,9 @@ function migrate_ui_edit_mappings($form, $form_state, $group_name,
       '#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(
       '#type' => 'fieldset',
       '#title' => t('Source fields'),
@@ -1045,10 +1078,14 @@ function migrate_ui_edit_mappings($form, $form_state, $group_name,
       else {
         $label_format = '!description';
       }
-      $label = t($label_format,
-        array('!source_field' => $name, '!description' => $description));
-      $short_label = t($label_format,
-        array('!source_field' => $name, '!description' => $short_description));
+      $label = t($label_format, array(
+        '!source_field' => filter_xss_admin($name),
+        '!description' => filter_xss_admin($description),
+      ));
+      $short_label = t($label_format, array(
+        '!source_field' => filter_xss_admin($name),
+        '!description' => filter_xss_admin($short_description),
+      ));
 
       $dnm_value = 0;
 
@@ -1171,6 +1208,16 @@ function migrate_ui_edit_mappings($form, $form_state, $group_name,
         '#options' => $source_migration_options,
         '#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(
       '#type' => 'select',
-      '#title' => $machine_name,
+      '#title' => check_plain($machine_name),
       '#default_value' => $default_value,
       '#options' => $dependency_options,
     );
@@ -1275,16 +1322,21 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
   $field_mappings = array();
   $default_values = array();
   $issue_group_values = array();
+  $xpaths = array();
 
   $migration = Migration::getInstance($machine_name);
   if (is_a($migration, 'Migration')) {
+    $xml = is_a($migration, 'XMLMigration') ? TRUE : FALSE;
     $existing_mappings = $migration->getFieldMappings();
     $coded_mappings = $migration->getCodedFieldMappings();
     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'] === '') {
         $info['default_value'] = NULL;
       }
+      if ($xml && $info['xpath'] === '') {
+        $info['xpath'] = NULL;
+      }
 
       // 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
@@ -1296,6 +1348,10 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
           $coded_mappings[$destination_field]->getDefaultValue();
         $coded_source_migration =
           $coded_mappings[$destination_field]->getSourceMigration();
+        if ($xml) {
+          $coded_xpath =
+            $coded_mappings[$destination_field]->getXpath();
+        }
         if ($info['mapping'] == '-1') {
           $info['mapping'] = NULL;
         }
@@ -1312,6 +1368,7 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
         if ($info['mapping'] == $coded_source_field &&
             $info['default_value'] == $coded_default_value &&
             $info['source_migration'] == $coded_source_migration &&
+            (!$xml || ($xml && ($info['xpath'] == $coded_xpath))) &&
             $dnm_matches) {
           continue;
         }
@@ -1321,6 +1378,9 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
       $default_values[$destination_field] = $info['default_value'];
       $source_migrations[$destination_field] = $info['source_migration'];
       $issue_group_values[$destination_field] = $info['issue_group'];
+      if ($xml) {
+        $xpaths[$destination_field] = $info['xpath'];
+      }
     }
 
     foreach ($field_mappings as $destination_field => $source_field) {
@@ -1342,10 +1402,12 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
       $mapping = NULL;
       if (isset($existing_mappings[$destination_field]) &&
           $issue_group_values[$destination_field] != 0) {
+        /** @var MigrateFieldMapping $old_mapping */
         $old_mapping = $existing_mappings[$destination_field];
         if ($source_field == $old_mapping->getSourceField() &&
             $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
           // preserve it as it was originally stored.
           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
       // new mapping.
       if (!$mapping) {
-        $mapping = new MigrateFieldMapping($destination_field, $source_field);
+        $mapping = _migrate_ui_get_mapping_object($migration, $destination_field, $source_field);
         $mapping->defaultValue($default);
+        if ($xml && $xpaths[$destination_field]) {
+          $mapping->xpath($xpaths[$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,
       // generate a DNM mapping.
       if ($value && !$code_ignored) {
-        $mapping = new MigrateFieldMapping(NULL, $source_field);
+        $mapping = _migrate_ui_get_mapping_object($migration, NULL, $source_field);
         $mapping->issueGroup(t('DNM'));
         $arguments['field_mappings'][] = $mapping;
       }
@@ -1410,7 +1475,7 @@ function migrate_ui_edit_mappings_submit(&$form, &$form_state) {
           }
         }
         if (!$mapping_found) {
-          $mapping = new MigrateFieldMapping(NULL, $source_field);
+          $mapping = _migrate_ui_get_mapping_object($migration, NULL, $source_field);
           $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";
 }
 
+/**
+ * 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
  * were defined through the UI.
@@ -1469,6 +1550,9 @@ function theme_migrate_ui_field_mapping_form($variables) {
   if (!empty($elements)) {
     $header = array(t('DNM'), t('Destination field'), t('Source field'),
                     t('Default value'), t('Source migration'));
+    if (!empty($form['#is_xml_migration'])) {
+      $header[] = t('Xpath');
+    }
     $rows = array();
     foreach ($elements as $mapping_key) {
       $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]['default_value']);
       $row[] = drupal_render($form[$mapping_key]['source_migration']);
+      if (!empty($form['#is_xml_migration'])) {
+        $row[] = drupal_render($form[$mapping_key]['xpath']);
+      }
       $rows[] = $row;
     }
     $output .= theme('table', array('header' => $header, 'rows' => $rows));
@@ -1536,7 +1623,11 @@ function migrate_ui_messages($group_name, $migration_name) {
   $header = array();
   // Add a table header for each source key in the migration's map.
   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');
@@ -1558,12 +1649,18 @@ function migrate_ui_messages($group_name, $migration_name) {
     // Add a table column for each source key.
     foreach ($source_key_map_flipped as $source_key => $source_field) {
       $row[] = array(
-        'data' => $message->{$source_key},
+        'data' => filter_xss_admin($message->{$source_key}),
         'class' => $classes,
       );
     }
-    $row[] = array('data' => $migration->getMessageLevelName($message->level), 'class' => $classes);
-    $row[] = array('data' => $message->message, 'class' => $classes);
+    $row[] = array(
+      'data' => filter_xss_admin($migration->getMessageLevelName($message->level)),
+      'class' => $classes,
+    );
+    $row[] = array(
+      'data' => filter_xss_admin($message->message),
+      'class' => $classes,
+    );
 
     $rows[] = $row;
 
@@ -1642,7 +1739,10 @@ function migrate_ui_configure_form($form, &$form_state) {
     if (!class_exists($row->class_name)) {
       $migrations[] = $row->machine_name;
       $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.
   $drush_path = trim(variable_get('migrate_drush_path', ''));
   $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.
     $uri = $GLOBALS['base_url'];
     $uid = $GLOBALS['user']->uid;
@@ -1818,9 +1918,9 @@ function migrate_ui_configure_form($form, &$form_state) {
               ->condition('type', 'class')
               ->execute()
               ->fetchField();
-    $row['module'] = $module;
-    $row['class'] = $class_name;
-    $row['types'] = implode(', ', $handler->getTypesHandled());
+    $row['module'] = check_plain($module);
+    $row['class'] = check_plain($class_name);
+    $row['types'] = filter_xss_admin(implode(', ', $handler->getTypesHandled()));
     $default_values[$class_name] = !in_array($class_name, $disabled);
     $rows[$class_name] = $row;
   }
@@ -1855,9 +1955,9 @@ function migrate_ui_configure_form($form, &$form_state) {
               ->condition('type', 'class')
               ->execute()
               ->fetchField();
-    $row['module'] = $module;
-    $row['class'] = $class_name;
-    $row['types'] = implode(', ', $handler->getTypesHandled());
+    $row['module'] = check_plain($module);
+    $row['class'] = check_plain($class_name);
+    $row['types'] = filter_xss_admin(implode(', ', $handler->getTypesHandled()));
     $default_values[$class_name] = !in_array($class_name, $disabled);
     $rows[$class_name] = $row;
   }

+ 1 - 1
sites/all/modules/contrib/migrate/migrate/plugins/destinations/comment.inc

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

+ 30 - 0
sites/all/modules/contrib/migrate/migrate/plugins/destinations/entity.inc

@@ -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)));
+            }
+          }
+        }
+      }
+    }
+  }
 }

+ 12 - 5
sites/all/modules/contrib/migrate/migrate/plugins/destinations/fields.inc

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

+ 7 - 6
sites/all/modules/contrib/migrate/migrate/plugins/destinations/file.inc

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

+ 1 - 1
sites/all/modules/contrib/migrate/migrate/plugins/destinations/node.inc

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

+ 1 - 1
sites/all/modules/contrib/migrate/migrate/plugins/destinations/term.inc

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

+ 1 - 1
sites/all/modules/contrib/migrate/migrate/plugins/destinations/user.inc

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

+ 1 - 1
sites/all/modules/contrib/migrate/migrate/plugins/sources/list.inc

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

+ 1 - 1
sites/all/modules/contrib/migrate/migrate/plugins/sources/multiitems.inc

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

+ 21 - 6
sites/all/modules/contrib/migrate/migrate/plugins/sources/sql.inc

@@ -376,14 +376,29 @@ class MigrateSourceSQL extends MigrateSource {
       // 3. If we are using highwater marks, also include rows above the mark.
       //    But, include all rows if the highwater mark is not set.
       if (isset($this->highwaterField['name']) && $this->activeMigration->getHighwater() !== '') {
-        if (isset($this->highwaterField['alias'])) {
-          $highwater = $this->highwaterField['alias'] . '.' . $this->highwaterField['name'];
+        // But, if there are any existing items marked as needing update which
+        // 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 {
-          $highwater = $this->highwaterField['name'];
+        if ($add_highwater_condition) {
+          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) {
         $this->query->condition($conditions);

+ 44 - 1
sites/all/modules/contrib/migrate/migrate/plugins/sources/sqlmap.inc

@@ -69,6 +69,11 @@ class MigrateSQLMap extends MigrateMap {
    */
   protected $ensured;
 
+  /**
+   * Provide caching for Source or Desination Map Lookups.
+   */
+  protected $cacheMapLookups;
+
   /**
    * Constructor.
    *
@@ -92,6 +97,13 @@ class MigrateSQLMap extends MigrateMap {
       $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);
 
     // Default generated table names, limited to 63 characters
@@ -302,6 +314,16 @@ class MigrateSQLMap extends MigrateMap {
    */
   public function lookupSourceID(array $destination_id) {
     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')
               ->fields('map', $this->sourceKeyMap);
     foreach ($this->destinationKeyMap as $key_name) {
@@ -309,6 +331,12 @@ class MigrateSQLMap extends MigrateMap {
     }
     $result = $query->execute();
     $source_id = $result->fetchAssoc();
+
+    // Store the id in a cache if enabled.
+    if ($this->cacheMapLookups) {
+      $cache[$serialized] = $destination_id;
+    }
+
     migrate_instrument_stop('lookupSourceID');
     return $source_id;
   }
@@ -324,6 +352,16 @@ class MigrateSQLMap extends MigrateMap {
    */
   public function lookupDestinationID(array $source_id) {
     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')
               ->fields('map', $this->destinationKeyMap);
     foreach ($this->sourceKeyMap as $key_name) {
@@ -331,10 +369,15 @@ class MigrateSQLMap extends MigrateMap {
     }
     $result = $query->execute();
     $destination_id = $result->fetchAssoc();
+
+    // Store the id in a cache if enabled.
+    if ($this->cacheMapLookups) {
+      $cache[$serialized] = $destination_id;
+    }
+
     migrate_instrument_stop('lookupDestinationID');
     return $destination_id;
   }
-
   /**
    * 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

+ 0 - 4
sites/all/modules/contrib/migrate/migrate/tests/import/options.test

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

+ 1 - 0
sites/all/modules/contrib/views/views_bulk_operations/actions/archive.action.inc

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

+ 2 - 0
sites/all/modules/contrib/views/views_bulk_operations/actions/book.action.inc

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

+ 1 - 0
sites/all/modules/contrib/views/views_bulk_operations/actions/delete.action.inc

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

+ 7 - 2
sites/all/modules/contrib/views/views_bulk_operations/actions/modify.action.inc

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

+ 1 - 0
sites/all/modules/contrib/views/views_bulk_operations/actions/user_cancel.action.inc

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

+ 12 - 17
sites/all/modules/contrib/views/views_bulk_operations/actions/user_roles.action.inc

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

+ 3 - 3
sites/all/modules/contrib/views/views_bulk_operations/actions_permissions.info

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

+ 17 - 15
sites/all/modules/contrib/views/views_bulk_operations/js/views_bulk_operations.js

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

+ 1 - 1
sites/all/modules/contrib/views/views_bulk_operations/plugins/operation_types/action.class.php

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

+ 3 - 1
sites/all/modules/contrib/views/views_bulk_operations/plugins/operation_types/rules_component.class.php

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

+ 22 - 0
sites/all/modules/contrib/views/views_bulk_operations/views/views_bulk_operations_handler_field_operations.inc

@@ -55,6 +55,7 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
       'contains' => array(
         'display_type' => array('default' => 0),
         'enable_select_all_pages' => array('default' => TRUE),
+        'row_clickable' => array('default' => TRUE),
         'force_single' => array('default' => FALSE),
         'entity_load_capacity' => array('default' => 10),
         'skip_batching' => array('default' => 0),
@@ -63,11 +64,26 @@ class views_bulk_operations_handler_field_operations extends views_handler_field
     $options['vbo_operations'] = array(
       'default' => array(),
       'unpack_translatable' => 'unpack_operations',
+      'export' => 'export_vbo_operations',
     );
 
     return $options;
   }
 
+  function export_vbo_operations($indent, $prefix, $storage, $option, $definition, $parents) {
+    // Anti-recursion, since we use the parent export helper.
+    unset($definition['export']);
+
+    // Find and remove all unselected/disabled operations.
+    foreach ($storage['vbo_operations'] as $operation_id => $operation) {
+      if (empty($operation['selected'])) {
+        unset($storage['vbo_operations'][$operation_id]);
+      }
+    }
+
+    return parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
+  }
+
   function unpack_operations(&$translatable, $storage, $option, $definition, $parents, $keys) {
     $translatable[] = array(
       'value' => t('- Choose an operation -'),
@@ -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'],
       '#description' => t('Check this box to enable the ability to select all items on all pages.'),
     );
+    $form['vbo_settings']['row_clickable'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Make the whole row clickable'),
+      '#default_value' => $this->options['vbo_settings']['row_clickable'],
+      '#description' => t('Check this box to select an item when its row has been clicked. Requires JavaScript.'),
+    );
     $form['vbo_settings']['force_single'] = array(
       '#type' => 'checkbox',
       '#title' => t('Force single'),

+ 3 - 3
sites/all/modules/contrib/views/views_bulk_operations/views_bulk_operations.info

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

+ 56 - 41
sites/all/modules/contrib/views/views_bulk_operations/views_bulk_operations.module

@@ -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
   // performance (hitting the filesystem with file_scan_directory(), and then
   // caching the result has its cost).
-  $path = drupal_get_path('module', 'views_bulk_operations') . '/actions/';
   $files = array(
-    'archive.action.inc',
-    'argument_selector.action.inc',
-    'book.action.inc',
-    'delete.action.inc',
-    'modify.action.inc',
-    'script.action.inc',
-    'user_roles.action.inc',
-    'user_cancel.action.inc',
+    'archive.action',
+    'argument_selector.action',
+    'book.action',
+    'delete.action',
+    'modify.action',
+    'script.action',
+    'user_roles.action',
+    'user_cancel.action',
   );
 
   if (!$loaded) {
     foreach ($files as $file) {
-      include_once $path . $file;
+      module_load_include('inc', 'views_bulk_operations', 'actions/' . $file);
     }
     $loaded = TRUE;
   }
@@ -77,7 +76,7 @@ function views_bulk_operations_load_action_includes() {
 function views_bulk_operations_cron() {
   db_delete('queue')
     ->condition('name', db_like('views_bulk_operations_active_queue_'), 'LIKE')
-    ->condition('created', REQUEST_TIME - 864000, '<')
+    ->condition('created', REQUEST_TIME - 86400, '<')
     ->execute();
 }
 
@@ -358,7 +357,7 @@ function views_bulk_operations_form_alter(&$form, &$form_state, $form_id) {
  */
 function views_bulk_operations_views_post_build(&$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;
   }
 }
@@ -420,7 +419,7 @@ function theme_views_bulk_operations_select_all($variables) {
     if ($enable_select_all_pages) {
       $form['select_all']['or'] = array(
         '#type' => 'markup',
-        '#markup' => '<em>OR</em>',
+        '#markup' => '<em>' . t('OR') . '</em>',
       );
       $form['select_all']['all_pages'] = array(
         '#type' => 'checkbox',
@@ -443,6 +442,13 @@ function theme_views_bulk_operations_select_all($variables) {
  */
 function views_bulk_operations_form($form, &$form_state, $vbo) {
   $form['#attached']['js'][] = drupal_get_path('module', 'views_bulk_operations') . '/js/views_bulk_operations.js';
+  $form['#attached']['js'][] = array(
+    'data' => array('vbo' => array(
+      'row_clickable' => $vbo->get_vbo_option('row_clickable'),
+    )),
+    'type' => 'setting',
+  );
+
   $form['#attached']['css'][] = drupal_get_path('module', 'views_bulk_operations') . '/css/views_bulk_operations.css';
   // Wrap the form in a div with specific classes for JS targeting and theming.
   $class = 'vbo-views-form';
@@ -595,14 +601,23 @@ function views_bulk_operations_confirm_form($form, &$form_state, $view, $output)
   $operation = $form_state['operation'];
   $rows = $form_state['selection'];
   $query = drupal_get_query_parameters($_GET, array('q'));
+  $title = t('Are you sure you want to perform %operation on the selected items?', array('%operation' => $operation->label()));
   $form = confirm_form($form,
-    t('Are you sure you want to perform %operation on the selected items?', array('%operation' => $operation->label())),
+    $title,
     array('path' => $view->get_url(), 'query' => $query),
     theme('views_bulk_operations_confirmation', array('rows' => $rows, 'vbo' => $vbo, 'operation' => $operation, 'select_all_pages' => $form_state['select_all_pages']))
   );
   // Add VBO's submit handler to the Confirm button added by config_form().
   $form['actions']['submit']['#submit'] = array('views_bulk_operations_form_submit');
 
+  // We can't set the View title here as $view is just a copy of the original,
+  // and our settings changes won't "stick" for the first page load of the
+  // confirmation form. We also can't just call drupal_set_title() directly
+  // because our title will be clobbered by the actual View title later. So
+  // let's tuck the title away in the form for use later.
+  // @see views_bulk_operations_preprocess_views_view()
+  $form['#vbo_confirm_form_title'] = $title;
+
   return $form;
 }
 
@@ -618,7 +633,7 @@ function theme_views_bulk_operations_confirmation($variables) {
   // Load the entities from the current page, and show their titles.
   $entities = _views_bulk_operations_entity_load($entity_type, array_values($rows), $vbo->revision);
   foreach ($entities as $entity) {
-    $items[] = check_plain(_views_bulk_operations_entity_label($entity_type, $entity));
+    $items[] = check_plain(entity_label($entity_type, $entity));
   }
   // All rows on all pages have been selected, so show a count of additional items.
   if ($select_all_pages) {
@@ -631,6 +646,29 @@ function theme_views_bulk_operations_confirmation($variables) {
   return $output;
 }
 
+/**
+ * Implements hook_preprocess_page().
+ *
+ * Hide action links on the configure and confirm pages.
+ */
+function views_bulk_operations_preprocess_page(&$variables) {
+  if (isset($_POST['select_all'], $_POST['operation'])) {
+    $variables['action_links'] = array();
+  }
+}
+
+/**
+ * Implements hook_preprocess_views_view().
+ */
+function views_bulk_operations_preprocess_views_view($variables) {
+  // If we've stored a title for the confirmation form, retrieve it here and
+  // retitle the View.
+  // @see views_bulk_operations_confirm_form()
+  if (array_key_exists('rows', $variables) && is_array($variables['rows']) && array_key_exists('#vbo_confirm_form_title', $variables['rows'])) {
+    $variables['view']->set_title($variables['rows']['#vbo_confirm_form_title']);
+  }
+}
+
 /**
  * Goes through the submitted values, and returns
  * an array of selected rows, in the form of
@@ -871,7 +909,7 @@ function views_bulk_operations_adjust_selection($queue_name, $operation, $option
       'views_row' => array(),
       'position' => array(
         'current' => ++$context['sandbox']['progress'],
-        'total' => $view->total_rows,
+        'total' => $context['sandbox']['max'],
       ),
     );
     // Some operations require full selected rows.
@@ -1042,7 +1080,7 @@ function views_bulk_operations_queue_item_process($queue_item_data, &$log = NULL
       $arguments = array(
         '%operation' => $operation->label(),
         '@type' => $entity_type,
-        '%title' => _views_bulk_operations_entity_label($entity_type, $entity),
+        '%title' => entity_label($entity_type, $entity),
       );
 
       if ($log) {
@@ -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(
           '%operation' => $operation->label(),
           '@type' => $entity_type,
-          '%title' => _views_bulk_operations_entity_label($entity_type, $entity),
+          '%title' => entity_label($entity_type, $entity),
         ));
         unset($entities[$id]);
       }
@@ -1236,29 +1274,6 @@ function _views_bulk_operations_entity_load($entity_type, $ids, $revision = FALS
   return $entities;
 }
 
-/**
- * Label function for entities.
- * Core entities don't declare the "label" key, so entity_label() fails,
- * and a fallback is needed. This function provides that fallback.
- */
-function _views_bulk_operations_entity_label($entity_type, $entity) {
-  $label = entity_label($entity_type, $entity);
-  if (!$label) {
-    $entity_info = entity_get_info($entity_type);
-    $id_key = $entity_info['entity keys']['id'];
-    // Many entity types (e.g. "user") have a name which fits the label perfectly.
-    if (isset($entity->name)) {
-      $label = $entity->name;
-    }
-    elseif (isset($entity->{$id_key})) {
-      // Fallback to the id key.
-      $label = $entity->{$id_key};
-    }
-  }
-
-  return $label;
-}
-
 /**
  * Helper function to report an error.
  */