Browse Source

updated webform, webform_localization, profile2, term_merge, search_api_saved_pages, rules, redirect, overide_node_options

Bachir Soussi Chiadmi 7 months ago
parent
commit
9adc940a67
100 changed files with 5444 additions and 1736 deletions
  1. 0 0
      1
  2. 45 0
      sites/all/modules/contrib/admin/override_node_options/README.txt
  3. 4 3
      sites/all/modules/contrib/admin/override_node_options/override_node_options.info
  4. 16 15
      sites/all/modules/contrib/admin/override_node_options/override_node_options.install
  5. 199 39
      sites/all/modules/contrib/admin/override_node_options/override_node_options.module
  6. 155 52
      sites/all/modules/contrib/admin/override_node_options/override_node_options.test
  7. 36 6
      sites/all/modules/contrib/admin/redirect/redirect.admin.inc
  8. 20 0
      sites/all/modules/contrib/admin/redirect/redirect.controller.inc
  9. 4 6
      sites/all/modules/contrib/admin/redirect/redirect.info
  10. 66 2
      sites/all/modules/contrib/admin/redirect/redirect.install
  11. 14 3
      sites/all/modules/contrib/admin/redirect/redirect.js
  12. 169 63
      sites/all/modules/contrib/admin/redirect/redirect.module
  13. 52 9
      sites/all/modules/contrib/admin/redirect/redirect.test
  14. 0 5
      sites/all/modules/contrib/admin/redirect/views/redirect_handler_field_redirect_redirect.inc
  15. 0 5
      sites/all/modules/contrib/admin/redirect/views/redirect_handler_field_redirect_source.inc
  16. 1 1
      sites/all/modules/contrib/admin/rules/DEVELOPER.txt
  17. 10 10
      sites/all/modules/contrib/admin/rules/README.txt
  18. 0 27
      sites/all/modules/contrib/admin/rules/fix_errors_on_update-2090511-214_2.patch
  19. 41 29
      sites/all/modules/contrib/admin/rules/includes/faces.inc
  20. 496 170
      sites/all/modules/contrib/admin/rules/includes/rules.core.inc
  21. 34 24
      sites/all/modules/contrib/admin/rules/includes/rules.event.inc
  22. 190 57
      sites/all/modules/contrib/admin/rules/includes/rules.plugins.inc
  23. 56 33
      sites/all/modules/contrib/admin/rules/includes/rules.processor.inc
  24. 80 46
      sites/all/modules/contrib/admin/rules/includes/rules.state.inc
  25. 111 30
      sites/all/modules/contrib/admin/rules/includes/rules.upgrade.inc
  26. 41 6
      sites/all/modules/contrib/admin/rules/modules/comment.rules.inc
  27. 76 10
      sites/all/modules/contrib/admin/rules/modules/data.eval.inc
  28. 68 20
      sites/all/modules/contrib/admin/rules/modules/data.rules.inc
  29. 17 5
      sites/all/modules/contrib/admin/rules/modules/entity.eval.inc
  30. 32 7
      sites/all/modules/contrib/admin/rules/modules/entity.rules.inc
  31. 115 58
      sites/all/modules/contrib/admin/rules/modules/events.inc
  32. 114 12
      sites/all/modules/contrib/admin/rules/modules/node.eval.inc
  33. 43 53
      sites/all/modules/contrib/admin/rules/modules/node.rules.inc
  34. 6 2
      sites/all/modules/contrib/admin/rules/modules/path.eval.inc
  35. 8 6
      sites/all/modules/contrib/admin/rules/modules/path.rules.inc
  36. 50 17
      sites/all/modules/contrib/admin/rules/modules/php.eval.inc
  37. 7 6
      sites/all/modules/contrib/admin/rules/modules/php.rules.inc
  38. 46 8
      sites/all/modules/contrib/admin/rules/modules/rules_core.eval.inc
  39. 34 7
      sites/all/modules/contrib/admin/rules/modules/rules_core.rules.inc
  40. 30 7
      sites/all/modules/contrib/admin/rules/modules/system.eval.inc
  41. 23 4
      sites/all/modules/contrib/admin/rules/modules/system.rules.inc
  42. 59 9
      sites/all/modules/contrib/admin/rules/modules/taxonomy.rules.inc
  43. 35 2
      sites/all/modules/contrib/admin/rules/modules/user.eval.inc
  44. 46 5
      sites/all/modules/contrib/admin/rules/modules/user.rules.inc
  45. 198 67
      sites/all/modules/contrib/admin/rules/rules.api.php
  46. 139 21
      sites/all/modules/contrib/admin/rules/rules.drush.inc
  47. 30 11
      sites/all/modules/contrib/admin/rules/rules.features.inc
  48. 13 5
      sites/all/modules/contrib/admin/rules/rules.info
  49. 127 6
      sites/all/modules/contrib/admin/rules/rules.install
  50. 419 113
      sites/all/modules/contrib/admin/rules/rules.module
  51. 37 13
      sites/all/modules/contrib/admin/rules/rules.rules.inc
  52. 28 12
      sites/all/modules/contrib/admin/rules/rules_admin/rules_admin.inc
  53. 7 6
      sites/all/modules/contrib/admin/rules/rules_admin/rules_admin.info
  54. 2 1
      sites/all/modules/contrib/admin/rules/rules_admin/rules_admin.module
  55. 126 0
      sites/all/modules/contrib/admin/rules/rules_admin/tests/rules_admin.test
  56. 5 3
      sites/all/modules/contrib/admin/rules/rules_i18n/rules_i18n.i18n.inc
  57. 4 4
      sites/all/modules/contrib/admin/rules/rules_i18n/rules_i18n.info
  58. 19 0
      sites/all/modules/contrib/admin/rules/rules_i18n/rules_i18n.install
  59. 4 2
      sites/all/modules/contrib/admin/rules/rules_i18n/rules_i18n.module
  60. 9 2
      sites/all/modules/contrib/admin/rules/rules_i18n/rules_i18n.rules.inc
  61. 14 7
      sites/all/modules/contrib/admin/rules/rules_i18n/rules_i18n.test
  62. 5 0
      sites/all/modules/contrib/admin/rules/rules_scheduler/includes/rules_scheduler.handler.inc
  63. 4 3
      sites/all/modules/contrib/admin/rules/rules_scheduler/includes/rules_scheduler.views.inc
  64. 3 3
      sites/all/modules/contrib/admin/rules/rules_scheduler/includes/rules_scheduler.views_default.inc
  65. 5 2
      sites/all/modules/contrib/admin/rules/rules_scheduler/includes/rules_scheduler_views_filter.inc
  66. 5 6
      sites/all/modules/contrib/admin/rules/rules_scheduler/rules_scheduler.admin.inc
  67. 4 4
      sites/all/modules/contrib/admin/rules/rules_scheduler/rules_scheduler.drush.inc
  68. 10 11
      sites/all/modules/contrib/admin/rules/rules_scheduler/rules_scheduler.info
  69. 71 5
      sites/all/modules/contrib/admin/rules/rules_scheduler/rules_scheduler.install
  70. 89 40
      sites/all/modules/contrib/admin/rules/rules_scheduler/rules_scheduler.module
  71. 8 5
      sites/all/modules/contrib/admin/rules/rules_scheduler/rules_scheduler.rules.inc
  72. 63 14
      sites/all/modules/contrib/admin/rules/rules_scheduler/rules_scheduler.test
  73. 3 4
      sites/all/modules/contrib/admin/rules/rules_scheduler/tests/rules_scheduler_test.info
  74. 370 141
      sites/all/modules/contrib/admin/rules/tests/rules.test
  75. 3 5
      sites/all/modules/contrib/admin/rules/tests/rules_test.info
  76. 5 1
      sites/all/modules/contrib/admin/rules/tests/rules_test.module
  77. 140 2
      sites/all/modules/contrib/admin/rules/tests/rules_test.rules.inc
  78. 6 3
      sites/all/modules/contrib/admin/rules/tests/rules_test.rules_defaults.inc
  79. 7 7
      sites/all/modules/contrib/admin/rules/tests/rules_test.test.inc
  80. 3 4
      sites/all/modules/contrib/admin/rules/tests/rules_test_invocation.info
  81. 12 5
      sites/all/modules/contrib/admin/rules/ui/rules.autocomplete.js
  82. 9 7
      sites/all/modules/contrib/admin/rules/ui/rules.ui.css
  83. 27 15
      sites/all/modules/contrib/admin/rules/ui/ui.controller.inc
  84. 213 87
      sites/all/modules/contrib/admin/rules/ui/ui.core.inc
  85. 205 24
      sites/all/modules/contrib/admin/rules/ui/ui.data.inc
  86. 129 57
      sites/all/modules/contrib/admin/rules/ui/ui.forms.inc
  87. 43 13
      sites/all/modules/contrib/admin/rules/ui/ui.plugins.inc
  88. 23 4
      sites/all/modules/contrib/admin/rules/ui/ui.theme.inc
  89. 3 3
      sites/all/modules/contrib/admin/token/tests/token_test.info
  90. 3 3
      sites/all/modules/contrib/admin/token/token.info
  91. 21 19
      sites/all/modules/contrib/admin/token/token.install
  92. 9 7
      sites/all/modules/contrib/admin/token/token.module
  93. 2 2
      sites/all/modules/contrib/admin/token/token.pages.inc
  94. 1 2
      sites/all/modules/contrib/admin/token/token.tokens.inc
  95. 19 10
      sites/all/modules/contrib/editor/wysiwyg_filter/README.txt
  96. 31 38
      sites/all/modules/contrib/editor/wysiwyg_filter/wysiwyg_filter.admin.inc
  97. 49 18
      sites/all/modules/contrib/editor/wysiwyg_filter/wysiwyg_filter.inc
  98. 3 3
      sites/all/modules/contrib/editor/wysiwyg_filter/wysiwyg_filter.info
  99. 8 7
      sites/all/modules/contrib/editor/wysiwyg_filter/wysiwyg_filter.module
  100. 0 0
      sites/all/modules/contrib/editor/wysiwyg_filter/wysiwyg_filter.pages.inc

+ 0 - 0
1


+ 45 - 0
sites/all/modules/contrib/admin/override_node_options/README.txt

@@ -0,0 +1,45 @@
+CONTENTS OF THIS FILE
+---------------------
+
+ * Introduction
+ * Requirements
+ * Installation
+ * Configuration
+ * Maintainers
+
+
+INTRODUCTION
+------------
+
+The Override Node Options module allows permissions to be set to each field
+within the Authoring information and Publishing options fieldsets on the node
+add/edit form so that they can be configued by non-admin users.
+
+
+REQUIREMENTS
+------------
+
+No special requirements.
+
+
+INSTALLATION
+------------
+
+Install as you would normally install a contributed Drupal module. Visit
+https://www.drupal.org/docs/7/extending-drupal-7/installing-drupal-7-contributed-modules
+for further information.
+
+
+CONFIGURATION
+-------------
+
+ * Adjust module settings in admin/config/content/override-node-options to
+   enable general and/or content type specific permissions.
+ * Assign permissions in admin/people/permissions#module-override_node_options.
+
+MAINTAINERS
+-----------
+
+Current maintainers:
+
+ * Oliver Davies (opdavies) - https://www.drupal.org/u/opdavies

+ 4 - 3
sites/all/modules/contrib/admin/override_node_options/override_node_options.info

@@ -2,11 +2,12 @@ name = Override node options
 description = "Allow non-admins to override the default publishing options for nodes they can edit."
 core = 7.x
 package = Permissions
+configure = admin/config/content/override-node-options
 files[] = override_node_options.test
 
-; Information added by Drupal.org packaging script on 2014-09-19
-version = "7.x-1.13"
+; Information added by Drupal.org packaging script on 2018-03-31
+version = "7.x-1.14"
 core = "7.x"
 project = "override_node_options"
-datestamp = "1411157931"
+datestamp = "1522482188"
 

+ 16 - 15
sites/all/modules/contrib/admin/override_node_options/override_node_options.install

@@ -9,27 +9,28 @@
  * Implements hook_install().
  */
 function override_node_options_install() {
-  db_update('system')->fields(array(
-    'weight' => -1
-  ))
-  ->condition('name', 'override_node_options', '=')
-  ->execute();
+  db_update('system')
+    ->fields(array('weight' => 1))
+    ->condition('name', 'override_node_options', '=')
+    ->execute();
 }
 
 /**
- * Implements hook_uninstall().
+ * Implements hook_update_N().
  */
-function override_node_options_uninstall() {
-  db_query("DELETE FROM {variable} WHERE name LIKE 'override_node_options_%'");
+function override_node_options_update_7113() {
+  db_update('system')
+    ->fields(array('weight' => -1))
+    ->condition('name', 'override_node_options', '=')
+    ->execute();
 }
 
 /**
- * Implements hook_update_N().
+ * Update the module weight.
  */
-function override_node_options_update_7113() {
-  db_update('system')->fields(array(
-    'weight' => -1
-  ))
-  ->condition('name', 'override_node_options', '=')
-  ->execute();
+function override_node_options_update_7114() {
+  db_update('system')
+    ->fields(array('weight' => 1))
+    ->condition('name', 'override_node_options', '=')
+    ->execute();
 }

+ 199 - 39
sites/all/modules/contrib/admin/override_node_options/override_node_options.module

@@ -2,19 +2,62 @@
 
 /**
  * @file
+ * Main module file for override_node_options.
+ *
  * Allow users to override the default publishing options for nodes they can
  * edit without giving them the 'administer nodes' permission.
  */
 
 /**
- * Implements hook_permisson().
+ * Implements hook_permission().
  */
 function override_node_options_permission() {
-  $permissions = array();
+  // Global permissions which apply to all node types.
+  $permissions = array(
+    'administer override node options' => array(
+      'title' => t('Administer override node options.'),
+    ),
+  );
 
-  // Generate override node permissions for all applicable node types.
-  foreach (node_permissions_get_configured_types() as $type) {
-    $permissions += override_node_options_list_permissions($type);
+  $show_perms = variable_get('override_node_options_permissions', array('general', 'specific'));
+  if (in_array('general', $show_perms, TRUE)) {
+    $permissions += array(
+      'override all published option' => array(
+        'title' => t('Override published option for all node types.'),
+      ),
+      'override all promote to front page option' => array(
+        'title' => t('Override promote to front page option for all node types.'),
+      ),
+      'override all sticky option' => array(
+        'title' => t('Override sticky option for all node types.'),
+      ),
+      'override all revision option' => array(
+        'title' => t('Override revision option for all node types.'),
+      ),
+      'enter all revision log entry' => array(
+        'title' => t('Enter revision log entry for all node types.'),
+      ),
+      'override all authored on option' => array(
+        'title' => t('Override authored on option for all node types.'),
+      ),
+      'override all authored by option' => array(
+        'title' => t('Override authored by option for all node types.'),
+      ),
+    );
+    if (module_exists('comment')) {
+      $permissions += array(
+        'override all comment setting option' => array(
+          'title' => t('Override comment setting option for all node types.'),
+        ),
+      );
+    }
+  }
+
+  if (in_array('specific', $show_perms, TRUE)) {
+    // Generate override node permissions for all applicable node types.
+    foreach (node_permissions_get_configured_types() as $type) {
+      $permissions += override_node_options_list_permissions($type);
+    }
   }
 
   return $permissions;
@@ -23,13 +66,16 @@ function override_node_options_permission() {
 /**
  * Helper function to generate override node permission list for a given type.
  *
- * @param $type
+ * @param string $type
  *   The machine-readable name of the node type.
- * @return
+ *
+ * @return array
  *   An array of permission names and description.
  */
-function override_node_options_list_permissions($type) {
-  $name = node_type_get_name($type);
+function override_node_options_list_permissions($type, $name = NULL) {
+  if (!$name) {
+    $name = node_type_get_name($type);
+  }
   $type = check_plain($type);
 
   $permissions = array(
@@ -56,63 +102,177 @@ function override_node_options_list_permissions($type) {
     ),
   );
 
+  if (module_exists('comment')) {
+    $permissions += array(
+      "override $type comment setting option" => array(
+        'title' => t('Override %type_name comment setting option.', array('%type_name' => $name)),
+      ),
+    );
+  }
+
   return $permissions;
 }
 
 /**
+ * Implements hook_menu().
+ */
+function override_node_options_menu() {
+  $items['admin/config/content/override-node-options'] = array(
+    'title' => 'Override Node Options Settings',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('override_node_options_settings_form'),
+    'access arguments' => array('administer override node options'),
+    'type' => MENU_NORMAL_ITEM,
+  );
+
+  return $items;
+}
+
+/**
+ * Settings form.
+ */
+function override_node_options_settings_form($form, &$form_values) {
+  $form = array();
+
+  $form['override_node_options_permissions'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Provide the following permissions:'),
+    '#options' => array(
+      'general' => t('General permissions, accross all node types'),
+      'specific' => t('Specific permissions, for each individual node type'),
+    ),
+    '#default_value' => variable_get('override_node_options_permissions', array('general', 'specific')),
+  );
+
+  $form['#submit'][] = 'override_node_options_settings_form_submit';
+
+  return system_settings_form($form);
+}
+
+/**
+ * Submit handler for settings form.
+ */
+function override_node_options_settings_form_submit(&$form, &$form_state) {
+  // Get old perms to compare.
+  $old_perms = variable_get('override_node_options_permissions', array('general', 'specific'));
+  $new_perms = $form_state['values']['override_node_options_permissions'];
+
+  // Clean up saved permissions.
+  $role_names = user_roles();
+  $revoke = array();
+
+  if (!in_array('specific', $new_perms, TRUE) && in_array('specific', $old_perms, TRUE)) {
+    $alert = TRUE;
+    $permissions = array();
+    foreach (node_permissions_get_configured_types() as $type) {
+      $permissions += override_node_options_list_permissions($type);
+    }
+
+    foreach ($permissions as $permission => $description) {
+      $revoke[$permission] = FALSE;
+    }
+
+    // Be sure to clear the cache.
+    cache_clear_all();
+  }
+
+  if (!in_array('general', $new_perms, TRUE) && in_array('general', $old_perms, TRUE)) {
+    $alert = TRUE;
+    $revoke = array(
+      'override all published option' => FALSE,
+      'override all promote to front page option' => FALSE,
+      'override all sticky option' => FALSE,
+      'override all revision option' => FALSE,
+      'enter all revision log entry' => FALSE,
+      'override all authored on option' => FALSE,
+      'override all authored by option' => FALSE,
+    );
+    if (module_exists('comment')) {
+      $revoke['override all comment setting option'] = FALSE;
+    }
+  }
+
+  // Any specific grants not used anymore need to be deleted.
+  if (!empty($revoke)) {
+    foreach ($role_names as $rid => $name) {
+      user_role_change_permissions($rid, $revoke);
+    }
+  }
+
+  // Set a helpful message.
+  $message = 'Configuration saved.';
+  $arguments = array();
+  $status = 'status';
+
+  if ($alert) {
+    if (user_access('administer permissions')) {
+      $arguments = array('!permissions' => l(t('the permissions page'), 'admin/config/people/permissions', array('fragment' => 'module-override_node_options')));
+      $message .= t(' Please visit !permissions and double check access.');
+      $status = 'warning';
+    }
+    else {
+      $message .= t(' Please visit the permissions page and double check access.');
+    }
+  }
+
+  drupal_set_message(t($message, $arguments), $status);
+}
+
+
+/**
  * Implements hook_form_alter().
  */
-function override_node_options_form_alter(&$form, $form_state, $form_id) {
+function override_node_options_form_alter(&$form, &$form_state, $form_id) {
   if (!empty($form['#node_edit_form']) && !user_access('administer nodes')) {
     // Get a copy of the current node object.
     $node = $form['#node'];
-    
-    // Add access to the 'Revision information: log message' field
-    $form['revision_information']['log']['#access'] = user_access('enter ' . $node->type . ' revision log entry');
+
+    // Add access to the 'Revision information: log message' field.
+    $form['revision_information']['log']['#access'] = user_access('enter ' . $node->type . ' revision log entry') || user_access('enter all revision log entry');
 
     // Add access to the 'Revision information' fieldset.
-    $form['revision_information']['revision']['#access'] = user_access('override ' . $node->type . ' revision option');
+    $form['revision_information']['revision']['#access'] = user_access('override ' . $node->type . ' revision option') || user_access('override all revision option');
     $form['revision_information']['#access'] = element_get_visible_children($form['revision_information']);
 
     // Add access to the 'Authoring information' fieldset.
-    $form['author']['name']['#access'] = user_access('override ' . $node->type . ' authored by option');
-    $form['author']['date']['#access'] = user_access('override ' . $node->type . ' authored on option');
-    if (key_exists('#access', $form['author'])) {
+    $form['author']['name']['#access'] = user_access('override ' . $node->type . ' authored by option') || user_access('override all authored by option');
+    $form['author']['date']['#access'] = user_access('override ' . $node->type . ' authored on option') || user_access('override all authored on option');
+    if (array_key_exists('#access', $form['author'])) {
       $form['author']['#access'] |= element_get_visible_children($form['author']);
     }
     else {
       $form['author']['#access'] = element_get_visible_children($form['author']);
     }
-    
+
     // Add access to the 'Publishing options' fieldset.
-    $form['options']['status']['#access'] = user_access('override ' . $node->type . ' published option');
-    $form['options']['promote']['#access'] = user_access('override ' . $node->type . ' promote to front page option');
-    $form['options']['sticky']['#access'] = user_access('override ' . $node->type . ' sticky option');    
-    if (key_exists('#access', $form['options'])) {
+    $form['options']['status']['#access'] = user_access(sprintf('override %s published option', $node->type));
+    $form['options']['promote']['#access'] = user_access(sprintf('override %s promote to front page option', $node->type));
+    $form['options']['sticky']['#access'] = user_access(sprintf('override %s sticky option', $node->type));
+
+    // If access is granted for promote or sticky, show (but disable) status.
+    // This keeps core's JS working, and correctly populates the vertical tab.
+    if ($form['options']['status']['#access'] == FALSE && ($form['options']['promote']['#access'] || $form['options']['sticky']['#access'])) {
+      $form['options']['status']['#access'] = TRUE;
+      $form['options']['status']['#disabled'] = TRUE;
+    }
+
+    $form['options']['status']['#access'] = user_access('override ' . $node->type . ' published option') || user_access('override all published option');
+    $form['options']['promote']['#access'] = user_access('override ' . $node->type . ' promote to front page option') || user_access('override all promote to front page option');
+    $form['options']['sticky']['#access'] = user_access('override ' . $node->type . ' sticky option') || user_access('override all sticky option');
+    if (array_key_exists('#access', $form['options'])) {
       $form['options']['#access'] |= element_get_visible_children($form['options']);
     }
     else {
       $form['options']['#access'] = element_get_visible_children($form['options']);
     }
 
-    // @todo Remove when http://drupal.org/node/683630 is fixed.
-    if ($form['author']['name']['#access']) {
-      $form['#submit'][] = 'override_node_options_submit_node';
+    // Add access to the 'Comment settings' fieldset.
+    if (module_exists('comment') && isset($form['comment_settings'])) {
+      $form['comment_settings']['#access'] |= user_access('override ' . $node->type . ' comment setting option') || user_access('override all comment setting option');
     }
-  }
-}
 
-/**
- * Perform additional node form submission processing normally skipped by core.
- *
- * @todo Remove when http://drupal.org/node/683630 is fixed.
- */
-function override_node_options_submit_node($form, &$form_state) {
-  // Populate the "authored by" field.
-  if ($account = user_load_by_name($form_state['values']['name'])) {
-    $form_state['values']['uid'] = $account->uid;
-  }
-  else {
-    $form_state['values']['uid'] = 0;
+    if (!empty($form['#node_edit_form']) && !user_access('administer nodes')) {
+      $form['author']['#attached']['js'][1]['data']['anonymous'] = $form['author']['name']['#default_value'];
+    }
   }
 }

+ 155 - 52
sites/all/modules/contrib/admin/override_node_options/override_node_options.test

@@ -5,57 +5,86 @@
  * Unit tests for the override_node_options module.
  */
 
+/**
+ * Defines a base class for testing the Override Node Options module.
+ */
 class OverrideNodeOptionsTestCase extends DrupalWebTestCase {
-  protected $normal_user;
-  protected $admin_user;
+
+  /**
+   * A standard user with basic permissions.
+   *
+   * @var stdClass
+   */
+  protected $normalUser;
+
+  /**
+   * A page node to test against.
+   *
+   * @var stdClass
+   */
   protected $node;
 
+  /**
+   * {@inheritdoc}
+   */
   public static function getInfo() {
     return array(
       'name' => 'Override node options',
-      'description' => 'Functional tests for overridding options on node forms.',
+      'description' => 'Functional tests for overriding options on node forms.',
       'group' => 'Override node options',
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
   public function setUp() {
     parent::setUp('override_node_options');
-    $this->normal_user = $this->drupalCreateUser(array('create page content', 'edit any page content'));
+
+    $this->normalUser = $this->drupalCreateUser(array('create page content', 'edit any page content'));
     $this->node = $this->drupalCreateNode();
   }
 
   /**
-   * Assert that fields in a node were updated to certail values.
+   * Assert that fields in a node were updated to certain values.
    *
-   * @param $node
+   * @param \stdClass $node
    *   The node object to check (will be reloaded from the database).
-   * @param $fields
+   * @param array $fields
    *   An array of values to check equality, keyed by node object property.
    */
-  function assertNodeFieldsUpdated(stdClass $node, array $fields) {
+  private function assertNodeFieldsUpdated(stdClass $node, array $fields) {
     // Re-load the node from the database to make sure we have the current
     // values.
     $node = node_load($node->nid, NULL, TRUE);
     foreach ($fields as $field => $value) {
-      $this->assertEqual($node->$field, $value, t('Node @field was updated to !value, expected !expected.', array('@field' => $field, '!value' => var_export($node->$field, TRUE), '!expected' => var_export($value, TRUE))));
+      $this->assertEqual(
+        $node->$field,
+        $value,
+        t('Node @field was updated to !value, expected !expected.', array(
+          '@field' => $field,
+          '!value' => var_export($node->$field, TRUE),
+          '!expected' => var_export($value, TRUE),
+        ))
+      );
     }
   }
 
   /**
    * Assert that the user cannot access fields on node add and edit forms.
    *
-   * @param $node
+   * @param \stdClass $node
    *   The node object, will be used on the node edit form.
-   * @param $fields
+   * @param array $fields
    *   An array of form fields to check.
    */
-  function assertNodeFieldsNoAccess(stdClass $node, array $fields) {
-    $this->drupalGet('node/add/' . $node->type);
+  private function assertNodeFieldsNoAccess(stdClass $node, array $fields) {
+    $this->drupalGet("node/add/{$node->type}");
     foreach ($fields as $field) {
       $this->assertNoFieldByName($field);
     }
 
-    $this->drupalGet('node/' . $this->node->nid . '/edit');
+    $this->drupalGet("node/{$this->node->nid}/edit");
     foreach ($fields as $field) {
       $this->assertNoFieldByName($field);
     }
@@ -64,61 +93,135 @@ class OverrideNodeOptionsTestCase extends DrupalWebTestCase {
   /**
    * Test the 'Authoring information' fieldset.
    */
-  function testNodeOptions() {
-    $this->admin_user = $this->drupalCreateUser(array('create page content', 'edit any page content', 'override page published option', 'override page promote to front page option', 'override page sticky option'));
-    $this->drupalLogin($this->admin_user);
-
-    $fields = array(
-      'status' => (bool) !$this->node->status,
-      'promote' => (bool) !$this->node->promote,
-      'sticky' => (bool) !$this->node->sticky,
-    );
-    $this->drupalPost('node/' . $this->node->nid . '/edit', $fields, t('Save'));
-    $this->assertNodeFieldsUpdated($this->node, $fields);
+  protected function testNodeOptions() {
+    $specific_user = $this->drupalCreateUser(array(
+      'create page content',
+      'edit any page content',
+      'override page published option',
+      'override page promote to front page option',
+      'override page sticky option',
+      'override page comment setting option',
+    ));
+
+    $general_user = $this->drupalCreateUser(array(
+      'create page content',
+      'edit any page content',
+      'override all published option',
+      'override all promote to front page option',
+      'override all sticky option',
+      'override all comment setting option',
+    ));
+
+    foreach (array($specific_user, $general_user) as $account) {
+      $this->drupalLogin($account);
+
+      $fields = array(
+        'status' => (bool) !$this->node->status,
+        'promote' => (bool) !$this->node->promote,
+        'sticky' => (bool) !$this->node->sticky,
+        'comment' => COMMENT_NODE_OPEN,
+      );
+      $this->drupalPost("node/{$this->node->nid}/edit", $fields, t('Save'));
+      $this->assertNodeFieldsUpdated($this->node, $fields);
+    }
 
-    $this->drupalLogin($this->normal_user);
+    $this->drupalLogin($this->normalUser);
     $this->assertNodeFieldsNoAccess($this->node, array_keys($fields));
   }
 
   /**
    * Test the 'Revision information' fieldset.
    */
-  function testNodeRevisions() {
-    $this->admin_user = $this->drupalCreateUser(array('create page content', 'edit any page content', 'override page revision option'));
-    $this->drupalLogin($this->admin_user);
-
-    $fields = array(
-      'revision' => TRUE,
-    );
-    $this->drupalPost('node/' . $this->node->nid . '/edit', $fields, t('Save'));
-    $this->assertNodeFieldsUpdated($this->node, array('vid' => $this->node->vid + 1));
+  protected function testNodeRevisions() {
+    $specific_user = $this->drupalCreateUser(array(
+      'create page content',
+      'edit any page content',
+      'override page revision option',
+    ));
+
+    $general_user = $this->drupalCreateUser(array(
+      'create page content',
+      'edit any page content',
+      'override all revision option',
+    ));
+
+    foreach (array($specific_user, $general_user) as $account) {
+      $this->drupalLogin($account);
+
+      // Ensure that we have the latest node data.
+      $node = node_load($this->node->nid, NULL, TRUE);
+
+      $fields = array(
+        'revision' => TRUE,
+      );
+      $this->drupalPost("node/{$node->nid}/edit", $fields, t('Save'));
+      $this->assertNodeFieldsUpdated($node, array('vid' => $node->vid + 1));
+    }
 
-    $this->drupalLogin($this->normal_user);
+    $this->drupalLogin($this->normalUser);
     $this->assertNodeFieldsNoAccess($this->node, array_keys($fields));
   }
 
   /**
    * Test the 'Authoring information' fieldset.
    */
-  function testNodeAuthor() {
-    $this->admin_user = $this->drupalCreateUser(array('create page content', 'edit any page content', 'override page authored on option', 'override page authored by option'));
-    $this->drupalLogin($this->admin_user);
-
-    $this->drupalPost('node/' . $this->node->nid . '/edit', array('name' => 'invalid-user'), t('Save'));
-    $this->assertText('The username invalid-user does not exist.');
+  protected function testNodeAuthor() {
+    $specific_user = $this->drupalCreateUser(array(
+      'create page content',
+      'edit any page content',
+      'override page authored on option',
+      'override page authored by option',
+    ));
+
+    $general_user = $this->drupalCreateUser(array(
+      'create page content',
+      'edit any page content',
+      'override all authored on option',
+      'override all authored by option',
+    ));
+
+    foreach (array($specific_user, $general_user) as $account) {
+      $this->drupalLogin($account);
+
+      $this->drupalPost("node/{$this->node->nid}/edit", array('name' => 'invalid-user'), t('Save'));
+      $this->assertText('The username invalid-user does not exist.');
+
+      $this->drupalPost("node/{$this->node->nid}/edit", array('date' => 'invalid-date'), t('Save'));
+      $this->assertText('You have to specify a valid date.');
+
+      $time = time() + 500;
+      $fields = array(
+        'name' => '',
+        'date' => format_date($time, 'custom', 'Y-m-d H:i:s O'),
+      );
+      $this->drupalPost("node/{$this->node->nid}/edit", $fields, t('Save'));
+      $this->assertNodeFieldsUpdated($this->node, array('uid' => 0, 'created' => $time));
+    }
 
-    $this->drupalPost('node/' . $this->node->nid . '/edit', array('date' => 'invalid-date'), t('Save'));
-    $this->assertText('You have to specify a valid date.');
+    $this->drupalLogin($this->normalUser);
+    $this->assertNodeFieldsNoAccess($this->node, array_keys($fields));
+  }
 
-    $time = time() + 500;
-    $fields = array(
-      'name' => '',
-      'date' => format_date($time, 'custom', 'Y-m-d H:i:s O'),
+  /**
+   * Ensure that the node created date does not change when the node is edited.
+   */
+  public function testNodeCreatedDateDoesNotChange() {
+    $this->drupalLogin(
+      $this->drupalCreateUser(array('edit any page content'))
     );
-    $this->drupalPost('node/' . $this->node->nid . '/edit', $fields, t('Save'));
-    $this->assertNodeFieldsUpdated($this->node, array('uid' => 0, 'created' => $time));
 
-    $this->drupalLogin($this->normal_user);
-    $this->assertNodeFieldsNoAccess($this->node, array_keys($fields));
+    $node = $this->drupalCreateNode();
+
+    // Update the node.
+    $this->drupalPost("node/{$node->nid}/edit", array(), t('Save'));
+
+    // Load a new instance of the node.
+    $node2 = node_load($node->nid);
+
+    // Ensure that the node was updated by comparing the changed dates, but the
+    // created dates still match.
+    $this->assertNotEqual($node->changed, $node2->changed, t('Changed values do not match.'));
+    $this->assertEqual($node->created, $node2->created, t('Created values do match.'));
   }
+
 }

+ 36 - 6
sites/all/modules/contrib/admin/redirect/redirect.admin.inc

@@ -18,6 +18,7 @@ function redirect_list_form($form, &$form_state) {
   $header = array(
     'source' => array('data' => t('From'), 'field' => 'source', 'sort' => 'asc'),
     'redirect' => array('data' => t('To'), 'field' => 'redirect'),
+    'status' => array('data' => t('Status'), 'field' => 'status'),
     'status_code' => array('data' => t('Type'), 'field' => 'status_code'),
     'language' => array('data' => t('Language'), 'field' => 'language'),
     'count' => array('data' => t('Count'), 'field' => 'count'),
@@ -84,6 +85,7 @@ function redirect_list_form($form, &$form_state) {
     drupal_alter('redirect_url', $redirect->redirect, $redirect->redirect_options);
     $row['source'] = l($source_url, $redirect->source, $redirect->source_options);
     $row['redirect'] = l($redirect_url, $redirect->redirect, $redirect->redirect_options);
+    $row['status'] = $redirect->status ? t('Enabled') : t('Disabled');
     $row['status_code'] = $redirect->status_code ? $redirect->status_code : t('Default (@default)', array('@default' => $default_status_code));
     $row['language'] = module_invoke('locale', 'language_name', $redirect->language);
     $row['count'] = $redirect->count;
@@ -262,6 +264,10 @@ function redirect_list_form_operations_submit($form, &$form_state) {
     }
     call_user_func_array($function, $args);
 
+    // We display the number of redirects the user selected, regardless of
+    // how many redirects actually changed status. Eg. if 1 enabled and 1
+    // disabled redirects are checked for being enabled, we'll still display
+    // "Enabled 1 redirect."
     $count = count($form_state['values']['rids']);
     watchdog('redirect', '@action @count redirects.', array('@action' => $operation['action_past'], '@count' => $count));
     drupal_set_message(format_plural(count($rids), '@action @count redirect.', '@action @count redirects.', array('@action' => $operation['action_past'], '@count' => $count)));
@@ -336,6 +342,10 @@ function redirect_edit_form($form, &$form_state, $redirect = NULL) {
     '#type' => 'value',
     '#value' => $redirect->hash,
   );
+  $form['uid'] = array(
+    '#type' => 'value',
+    '#value' => $redirect->uid,
+  );
 
   $form['source'] = array(
     '#type' => 'textfield',
@@ -361,6 +371,7 @@ function redirect_edit_form($form, &$form_state, $redirect = NULL) {
     '#description' => t('Enter an internal Drupal path, path alias, or complete external URL (like http://example.com/) to redirect to. Use %front to redirect to the front page.', array('%front' => '<front>')),
     '#element_validate' => array('redirect_element_validate_redirect'),
   );
+
   $form['redirect_options'] = array(
     '#type' => 'value',
     '#value' => $redirect->redirect_options,
@@ -373,6 +384,14 @@ function redirect_edit_form($form, &$form_state, $redirect = NULL) {
     '#value' => $redirect->language,
   );
 
+  $form['status'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enabled'),
+    '#description' => t('If this box is checked, this redirect will be enabled.'),
+    '#default_value' => $redirect->status,
+    '#required' => FALSE,
+  );
+
   $form['advanced'] = array(
     '#type' => 'fieldset',
     '#title' => t('Advanced options'),
@@ -465,7 +484,7 @@ function redirect_element_validate_redirect($element, &$form_state) {
   // Normalize the path.
   $value = drupal_get_normal_path($value, $form_state['values']['language']);
 
-  if (!valid_url($value) && !valid_url($value, TRUE) && $value != '<front>' && $value != '') {
+  if (!valid_url($value) && !valid_url($value, TRUE) && $value != '<front>' && $value != '' && !file_exists($value)) {
     form_error($element, t('The redirect path %value is not valid.', array('%value' => $value)));
   }
 
@@ -520,10 +539,13 @@ function redirect_edit_form_validate($form, &$form_state) {
   $redirect = (object) $form_state['values'];
 
   if (empty($form_state['values']['override'])) {
-    if ($existing = redirect_load_by_source($redirect->source, $redirect->language)) {
+    // Find out if any (disabled or enabled) redirect with this source already
+    // exists.
+    if ($existing = redirect_load_by_source($redirect->source, $redirect->language, array(), FALSE)) {
       if ($redirect->rid != $existing->rid && $redirect->language == $existing->language) {
-        // The "from" path should not conflict with another redirect
-        $form_state['storage']['override_messages']['redirect-conflict'] = t('The base source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => $redirect->source, '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid)));
+        // The "from" path should not conflict with another (disabled or
+        // enabled) redirect.
+        $form_state['storage']['override_messages']['redirect-conflict'] = t('A redirect already exists for the source path %source. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => $redirect->source, '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid)));
         $form_state['rebuild'] = TRUE;
       }
     }
@@ -621,7 +643,7 @@ function redirect_settings_form($form, &$form_state) {
     '#type' => 'checkbox',
     '#title' => t('Allow redirects to be saved into the page cache.'),
     '#default_value' => variable_get('redirect_page_cache', 0),
-    '#description' => t('This feature requires <a href="@performance">Cache pages for anonymous users</a> to be enabled and the %variable variable to be TRUE.', array('@performance' => url('admin/config/development/performance'), '%variable' => "\$conf['page_cache_invoke_hooks']")),
+    '#description' => t('This feature requires <a href="@performance">Cache pages for anonymous users</a> to be enabled and the %variable variable to be true (currently set to @value).', array('@performance' => url('admin/config/development/performance'), '%variable' => "\$conf['page_cache_invoke_hooks']", '@value' => var_export(variable_get('page_cache_invoke_hooks', TRUE), TRUE))),
     '#disabled' => !variable_get('cache', 0) || !variable_get('page_cache_invoke_hooks', TRUE),
   );
   $form['redirect_purge_inactive'] = array(
@@ -629,7 +651,7 @@ function redirect_settings_form($form, &$form_state) {
     '#title' => t('Delete redirects that have not been accessed for'),
     '#default_value' => variable_get('redirect_purge_inactive', 0),
     '#options' => array(0 => t('Never (do not discard)')) + drupal_map_assoc(array(604800, 1209600, 1814400, 2592000, 5184000, 7776000, 10368000, 15552000, 31536000), 'format_interval'),
-    '#description' => t('Only redirects managaged by the redirect module itself will be deleted. Redirects managed by other modules will be left alone.'),
+    '#description' => t('Only redirects managed by the redirect module itself will be deleted. Redirects managed by other modules will be left alone.'),
     '#disabled' => variable_get('redirect_page_cache', 0) && !variable_get('page_cache_invoke_hooks', TRUE),
   );
 
@@ -814,6 +836,7 @@ function redirect_list_table($redirects, $header) {
   $header = array_intersect_key(array(
     'source' => array('data' => t('From'), 'field' => 'source', 'sort' => 'asc'),
     'redirect' => array('data' => t('To'), 'field' => 'redirect'),
+    'status' => array('data' => t('Status'), 'field' => 'status'),
     'status_code' => array('data' => t('Type'), 'field' => 'status_code'),
     'language' => array('data' => t('Language'), 'field' => 'language'),
     'count' => array('data' => t('Count'), 'field' => 'count'),
@@ -834,6 +857,7 @@ function redirect_list_table($redirects, $header) {
     $redirect_url = redirect_url($redirect->redirect, array_merge($redirect->redirect_options, array('alias' => TRUE)));
     $row['data']['source'] = l($source_url, $redirect->source, $redirect->source_options);
     $row['data']['redirect'] = l($redirect_url, $redirect->redirect, $redirect->redirect_options);
+    $row['data']['status'] = $redirect->status ? t('Enabled') : t('Disabled');
     $row['data']['status_code'] = $redirect->status_code ? $redirect->status_code : t('Default (@default)', array('@default' => $default_status_code));
     $row['data']['language'] = module_invoke('locale', 'language_name', $redirect->language);
     $row['data']['count'] = $redirect->count;
@@ -852,6 +876,12 @@ function redirect_list_table($redirects, $header) {
       $row['class'][] = 'warning';
       $row['title'] = t('This redirect overrides an existing internal path.');
     }
+    if ($redirect->status) {
+      $row['class'][] = 'redirect-enabled';
+    }
+    else {
+      $row['class'][] = 'redirect-disabled';
+    }
 
     $operations = array();
     if (redirect_access('update', $redirect)) {

+ 20 - 0
sites/all/modules/contrib/admin/redirect/redirect.controller.inc

@@ -0,0 +1,20 @@
+<?php
+
+
+/**
+ * Controller class for redirects.
+ *
+ * This extends the DrupalDefaultEntityController class, adding required
+ * special handling for redirect objects.
+ */
+class RedirectController extends DrupalDefaultEntityController {
+
+  protected function attachLoad(&$redirects, $revision_id = FALSE) {
+    // Unserialize the URL option fields.
+    foreach ($redirects as $key => $redirect) {
+      $redirects[$key]->source_options = unserialize($redirect->source_options);
+      $redirects[$key]->redirect_options = unserialize($redirect->redirect_options);
+    }
+    parent::attachLoad($redirects, $revision_id);
+  }
+}

+ 4 - 6
sites/all/modules/contrib/admin/redirect/redirect.info

@@ -1,9 +1,7 @@
 name = Redirect
 description = Allows users to redirect from old URLs to new URLs.
 core = 7.x
-files[] = redirect.module
-files[] = redirect.admin.inc
-files[] = redirect.install
+files[] = redirect.controller.inc
 files[] = redirect.test
 files[] = views/redirect.views.inc
 ;files[] = views/redirect_handler_field_redirect_type.inc
@@ -15,9 +13,9 @@ files[] = views/redirect_handler_field_redirect_link_edit.inc
 files[] = views/redirect_handler_field_redirect_link_delete.inc
 configure = admin/config/search/redirect/settings
 
-; Information added by drupal.org packaging script on 2012-09-18
-version = "7.x-1.0-rc1"
+; Information added by Drupal.org packaging script on 2015-07-08
+version = "7.x-1.0-rc3"
 core = "7.x"
 project = "redirect"
-datestamp = "1347989995"
+datestamp = "1436393342"
 

+ 66 - 2
sites/all/modules/contrib/admin/redirect/redirect.install

@@ -86,7 +86,14 @@ function redirect_schema() {
         'unsigned' => TRUE,
         'not null' => TRUE,
         'default' => 0,
-        'description' => 'The timestamp of when the redirect was last accessed.'
+        'description' => 'The timestamp of when the redirect was last accessed.',
+      ),
+      'status' => array(
+        'type' => 'int',
+        'size' => 'small',
+        'not null' => TRUE,
+        'default' => 1,
+        'description' => 'Boolean indicating whether the redirect is enabled (visible to non-administrators).',
       ),
     ),
     'primary key' => array('rid'),
@@ -95,7 +102,11 @@ function redirect_schema() {
     ),
     'indexes' => array(
       'expires' => array('type', 'access'),
-      'source_language' => array('source', 'language'),
+      'status_source_language' => array(
+        'status',
+        'source',
+        'language',
+      ),
     ),
   );
 
@@ -264,6 +275,58 @@ function redirect_update_7000(&$sandbox) {
 }
 
 /**
+ * Rebuild the registry and clear the entity info cache.
+ */
+function redirect_update_7100() {
+  if (!class_exists('RedirectController')) {
+    registry_rebuild();
+    entity_info_cache_clear();
+  }
+}
+
+/**
+ * Add status field.
+ */
+function redirect_update_7101() {
+  db_add_field('redirect', 'status', array(
+    'type' => 'int',
+    'size' => 'small',
+    'not null' => TRUE,
+    'default' => 1,
+    'description' => 'Boolean indicating whether the redirect is enabled (visible to non-administrators).',
+  ));
+  db_drop_index('redirect', 'source_language');
+  db_add_index('redirect', 'status_source_language', array(
+    'status',
+    'source',
+    'language',
+  ));
+}
+
+/**
+ * Disable redirects that could cause infinite loops.
+ */
+function redirect_update_7102() {
+  $rids = db_query("SELECT r.rid FROM {redirect} r INNER JOIN {url_alias} u ON r.source = u.alias AND r.redirect = u.source AND r.language = u.language")->fetchCol();
+  if ($rids) {
+    // Disable redirects
+    $count = db_update('redirect')
+      ->fields(array('status' => 0))
+      ->condition('rid', $rids)
+      ->execute();
+
+    $disabled_redirects_message = format_plural($count,
+      '1 circular redirect causing infinite loop was disabled.',
+      '@count circular redirects causing infinite loop were disabled.');
+
+    return $disabled_redirects_message;
+  }
+  else {
+    return t('No circular redirects were found that could cause infinite loops.');
+  }
+}
+
+/**
  * Migrate a path redirect redirect to a redirect redirect.
  */
 function _redirect_migrate_path_redirect_redirect($old_redirect) {
@@ -315,6 +378,7 @@ function _redirect_migrate_path_redirect_redirect($old_redirect) {
         'redirect_options' => serialize($redirect->redirect_options),
         'language' => $redirect->language,
         'status_code' => $redirect->status_code,
+        'status' => 1,
         'count' => 0,
         'access' => $old_redirect->last_used,
       ))

+ 14 - 3
sites/all/modules/contrib/admin/redirect/redirect.js

@@ -4,12 +4,23 @@
 Drupal.behaviors.redirectFieldsetSummaries = {
   attach: function (context) {
     $('fieldset.redirect-list', context).drupalSetSummary(function (context) {
-      if ($('table.redirect-list tbody td.empty', context).size()) {
+      if ($('table.redirect-list tbody td.empty', context).length) {
         return Drupal.t('No redirects');
       }
       else {
-        var redirects = $('table.redirect-list tbody tr').size();
-        return Drupal.formatPlural(redirects, '1 redirect', '@count redirects');
+        var enabled_redirects = $('table.redirect-list tbody tr.redirect-enabled', context).length;
+        var disabled_redirects = $('table.redirect-list tbody tr.redirect-disabled', context).length;
+        var text = '';
+        if (enabled_redirects > 0) {
+          var text = Drupal.formatPlural(enabled_redirects, '1 enabled redirect', '@count enabled redirects');
+        }
+        if (disabled_redirects > 0) {
+          if (text.length > 0) {
+            text = text + '<br />';
+          }
+          text = text + Drupal.formatPlural(disabled_redirects, '1 disabled redirect', '@count disabled redirects');
+        }
+        return text;
       }
     });
   }

+ 169 - 63
sites/all/modules/contrib/admin/redirect/redirect.module

@@ -47,24 +47,6 @@ function redirect_entity_info() {
 }
 
 /**
- * Controller class for redirects.
- *
- * This extends the DrupalDefaultEntityController class, adding required
- * special handling for redirect objects.
- */
-class RedirectController extends DrupalDefaultEntityController {
-
-  protected function attachLoad(&$redirects, $revision_id = FALSE) {
-    // Unserialize the URL option fields.
-    foreach ($redirects as $key => $redirect) {
-      $redirects[$key]->source_options = unserialize($redirect->source_options);
-      $redirects[$key]->redirect_options = unserialize($redirect->redirect_options);
-    }
-    parent::attachLoad($redirects, $revision_id);
-  }
-}
-
-/**
  * Implements hook_hook_info().
  */
 function redirect_hook_info() {
@@ -245,11 +227,12 @@ function redirect_url_inbound_alter(&$path, $original_path, $path_language) {
   }
 
   // Redirect to canonical URLs.
-  if ($path && variable_get('redirect_canonical', 1)) {
-    $alias = drupal_get_path_alias($path, $path_language);
-    if ($alias != $path && $alias != $original_path) {
+  // Commented out per https://www.drupal.org/node/2048137.
+  //if ($path && variable_get('redirect_canonical', 1)) {
+    //$alias = drupal_get_path_alias($path, $path_language);
+    //if ($alias != $path && $alias != $original_path) {
       //return redirect_redirect(array('redirect' => $alias, 'type' => 'global'));
-    }
+    //}
 
     // Redirect from default entity paths to the proper entity path.
     //if ($path_entity = redirect_load_entity_from_path($path)) {
@@ -259,7 +242,7 @@ function redirect_url_inbound_alter(&$path, $original_path, $path_language) {
     //    }
     //  }
     //}
-  }
+  //}
 }
 
 /**
@@ -394,6 +377,8 @@ function redirect_path_update(array $path) {
   }
 
   if (!empty($path['original']['pid']) && $path['original']['pid'] == $path['pid'] && $path['original']['alias'] != $path['alias']) {
+    // Disable all redirects having the same source as this alias.
+    redirect_disable_by_path($path['alias'], $path['language']);
     $redirect = new stdClass();
     redirect_object_prepare($redirect);
     $redirect->source = $path['original']['alias'];
@@ -401,9 +386,25 @@ function redirect_path_update(array $path) {
     $redirect->language = $path['original']['language'];
     // Check if the redirect exists before saving.
     $hash = redirect_hash($redirect);
-    if (!redirect_load_by_hash($hash)) {
+    $existing = redirect_load_by_hash($hash);
+    if (!$existing) {
       redirect_save($redirect);
     }
+    // If the existing redirect is disabled, re-enable it.
+    elseif (isset($existing->status) && $existing->status == 0)  {
+      $existing->status = 1;
+      redirect_save($existing);
+    }
+  }
+}
+
+/**
+ * Implements hook_path_insert().
+ */
+function redirect_path_insert(array $path) {
+  if (!empty($path['alias'])) {
+    // Disable all redirects having the same source as this alias.
+    redirect_disable_by_path($path['alias'], $path['language']);
   }
 }
 
@@ -542,24 +543,26 @@ function redirect_load_by_hash($hash, $reset = FALSE) {
 }
 
 /**
- * Load multiple URL redirects from the database by {redirect}.source.
+ * Fetches multiple URL redirect IDs from the database by {redirect}.source.
  *
  * @param $source
  *   The source of the URL redirect.
+ * @param $language
+ *   Language of the source URL.
+ * @param $enabled_only
+ *   Boolean that indicates whether to only load enabled redirects.
  *
- * @return
- *   An array of URL redirect objects indexed by redirect IDs.
- *
- * @see redirect_load_multiple()
- * @see _redirect_uasort()
- * @see redirect_compare_array_recursive()
- *
- * @ingroup redirect_api
+ * @return array
+ *   An indexed array of IDs, or an empty array if there is no result set.
  */
-function redirect_load_by_source($source, $language = LANGUAGE_NONE, array $query = array()) {
+function redirect_fetch_rids_by_path($source, $language, $enabled_only = FALSE) {
   // Run a case-insensitive query for matching RIDs first.
   $rid_query = db_select('redirect');
   $rid_query->addField('redirect', 'rid');
+  // Prevent errors if redirect_update_7101() has not yet been run.
+  if ($enabled_only && db_field_exists('redirect', 'status')) {
+    $rid_query->condition('status', 1);
+  }
   if ($source != variable_get('site_frontpage', 'node')) {
     $rid_query->condition('source', db_like($source), 'LIKE');
   }
@@ -571,7 +574,33 @@ function redirect_load_by_source($source, $language = LANGUAGE_NONE, array $quer
   }
   $rid_query->condition('language', array($language, LANGUAGE_NONE));
   $rids = $rid_query->execute()->fetchCol();
+  return $rids;
+}
 
+
+/**
+ * Load multiple URL redirects from the database by {redirect}.source.
+ *
+ * @param $source
+ *   The source of the URL redirect.
+ * @param $language
+ *   Language of the source URL.
+ * @param $query
+ *   Array of URL query parameters.
+ * @param $enabled_only
+ *   Boolean that indicates whether to only load enabled redirects.
+ *
+ * @return
+ *   The first matched URL redirect object, or FALSE if there aren't any.
+ *
+ * @see redirect_load_multiple()
+ * @see _redirect_uasort()
+ * @see redirect_compare_array_recursive()
+ *
+ * @ingroup redirect_api
+ */
+function redirect_load_by_source($source, $language = LANGUAGE_NONE, array $query = array(), $enabled_only = TRUE) {
+  $rids = redirect_fetch_rids_by_path($source, $language, $enabled_only);
   if ($rids && $redirects = redirect_load_multiple($rids)) {
     // Narrow down the list of candidates.
     foreach ($redirects as $rid => $redirect) {
@@ -700,11 +729,11 @@ function redirect_validate($redirect, $form, &$form_state) {
   redirect_hash($redirect);
   if ($existing = redirect_load_by_hash($redirect->hash)) {
     if ($redirect->rid != $existing->rid) {
-      form_set_error('source', t('The source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => redirect_url($redirect->source, $redirect->source_options), '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid))));
+      form_set_error('source', t('A redirect already exists for the source path %source. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => redirect_url($redirect->source, $redirect->source_options), '@edit-page' => url('admin/config/search/redirect/edit/'. $existing->rid))));
     }
   }
 
-  // Allow other modules to validate the SSH public key.
+  // Allow other modules to validate the redirect.
   foreach (module_implements('redirect_validate') as $module) {
     $function = $module . '_redirect_validate';
     $function($redirect, $form, $form_state);
@@ -723,6 +752,7 @@ function redirect_object_prepare($redirect, $defaults = array()) {
     'count' => 0,
     'access' => 0,
     'hash' => '',
+    'status' => 1,
   );
 
   foreach ($defaults as $key => $default) {
@@ -866,6 +896,28 @@ function redirect_delete_by_path($path) {
 }
 
 /**
+ * Disable any redirects associated with a path.
+ *
+ * Given a source like 'node/1' this function will delete any redirects that
+ * have that specific source.
+ *
+ * @param $path
+ *   An string with an internal Drupal path.
+ *
+ * @param @langauge
+ *   The langcode of the path.
+ *
+ * @ingroup redirect_api
+ */
+function redirect_disable_by_path($path, $language) {
+  $rids = redirect_fetch_rids_by_path($path, $language, FALSE);
+
+  if ($rids) {
+    return redirect_change_status_multiple($rids, 0);
+  }
+}
+
+/**
  * Delete an entity URL alias and any of its sub-paths.
  *
  * This function also checks to see if the default entity URI is different from
@@ -879,7 +931,8 @@ function redirect_delete_by_path($path) {
  * @ingroup redirect_api
  */
 function redirect_delete_by_entity_path($entity_type, $entity) {
-  if ($uri = entity_uri($entity_type, $entity)) {
+  $uri = entity_uri($entity_type, $entity);
+  if (!empty($uri['path'])) {
     redirect_delete_by_path($uri['path']);
   }
 
@@ -894,6 +947,48 @@ function redirect_delete_by_entity_path($entity_type, $entity) {
 }
 
 /**
+ * Change the status of multiple URL redirects.
+ *
+ * @param array $rids
+ *   An array of redirect IDs to disable.
+ * @param int|string $status
+ *   The status to set the redirect to: either disabled (0) or enabled (1).
+ *
+ * @ingroup redirect_api
+ */
+function redirect_change_status_multiple(array $rids, $status) {
+  if ($status !== 0 && $status !== 1 && $status !== '0' && $status !== '1') {
+    watchdog('Cannot change redirect status to %status', array('%status' => $status));
+    drupal_set_message(t('Cannot change redirect status to %status', array('%status' => $status)));
+    return;
+  }
+  if (!empty($rids)) {
+    $redirects = redirect_load_multiple($rids, array(), TRUE);
+
+    foreach ($redirects as $rid => $redirect) {
+      if (isset($redirect->status) && $redirect->status == $status) {
+        // We no-op if the redirect is not actually changing status.
+        // So if a disabled redirect is disabled, neither redirect_save() is
+        // triggered, nor do we log any message.
+        continue;
+      }
+
+      $redirect->status = $status;
+      redirect_save($redirect);
+
+      if ($status) {
+        $redirect_link = l($redirect->redirect, $redirect->redirect) . '=> ' . l($redirect->source, $redirect->source);
+        watchdog('redirect', 'Enabled redirect: !redirect_source_link', array('!redirect_link' => $redirect_link));
+      }
+      else {
+        $redirect_link = l($redirect->redirect, $redirect->redirect) . '=> ' . l($redirect->source, $redirect->source);
+        watchdog('redirect', 'Disabled redirect: !redirect_source_link', array('!redirect_link' => $redirect_link));
+      }
+    }
+  }
+}
+
+/**
  * Delete multiple URL redirects.
  *
  * @param $rids
@@ -947,10 +1042,15 @@ function redirect_purge_inactive_redirects(array $types = array('redirect'), $in
     $interval = variable_get('redirect_purge_inactive', 0);
   }
 
-  if (!$interval || !variable_get('redirect_page_cache', 0) || !variable_get('page_cache_invoke_hooks', TRUE)) {
+  if (!$interval) {
+    return FALSE;
+  }
+
+  if (variable_get('redirect_page_cache', 0) && !variable_get('page_cache_invoke_hooks', TRUE)) {
     // If serving redirects from the page cache is enabled and hooks are not
     // executed during page caching, then we cannot track when a redirect is
     // used. Therefore, we cannot remove unused redirects.
+    watchdog('redirect', 'Due to existing settings, could not track when a redirect is used, so could not remove unused redirects.');
     return FALSE;
   }
 
@@ -979,17 +1079,6 @@ function redirect_purge_inactive_redirects(array $types = array('redirect'), $in
  * @ingroup redirect_api
  */
 function redirect_redirect($redirect = NULL) {
-  // First check if we're in an infinite loop.
-  $session_id = session_id();
-  if (flood_is_allowed('redirection', 5, 15, $session_id ? $session_id : NULL)) {
-    flood_register_event('redirection', 60, $session_id ? $session_id : NULL);
-  }
-  else {
-    watchdog('redirect', 'Infinite loop stopped.');
-    drupal_set_message('Oops, looks like this request tried to create an infinite loop. We do not allow such things here. We are a professional website!');
-    return FALSE;
-  }
-
   if (!isset($redirect)) {
     $redirect = new stdClass();
   }
@@ -1017,6 +1106,10 @@ function redirect_redirect($redirect = NULL) {
 
   // Continue if the redirect has not been disabled by hook_redirect_alter().
   if (isset($redirect->redirect) && isset($redirect->callback) && $redirect->redirect !== FALSE && function_exists($redirect->callback)) {
+    // Prevent circular redirects.
+    if ($GLOBALS['base_root'] . request_uri() == url($redirect->redirect, array('absolute' => TRUE) + $redirect->redirect_options)) {
+      return FALSE;
+    }
     // Perform the actual redirect.
     $callback = $redirect->callback;
     $callback($redirect);
@@ -1150,7 +1243,7 @@ function redirect_can_redirect() {
     $path = current_path();
     $can_redirect = TRUE;
 
-    if ($_SERVER['SCRIPT_NAME'] != $GLOBALS['base_path'] . 'index.php') {
+    if (!preg_match('/index\.php$/', $_SERVER['SCRIPT_NAME'])) {
       // Do not redirect if the root script is not /index.php.
       $can_redirect = FALSE;
     }
@@ -1162,7 +1255,7 @@ function redirect_can_redirect() {
       // If this is a command line request (Drush, etc), skip processing.
       $can_redirect = FALSE;
     }
-    elseif (variable_get('maintenance_mode', 0) || defined('MAINTENANCE_MODE')) {
+    elseif ((variable_get('maintenance_mode', 0) || defined('MAINTENANCE_MODE')) && !user_access('access site in maintenance mode')) {
       // Do not redirect in offline or maintenance mode.
       $can_redirect = FALSE;
     }
@@ -1176,7 +1269,7 @@ function redirect_can_redirect() {
 }
 
 /**
- * Compare tha all values and associations in one array match another array.
+ * Compare that all values and associations in one array match another array.
  *
  * We cannot use array_diff_assoc() here because we need to be recursive.
  *
@@ -1457,7 +1550,7 @@ function redirect_field_attach_form($entity_type, $entity, &$form, &$form_state,
   }
 
   $uri = entity_uri($entity_type, $entity);
-  if (empty($uri)) {
+  if (empty($uri['path'])) {
     // If the entity has no source path, then we cannot lookup the existing
     // redirects.
     return;
@@ -1467,7 +1560,6 @@ function redirect_field_attach_form($entity_type, $entity, &$form, &$form_state,
   $form['redirect'] = array(
     '#type' => 'fieldset',
     '#title' => t('URL redirects'),
-    '#description' => t('The following are a list of URL redirects that point to this @entitytype.', array('@entitytype' => drupal_strtolower($info['label']))),
     '#collapsible' => TRUE,
     '#collapsed' => TRUE,
     '#access' => user_access('administer redirects'),
@@ -1483,13 +1575,6 @@ function redirect_field_attach_form($entity_type, $entity, &$form, &$form_state,
     }
   }
 
-  // We don't have to put our include in $form_state['build_info']['files']
-  // since the build array will already be cached.
-  module_load_include('inc', 'redirect', 'redirect.admin');
-  $redirects = redirect_load_multiple(FALSE, array('redirect' => $uri['path']));
-  $header = array('source', 'status_code', 'language', 'count', 'access', 'operations');
-  $form['redirect'] += redirect_list_table($redirects, $header);
-
   $redirect = array(
     'redirect' => $uri['path'],
     'redirect_options' => array_diff_key($uri['options'], array('entity_type' => '', 'entity' => '')),
@@ -1508,6 +1593,13 @@ function redirect_field_attach_form($entity_type, $entity, &$form, &$form_state,
       'query' => array_filter($redirect) + drupal_get_destination(),
     );
   }
+
+  // We don't have to put our include in $form_state['build_info']['files']
+  // since the build array will already be cached.
+  module_load_include('inc', 'redirect', 'redirect.admin');
+  $redirects = redirect_load_multiple(FALSE, array('redirect' => $uri['path']));
+  $header = array('source', 'status', 'status_code', 'language', 'count', 'access', 'operations');
+  $form['redirect'] += redirect_list_table($redirects, $header);
 }
 
 /**
@@ -1516,8 +1608,8 @@ function redirect_field_attach_form($entity_type, $entity, &$form, &$form_state,
 function redirect_field_extra_fields() {
   $entity_info = entity_get_info();
   foreach (array_keys($entity_info) as $entity_type) {
-    if ($entity_type == 'comment') {
-      // The comment entity type supports URIs, but they're not real.
+    if (!redirect_entity_type_supports_redirects($entity_type)) {
+      // Redirect is explicitly disabled for this entity type.
       continue;
     }
     foreach (array_keys($entity_info[$entity_type]['bundles']) as $bundle) {
@@ -1564,5 +1656,19 @@ function redirect_redirect_operations() {
     'callback' => 'redirect_delete_multiple',
     'confirm' => TRUE,
   );
+  $operations['disable'] = array(
+    'action' => t('Disable'),
+    'action_past' => t('Disabled'),
+    'callback' => 'redirect_change_status_multiple',
+    'callback arguments' => array(0),
+    'confirm' => TRUE,
+  );
+  $operations['enable'] = array(
+    'action' => t('Enable'),
+    'action_past' => t('Enabled'),
+    'callback' => 'redirect_change_status_multiple',
+    'callback arguments' => array(1),
+    'confirm' => TRUE,
+  );
   return $operations;
 }

+ 52 - 9
sites/all/modules/contrib/admin/redirect/redirect.test

@@ -229,16 +229,59 @@ class RedirectFunctionalTest extends RedirectTestHelper {
     $node = $this->drupalCreateNode(array('type' => 'article', 'path' => array('alias' => 'first-alias')));
 
     // Change the node's alias will create an automatic redirect from 'first-alias' to the node.
-    $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), 'Save');
-    //$redirect = redirect_load_by_source('first-alias');
-    //$this->assertRedirect($redirect);
+    $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), t('Save'));
+    $this->drupalGet('first-alias');
+    $this->assertText($node->title);
+
+    $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'first-alias'), t('Save'));
+    $this->assertResponse(200, "Changing node's alias back to 'first-alias' does not break page load with a circular redirect.");
+    $this->assertNoText('Infinite redirect loop prevented.');
+    $this->drupalGet('second-alias');
+    $this->assertText($node->title);
+
+    $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), t('Save'));
+    $this->assertResponse(200, "Changing node's alias back to 'second-alias' does not break page load with a circular redirect.");
+    $this->assertNoText('Infinite redirect loop prevented.');
+    // Check that first-alias redirect has been re-enabled.
+    $this->drupalGet('first-alias');
+    $this->assertText($node->title);
+  }
+
+  function testPathAddOverwriteRedirects() {
+    // Create an initial article node with a path alias.
+    $first_node = $this->drupalCreateNode(array('type' => 'article', 'path' => array('alias' => 'first-alias')));
+    // Change the node's alias will create an automatic redirect from 'first-alias' to the node.
+    $this->drupalPost("node/{$first_node->nid}/edit", array('path[alias]' => 'second-alias'), t('Save'));
+
+    // Now create a second article node with the same alias as the redirect
+    // created above.
+    $second_node = $this->drupalCreateNode(array('type' => 'article', 'path' => array('alias' => 'first-alias')));
 
-    $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'first-alias'), 'Save');
-    //$redirect = redirect_load_by_source('second-alias');
-    //$this->assertRedirect($redirect);
+    // Visit the path 'first-alias' which should be an alias for $second_node.
+    $this->drupalGet('first-alias');
+    $this->assertNoText($first_node->title, 'Adding a new path alias that matches an existing redirect disables the redirect.');
+    $this->assertText($second_node->title, 'Adding a new path alias that matches an existing redirect disables the redirect.');
+  }
 
-    $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), 'Save');
-    //$redirect = redirect_load_by_source('first-alias');
-    //$this->assertRedirect($redirect);
+  function testDisableEnableRedirect() {
+    // Add a new redirect.
+    $redirect = $this->addRedirect('redirect', 'node');
+    // Check that it is enabled.
+    $this->assertEqual($redirect->status, 1);
+
+    // Disable the redirect.
+    $edit = array('status' => FALSE);
+    $this->drupalPost("admin/config/search/redirect/edit/{$redirect->rid}", $edit, t('Save'));
+    $redirect = redirect_load($redirect->rid);
+    // Check that it has been disabled.
+    $this->assertEqual($redirect->status, 0);
+    $this->drupalGet("admin/config/search/redirect/edit/{$redirect->rid}");
+    $this->assertNoFieldChecked('edit-status', 'status is unchecked');
+    $this->assertNoRedirect($redirect);
+
+    // Re-enable the redirect.
+    $edit = array('status' => 1);
+    $this->drupalPost("admin/config/search/redirect/edit/{$redirect->rid}", $edit, t('Save'));
+    $this->assertRedirect($redirect);
   }
 }

+ 0 - 5
sites/all/modules/contrib/admin/redirect/views/redirect_handler_field_redirect_redirect.inc

@@ -45,11 +45,6 @@ class redirect_handler_field_redirect_redirect extends views_handler_field {
     );
   }
 
-  function query() {
-    $this->ensure_my_table();
-    $this->add_additional_fields();
-  }
-
   function render($values) {
     $redirect = $values->{$this->aliases['redirect']};
     $redirect_options = unserialize($values->{$this->aliases['redirect_options']});

+ 0 - 5
sites/all/modules/contrib/admin/redirect/views/redirect_handler_field_redirect_source.inc

@@ -45,11 +45,6 @@ class redirect_handler_field_redirect_source extends views_handler_field {
     );
   }
 
-  function query() {
-    $this->ensure_my_table();
-    $this->add_additional_fields();
-  }
-
   function render($values) {
     $source = $values->{$this->aliases['source']};
     $source_options = unserialize($values->{$this->aliases['source_options']});

+ 1 - 1
sites/all/modules/contrib/admin/rules/DEVELOPER.txt

@@ -23,4 +23,4 @@ Terminology & Overview
     outside of the rule admin module too. In fact the rules admin module is
     pretty small, as it just relies on the provided UI of the components.
   * The UI is incorporated using the faces object extension mechanism, see
-    rules_rules_plugin_info() for an overview of the used UI extenders.
+    rules_rules_plugin_info() for an overview of the used UI extenders.

+ 10 - 10
sites/all/modules/contrib/admin/rules/README.txt

@@ -9,7 +9,7 @@ Maintainers:
 The Rules module allows site administrators to define conditionally executed
 actions based on occurring events (ECA-rules).
 
-Project homepage: http://drupal.org/project/rules
+Project homepage: https://www.drupal.org/project/rules
 
 
 Installation
@@ -18,10 +18,10 @@ Installation
 *Before* starting, make sure that you have read at least the introduction - so
 you know at least the basic concepts. You can find it here:
 
-                      http://drupal.org/node/298480
+                 https://www.drupal.org/node/298480
 
  * Rules depends on the Entity API module, download and install it from
-   http://drupal.org/project/entity
+   https://www.drupal.org/project/entity
  * Copy the whole rules directory to your modules directory
    (e.g. DRUPAL_ROOT/sites/all/modules) and activate the Rules and Rules UI
    modules.
@@ -30,8 +30,8 @@ you know at least the basic concepts. You can find it here:
 
 Documentation
 -------------
-* Check out the general docs at http://drupal.org/node/298476
-* Check out the developer targeted docs at http://drupal.org/node/878718
+* Check out the general docs at https://www.drupal.org/node/298476
+* Check out the developer targeted docs at https://www.drupal.org/node/878718
 
 
 Rules Scheduler
@@ -41,9 +41,9 @@ Rules Scheduler
    to schedule the execution of Rules components.
  * Make sure that you have configured cron for your drupal installation as cron
    is used for scheduling the Rules components. For help see
-   http://drupal.org/cron
- * If the Views module (http://drupal.org/project/views) is installed, the module
-   displays the list of scheduled tasks in the UI.
+   https://www.drupal.org/cron
+ * If the Views module (https://www.drupal.org/project/views) is installed, the
+   module displays the list of scheduled tasks in the UI.
 
 
 Upgrade from Rules 6.x-1.x to Rules 7.x-2.x
@@ -60,7 +60,7 @@ Upgrade from Rules 6.x-1.x to Rules 7.x-2.x
      * Note that for importing an export the export needs to pass the
        configuration integrity check, what might be troublesome if the
        conversion was not 100% successful. In that case, try choosing the
-       immediate saving method and correct the configuration after conversion.  
+       immediate saving method and correct the configuration after conversion.
      * A rule configuration might require multiple modules to be in place and
        upgraded to work properly. E.g. if you used an action provided
        by a third party module, make sure the module is in place and upgraded
@@ -85,7 +85,7 @@ Upgrade from Rules 6.x-1.x to Rules 7.x-2.x
     for Drupal 7. The Drupal 6 tasks are preserved in the database as long as
     you do not clear your Rules 1.x configuration though.
   * The Rules Forms module has not been updated to Drupal 7 and there are no
-    plans to do so, as unfortuntely the module's design does not allow for
+    plans to do so, as unfortunately the module's design does not allow for
     automatic configuration updates.
     Thus, a possible future Rules 2.x Forms module is likely to work
     different, e.g. by working only for entity forms on the field level.

+ 0 - 27
sites/all/modules/contrib/admin/rules/fix_errors_on_update-2090511-214_2.patch

@@ -1,27 +0,0 @@
-diff --git a/rules.module b/rules.module
-index 719852c..9e4ec8f 100644
---- a/rules.module
-+++ b/rules.module
-@@ -8,6 +8,22 @@
- // hook_init().
- require_once dirname(__FILE__) . '/modules/events.inc';
- 
-+/** Rules >=2.4 introduces a class called 'RulesEventHandlerEntityBundle' found in
-+ * 'includes/rules.event.inc', so we include this file for versions older than 2.4
-+ * in order to prevent a bug at node/2090511.
-+ */
-+$result = db_query("SELECT schema_version FROM {system} WHERE name = :name", array(
-+  ':name' => 'rules',
-+));
-+
-+if ($result) {
-+  while ($row = $result->fetchAssoc()) {
-+    if ($row['schema_version'] <7210) {
-+      require_once dirname(__FILE__) . '/includes/rules.event.inc';
-+    }
-+  }
-+}
-+
- /**
-  * Implements hook_module_implements_alter().
-  */

+ 41 - 29
sites/all/modules/contrib/admin/rules/includes/faces.inc

@@ -1,7 +1,8 @@
 <?php
 
 /**
- * @file Extendable Object Faces API. Provided by the faces module.
+ * @file
+ * Extendable Object Faces API. Provided by the faces module.
  */
 
 if (!interface_exists('FacesExtenderInterface', FALSE)) {
@@ -14,12 +15,13 @@ if (!interface_exists('FacesExtenderInterface', FALSE)) {
     /**
      * Constructs an instance of the extender.
      */
-    function __construct(FacesExtendable $object);
+    public function __construct(FacesExtendable $object);
 
     /**
      * Returns the extended object.
      */
     public function getExtendable();
+
   }
 
   /**
@@ -31,9 +33,10 @@ if (!interface_exists('FacesExtenderInterface', FALSE)) {
 
 if (!class_exists('FacesExtender', FALSE)) {
   /**
-   * A common base class for FacesExtenders. Extenders may access protected
-   * methods and properties of the extendable using the property() and call()
-   * methods.
+   * A common base class for FacesExtenders.
+   *
+   * Extenders may access protected methods and properties of the extendable
+   * using the property() and call() methods.
    */
   abstract class FacesExtender implements FacesExtenderInterface {
 
@@ -42,8 +45,7 @@ if (!class_exists('FacesExtender', FALSE)) {
      */
     protected $object;
 
-
-    function __construct(FacesExtendable $object) {
+    public function __construct(FacesExtendable $object) {
       $this->object = $object;
     }
 
@@ -63,17 +65,17 @@ if (!class_exists('FacesExtender', FALSE)) {
     }
 
     /**
-     * Invokes any method on the extended object. May be used to invoke
-     * protected methods.
+     * Invokes any method on the extended object, including protected methods.
      *
-     * @param $name
+     * @param string $name
      *   The method name.
-     * @param $arguments
+     * @param array $args
      *   An array of arguments to pass to the method.
      */
     protected function call($name, array $args = array()) {
       return $this->object->call($name, $args);
     }
+
   }
 }
 
@@ -108,7 +110,7 @@ if (!class_exists('FacesExtendable', FALSE)) {
     /**
      * Magic method: Invoke the dynamically implemented methods.
      */
-    function __call($name, $arguments = array()) {
+    public function __call($name, $arguments = array()) {
       if (isset($this->facesMethods[$name])) {
         $method = $this->facesMethods[$name];
         // Include code, if necessary.
@@ -134,9 +136,11 @@ if (!class_exists('FacesExtendable', FALSE)) {
     }
 
     /**
-     * Returns the extender object for the given class. May be used to
-     * explicitly invoke a specific extender, e.g. a function overriding a
-     * method may use that to explicitly invoke the original extender.
+     * Returns the extender object for the given class.
+     *
+     * May be used to explicitly invoke a specific extender, e.g. a function
+     * overriding a method may use that to explicitly invoke the original
+     * extender.
      */
     public function extender($class) {
       if (!isset($this->facesClassInstances[$class])) {
@@ -146,14 +150,17 @@ if (!class_exists('FacesExtendable', FALSE)) {
     }
 
     /**
+     * Returns whether the object can face as the given interface.
+     *
      * Returns whether the object can face as the given interface, thus it
-     * returns TRUE if this oject has been extended by an appropriate
+     * returns TRUE if this object has been extended by an appropriate
      * implementation.
      *
      * @param $interface
-     *   Optional. A interface to test for. If it's omitted, all interfaces that
-     *   the object can be faced as are returned.
-     * @return
+     *   Optional. An interface to test for. If it's omitted, all interfaces
+     *   that the object can be faced as are returned.
+     *
+     * @return bool
      *   Whether the object can face as the interface or an array of interface
      *   names.
      */
@@ -169,9 +176,9 @@ if (!class_exists('FacesExtendable', FALSE)) {
      *
      * @param $interface
      *   The interface name or an array of interface names.
-     * @param $class
+     * @param $className
      *   The extender class, which has to implement the FacesExtenderInterface.
-     * @param $include
+     * @param array $includes
      *   An optional array describing the file to include before invoking the
      *   class. The array entries known are 'type', 'module', and 'name'
      *   matching the parameters of module_load_include(). Only 'module' is
@@ -206,10 +213,10 @@ if (!class_exists('FacesExtendable', FALSE)) {
      * @param $interface
      *   The interface name or FALSE to extend the object without a given
      *   interface.
-     * @param $methods
+     * @param array $callbacks
      *   An array, where the keys are methods of the given interface and the
      *   values the callback functions to use.
-     * @param $includes
+     * @param array $includes
      *   An optional array to describe files to include before invoking the
      *   callbacks. You may pass a single array describing one include for all
      *   callbacks or an array of arrays, keyed by the method names. Look at the
@@ -234,11 +241,11 @@ if (!class_exists('FacesExtendable', FALSE)) {
     /**
      * Override the implementation of an extended method.
      *
-     * @param $methods
-     *   An array of methods of the interface, that should be overriden, where
+     * @param array $callbacks
+     *   An array of methods of the interface, that should be overridden, where
      *   the keys are methods to override and the values the callback functions
      *   to use.
-     * @param $includes
+     * @param array $includes
      *   An optional array to describe files to include before invoking the
      *   callbacks. You may pass a single array describing one include for all
      *   callbacks or an array of arrays, keyed by the method names. Look at the
@@ -257,6 +264,7 @@ if (!class_exists('FacesExtendable', FALSE)) {
 
     /**
      * Adds in include files for the given methods while removing any old files.
+     *
      * If a single include file is described, it's added for all methods.
      */
     protected function addIncludes($methods, $includes) {
@@ -272,6 +280,8 @@ if (!class_exists('FacesExtendable', FALSE)) {
     }
 
     /**
+     * Destroys all references to created instances.
+     *
      * Destroys all references to created instances so that PHP's garbage
      * collection can do its work. This is needed as PHP's gc has troubles with
      * circular references until PHP < 5.3.
@@ -296,9 +306,9 @@ if (!class_exists('FacesExtendable', FALSE)) {
      * This also allows to pass arguments by reference, so it may be used to
      * pass arguments by reference to dynamically extended methods.
      *
-     * @param $name
+     * @param string $name
      *   The method name.
-     * @param $arguments
+     * @param array $args
      *   An array of arguments to pass to the method.
      */
     public function call($name, array $args = array()) {
@@ -307,5 +317,7 @@ if (!class_exists('FacesExtendable', FALSE)) {
       }
       return $this->__call($name, $args);
     }
+
   }
-}
+
+}

File diff suppressed because it is too large
+ 496 - 170
sites/all/modules/contrib/admin/rules/includes/rules.core.inc


+ 34 - 24
sites/all/modules/contrib/admin/rules/includes/rules.event.inc

@@ -70,6 +70,7 @@ interface RulesEventHandlerInterface {
    * Returns an array of default settings.
    *
    * @return array
+   *   The array of default settings.
    */
   public function getDefaults();
 
@@ -145,6 +146,7 @@ interface RulesEventHandlerInterface {
    *   The info array of the event the event handler belongs to.
    */
   public function getEventInfo();
+
 }
 
 /**
@@ -169,6 +171,7 @@ interface RulesEventDispatcherInterface extends RulesEventHandlerInterface {
    *   TRUE if the event dispatcher is currently active, FALSE otherwise.
    */
   public function isWatching();
+
 }
 
 /**
@@ -198,7 +201,7 @@ abstract class RulesEventHandlerBase implements RulesEventHandlerInterface {
   protected $settings = array();
 
   /**
-   * Implements RulesEventHandlerInterface::__construct()
+   * Implements RulesEventHandlerInterface::__construct().
    */
   public function __construct($event_name, $info) {
     $this->eventName = $event_name;
@@ -207,14 +210,14 @@ abstract class RulesEventHandlerBase implements RulesEventHandlerInterface {
   }
 
   /**
-   * Implements RulesEventHandlerInterface::getSettings()
+   * Implements RulesEventHandlerInterface::getSettings().
    */
   public function getSettings() {
     return $this->settings;
   }
 
   /**
-   * Implements RulesEventHandlerInterface::setSettings()
+   * Implements RulesEventHandlerInterface::setSettings().
    */
   public function setSettings(array $settings) {
     $this->settings = $settings + $this->getDefaults();
@@ -222,14 +225,14 @@ abstract class RulesEventHandlerBase implements RulesEventHandlerInterface {
   }
 
   /**
-   * Implements RulesEventHandlerInterface::validate()
+   * Implements RulesEventHandlerInterface::validate().
    */
   public function validate() {
     // Nothing to check by default.
   }
 
   /**
-   * Implements RulesEventHandlerInterface::extractFormValues()
+   * Implements RulesEventHandlerInterface::extractFormValues().
    */
   public function extractFormValues(array &$form, array &$form_state) {
     foreach ($this->getDefaults() as $key => $setting) {
@@ -238,66 +241,68 @@ abstract class RulesEventHandlerBase implements RulesEventHandlerInterface {
   }
 
   /**
-   * Implements RulesEventHandlerInterface::availableVariables()
+   * Implements RulesEventHandlerInterface::availableVariables().
    */
   public function availableVariables() {
     return isset($this->eventInfo['variables']) ? $this->eventInfo['variables'] : array();
   }
 
   /**
-   * Implements RulesEventHandlerInterface::getEventName()
+   * Implements RulesEventHandlerInterface::getEventName().
    */
   public function getEventName() {
     return $this->eventName;
   }
 
   /**
-   * Implements RulesEventHandlerInterface::getEventInfo()
+   * Implements RulesEventHandlerInterface::getEventInfo().
    */
   public function getEventInfo() {
     return $this->eventInfo;
   }
+
 }
 
 /**
  * A handler for events having no settings. This is the default handler.
  */
-class RulesEventDefaultHandler extends RulesEventHandlerBase  {
+class RulesEventDefaultHandler extends RulesEventHandlerBase {
 
   /**
-   * Implements RulesEventHandlerInterface::buildForm()
+   * Implements RulesEventHandlerInterface::buildForm().
    */
   public function buildForm(array &$form_state) {
     return array();
   }
 
   /**
-   * Implements RulesEventHandlerInterface::getConfiguredEventName()
+   * Implements RulesEventHandlerInterface::getConfiguredEventName().
    */
   public function getEventNameSuffix() {
     return '';
   }
 
   /**
-   * Implements RulesEventHandlerInterface::summary()
+   * Implements RulesEventHandlerInterface::summary().
    */
   public function summary() {
     return check_plain($this->eventInfo['label']);
   }
 
   /**
-   * Implements RulesEventHandlerInterface::getDefaults()
+   * Implements RulesEventHandlerInterface::getDefaults().
    */
   public function getDefaults() {
     return array();
   }
 
   /**
-   * Implements RulesEventHandlerInterface::getSettings()
+   * Implements RulesEventHandlerInterface::getSettings().
    */
   public function getSettings() {
     return NULL;
   }
+
 }
 
 /**
@@ -305,10 +310,12 @@ class RulesEventDefaultHandler extends RulesEventHandlerBase  {
  */
 class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
 
-  protected $entityType, $entityInfo, $bundleKey;
+  protected $entityType;
+  protected $entityInfo;
+  protected $bundleKey;
 
   /**
-   * Implements RulesEventHandlerInterface::__construct()
+   * Implements RulesEventHandlerInterface::__construct().
    */
   public function __construct($event_name, $info) {
     parent::__construct($event_name, $info);
@@ -321,7 +328,7 @@ class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
   }
 
   /**
-   * Implements RulesEventHandlerInterface::summary()
+   * Implements RulesEventHandlerInterface::summary().
    */
   public function summary() {
     $bundle = &$this->settings['bundle'];
@@ -331,7 +338,7 @@ class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
   }
 
   /**
-   * Implements RulesEventHandlerInterface::buildForm()
+   * Implements RulesEventHandlerInterface::buildForm().
    */
   public function buildForm(array &$form_state) {
     $form['bundle'] = array(
@@ -340,6 +347,7 @@ class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
       '#description' => t('If you need to filter for multiple values, either add multiple events or use the "Entity is of bundle" condition instead.'),
       '#default_value' => $this->settings['bundle'],
       '#empty_value' => '',
+      '#options' => array(),
     );
     foreach ($this->entityInfo['bundles'] as $name => $bundle_info) {
       $form['bundle']['#options'][$name] = $bundle_info['label'];
@@ -351,20 +359,21 @@ class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
    * Returns the label to use for the bundle property.
    *
    * @return string
+   *   The label to use for the bundle property.
    */
   protected function getBundlePropertyLabel() {
     return $this->entityInfo['entity keys']['bundle'];
   }
 
   /**
-   * Implements RulesEventHandlerInterface::extractFormValues()
+   * Implements RulesEventHandlerInterface::extractFormValues().
    */
   public function extractFormValues(array &$form, array &$form_state) {
     $this->settings['bundle'] = !empty($form_state['values']['bundle']) ? $form_state['values']['bundle'] : NULL;
   }
 
   /**
-   * Implements RulesEventHandlerInterface::validate()
+   * Implements RulesEventHandlerInterface::validate().
    */
   public function validate() {
     if ($this->settings['bundle'] && empty($this->entityInfo['bundles'][$this->settings['bundle']])) {
@@ -373,19 +382,19 @@ class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
           '%bundle' => $this->settings['bundle'],
           '%entity_type' => $this->entityInfo['label'],
           '@bundle' => $this->getBundlePropertyLabel(),
-      )), array(NULL, 'bundle'));
+        )), array(NULL, 'bundle'));
     }
   }
 
   /**
-   * Implements RulesEventHandlerInterface::getConfiguredEventName()
+   * Implements RulesEventHandlerInterface::getConfiguredEventName().
    */
   public function getEventNameSuffix() {
     return $this->settings['bundle'];
   }
 
   /**
-   * Implements RulesEventHandlerInterface::getDefaults()
+   * Implements RulesEventHandlerInterface::getDefaults().
    */
   public function getDefaults() {
     return array(
@@ -394,7 +403,7 @@ class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
   }
 
   /**
-   * Implements RulesEventHandlerInterface::availableVariables()
+   * Implements RulesEventHandlerInterface::availableVariables().
    */
   public function availableVariables() {
     $variables = $this->eventInfo['variables'];
@@ -408,4 +417,5 @@ class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
     }
     return $variables;
   }
+
 }

+ 190 - 57
sites/all/modules/contrib/admin/rules/includes/rules.plugins.inc

@@ -1,15 +1,18 @@
 <?php
 
 /**
- * @file Contains plugin info and implementations not needed for rule evaluation.
+ * @file
+ * Contains plugin info and implementations not needed for rule evaluation.
  */
 
-
 /**
  * Implements a rules action.
  */
 class RulesAction extends RulesAbstractPlugin implements RulesActionInterface {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'action';
 
   /**
@@ -69,6 +72,7 @@ class RulesAction extends RulesAbstractPlugin implements RulesActionInterface {
       }
     }
   }
+
 }
 
 /**
@@ -76,7 +80,14 @@ class RulesAction extends RulesAbstractPlugin implements RulesActionInterface {
  */
 class RulesCondition extends RulesAbstractPlugin implements RulesConditionInterface {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'condition';
+
+  /**
+   * @var bool
+   */
   protected $negate = FALSE;
 
   public function providesVariables() {
@@ -132,19 +143,28 @@ class RulesCondition extends RulesAbstractPlugin implements RulesConditionInterf
 
   public function label() {
     $label = parent::label();
-    return $this->negate ? t('NOT @condition', array('@condition' => $label)) : $label;
+    return $this->negate ? t('NOT !condition', array('!condition' => $label)) : $label;
   }
+
 }
 
 /**
  * An actual rule.
+ *
  * Note: A rule also implements the RulesActionInterface (inherited).
  */
 class Rule extends RulesActionContainer {
 
   protected $conditions = NULL;
+
+  /**
+   * @var string
+   */
   protected $itemName = 'rule';
 
+  /**
+   * @var string
+   */
   public $label = 'unlabeled';
 
   public function __construct($variables = array(), $providesVars = array()) {
@@ -159,8 +179,9 @@ class Rule extends RulesActionContainer {
   }
 
   /**
-   * Get an iterator over all contained conditions. Note that this iterator also
-   * implements the ArrayAcces interface.
+   * Gets an iterator over all contained conditions.
+   *
+   * Note that this iterator also implements the ArrayAccess interface.
    *
    * @return RulesRecursiveElementIterator
    */
@@ -183,8 +204,9 @@ class Rule extends RulesActionContainer {
   }
 
   /**
-   * Get an iterator over all contained actions. Note that this iterator also
-   * implements the ArrayAcces interface.
+   * Gets an iterator over all contained actions.
+   *
+   * Note that this iterator also implements the ArrayAccess interface.
    *
    * @return RulesRecursiveElementIterator
    */
@@ -193,11 +215,12 @@ class Rule extends RulesActionContainer {
   }
 
   /**
-   * Add a condition. Pass either an instance of the RulesConditionInterface
-   * or the arguments as needed by rules_condition().
+   * Adds a condition.
+   *
+   * Pass either an instance of the RulesConditionInterface or the arguments as
+   * needed by rules_condition().
    *
-   * @return Rule
-   *   Returns $this to support chained usage.
+   * @return $this
    */
   public function condition($name, $settings = array()) {
     $this->conditions->condition($name, $settings);
@@ -309,7 +332,9 @@ class Rule extends RulesActionContainer {
   }
 
   /**
-   * Rules may not provided any variable info assertions, as Rules are only
+   * Overrides RulesPlugin::variableInfoAssertions().
+   *
+   * Rules may not provide any variable info assertions, as Rules are only
    * conditionally executed.
    */
   protected function variableInfoAssertions() {
@@ -324,7 +349,7 @@ class Rule extends RulesActionContainer {
   }
 
   /**
-   * Overriden to expose the variables of all actions for embedded rules.
+   * Overridden to expose the variables of all actions for embedded rules.
    */
   public function providesVariables() {
     $provides = parent::providesVariables();
@@ -340,6 +365,7 @@ class Rule extends RulesActionContainer {
     parent::resetInternalCache();
     $this->conditions->resetInternalCache();
   }
+
 }
 
 /**
@@ -347,22 +373,30 @@ class Rule extends RulesActionContainer {
  */
 class RulesReactionRule extends Rule implements RulesTriggerableInterface {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'reaction rule';
+
+  /**
+   * @var array
+   */
   protected $events = array();
 
   /**
-   * Returns the array of events associated with that Rule.
+   * @var array
+   */
+  protected $eventSettings = array();
+
+  /**
+   * Implements RulesTriggerableInterface::events().
    */
-  public function &events() {
+  public function events() {
     return $this->events;
   }
 
   /**
-   * Removes an event from the rule configuration.
-   *
-   * @param $event
-   *   The name of the event to remove.
-   * @return RulesReactionRule
+   * Implements RulesTriggerableInterface::removeEvent().
    */
   public function removeEvent($event) {
     if (($id = array_search($event, $this->events)) !== FALSE) {
@@ -372,10 +406,43 @@ class RulesReactionRule extends Rule implements RulesTriggerableInterface {
   }
 
   /**
-   * @return RulesReactionRule
+   * Implements RulesTriggerableInterface::event().
    */
-  public function event($event) {
-    $this->events[] = $event;
+  public function event($event_name, array $settings = NULL) {
+    // Process any settings and determine the configured event's name.
+    if ($settings) {
+      $handler = rules_get_event_handler($event_name, $settings);
+      if ($suffix = $handler->getEventNameSuffix()) {
+        $event_name .= '--' . $suffix;
+        $this->eventSettings[$event_name] = $settings;
+      }
+      else {
+        // Do not store settings if there is no suffix.
+        unset($this->eventSettings[$event_name]);
+      }
+    }
+    if (array_search($event_name, $this->events) === FALSE) {
+      $this->events[] = $event_name;
+    }
+    return $this;
+  }
+
+  /**
+   * Implements RulesTriggerableInterface::getEventSettings().
+   */
+  public function getEventSettings($event_name) {
+    if (isset($this->eventSettings[$event_name])) {
+      return $this->eventSettings[$event_name];
+    }
+  }
+
+  public function integrityCheck() {
+    parent::integrityCheck();
+    // Check integrity of the configured events.
+    foreach ($this->events as $event_name) {
+      $handler = rules_get_event_handler($event_name, $this->getEventSettings($event_name));
+      $handler->validate();
+    }
     return $this;
   }
 
@@ -394,9 +461,9 @@ class RulesReactionRule extends Rule implements RulesTriggerableInterface {
   }
 
   public function access() {
-    $event_info = rules_fetch_data('event_info');
-    foreach ($this->events as $event) {
-      if (!empty($event_info[$event]['access callback']) && !call_user_func($event_info[$event]['access callback'], 'event', $event)) {
+    foreach ($this->events as $event_name) {
+      $event_info = rules_get_event_info($event_name);
+      if (!empty($event_info['access callback']) && !call_user_func($event_info['access callback'], 'event', $event_info['name'])) {
         return FALSE;
       }
     }
@@ -405,10 +472,10 @@ class RulesReactionRule extends Rule implements RulesTriggerableInterface {
 
   public function dependencies() {
     $modules = array_flip(parent::dependencies());
-    $event_info = rules_fetch_data('event_info');
-    foreach ($this->events as $event) {
-      if (isset($event_info[$event]['module'])) {
-        $modules[$event_info[$event]['module']] = TRUE;
+    foreach ($this->events as $event_name) {
+      $event_info = rules_get_event_info($event_name);
+      if (isset($event_info['module'])) {
+        $modules[$event_info['module']] = TRUE;
       }
     }
     return array_keys($modules);
@@ -433,15 +500,20 @@ class RulesReactionRule extends Rule implements RulesTriggerableInterface {
       else {
         // The intersection of the variables provided by the events are
         // available.
-        $event_info = rules_fetch_data('event_info');
-        $events = array_intersect($this->events, array_keys($event_info));
-        foreach ($events as $event) {
-          $event_info[$event] += array('variables' => array());
+        foreach ($this->events as $event_name) {
+          $handler = rules_get_event_handler($event_name, $this->getEventSettings($event_name));
+
           if (isset($this->availableVariables)) {
-            $this->availableVariables = array_intersect_key($this->availableVariables, $event_info[$event]['variables']);
+            $event_vars = $handler->availableVariables();
+            // Merge variable info by intersecting the variable-info keys also,
+            // so we have only metadata available that is valid for all of the
+            // provided variables.
+            foreach (array_intersect_key($this->availableVariables, $event_vars) as $name => $variable_info) {
+              $this->availableVariables[$name] = array_intersect_key($variable_info, $event_vars[$name]);
+            }
           }
           else {
-            $this->availableVariables = $event_info[$event]['variables'];
+            $this->availableVariables = $handler->availableVariables();
           }
         }
         $this->availableVariables = isset($this->availableVariables) ? RulesState::defaultVariables() + $this->availableVariables : RulesState::defaultVariables();
@@ -451,18 +523,38 @@ class RulesReactionRule extends Rule implements RulesTriggerableInterface {
   }
 
   public function __sleep() {
-    return parent::__sleep() + drupal_map_assoc(array('events'));
+    return parent::__sleep() + drupal_map_assoc(array('events', 'eventSettings'));
   }
 
   protected function exportChildren($key = 'ON') {
-    $export[$key] = array_values($this->events);
+    foreach ($this->events as $event_name) {
+      $export[$key][$event_name] = (array) $this->getEventSettings($event_name);
+    }
     return $export + parent::exportChildren();
   }
 
   protected function importChildren($export, $key = 'ON') {
-    $this->events = $export[$key];
+    // Detect and support old-style exports: a numerically indexed array of
+    // event names.
+    if (is_string(reset($export[$key])) && is_numeric(key($export[$key]))) {
+      $this->events = $export[$key];
+    }
+    else {
+      $this->events = array_keys($export[$key]);
+      $this->eventSettings = array_filter($export[$key]);
+    }
     parent::importChildren($export);
   }
+
+  /**
+   * Overrides optimize().
+   */
+  public function optimize() {
+    parent::optimize();
+    // No need to keep event settings for evaluation.
+    $this->eventSettings = array();
+  }
+
 }
 
 /**
@@ -470,6 +562,9 @@ class RulesReactionRule extends Rule implements RulesTriggerableInterface {
  */
 class RulesAnd extends RulesConditionContainer {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'and';
 
   public function evaluate(RulesState $state) {
@@ -486,6 +581,7 @@ class RulesAnd extends RulesConditionContainer {
   public function label() {
     return !empty($this->label) ? $this->label : ($this->negate ? t('NOT AND') : t('AND'));
   }
+
 }
 
 /**
@@ -493,6 +589,9 @@ class RulesAnd extends RulesConditionContainer {
  */
 class RulesOr extends RulesConditionContainer {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'or';
 
   public function evaluate(RulesState $state) {
@@ -511,6 +610,8 @@ class RulesOr extends RulesConditionContainer {
   }
 
   /**
+   * Overrides RulesContainerPlugin::stateVariables().
+   *
    * Overridden to exclude all variable assertions as in an OR we cannot assert
    * the children are successfully evaluated.
    */
@@ -527,6 +628,7 @@ class RulesOr extends RulesConditionContainer {
     }
     return $vars;
   }
+
 }
 
 /**
@@ -534,6 +636,9 @@ class RulesOr extends RulesConditionContainer {
  */
 class RulesLoop extends RulesActionContainer {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'loop';
   protected $listItemInfo;
 
@@ -640,6 +745,7 @@ class RulesLoop extends RulesActionContainer {
       $this->settings['item:label'] = reset($export['ITEM']);
     }
   }
+
 }
 
 /**
@@ -647,6 +753,9 @@ class RulesLoop extends RulesActionContainer {
  */
 class RulesActionSet extends RulesActionContainer {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'action set';
 
 }
@@ -656,6 +765,9 @@ class RulesActionSet extends RulesActionContainer {
  */
 class RulesRuleSet extends RulesActionContainer {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'rule set';
 
   /**
@@ -672,6 +784,7 @@ class RulesRuleSet extends RulesActionContainer {
   protected function importChildren($export, $key = 'RULES') {
     parent::importChildren($export, $key);
   }
+
 }
 
 /**
@@ -679,8 +792,16 @@ class RulesRuleSet extends RulesActionContainer {
  */
 class RulesEventSet extends RulesRuleSet {
 
+  /**
+   * @var string
+   */
   protected $itemName = 'event set';
-  // Event sets may recurse as we block recursions on rule-level.
+
+  /**
+   * Event sets may recurse as we block recursions on rule-level.
+   *
+   * @var bool
+   */
   public $recursion = TRUE;
 
   public function __construct($info = array()) {
@@ -698,7 +819,10 @@ class RulesEventSet extends RulesRuleSet {
   }
 
   /**
-   * Cache event-sets per event to allow efficient usage via rules_invoke_event().
+   * Rebuilds the event cache.
+   *
+   * We cache event-sets per event in order to allow efficient usage via
+   * rules_invoke_event().
    *
    * @see rules_get_cache()
    * @see rules_invoke_event()
@@ -711,18 +835,27 @@ class RulesEventSet extends RulesRuleSet {
     $rules = rules_config_load_multiple(FALSE, array('plugin' => 'reaction rule', 'active' => TRUE));
 
     foreach ($rules as $name => $rule) {
-      foreach ($rule->events() as $event) {
+      foreach ($rule->events() as $event_name) {
+        $event_base_name = rules_get_event_base_name($event_name);
         // Skip not defined events.
-        if (empty($events[$event])) {
+        if (empty($events[$event_base_name])) {
           continue;
         }
         // Create an event set if not yet done.
-        if (!isset($sets[$event])) {
-          $event_info = $events[$event] + array(
-            'variables' => isset($events[$event]['arguments']) ? $events[$event]['arguments'] : array(),
-          );
-          $sets[$event] = new RulesEventSet($event_info);
-          $sets[$event]->name = $event;
+        if (!isset($sets[$event_name])) {
+          $handler = rules_get_event_handler($event_name, $rule->getEventSettings($event_name));
+
+          // Start the event dispatcher for this event, if any.
+          if ($handler instanceof RulesEventDispatcherInterface && !$handler->isWatching()) {
+            $handler->startWatching();
+          }
+
+          // Update the event info with the variables available based on the
+          // event settings.
+          $event_info = $events[$event_base_name];
+          $event_info['variables'] = $handler->availableVariables();
+          $sets[$event_name] = new RulesEventSet($event_info);
+          $sets[$event_name]->name = $event_name;
         }
 
         // If a rule is marked as dirty, check if this still applies.
@@ -732,23 +865,22 @@ class RulesEventSet extends RulesRuleSet {
         if (!$rule->dirty) {
           // Clone the rule to avoid modules getting the changed version from
           // the static cache.
-          $sets[$event]->rule(clone $rule);
+          $sets[$event_name]->rule(clone $rule);
         }
       }
     }
 
     // Create cache items for all created sets.
-    foreach ($sets as $event => $set) {
+    foreach ($sets as $event_name => $set) {
       $set->sortChildren();
       $set->optimize();