diff --git a/profiles/wcm_base/CHANGELOG.txt b/profiles/wcm_base/CHANGELOG.txt
index 2be6310a2544bf09b56db2f5b3f3fe2df0ba1b08..861d1ae0442698db74f95da60bffce453d43a12c 100644
--- a/profiles/wcm_base/CHANGELOG.txt
+++ b/profiles/wcm_base/CHANGELOG.txt
@@ -1,3 +1,18 @@
+WCM Base 7.x-1.12-rc3, 2019-03-05
+---------------------------------
+- WCM Base:
+  - Added WCM Watchdog to Slack feature along with required contrib modules.
+  - Updated Better Exposed Filters to 3.6.
+  - Updated ctools to 1.15.
+  - Updated Features to 2.11.
+  - Updated Fieldable Panels Panes to 1.11.
+  - Updated Google Analytics to 2.6.
+  - Updated Media to 2.21.
+  - Updated Media Youtube to 3.8.
+  - Updated Menu Block to 2.8.
+  - Updated Real Name to 1.4.
+  - Updated Views Accordion to 1.2.
+
 WCM Base 7.x-1.12-rc2, 2019-02-28
 ---------------------------------
 - OCIO News: Reset default filter overrides for news view.
diff --git a/profiles/wcm_base/build-wcm_base-dev.make b/profiles/wcm_base/build-wcm_base-dev.make
index 23dc7855517e0a0837a3f9cf757faa8d399d934e..60f8393176b10ae34c636c3fa74f32084b6f9e45 100644
--- a/profiles/wcm_base/build-wcm_base-dev.make
+++ b/profiles/wcm_base/build-wcm_base-dev.make
@@ -46,6 +46,7 @@ projects[wcm_user_profile][options][working-copy] = TRUE
 projects[smtp_html_mail][options][working-copy] = TRUE
 projects[wcm_news_client_display][options][working-copy] = TRUE
 projects[news_client][options][working-copy] = TRUE
+projects[wcm_watchdog_to_slack][options][working-copy] = TRUE
 
 ;themes
 projects[ocio_seven][options][working-copy] = TRUE
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.api.php b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.api.php
index 91485192d466e4354a5c55df84c690e3ea5cdeae..7622e248e2951c1fc401ec536992c724912531f5 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.api.php
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.api.php
@@ -1,16 +1,19 @@
 <?php
+
 /**
  * @file
  * Hooks provided by the Better Expoosed Filters module.
  */
 
 /**
+ * Alter Better Exposed filters.
+ *
  * Alters Better Exposed Filters settings before the exposed form widgets are
  * built.
  *
- * @param $settings
+ * @param array &$settings.
  *   The settings array.
- * @param $context
+ * @param array $context.
  *   The view and display to which the settings apply.
  */
 function hook_better_exposed_filters_settings_alter(&$settings, $context) {
@@ -19,7 +22,6 @@ function hook_better_exposed_filters_settings_alter(&$settings, $context) {
   $settings['field_price_value']['slider_options']['bef_slider_max'] = 5000;
 }
 
-
 /**
  * Modify the array of BEF display options for an exposed filter.
  *
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.css b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.css
index 4d498038bcf5905207d6a98dc9a9d9101ba22740..a5f28e86978a3f6934402a8d8344dcc82e19166e 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.css
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.css
@@ -1,6 +1,7 @@
 /**
- * @file better_exposed_filters.css
- * 
+ * @file
+ * better_exposed_filters.css
+ *
  * Basic styling for features added by Better Exposed Filters
  */
 
@@ -9,8 +10,7 @@
  * Prevents collapsed filters from disappearing completely
  */
 fieldset.bef-select-as-radios-fieldset.collapsed legend,
-fieldset.bef-select-as-checkboxes-fieldset.collapsed legend
-{
+fieldset.bef-select-as-checkboxes-fieldset.collapsed legend {
   position: relative;
 }
 
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.info b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.info
index a0b782d755cff07b3aa4f85fdf2ddac6ef2b4133..861ec62b43b7f6c045d3b534382bb4934326f354 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.info
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.info
@@ -4,12 +4,12 @@ description = Allow the use of checkboxes or radio buttons for exposed Views fil
 core = 7.x
 package = Views
 dependencies[] = views
+test_dependencies[] = date
 files[] = better_exposed_filters_exposed_form_plugin.inc
 files[] = tests/better_exposed_filters.test
 
-; Information added by Drupal.org packaging script on 2017-10-25
-version = "7.x-3.5"
+; Information added by Drupal.org packaging script on 2018-09-03
+version = "7.x-3.6"
 core = "7.x"
 project = "better_exposed_filters"
-datestamp = "1508952850"
-
+datestamp = "1536016685"
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.install b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.install
index 66222c64134af7fd9d02ef91d3b9de026ff6c857..77ef8c95ea6175bb20e00f7ef1346bee27ed4652 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.install
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.install
@@ -1,46 +1,46 @@
-<?php
-/**
- * @file
- * Install/Uninstall code for the Better Exposed Filters module.
- */
-
-/**
- * Implements hook_disable.
- */
-function better_exposed_filters_disable() {
-  // Check any views have their exposed form plugin set to BEF. If so, offer
-  // a warning about disabling the module.
-  $warnings = array();
-  foreach (views_get_all_views() as $view) {
-    foreach ($view->display as $display) {
-      if (!empty($display->display_options['exposed_form']['type']) && 'better_exposed_filters' == $display->display_options['exposed_form']['type']) {
-        $warnings[] = t('The %display_title display in the %view_name view. (<a href="@link" target="_blank">Update this display</a>)',
-          array(
-            '%display_title' => $display->display_title,
-            '%view_name' => $view->human_name,
-            '@link' => url('admin/structure/views/view/' . $view->name . '/edit/' . $display->id),
-          )
-        );
-      }
-    }
-  }
-
-  if (!empty($warnings)) {
-    $message = t('The following Views displays are using the Better Exposed Filters plugin, which is no longer enabled. It is recommended that you update these displays (links open in a new window) before removing the code associated with this module. Not doing so may cause unexpected results.');
-    $message .= '<ul><li>' . join('</li></li>', $warnings) . '</li></ul>';
-    drupal_set_message($message, 'warning');
-  }
-}
-
-/*
- * Implementations of hook_update_N.
- *
- * Comments above each function appear in the update database message.
- */
-/**
- * Rebuild the theme registry to discover new theme_bef_checkbox() function.
- */
-function better_exposed_filters_update_7000() {
-  drupal_theme_rebuild();
-  return t('Theme registry has been rebuilt.');
-}
+<?php
+/**
+ * @file
+ * Install/Uninstall code for the Better Exposed Filters module.
+ */
+
+/**
+ * Implements hook_disable.
+ */
+function better_exposed_filters_disable() {
+  // Check any views have their exposed form plugin set to BEF. If so, offer
+  // a warning about disabling the module.
+  $warnings = array();
+  foreach (views_get_all_views() as $view) {
+    foreach ($view->display as $display) {
+      if (!empty($display->display_options['exposed_form']['type']) && 'better_exposed_filters' == $display->display_options['exposed_form']['type']) {
+        $warnings[] = t('The %display_title display in the %view_name view. (<a href="@link" target="_blank">Update this display</a>)',
+          array(
+            '%display_title' => $display->display_title,
+            '%view_name' => $view->human_name,
+            '@link' => url('admin/structure/views/view/' . $view->name . '/edit/' . $display->id),
+          )
+        );
+      }
+    }
+  }
+
+  if (!empty($warnings)) {
+    $message = t('The following Views displays are using the Better Exposed Filters plugin, which is no longer enabled. It is recommended that you update these displays (links open in a new window) before removing the code associated with this module. Not doing so may cause unexpected results.');
+    $message .= '<ul><li>' . join('</li></li>', $warnings) . '</li></ul>';
+    drupal_set_message($message, 'warning');
+  }
+}
+
+/*
+ * Implementations of hook_update_N.
+ *
+ * Comments above each function appear in the update database message.
+ */
+/**
+ * Rebuild the theme registry to discover new theme_bef_checkbox() function.
+ */
+function better_exposed_filters_update_7000() {
+  drupal_theme_rebuild();
+  return t('Theme registry has been rebuilt.');
+}
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.js b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.js
index 40da193d5673fda484e15ce8b12bcd01d710db21..e6d8348d95196c39d62ad334a638b3a629197f28 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.js
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.js
@@ -318,21 +318,37 @@
           // We have to prevent the page load triggered by the links.
           event.preventDefault();
           event.stopPropagation();
-          // Un select old select value.
-          $wrapper.find('select option').removeAttr('selected');
+          // Un select if previously seleted toogle is selected.
+          var link_text = $(this).text();
+          removed = '';
+          $($options).each(function(i) {
+            if ($(this).attr('selected')) {
+              if (link_text == $(this).text()) {
+                removed = $(this).text();
+                $(this).removeAttr('selected');
+              }
+            }
+          });
 
           // Set the corresponding option inside the select element as selected.
-          var link_text = $(this).text();
           $selected = $options.filter(function() {
-            return $(this).text() == link_text;
+            return $(this).text() == link_text && removed != link_text;
           });
           $selected.attr('selected', 'selected');
           $wrapper.find('.bef-new-value').val($selected.val());
-          $wrapper.find('a').removeClass('active');
+          $wrapper.find('.bef-new-value[value=""]').attr("disabled", "disabled");
           $(this).addClass('active');
           // Submit the form.
           $wrapper.parents('form').find('.views-submit-button *[type=submit]').click();
         });
+
+        $('.bef-select-as-link').ready(function() {
+          $('.bef-select-as-link').find('a').removeClass('active');
+          $('.bef-new-value').each(function(i, val) {
+            id = $(this).parent().find('select').attr('id') + '-' + $(this).val();
+            $('#'+id).find('a').addClass('active');
+          });
+        });
       });
     }
   };
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.theme b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.theme
index 98e7cd8f31cd1097d440dadefab4f61f84aeaad1..f97d9be8a5bf744489ec952642dcf3080cb49fde 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.theme
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.theme
@@ -105,7 +105,8 @@ function theme_select_as_checkboxes($vars) {
     // Check for Taxonomy-based filters.
     if (is_object($elem)) {
       $slice = array_slice($elem->option, 0, 1, TRUE);
-      list($option, $elem) = each($slice);
+      $option = key($slice);
+      $elem = current($slice);
     }
 
     // Check for optgroups.  Put subelements in the $element_set array and add
@@ -176,7 +177,8 @@ function theme_select_as_hidden($vars) {
     // Check for Taxonomy-based filters.
     if (is_object($elem)) {
       $slice = array_slice($elem->option, 0, 1, TRUE);
-      list($option, $elem) = each($slice);
+      $option = key($slice);
+      $elem = current($slice);
     }
 
     // Check for optgroups.  Put subelements in the $element_set array and add a
@@ -331,7 +333,8 @@ function theme_select_as_tree($vars) {
     // Check for Taxonomy-based filters.
     if (is_object($option_label)) {
       $slice = array_slice($option_label->option, 0, 1, TRUE);
-      list($option_value, $option_label) = each($slice);
+      $option_value = key($slice);
+      $option_label = current($slice);
     }
 
     // Check for optgroups -- which is basically a two-level deep tree.
@@ -518,7 +521,8 @@ function theme_select_as_links($vars) {
     // Check for Taxonomy-based filters.
     if (is_object($elem)) {
       $slice = array_slice($elem->option, 0, 1, TRUE);
-      list($option, $elem) = each($slice);
+      $option = key($slice);
+      $elem = current($slice);
     }
 
     // Check for optgroups.  Put subelements in the $element_set array and add
@@ -556,8 +560,11 @@ function theme_select_as_links($vars) {
       // Add "active" class to the currently active filter link.
       if (in_array((string) $key, $selected_options)) {
         $link_options['attributes'] = array('class' => array('active'));
+        $url = bef_replace_query_string_arg($name, $key, $multiple, TRUE, $path);
+      }
+      else {
+        $url = bef_replace_query_string_arg($name, $key, $multiple, FALSE, $path);
       }
-      $url = bef_replace_query_string_arg($name, $key, $multiple, FALSE, $path);
       $elem['#children'] = l($value, $url, $link_options);
       $element_output = theme('form_element', array('element' => $elem));
 
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.views.inc b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.views.inc
index d91d2f5bfb3a10608dfda993831b129d64ee64b1..57253ca941f65c51bc0ba68a43e0e92d4c0b7bb1 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.views.inc
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters.views.inc
@@ -1,4 +1,5 @@
 <?php
+
 /**
  * @file
  * Adds Views3 support.
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters_exposed_form_plugin.inc b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters_exposed_form_plugin.inc
index 3da4660cbc39e9173f8368fec05bcabc65e02426..2c87b366260ea051fc48e5e174047a2d3c4322b4 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters_exposed_form_plugin.inc
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/better_exposed_filters_exposed_form_plugin.inc
@@ -1,9 +1,13 @@
 <?php
+
 /**
  * @file
  * Provides an Better Exposed Filters exposed form plugin for View 3.x.
  */
 
+/**
+ * Better exposed filter form plugin class.
+ */
 class better_exposed_filters_exposed_form_plugin extends views_plugin_exposed_form_basic {
 
   function init(&$view, &$display, $options = array()) {
@@ -13,7 +17,7 @@ class better_exposed_filters_exposed_form_plugin extends views_plugin_exposed_fo
     $this->localization_keys = $this->unpack_translatable_keys();
 
     $this->unpack_options($this->options, $options);
-    //$this->unpack_options($this->options, $options, NULL, FALSE);
+    // $this->unpack_options($this->options, $options, NULL, FALSE);.
   }
 
   function summary_title() {
@@ -48,7 +52,7 @@ class better_exposed_filters_exposed_form_plugin extends views_plugin_exposed_fo
       '#type' => 'checkbox',
       '#default_value' => $existing['general']['input_required'],
       '#title' => t('Require input before results are shown'),
-      '#description' => t("Emulates the built in <em>Input Required</em> exposed filter handler")
+      '#description' => t("Emulates the built in <em>Input Required</em> exposed filter handler"),
     );
 
     $bef_options['general']['text_input_required'] = array(
@@ -104,7 +108,7 @@ class better_exposed_filters_exposed_form_plugin extends views_plugin_exposed_fo
       '#default_value' => $existing['general']['secondary_collapse_override'],
       '#description' => t(
         'This setting overrides the secondary options fieldset collapsed value. By default the fieldset renders opened if a value within is selected and closed otherwise.'
-        ),
+      ),
       '#options' => array(
         0 => t('Default'),
         1 => t('Always open'),
@@ -167,7 +171,7 @@ class better_exposed_filters_exposed_form_plugin extends views_plugin_exposed_fo
         '#suffix' => '</div>',
         '#states' => array(
           'visible' => array(
-            'input[name="exposed_form_options[bef][sort][advanced][collapsible]"]' => array('checked' => TRUE)
+            'input[name="exposed_form_options[bef][sort][advanced][collapsible]"]' => array('checked' => TRUE),
           ),
         ),
       );
@@ -178,7 +182,7 @@ class better_exposed_filters_exposed_form_plugin extends views_plugin_exposed_fo
         '#description' => t('Combines the sort by options and order (ascending or decending) into a single list.  Use this to display "Option1 Desc", "Option1 Asc", "Option2 Desc", "Option2 Asc" in a single form element. "Expose sort order" must be checked to enable this option.'),
         '#states' => array(
           'disabled' => array(
-            'input[name="exposed_form_options[expose_sort_order]"]' => array('checked' => FALSE)
+            'input[name="exposed_form_options[expose_sort_order]"]' => array('checked' => FALSE),
           ),
         ),
       );
@@ -232,7 +236,7 @@ Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option alt
         '#suffix' => '</div>',
         '#states' => array(
           'required' => array(
-              'input[name="exposed_form_options[bef][sort][advanced][reset]"]' => array('checked' => TRUE),
+            'input[name="exposed_form_options[bef][sort][advanced][reset]"]' => array('checked' => TRUE),
           ),
           'visible' => array(
             'input[name="exposed_form_options[bef][sort][advanced][reset]"]' => array('checked' => TRUE),
@@ -432,8 +436,8 @@ Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option alt
           $bef_hidden = TRUE;
         }
       }
-      // Search API numeric filters support
-      elseif ($filter instanceof SearchApiViewsHandlerFilter && !($filter instanceof SearchApiViewsHandlerFilterFulltext))  {
+      // Search API numeric filters support.
+      elseif ($filter instanceof SearchApiViewsHandlerFilter && !($filter instanceof SearchApiViewsHandlerFilterFulltext)) {
         $bef_slider = TRUE;
       }
       elseif ($filter instanceof SearchApiViewsHandlerFilterBoolean) {
@@ -478,7 +482,7 @@ Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option alt
       $filter_key = $filter->options['is_grouped'] ? 'group_info' : 'expose';
       $identifier = '"' . $filter->options[$filter_key]['identifier'] . '"';
       if (!empty($filter->options[$filter_key]['label'])) {
-        $identifier .= t(' (Filter label: "@fl")', array('@fl' => $filter->options[$filter_key]['label']));
+        $identifier .= ' ' . t('(Filter label: "@fl")', array('@fl' => $filter->options[$filter_key]['label']));
       }
       $bef_options[$label]['bef_format'] = array(
         '#type' => 'select',
@@ -512,7 +516,11 @@ Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option alt
             ),
           ),
           '#description' => t('The minimum allowed value for the jQuery range slider. It can be positive, negative, or zero and have up to 11 decimal places.'),
-          '#element_validate' => array('element_validate_number', 'better_exposed_filters_element_validate_slider_required', 'better_exposed_filters_element_validate_slider_min_max'),
+          '#element_validate' => array(
+            'element_validate_number',
+            'better_exposed_filters_element_validate_slider_required',
+            'better_exposed_filters_element_validate_slider_min_max',
+          ),
         );
         $bef_options[$label]['slider_options']['bef_slider_max'] = array(
           '#type' => 'textfield',
@@ -525,7 +533,11 @@ Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option alt
             ),
           ),
           '#description' => t('The maximum allowed value for the jQuery range slider. It can be positive, negative, or zero and have up to 11 decimal places.'),
-          '#element_validate' => array('element_validate_number', 'better_exposed_filters_element_validate_slider_required', 'better_exposed_filters_element_validate_slider_min_max'),
+          '#element_validate' => array(
+            'element_validate_number',
+            'better_exposed_filters_element_validate_slider_required',
+            'better_exposed_filters_element_validate_slider_min_max',
+          ),
         );
         $bef_options[$label]['slider_options']['bef_slider_step'] = array(
           '#type' => 'textfield',
@@ -538,9 +550,13 @@ Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option alt
             ),
           ),
           '#description' => t('Determines the size or amount of each interval or step the slider takes between the min and max.') . '<br />' .
-                            t('The full specified value range of the slider (Range maximum - Range minimum) must be evenly divisible by the step.') . '<br />' .
-                            t('The step must be a positive number of up to 5 decimal places.'),
-          '#element_validate' => array('element_validate_number', 'better_exposed_filters_element_validate_slider_required', 'better_exposed_filters_element_validate_slider_step'),
+          t('The full specified value range of the slider (Range maximum - Range minimum) must be evenly divisible by the step.') . '<br />' .
+          t('The step must be a positive number of up to 5 decimal places.'),
+          '#element_validate' => array(
+            'element_validate_number',
+            'better_exposed_filters_element_validate_slider_required',
+            'better_exposed_filters_element_validate_slider_step',
+          ),
         );
         $bef_options[$label]['slider_options']['bef_slider_animate'] = array(
           '#type' => 'textfield',
@@ -664,7 +680,7 @@ Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option alt
         '#type' => 'textfield',
         '#title' => t('Override "Any" option label'),
         '#default_value' => $existing[$label]['more_options']['any_label'],
-        '#description' => t('Leave blank to use Views\' default value.'),
+        '#description' => t('Leave blank to use Views default value.'),
       );
 
       // Build a description option form element -- available to all exposed
@@ -731,7 +747,6 @@ Title Desc|Z -> A</pre> Leave the replacement text blank to remove an option alt
         }
         else {
           $bef_options[$label]['more_options']['tokens']['list'] = array(
-            //'#title' => t('Filter-specific tokens'),
             '#theme' => 'token_tree',
             '#token_types' => $filter_specific,
             '#global_types' => FALSE,
@@ -809,9 +824,9 @@ dateFormat: "dd-mm-yy"
    * Tweak the exposed filter form to show Better Exposed Filter options.
    *
    * @param array $form
-   *   Exposed form array
+   *   Exposed form array.
    * @param array $form_state
-   *   Current state of form variables
+   *   Current state of form variables.
    */
   function exposed_form_alter(&$form, &$form_state) {
     parent::exposed_form_alter($form, $form_state);
@@ -927,16 +942,16 @@ dateFormat: "dd-mm-yy"
               // will be the first one if there are multiple sort criteria.
               $selected = "$by_key $order_key";
             }
-           if ($settings['sort']['bef_format'] == 'bef_toggle_links') {
-             if (isset($used_sort_keys[$by_key])
+            if ($settings['sort']['bef_format'] == 'bef_toggle_links') {
+              if (isset($used_sort_keys[$by_key])
                || (!empty($form_state['input'][$settings['sort']['advanced']['combine_param']]) && $form_state['input'][$settings['sort']['advanced']['combine_param']] == "$by_key $order_key")
                || (empty($form_state['input'][$settings['sort']['advanced']['combine_param']]) && $selected == "$by_key $order_key")
-             ) {
+              ) {
                 $hidden_options["$by_key $order_key"] = "$by_val $order_val";
-             }
-             else {
+              }
+              else {
                 $used_sort_keys[$by_key] = $order_key;
-             }
+              }
             }
           }
         }
@@ -974,7 +989,7 @@ dateFormat: "dd-mm-yy"
           '#settings' => array(
             'toggle_links' => ($settings['sort']['bef_format'] == 'bef_toggle_links'),
             'combine_param' => $settings['sort']['advanced']['combine_param'],
-           ),
+          ),
           '#default_value' => $selected,
           // Already sanitized by Views.
           '#title' => $form['sort_by']['#title'],
@@ -1222,10 +1237,10 @@ dateFormat: "dd-mm-yy"
             }
             else {
               if ($is_object) {
-                // dsm($form[$filter_id]['#options'][$index]->option, "$filter_id at $index");
                 // Taxonomy term filters are stored as objects. Use str_replace
                 // to ensure that keep hyphens for hierarchical filters.
-                list($tid, $original) = each($form[$filter_id]['#options'][$index]->option);
+                $tid = key($form[$filter_id]['#options'][$index]->option);
+                $original = current($form[$filter_id]['#options'][$index]->option);
                 $form[$filter_id]['#options'][$index]->option[$tid] = str_replace($option, $rewrite[$option], $original);
               }
               else {
@@ -1365,7 +1380,7 @@ dateFormat: "dd-mm-yy"
               // Seconds, with leading zeros 00 through 59.
               // 's' => ' ',
               // Microseconds (added in PHP 5.2.2) Example: 654321.
-              // 'u' => ' ',
+              // 'u' => ' ',.
             );
 
             $format = '';
@@ -1511,7 +1526,6 @@ dateFormat: "dd-mm-yy"
           // element to see whether the form is submitted or not and then we
           // need to look at $_GET directly to see whether the checkbox is
           // there. For security reasons, we must not copy the $_GET value.
-
           // First, let's figure out a short name for the signal element and
           // then add it.
           if (empty($signal)) {
@@ -1544,7 +1558,7 @@ dateFormat: "dd-mm-yy"
             }
             array_unshift($form[$filter_id]['#process'], 'form_process_radios');
 
-            // Add description
+            // Add description.
             if (!empty($form[$filter_id]['#bef_description'])) {
               $form[$filter_id]['#description'] = $form[$filter_id]['#bef_description'];
             }
@@ -1556,7 +1570,8 @@ dateFormat: "dd-mm-yy"
             foreach ($opts as $index => $opt) {
               if (is_object($opt)) {
                 reset($opt->option);
-                list($key, $val) = each($opt->option);
+                $key = key($opt->option);
+                $val = current($opt->option);
                 $form[$filter_id]['#options'][$key] = $val;
               }
               else {
@@ -1599,7 +1614,7 @@ dateFormat: "dd-mm-yy"
               if ($filters[$label]->options['expose']['use_operator']) {
                 $operator_id = $filters[$label]->options['expose']['operator_id'];
                 $form[$filter_id]['#bef_operator'] = $form[$operator_id];
-                unset ($form[$operator_id]);
+                unset($form[$operator_id]);
               }
 
               // Add collapse/expand Javascript and BEF CSS to prevent collapsed
@@ -1648,7 +1663,7 @@ dateFormat: "dd-mm-yy"
               if ($filters[$label]->options['expose']['use_operator']) {
                 $operator_id = $filters[$label]->options['expose']['operator_id'];
                 $form[$filter_id]['#bef_operator'] = $form[$operator_id];
-                unset ($form[$operator_id]);
+                unset($form[$operator_id]);
               }
 
               // Add collapse/expand Javascript and BEF CSS to prevent collapsed
@@ -1691,7 +1706,7 @@ dateFormat: "dd-mm-yy"
             foreach ($form[$filter_id]['#options'] as $tid => $option) {
               if (is_object($option)) {
                 reset($option->option);
-                list ($tid, ) = each($option->option);
+                $tid = key($option->option);
               }
               $tids[] = $tid;
             }
@@ -1799,11 +1814,11 @@ dateFormat: "dd-mm-yy"
           // along to the theme function for rendering. Multiply existing
           // position by 2 so that we have room to stick the exposed operator
           // before the filter.
-          if (isset($filters[$identifier]->position)) {
-            $secondary[$identifier]['#bef_position'] = $filters[$identifier]->position * 2;
+          if (isset($filters[$label]->position)) {
+            $secondary[$identifier]['#bef_position'] = $filters[$label]->position * 2;
           }
 
-          // Move exposed operators with exposed filters
+          // Move exposed operators with exposed filters.
           if (!empty($filters[$label]->options['expose']['use_operator'])) {
             $op_id = $filters[$label]->options['expose']['operator_id'];
             $secondary[$op_id] = $form[$op_id];
@@ -1811,8 +1826,8 @@ dateFormat: "dd-mm-yy"
 
             // Make sure operators appear just before the filter they are
             // associated with.
-            if (isset($filters[$identifier]->position)) {
-              $secondary[$op_id]['#bef_position'] = ($filters[$identifier]->position * 2) - 1;
+            if (isset($filters[$label]->position)) {
+              $secondary[$op_id]['#bef_position'] = ($filters[$label]->position * 2) - 1;
             }
           }
         }
@@ -1959,7 +1974,7 @@ dateFormat: "dd-mm-yy"
   /**
    * Returns an array of default or current existing values for BEF settings.
    *
-   * This helps us as we add new options and prevents a lot of
+   * This helps us as we add new options and prevents a lot of.
    * @code
    *    if (isset($settings['new_settings'])) { ... }
    * @endcode
@@ -2058,7 +2073,7 @@ dateFormat: "dd-mm-yy"
 
   /**
    * Utility function to determine if any filters have been applied.
-   * Borrowed from views_plugin_exposed_form_input_required
+   * Borrowed from views_plugin_exposed_form_input_required.
    */
   function exposed_filter_applied() {
     static $cache = NULL;
@@ -2144,11 +2159,27 @@ dateFormat: "dd-mm-yy"
     $keys = array(
       // @TODO: Do we need to give this a better key so it makes more sense in
       // the localization UI?
-      'value' => array('bef', 'general', 'text_input_required', 'text_input_required', 'value'),
+      'value' => array(
+        'bef',
+        'general',
+        'text_input_required',
+        'text_input_required',
+        'value',
+      ),
 
       'general_secondary_label' => array('bef', 'general', 'secondary_label'),
-      'sort_collapsible_label' => array('bef', 'sort', 'advanced', 'collapsible_label'),
-      'sort_combine_rewrite' => array('bef', 'sort', 'advanced', 'combine_rewrite'),
+      'sort_collapsible_label' => array(
+        'bef',
+        'sort',
+        'advanced',
+        'collapsible_label',
+      ),
+      'sort_combine_rewrite' => array(
+        'bef',
+        'sort',
+        'advanced',
+        'combine_rewrite',
+      ),
       'sort_reset_label' => array('bef', 'sort', 'advanced', 'reset_label'),
     );
 
@@ -2157,10 +2188,31 @@ dateFormat: "dd-mm-yy"
       if (!$filter->options['exposed']) {
         continue;
       }
-      $keys[$label . '_filter_description'] = array('bef', $label, 'more_options', 'bef_filter_description');
-      $keys[$label . '_any_label'] = array('bef', $label, 'more_options', 'any_label');
-      $keys[$label . '_rewrite_values'] = array('bef', $label, 'more_options', 'rewrite', 'filter_rewrite_values');
-      $keys[$label . '_datepicker_options'] = array('bef', $label, 'more_options', 'datepicker_options');
+      $keys[$label . '_filter_description'] = array(
+        'bef',
+        $label,
+        'more_options',
+        'bef_filter_description',
+      );
+      $keys[$label . '_any_label'] = array(
+        'bef',
+        $label,
+        'more_options',
+        'any_label',
+      );
+      $keys[$label . '_rewrite_values'] = array(
+        'bef',
+        $label,
+        'more_options',
+        'rewrite',
+        'filter_rewrite_values',
+      );
+      $keys[$label . '_datepicker_options'] = array(
+        'bef',
+        $label,
+        'more_options',
+        'datepicker_options',
+      );
     }
 
     return $keys;
@@ -2174,8 +2226,8 @@ dateFormat: "dd-mm-yy"
       $parent = end($parents);
       $value = isset($options[$parent]) ? $options[$parent] : NULL;
 
-      // Don't localize strings during editing. When editing, we need to work with
-      // the original data, not the translated version.
+      // Don't localize strings during editing. When editing, we need to work
+      // with the original data, not the translated version.
       if (empty($this->view->editing) && !empty($value)) {
         if (!empty($this->view) && $this->view->is_translatable()) {
           // Allow other modules to make changes to the string before it's
@@ -2194,9 +2246,10 @@ dateFormat: "dd-mm-yy"
           $storage[$parent] = t($value);
         }
       }
-      else if (!empty($value)) {
+      elseif (!empty($value)) {
         $storage[$parent] = $value;
       }
     }
   }
+
 }
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/bef_test_content/bef_test_content.info b/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/bef_test_content/bef_test_content.info
index 7ac42b8462417d3e85ba6f50c13c84adff3271f6..164268c7cccf009f4639c5cf04ebe8f1594cb6f4 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/bef_test_content/bef_test_content.info
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/bef_test_content/bef_test_content.info
@@ -41,9 +41,8 @@ features_exclude[field][node-bef_test-field_price] = node-bef_test-field_price
 features_exclude[field][node-bef_test-field_date] = node-bef_test-field_date
 hidden = 1
 
-; Information added by Drupal.org packaging script on 2017-10-25
-version = "7.x-3.5"
+; Information added by Drupal.org packaging script on 2018-09-03
+version = "7.x-3.6"
 core = "7.x"
 project = "better_exposed_filters"
-datestamp = "1508952850"
-
+datestamp = "1536016685"
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/bef_test_content/bef_test_content.install b/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/bef_test_content/bef_test_content.install
index ed3b3175b681014f56923705dd0d9e77bb05c9d9..89fe22fd5d4d42425e69bc10a9cdaa634084b9e4 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/bef_test_content/bef_test_content.install
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/bef_test_content/bef_test_content.install
@@ -1,81 +1,81 @@
-<?php
-/**
- * Provides install hooks for the BEF Test Content module.
- */
-
-/**
- * Adds terms to the hierarchical vocabulary.
- */
-function bef_test_content_update_7001() {
-  // Set up an example hierarchical terms in the "Location" vocab.
-  $locations = array(
-    'United States' => array(
-      'California' => array(
-        'San Francisco',
-        'San Diego',
-        'Santa Barbara',
-      ),
-      'Oregon' => array(
-        'Portland',
-        'Eugene',
-      ),
-      'Washington' => array(
-        'Seattle',
-        'Spokane',
-        'Walla Walla',
-      ),
-    ),
-    'Canada' => array(
-      'British Columbia' => array(
-        'Vancouver',
-        'Victoria',
-        'Whistler',
-      ),
-      'Alberta' => array(
-        'Calgary',
-        'Edmonton',
-        'Lake Louise',
-      ),
-    ),
-    'Mexico' => array(),
-  );
-  foreach ($locations as $country => $states) {
-    $country_tid = _bef_test_content_add_term($country);
-    if ($country_tid && !empty($states)) {
-      foreach ($states as $state => $cities) {
-        $state_tid = _bef_test_content_add_term($state, $country_tid);
-        if ($state_tid && !empty($cities)) {
-          foreach ($cities as $city) {
-            _bef_test_content_add_term($city, $state_tid);
-          }
-        }
-      }
-    }
-  }
-}
-
-/**
- * Adds a new term to the bef_test-location vocabulary. If a TID is specified
- * in $parent, the new term is added as a child of that term.
- *
- * @param string $name
- *   The name of the new term.
- * @param int $parent
- *   The (optional) TID of the parent term.
- *
- * @return int
- *   TID of the newly created term or 0 on an error.
- */
-function _bef_test_content_add_term($name, $parent = 0) {
-  $term = new stdClass();
-  // Features manages to create a vocab machine name that includes illegal
-  // characters (taxonomy-bef_test-location -- the hyphen is not allowed). So
-  // we use the VID of the vocab instead.
-  $term->vid = 2;
-  $term->parent = $parent;
-  $term->name = $name;
-  if (taxonomy_term_save($term) == SAVED_NEW) {
-    return $term->tid;
-  }
-  return 0;
-}
+<?php
+/**
+ * Provides install hooks for the BEF Test Content module.
+ */
+
+/**
+ * Adds terms to the hierarchical vocabulary.
+ */
+function bef_test_content_update_7001() {
+  // Set up an example hierarchical terms in the "Location" vocab.
+  $locations = array(
+    'United States' => array(
+      'California' => array(
+        'San Francisco',
+        'San Diego',
+        'Santa Barbara',
+      ),
+      'Oregon' => array(
+        'Portland',
+        'Eugene',
+      ),
+      'Washington' => array(
+        'Seattle',
+        'Spokane',
+        'Walla Walla',
+      ),
+    ),
+    'Canada' => array(
+      'British Columbia' => array(
+        'Vancouver',
+        'Victoria',
+        'Whistler',
+      ),
+      'Alberta' => array(
+        'Calgary',
+        'Edmonton',
+        'Lake Louise',
+      ),
+    ),
+    'Mexico' => array(),
+  );
+  foreach ($locations as $country => $states) {
+    $country_tid = _bef_test_content_add_term($country);
+    if ($country_tid && !empty($states)) {
+      foreach ($states as $state => $cities) {
+        $state_tid = _bef_test_content_add_term($state, $country_tid);
+        if ($state_tid && !empty($cities)) {
+          foreach ($cities as $city) {
+            _bef_test_content_add_term($city, $state_tid);
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Adds a new term to the bef_test-location vocabulary. If a TID is specified
+ * in $parent, the new term is added as a child of that term.
+ *
+ * @param string $name
+ *   The name of the new term.
+ * @param int $parent
+ *   The (optional) TID of the parent term.
+ *
+ * @return int
+ *   TID of the newly created term or 0 on an error.
+ */
+function _bef_test_content_add_term($name, $parent = 0) {
+  $term = new stdClass();
+  // Features manages to create a vocab machine name that includes illegal
+  // characters (taxonomy-bef_test-location -- the hyphen is not allowed). So
+  // we use the VID of the vocab instead.
+  $term->vid = 2;
+  $term->parent = $parent;
+  $term->name = $name;
+  if (taxonomy_term_save($term) == SAVED_NEW) {
+    return $term->tid;
+  }
+  return 0;
+}
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/better_exposed_filters.test b/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/better_exposed_filters.test
index 9784b8c196a66ae19853305c9da5b9b6d7ae3d4b..2169e1c1da7a28650bf94e2402b813496d4e7217 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/better_exposed_filters.test
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/better_exposed_filters.test
@@ -15,6 +15,7 @@ class BEF_TestOptions extends BEF_TestBase {
       'name' => 'BEF Options tests',
       'description' => 'Checks that BEF options appear when should.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -123,6 +124,7 @@ class BEF_TestRadios extends BEF_TestBase {
       'name' => 'BEF radio button tests',
       'description' => 'Verifies rendering filters as radio buttons.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -179,6 +181,7 @@ class BEF_TestCheckboxes extends BEF_TestBase {
       'name' => 'BEF checkbox tests',
       'description' => 'Verifies rendering filter options as checkboxes.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -247,6 +250,7 @@ class BEF_TestHidden extends BEF_TestBase {
       'name' => 'BEF hidden tests',
       'description' => 'Verifies rendering filter options as hidden elements.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -297,6 +301,7 @@ class BEF_TestTaxonomyFilters extends BEF_TestBase {
       'name' => 'BEF taxonomy filter tests',
       'description' => 'Verifies rendering of taxonomy filters.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -377,6 +382,7 @@ class BEF_TestSort extends BEF_TestBase {
       'name' => 'BEF sort tests',
       'description' => 'Verifies rendering exposed sort options.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -439,6 +445,7 @@ class BEF_TestSliders extends BEF_TestBase {
       'name' => 'BEF slider tests',
       'description' => 'Verifies rendering filters jQueryUI sliders.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -528,6 +535,7 @@ class BEF_TestDatepicker extends BEF_TestBase {
       'name' => 'BEF datepicker tests',
       'description' => 'Verifies rendering filter options as a jQueryUI Datepicker.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -609,6 +617,7 @@ class BEF_TestLinks extends BEF_TestBase {
       'name' => 'BEF links tests',
       'description' => 'Verifies rendering filter options as toggle links.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -718,6 +727,7 @@ class BEF_TestSecondaryFilters extends BEF_TestBase {
       'name' => 'BEF secondary filter tests',
       'description' => 'Verifies rendering filter options within the secondary filter fieldset.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -826,6 +836,7 @@ class BEF_TestRewrite extends BEF_TestBase {
       'name' => 'BEF rewrite tests',
       'description' => 'Verifies rewriting filter and sort options.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
@@ -906,6 +917,7 @@ class BEF_TestMisc extends BEF_TestBase {
       'name' => 'BEF miscellaneous tests',
       'description' => 'Verifies misc BEF functional.',
       'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
     );
   }
 
diff --git a/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/better_exposed_filters_TestBase.php b/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/better_exposed_filters_TestBase.php
index e7082ed4438d564b55ec7c0bdc56146e29e48b64..463e14bac69539557ee2a771d4c7ef70b213df15 100644
--- a/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/better_exposed_filters_TestBase.php
+++ b/profiles/wcm_base/modules/contrib/better_exposed_filters/tests/better_exposed_filters_TestBase.php
@@ -1,275 +1,277 @@
-<?php
-
-/**
- * @file Base class for testing the Better Exposed Filters module.
- * @author mikeker
- */
-
-/**
- * Helper functions for Better Exposed Filters tests.
- */
-class BEF_TestBase extends DrupalWebTestCase {
-  /**
-   * User with 'Administrator' role.
-   */
-  protected $admin_user;
-
-  /**
-   * Stores information about the view used in these tests.
-   */
-  protected $view = array();
-
-  public static function getInfo() {
-    return array(
-      'name' => 'BEF Basic functionality tests',
-      'description' => 'Basic tests for Better Exposed Filters.',
-      'group' => 'Better Exposed Filters',
-    );
-  }
-
-  public function setUp() {
-    // For benchmarking.
-    $this->start = time();
-
-    // Enable any modules required for the test.
-    parent::setUp(
-      'better_exposed_filters',
-      'date',
-      'date_views',
-      'list',
-      'number',
-      'taxonomy',
-      'text',
-      'views',
-      'views_ui'
-    );
-
-    // One of these days I'll figure out why Features is breaking all my tests.
-    module_enable(array('bef_test_content'));
-
-    // User with edit views perms
-    $this->admin_user = $this->drupalCreateUser();
-    $role = user_role_load_by_name('administrator');
-    $this->assertTrue(!empty($role->rid), 'Found the "administrator" role.');
-    user_save($this->admin_user, array('roles' => array($role->rid => $role->rid)));
-    $this->drupalLogin($this->admin_user);
-
-    // Build a basic view for use in tests.
-    $this->createView();
-
-    // $this->createDisplay('Page', array('path' => array('path' => 'bef_test_page')));
-
-    // Add field to default display
-    // $this->addField('node.title');
-
-    // Turn of Better Exposed Filters
-    $this->setBefExposedForm();
-  }
-
-  public function tearDown() {
-    debug('This test run took ' . (time() - $this->start) . ' seconds.');
-    unset($this->view);
-    parent::tearDown();
-  }
-
-  /*******************************************************************************
-   * Helper functions
-   ******************************************************************************/
-
-  /**
-   * Returns the URL for the BEF exposed form settings page.
-   */
-  protected function getBefSettingsUrl() {
-    return 'admin/structure/views/nojs/display/' . $this->view['machine_name'] . '/default/exposed_form_options';
-  }
-
-  protected function createView($name = '') {
-    if (!empty($this->view)) {
-      debug('WARNING: createView called after view has already been created.');
-      return;
-    }
-
-    if (empty($name)) {
-      $name = $this->randomName(8);
-    }
-    $this->view['name'] = $name;
-    $this->view['machine_name'] = strtolower($name);
-
-    $edit = array(
-      'human_name' => $this->view['name'],
-      'name' => $this->view['machine_name'],
-      // Default is to create a page display.
-      'page[create]' => FALSE,
-    );
-    $this->drupalPost('admin/structure/views/add', $edit, 'Save & exit');
-
-    // URL to edit this view.
-    $this->view['edit_url'] = 'admin/structure/views/view/' . $this->view['machine_name'] . '/edit';
-  }
-
-  /**
-   * Creates a display of $type.  Currently supports:
-   *    'Page'
-   *
-   * @todo: support more types...
-   */
-  protected function createDisplay($type = 'Page', $settings = NULL) {
-    if (!isset($this->view['displays'])) {
-      $this->view['displays'] = array();
-    }
-
-    // Add a display of $type to the view
-    $this->drupalPost($this->view['edit_url'], array(), "Add $type");
-
-    // Grab the name of the newly created display and store some info about it.
-    $url = $this->getUrl();
-    $display_name = substr($url, strrpos($url, '/') + 1);
-    $this->view['displays'][$display_name] = array(
-      'machine_name' => $display_name,
-      'edit_url' => 'admin/structure/views/view/' . $this->view['machine_name'] . '/edit/' . $display_name,
-      'settings_base_url' => 'admin/structure/views/nojs/display/' . $this->view['machine_name'] . '/' . $display_name,
-    );
-
-    // Settings should be in the form of 'path' => array_of_form_settings. Eg:
-    // to set the title for a new display as an override:
-    //  'title' => array(
-    //    'title' => 'This is an override title',
-    //    'override[dropdown]' => display_machine_name_goes_here,
-    //  )
-    //
-    // If you navigate to
-    //  admin/structure/views/nojs/display/<view_name>/<display_name>/title
-    // you will see the form in question.
-    foreach ($settings as $path => $values) {
-      $this->drupalPost($this->view['displays'][$display_name]['settings_base_url'] . "/$path", $values, 'Apply');
-    }
-    $this->saveView();
-  }
-
-  /**
-   * Adds a filter to a view display.
-   *
-   * $field: string in the form of node.status or
-   *   field_data_field_example.field_example_value
-   * $settings: (array) Settings on the "Configure filter criterion" dialog.
-   *   NOTE: called after the "Expose filter" button is pressed if $exposed
-   *   is TRUE so you can set things like "Allow multiple items" or grouped
-   *   filter options.
-   * $additional: (array) settings for any additional configuration forms such
-   *   as taxonomy term settings.
-   * $display: machine name of the display to add this filter to. NOTE:
-   *   Currently only allows filters on the master display, no overrides.
-   *   @todo: fix that, if needed.
-   * $exposed: (bool) (optional, default: TRUE) Is this an exposed filter?
-   *
-   * Note: This routine expects the caller to save the view, as needed.
-   */
-  protected function addFilter($field, $settings = array(), $additional = array(), $exposed = TRUE, $display = 'default') {
-    $edit = array(
-      "name[$field]" => TRUE,
-    );
-    $url = 'admin/structure/views/nojs/add-item/' . $this->view['machine_name'] . "/$display/filter";
-    $this->drupalPost($url, $edit, 'Add and configure filter criteria');
-
-    if (!empty($additional)) {
-      // Handle filter-specific options screen.
-      $this->drupalPost(NULL, $additional, 'Apply');
-    }
-
-    if ($exposed) {
-      $this->drupalPost(NULL, array(), 'Expose filter');
-    }
-    $this->drupalPost(NULL, $settings, 'Apply');
-  }
-
-  /**
-   * Edits an existing filter in the current view. See addFilter for param
-   * definitions.
-   */
-  protected function editFilter($field, $settings, $additional = array(), $display = 'default') {
-    if (FALSE !== ($pos = strpos($field, '.'))) {
-      $field = substr($field, $pos + 1);
-    }
-    $url = 'admin/structure/views/nojs/config-item/' . $this->view['machine_name'] . "/$display/filter/$field";
-    $this->drupalPost($url, $settings, 'Apply');
-
-    if (!empty($additional)) {
-      // Handle filter-specific options screen.
-      $this->drupalPost(NULL, $additional, 'Apply');
-    }
-  }
-
-  /**
-   * Adds a sort to a view display. See addFilter for parameter options.
-   *
-   * Note: This routine expects the caller to save the view, as needed.
-   */
-  protected function addSort($field, $settings = array(), $additional = array(), $exposed = TRUE, $display = 'default') {
-    $edit = array(
-      "name[$field]" => TRUE,
-    );
-    $url = 'admin/structure/views/nojs/add-item/' . $this->view['machine_name'] . "/$display/sort";
-    $this->drupalPost($url, $edit, 'Add and configure sort criteria');
-
-    if (!empty($additional)) {
-      // Handle filter-specific options screen.
-      $this->drupalPost(NULL, $additional, 'Apply');
-    }
-
-    if ($exposed) {
-      $this->drupalPost(NULL, array(), 'Expose sort');
-    }
-    $this->drupalPost(NULL, $settings, 'Apply');
-  }
-
-  /**
-   * Adds a field to a view display. See addFilter for parameter options.
-   *
-   * Note: This routine expects the caller to save the view, as needed.
-   */
-  protected function addField($field, $settings = array(), $display = 'default') {
-    $edit = array(
-      "name[$field]" => TRUE,
-    );
-    $url = 'admin/structure/views/nojs/add-item/' . $this->view['machine_name'] . "/$display/field";
-    $this->drupalPost($url, $edit, 'Add and configure fields');
-    $this->drupalPost(NULL, $settings, 'Apply');
-  }
-
-  /**
-   * Ensures that BEF is selected as the exposed form option
-   *
-   * Note: This routine expects the caller to save the view, as needed.
-   */
-  protected function setBefExposedForm($display = 'default') {
-    $edit = array(
-      "exposed_form[type]" => 'better_exposed_filters',
-    );
-    $url = 'admin/structure/views/nojs/display/' . $this->view['machine_name'] . "/$display/exposed_form";
-    $this->drupalPost($url, $edit, 'Apply');
-
-    // BEF settings is covered under setBefSettings() so we just accept the
-    // default values and move on.
-    $this->drupalPost(NULL, array(), 'Apply');
-  }
-
-  /**
-   * Sets various BEF exposed form settings. If $error is specified it also
-   * asserts that the error text apepars when trying to apply $settings.
-   *
-   * Note: This routine expects the caller to save the view, as needed.
-   */
-  protected function setBefSettings($settings, $error = '') {
-    $this->drupalPost($this->getBefSettingsUrl(), $settings, 'Apply');
-    if (!empty($error)) {
-      $this->assertText($error);
-    }
-  }
-
-  /**
-   * Saves the view
-   */
-  protected function saveView() {
-    $this->drupalPost($this->view['edit_url'], array(), 'Save');
-  }
-}
+<?php
+
+/**
+ * @file
+ * Base class for testing the Better Exposed Filters module.
+ * @author mikeker
+ */
+
+/**
+ * Helper functions for Better Exposed Filters tests.
+ */
+class BEF_TestBase extends DrupalWebTestCase {
+  /**
+   * User with 'Administrator' role.
+   */
+  protected $admin_user;
+
+  /**
+   * Stores information about the view used in these tests.
+   */
+  protected $view = array();
+
+  public static function getInfo() {
+    return array(
+      'name' => 'BEF Basic functionality tests',
+      'description' => 'Basic tests for Better Exposed Filters.',
+      'group' => 'Better Exposed Filters',
+      'dependencies' => array('date', 'date_views', 'views', 'views_ui')
+    );
+  }
+
+  public function setUp() {
+    // For benchmarking.
+    $this->start = time();
+
+    // Enable any modules required for the test.
+    parent::setUp(
+      'better_exposed_filters',
+      'date',
+      'date_views',
+      'list',
+      'number',
+      'taxonomy',
+      'text',
+      'views',
+      'views_ui'
+    );
+
+    // One of these days I'll figure out why Features is breaking all my tests.
+    module_enable(array('bef_test_content'));
+
+    // User with edit views perms.
+    $this->admin_user = $this->drupalCreateUser();
+    $role = user_role_load_by_name('administrator');
+    $this->assertTrue(!empty($role->rid), 'Found the "administrator" role.');
+    user_save($this->admin_user, array('roles' => array($role->rid => $role->rid)));
+    $this->drupalLogin($this->admin_user);
+
+    // Build a basic view for use in tests.
+    $this->createView();
+
+    // $this->createDisplay('Page', array('path' => array('path' => 'bef_test_page')));
+
+    // Add field to default display
+    // $this->addField('node.title');
+
+    // Turn of Better Exposed Filters.
+    $this->setBefExposedForm();
+  }
+
+  public function tearDown() {
+    debug('This test run took ' . (time() - $this->start) . ' seconds.');
+    unset($this->view);
+    parent::tearDown();
+  }
+
+  /*******************************************************************************
+   * Helper functions
+   ******************************************************************************/
+
+  /**
+   * Returns the URL for the BEF exposed form settings page.
+   */
+  protected function getBefSettingsUrl() {
+    return 'admin/structure/views/nojs/display/' . $this->view['machine_name'] . '/default/exposed_form_options';
+  }
+
+  protected function createView($name = '') {
+    if (!empty($this->view)) {
+      debug('WARNING: createView called after view has already been created.');
+      return;
+    }
+
+    if (empty($name)) {
+      $name = $this->randomName(8);
+    }
+    $this->view['name'] = $name;
+    $this->view['machine_name'] = strtolower($name);
+
+    $edit = array(
+      'human_name' => $this->view['name'],
+      'name' => $this->view['machine_name'],
+      // Default is to create a page display.
+      'page[create]' => FALSE,
+    );
+    $this->drupalPost('admin/structure/views/add', $edit, 'Save & exit');
+
+    // URL to edit this view.
+    $this->view['edit_url'] = 'admin/structure/views/view/' . $this->view['machine_name'] . '/edit';
+  }
+
+  /**
+   * Creates a display of $type.  Currently supports:
+   *    'Page'
+   *
+   * @todo: support more types...
+   */
+  protected function createDisplay($type = 'Page', $settings = NULL) {
+    if (!isset($this->view['displays'])) {
+      $this->view['displays'] = array();
+    }
+
+    // Add a display of $type to the view.
+    $this->drupalPost($this->view['edit_url'], array(), "Add $type");
+
+    // Grab the name of the newly created display and store some info about it.
+    $url = $this->getUrl();
+    $display_name = substr($url, strrpos($url, '/') + 1);
+    $this->view['displays'][$display_name] = array(
+      'machine_name' => $display_name,
+      'edit_url' => 'admin/structure/views/view/' . $this->view['machine_name'] . '/edit/' . $display_name,
+      'settings_base_url' => 'admin/structure/views/nojs/display/' . $this->view['machine_name'] . '/' . $display_name,
+    );
+
+    // Settings should be in the form of 'path' => array_of_form_settings. Eg:
+    // to set the title for a new display as an override:
+    //  'title' => array(
+    //    'title' => 'This is an override title',
+    //    'override[dropdown]' => display_machine_name_goes_here,
+    //  )
+    //
+    // If you navigate to
+    //  admin/structure/views/nojs/display/<view_name>/<display_name>/title
+    // you will see the form in question.
+    foreach ($settings as $path => $values) {
+      $this->drupalPost($this->view['displays'][$display_name]['settings_base_url'] . "/$path", $values, 'Apply');
+    }
+    $this->saveView();
+  }
+
+  /**
+   * Adds a filter to a view display.
+   *
+   * $field: string in the form of node.status or
+   *   field_data_field_example.field_example_value
+   * $settings: (array) Settings on the "Configure filter criterion" dialog.
+   *   NOTE: called after the "Expose filter" button is pressed if $exposed
+   *   is TRUE so you can set things like "Allow multiple items" or grouped
+   *   filter options.
+   * $additional: (array) settings for any additional configuration forms such
+   *   as taxonomy term settings.
+   * $display: machine name of the display to add this filter to. NOTE:
+   *   Currently only allows filters on the master display, no overrides.
+   *   @todo: fix that, if needed.
+   * $exposed: (bool) (optional, default: TRUE) Is this an exposed filter?
+   *
+   * Note: This routine expects the caller to save the view, as needed.
+   */
+  protected function addFilter($field, $settings = array(), $additional = array(), $exposed = TRUE, $display = 'default') {
+    $edit = array(
+      "name[$field]" => TRUE,
+    );
+    $url = 'admin/structure/views/nojs/add-item/' . $this->view['machine_name'] . "/$display/filter";
+    $this->drupalPost($url, $edit, 'Add and configure filter criteria');
+
+    if (!empty($additional)) {
+      // Handle filter-specific options screen.
+      $this->drupalPost(NULL, $additional, 'Apply');
+    }
+
+    if ($exposed) {
+      $this->drupalPost(NULL, array(), 'Expose filter');
+    }
+    $this->drupalPost(NULL, $settings, 'Apply');
+  }
+
+  /**
+   * Edits an existing filter in the current view. See addFilter for param
+   * definitions.
+   */
+  protected function editFilter($field, $settings, $additional = array(), $display = 'default') {
+    if (FALSE !== ($pos = strpos($field, '.'))) {
+      $field = substr($field, $pos + 1);
+    }
+    $url = 'admin/structure/views/nojs/config-item/' . $this->view['machine_name'] . "/$display/filter/$field";
+    $this->drupalPost($url, $settings, 'Apply');
+
+    if (!empty($additional)) {
+      // Handle filter-specific options screen.
+      $this->drupalPost(NULL, $additional, 'Apply');
+    }
+  }
+
+  /**
+   * Adds a sort to a view display. See addFilter for parameter options.
+   *
+   * Note: This routine expects the caller to save the view, as needed.
+   */
+  protected function addSort($field, $settings = array(), $additional = array(), $exposed = TRUE, $display = 'default') {
+    $edit = array(
+      "name[$field]" => TRUE,
+    );
+    $url = 'admin/structure/views/nojs/add-item/' . $this->view['machine_name'] . "/$display/sort";
+    $this->drupalPost($url, $edit, 'Add and configure sort criteria');
+
+    if (!empty($additional)) {
+      // Handle filter-specific options screen.
+      $this->drupalPost(NULL, $additional, 'Apply');
+    }
+
+    if ($exposed) {
+      $this->drupalPost(NULL, array(), 'Expose sort');
+    }
+    $this->drupalPost(NULL, $settings, 'Apply');
+  }
+
+  /**
+   * Adds a field to a view display. See addFilter for parameter options.
+   *
+   * Note: This routine expects the caller to save the view, as needed.
+   */
+  protected function addField($field, $settings = array(), $display = 'default') {
+    $edit = array(
+      "name[$field]" => TRUE,
+    );
+    $url = 'admin/structure/views/nojs/add-item/' . $this->view['machine_name'] . "/$display/field";
+    $this->drupalPost($url, $edit, 'Add and configure fields');
+    $this->drupalPost(NULL, $settings, 'Apply');
+  }
+
+  /**
+   * Ensures that BEF is selected as the exposed form option.
+   *
+   * Note: This routine expects the caller to save the view, as needed.
+   */
+  protected function setBefExposedForm($display = 'default') {
+    $edit = array(
+      "exposed_form[type]" => 'better_exposed_filters',
+    );
+    $url = 'admin/structure/views/nojs/display/' . $this->view['machine_name'] . "/$display/exposed_form";
+    $this->drupalPost($url, $edit, 'Apply');
+
+    // BEF settings is covered under setBefSettings() so we just accept the
+    // default values and move on.
+    $this->drupalPost(NULL, array(), 'Apply');
+  }
+
+  /**
+   * Sets various BEF exposed form settings. If $error is specified it also
+   * asserts that the error text apepars when trying to apply $settings.
+   *
+   * Note: This routine expects the caller to save the view, as needed.
+   */
+  protected function setBefSettings($settings, $error = '') {
+    $this->drupalPost($this->getBefSettingsUrl(), $settings, 'Apply');
+    if (!empty($error)) {
+      $this->assertText($error);
+    }
+  }
+
+  /**
+   * Saves the view.
+   */
+  protected function saveView() {
+    $this->drupalPost($this->view['edit_url'], array(), 'Save');
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/ctools/bulk_export/bulk_export.info b/profiles/wcm_base/modules/contrib/ctools/bulk_export/bulk_export.info
index 388cd6f7361644b0257f6eabee7000f9b2e3c7ed..9bb3394450f4f0b2b60b71a0bab9022f32b7c43a 100644
--- a/profiles/wcm_base/modules/contrib/ctools/bulk_export/bulk_export.info
+++ b/profiles/wcm_base/modules/contrib/ctools/bulk_export/bulk_export.info
@@ -3,11 +3,9 @@ description = Performs bulk exporting of data objects known about by Chaos tools
 core = 7.x
 dependencies[] = ctools
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/css/dropbutton.css b/profiles/wcm_base/modules/contrib/ctools/css/dropbutton.css
index 376fc674d0ab3d9450a3449113b600e5cfdf7b38..486ac3edaa0c63a08124524dda08cad54fcc5d57 100644
--- a/profiles/wcm_base/modules/contrib/ctools/css/dropbutton.css
+++ b/profiles/wcm_base/modules/contrib/ctools/css/dropbutton.css
@@ -19,7 +19,6 @@
   height: auto;
   position: absolute;
   right: 0;
-  text-indent: -9999px; /* LTR */
   top: 0;
   width: 17px;
 }
@@ -50,6 +49,7 @@
   right: 6px;
   position: absolute;
   top: 0.75em;
+  white-space: nowrap;
 }
 
 .ctools-dropbutton-processed.open .ctools-twisty {
diff --git a/profiles/wcm_base/modules/contrib/ctools/ctools.info b/profiles/wcm_base/modules/contrib/ctools/ctools.info
index c9d0c63a501084fbd2f76623e8ef7502e725186b..188b55dde8b3216d7aafee46b98ad5b5409fe030 100644
--- a/profiles/wcm_base/modules/contrib/ctools/ctools.info
+++ b/profiles/wcm_base/modules/contrib/ctools/ctools.info
@@ -19,9 +19,8 @@ files[] = tests/object_cache.test
 files[] = tests/object_cache_unit.test
 files[] = tests/page_tokens.test
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/ctools.module b/profiles/wcm_base/modules/contrib/ctools/ctools.module
index 3a805808b772510b6d52946f70aba6b0e1036863..62f291923045852e2811c187c10063546a0619f9 100644
--- a/profiles/wcm_base/modules/contrib/ctools/ctools.module
+++ b/profiles/wcm_base/modules/contrib/ctools/ctools.module
@@ -22,6 +22,9 @@ define('CTOOLS_API_VERSION', '2.0.9');
  * simply include a dependency line in that module's info file, e.g.:
  *   ; Requires CTools v7.x-1.4 or newer.
  *   dependencies[] = ctools (>=1.4)
+ *
+ * @deprecated in CTools 1.15 and will be removed before CTools 2.0.0.
+ *   Use the version provided by the drupal.org packaging system.
  */
 define('CTOOLS_MODULE_VERSION', '7.x-1.13');
 
diff --git a/profiles/wcm_base/modules/contrib/ctools/ctools_access_ruleset/ctools_access_ruleset.info b/profiles/wcm_base/modules/contrib/ctools/ctools_access_ruleset/ctools_access_ruleset.info
index 84a922fb5adf3b951ac173d13b486d2a938fd096..3fc355649a56bd68c919bf68c2159750a8d4bbc9 100644
--- a/profiles/wcm_base/modules/contrib/ctools/ctools_access_ruleset/ctools_access_ruleset.info
+++ b/profiles/wcm_base/modules/contrib/ctools/ctools_access_ruleset/ctools_access_ruleset.info
@@ -2,12 +2,10 @@ name = Custom rulesets
 description = Create custom, exportable, reusable access rulesets for applications like Panels.
 core = 7.x
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 dependencies[] = ctools
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/ctools_ajax_sample/ctools_ajax_sample.info b/profiles/wcm_base/modules/contrib/ctools/ctools_ajax_sample/ctools_ajax_sample.info
index 4bb8943cdeaa09e93f4eb15cbf6fabb3c0a2dcbb..c2a52df408b295b7f8f45443b16ec847de080c8d 100644
--- a/profiles/wcm_base/modules/contrib/ctools/ctools_ajax_sample/ctools_ajax_sample.info
+++ b/profiles/wcm_base/modules/contrib/ctools/ctools_ajax_sample/ctools_ajax_sample.info
@@ -1,13 +1,11 @@
 name = Chaos Tools (CTools) AJAX Example
 description = Shows how to use the power of Chaos AJAX.
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 dependencies[] = ctools
 core = 7.x
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/ctools_custom_content/ctools_custom_content.info b/profiles/wcm_base/modules/contrib/ctools/ctools_custom_content/ctools_custom_content.info
index 637bf25d547d4fffdb27de123853967251c98bdc..2bc73489c776bdc22045c8e4ab350b932226d6a1 100644
--- a/profiles/wcm_base/modules/contrib/ctools/ctools_custom_content/ctools_custom_content.info
+++ b/profiles/wcm_base/modules/contrib/ctools/ctools_custom_content/ctools_custom_content.info
@@ -2,12 +2,10 @@ name = Custom content panes
 description = Create custom, exportable, reusable content panes for applications like Panels.
 core = 7.x
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 dependencies[] = ctools
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/ctools_plugin_example/ctools_plugin_example.info b/profiles/wcm_base/modules/contrib/ctools/ctools_plugin_example/ctools_plugin_example.info
index 40f133bc8130ee9eb39bf35665debb1f91b007ac..3f268dbc0ca54c521abf7497ec2064c423b4dcae 100644
--- a/profiles/wcm_base/modules/contrib/ctools/ctools_plugin_example/ctools_plugin_example.info
+++ b/profiles/wcm_base/modules/contrib/ctools/ctools_plugin_example/ctools_plugin_example.info
@@ -1,16 +1,14 @@
 name = Chaos Tools (CTools) Plugin Example
 description = Shows how an external module can provide ctools plugins (for Panels, etc.).
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 dependencies[] = ctools
 dependencies[] = panels
 dependencies[] = page_manager
 dependencies[] = advanced_help
 core = 7.x
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/ctools_plugin_example/ctools_plugin_example.pages_default.inc b/profiles/wcm_base/modules/contrib/ctools/ctools_plugin_example/ctools_plugin_example.pages_default.inc
index bbabf22710b67a0f8b8844f10a10723afe6a04f7..ed3ffd776eadbf9b6e1719b41eefe454361171e2 100644
--- a/profiles/wcm_base/modules/contrib/ctools/ctools_plugin_example/ctools_plugin_example.pages_default.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/ctools_plugin_example/ctools_plugin_example.pages_default.inc
@@ -95,6 +95,7 @@ function ctools_plugin_example_default_page_manager_pages() {
     'access' => array(
       'logic' => 'and',
     ),
+    'pipeline' => 'standard',
   );
   $display = new panels_display();
   $display->layout = 'threecol_33_34_33_stacked';
@@ -154,7 +155,7 @@ function ctools_plugin_example_default_page_manager_pages() {
   $pane->configuration = array(
     'title' => 'Long Arg Visibility Block',
     'body' => 'This block will be here when the argument is longer than configured arg length. It uses the \'arg_length\' access plugin to test against the length of the argument used for Simplecontext.',
-    'format' => '1',
+    'format' => 'filtered_html',
     'substitute' => 1,
   );
   $pane->cache = array();
@@ -185,7 +186,7 @@ function ctools_plugin_example_default_page_manager_pages() {
   $pane->configuration = array(
     'title' => 'Short Arg Visibility',
     'body' => 'This block appears when the simplecontext argument is <i>less than</i> the configured length.',
-    'format' => '1',
+    'format' => 'filtered_html',
     'substitute' => 1,
   );
   $pane->cache = array();
@@ -297,7 +298,7 @@ function ctools_plugin_example_default_page_manager_pages() {
     item1 is %sc:item1
     item2 is %sc:item2
     description is %sc:description',
-    'format' => '1',
+    'format' => 'filtered_html',
     'substitute' => 1,
   );
   $pane->cache = array();
@@ -363,7 +364,7 @@ function ctools_plugin_example_default_page_manager_pages() {
     'body' => 'The CTools Plugin Example module (and this panel page) are just here to demonstrate how to build CTools plugins.
 
             ',
-    'format' => '2',
+    'format' => 'full_html',
     'substitute' => 1,
   );
   $pane->cache = array();
@@ -408,6 +409,7 @@ function ctools_plugin_example_default_page_manager_pages() {
     'css' => '',
     'contexts' => array(),
     'relationships' => array(),
+    'pipeline' => 'standard',
   );
   $display = new panels_display();
   $display->layout = 'onecol';
@@ -428,7 +430,7 @@ function ctools_plugin_example_default_page_manager_pages() {
   $pane->configuration = array(
     'title' => 'Use this page with an argument',
     'body' => 'This demo page works if you use an argument, like <a href="ctools_plugin_example/xxxxx">ctools_plugin_example/xxxxx</a>.',
-    'format' => '1',
+    'format' => 'filtered_html',
     'substitute' => NULL,
   );
   $pane->cache = array();
diff --git a/profiles/wcm_base/modules/contrib/ctools/drush/ctools.drush.inc b/profiles/wcm_base/modules/contrib/ctools/drush/ctools.drush.inc
index 3677b1712fc856fa57b79b2382c6e7f71e6c4897..34fd4d3a571de8ed720bb39269ff28f445e3021a 100644
--- a/profiles/wcm_base/modules/contrib/ctools/drush/ctools.drush.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/drush/ctools.drush.inc
@@ -452,6 +452,7 @@ function ctools_drush_export_info() {
  * all arg handling etc in one place.
  */
 function ctools_drush_export_op_command() {
+  $args = func_get_args();
   // Get all info for the current drush command.
   $command = drush_get_command();
   $op = '';
@@ -495,7 +496,6 @@ function ctools_drush_export_op_command() {
     }
   }
   else {
-    $args = func_get_args();
     // Table name should always be first arg...
     $table_name = array_shift($args);
     // Any additional args are assumed to be exportable names.
diff --git a/profiles/wcm_base/modules/contrib/ctools/includes/dropbutton.theme.inc b/profiles/wcm_base/modules/contrib/ctools/includes/dropbutton.theme.inc
index d69c98831732d781c40f1034eaa230e9ca1116b5..4b95ef0dfb65ed9130c0ff836241a340d577f1ac 100644
--- a/profiles/wcm_base/modules/contrib/ctools/includes/dropbutton.theme.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/includes/dropbutton.theme.inc
@@ -104,10 +104,10 @@ function theme_links__ctools_dropbutton($vars) {
 
       $output .= '<div class="ctools-link">';
       if ($vars['image']) {
-        $output .= '<a href="#" class="ctools-twisty ctools-image">' . $vars['title'] . '</a>';
+        $output .= '<a href="#" class="ctools-twisty ctools-image"><span class="element-invisible">' . $vars['title'] . '</span></a>';
       }
       else {
-        $output .= '<a href="#" class="ctools-twisty ctools-text">' . $vars['title'] . '</a>';
+        $output .= '<a href="#" class="ctools-twisty ctools-text"><span class="element-invisible">' . $vars['title'] . '</span></a>';
       }
       // ctools-link.
       $output .= '</div>';
diff --git a/profiles/wcm_base/modules/contrib/ctools/includes/export-ui.inc b/profiles/wcm_base/modules/contrib/ctools/includes/export-ui.inc
index 1eb87ae7792ff8c23b1fa54b3ccb989c53c518e2..72e5a2fdf165a0775e87029cad61ab6a468888e6 100644
--- a/profiles/wcm_base/modules/contrib/ctools/includes/export-ui.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/includes/export-ui.inc
@@ -333,7 +333,7 @@ function ctools_export_ui_process(&$plugin, $info) {
 
   $plugin['strings']['confirmation']['delete'] += array(
     'question' => t('Are you sure you want to delete %title?'),
-    'information' => t('This action will permanently remove this item from your database..'),
+    'information' => t('This action will permanently remove this item from your database.'),
     'success' => t('The item has been deleted.'),
   );
 
diff --git a/profiles/wcm_base/modules/contrib/ctools/includes/wizard.inc b/profiles/wcm_base/modules/contrib/ctools/includes/wizard.inc
index 8b0d061c400a7f4b2a97b27adcc31c65fd36a3a2..e0b8fbd14e8f178226ac87846f68e5b85d111c43 100644
--- a/profiles/wcm_base/modules/contrib/ctools/includes/wizard.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/includes/wizard.inc
@@ -503,7 +503,7 @@ function ctools_wizard_defaults(&$form_info) {
   $form_info = $form_info + $defaults;
   // Set form callbacks if they aren't defined.
   foreach ($form_info['forms'] as $step => $params) {
-    if (!$params['form id']) {
+    if (empty($params['form id'])) {
       $form_callback = $hook . '_' . $step . '_form';
       $form_info['forms'][$step]['form id'] = $form_callback;
     }
diff --git a/profiles/wcm_base/modules/contrib/ctools/js/modal.js b/profiles/wcm_base/modules/contrib/ctools/js/modal.js
index e65f51afaff48d519210a676d3baea75b7985e7d..ec7b02aea372fe7b58098b38fff99e8ef4ec333b 100644
--- a/profiles/wcm_base/modules/contrib/ctools/js/modal.js
+++ b/profiles/wcm_base/modules/contrib/ctools/js/modal.js
@@ -378,7 +378,7 @@
       }
     }
 
-    if (!speed) {
+    if (!speed && 0 !== speed) {
       speed = 'fast';
     }
 
@@ -555,7 +555,7 @@
     // Create our content div, get the dimensions, and hide it
     var modalContent = $('#modalContent').css('top','-1000px');
     var $modalHeader = modalContent.find('.modal-header');
-    var mdcTop = wt + ( winHeight / 2 ) - (  modalContent.outerHeight() / 2);
+    var mdcTop = wt + Math.max((winHeight / 2) - (modalContent.outerHeight() / 2), 0);
     var mdcLeft = ( winWidth / 2 ) - ( modalContent.outerWidth() / 2);
     $('#modalBackdrop').css(css).css('top', 0).css('height', docHeight + 'px').css('width', docWidth + 'px').show();
     modalContent.css({top: mdcTop + 'px', left: mdcLeft + 'px'}).hide()[animation](speed);
@@ -588,35 +588,43 @@
       $('body').unbind( 'keypress', modalEventHandler );
       $('body').unbind( 'keydown', modalTabTrapHandler );
       $('.close', $modalHeader).unbind('click', modalContentClose);
-      $('body').unbind('keypress', modalEventEscapeCloseHandler);
+      $(document).unbind('keydown', modalEventEscapeCloseHandler);
       $(document).trigger('CToolsDetachBehaviors', $('#modalContent'));
 
-      // Set our animation parameters and use them
-      if ( animation == 'fadeIn' ) animation = 'fadeOut';
-      if ( animation == 'slideDown' ) animation = 'slideUp';
-      if ( animation == 'show' ) animation = 'hide';
+      // Closing animation.
+      switch (animation) {
+        case 'fadeIn':
+          modalContent.fadeOut(speed, modalContentRemove);
+          break;
 
-      // Close the content
-      modalContent.hide()[animation](speed);
+        case 'slideDown':
+          modalContent.slideUp(speed, modalContentRemove);
+          break;
 
-      // Remove the content
+        case 'show':
+          modalContent.hide(speed, modalContentRemove);
+          break;
+      }
+    }
+
+    // Remove the content.
+    modalContentRemove = function () {
       $('#modalContent').remove();
       $('#modalBackdrop').remove();
 
-      // Restore focus to where it was before opening the dialog
+      // Restore focus to where it was before opening the dialog.
       $(oldFocus).focus();
     };
 
     // Move and resize the modalBackdrop and modalContent on window resize.
-    modalContentResize = function(){
-
+    modalContentResize = function () {
       // Reset the backdrop height/width to get accurate document size.
       $('#modalBackdrop').css('height', '').css('width', '');
 
       // Position code lifted from:
       // http://www.quirksmode.org/viewport/compatibility.html
       if (self.pageYOffset) { // all except Explorer
-      var wt = self.pageYOffset;
+        var wt = self.pageYOffset;
       } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
         var wt = document.documentElement.scrollTop;
       } else if (document.body) { // all other Explorers
@@ -632,7 +640,7 @@
 
       // Get where we should move content to
       var modalContent = $('#modalContent');
-      var mdcTop = wt + ( winHeight / 2 ) - ( modalContent.outerHeight() / 2);
+      var mdcTop = wt + Math.max((winHeight / 2) - (modalContent.outerHeight() / 2), 0);
       var mdcLeft = ( winWidth / 2 ) - ( modalContent.outerWidth() / 2);
 
       // Apply the changes
diff --git a/profiles/wcm_base/modules/contrib/ctools/page_manager/page_manager.info b/profiles/wcm_base/modules/contrib/ctools/page_manager/page_manager.info
index 5866138514dc665e6da466c52b8728b92f7766b1..a56663b8d31b0622f0f5dcd79fbaa208f5c83976 100644
--- a/profiles/wcm_base/modules/contrib/ctools/page_manager/page_manager.info
+++ b/profiles/wcm_base/modules/contrib/ctools/page_manager/page_manager.info
@@ -3,13 +3,11 @@ description = Provides a UI and API to manage pages within the site.
 core = 7.x
 dependencies[] = ctools
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 
 files[] = tests/head_links.test
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/page_manager/plugins/tasks/page.inc b/profiles/wcm_base/modules/contrib/ctools/page_manager/plugins/tasks/page.inc
index 8d24d3309c331fb109082e0fa973b614144a4be6..100dfd8176bb796329ba04e1a1a655da1a52dfb9 100644
--- a/profiles/wcm_base/modules/contrib/ctools/page_manager/plugins/tasks/page.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/page_manager/plugins/tasks/page.inc
@@ -261,6 +261,7 @@ function page_manager_page_theme(&$items, $task) {
  *   creating named arguments in the path.
  */
 function page_manager_page_execute($subtask_id) {
+  $func_args = func_get_args();
   $page = page_manager_page_load($subtask_id);
   $task = page_manager_get_task($page->task);
   $subtask = page_manager_get_task_subtask($task, $subtask_id);
@@ -268,7 +269,7 @@ function page_manager_page_execute($subtask_id) {
   // Turn the contexts into a properly keyed array.
   $contexts = array();
   $args = array();
-  foreach (func_get_args() as $count => $arg) {
+  foreach ($func_args as $count => $arg) {
     if (is_object($arg) && get_class($arg) == 'ctools_context') {
       $contexts[$arg->id] = $arg;
       $args[] = $arg->original_argument;
@@ -430,7 +431,7 @@ function page_manager_page_save(&$page) {
 /**
  * Remove a page subtask.
  */
-function page_manager_page_delete($page) {
+function page_manager_page_delete($page, $skip_menu_rebuild = FALSE) {
   $task = page_manager_get_task($page->task);
   if ($function = ctools_plugin_get_function($task, 'delete')) {
     $function($page);
@@ -448,7 +449,11 @@ function page_manager_page_delete($page) {
   // rebuild this page again.
   ctools_include('export');
   ctools_export_load_object_reset('page_manager_pages');
-  menu_rebuild();
+  // Allow menu rebuild to be skipped when calling code is deleting multiple
+  // pages.
+  if (!$skip_menu_rebuild) {
+    menu_rebuild();
+  }
 }
 
 /**
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_attachments.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_attachments.inc
index c55e0752d2366426df37c64176ba0098776ba032..51c95a5e9deb3a2c0ad646c4c273dbe99e57a68c 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_attachments.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_attachments.inc
@@ -21,7 +21,7 @@ if (module_exists('upload')) {
 
 function ctools_node_form_attachments_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->title = t('Attach files');
   $block->delta = 'url-path-options';
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_author.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_author.inc
index f9e937198307cee1a0b7cc734a93072bea656a0e..86f3d7314201c620b8872abf708b27f4d40811aa 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_author.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_author.inc
@@ -17,7 +17,7 @@ $plugin = array(
 
 function ctools_node_form_author_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->title = t('Authoring information');
   $block->delta = 'author-options';
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_book.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_book.inc
index af022bfd6976b07ed01604230a105dc9a7cc8700..b7ad36bdf2b969ba043b016c1e240cfa101c2a48 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_book.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_book.inc
@@ -21,7 +21,7 @@ if (module_exists('book')) {
 
 function ctools_node_form_book_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->title = t('Book outline');
   $block->delta = 'book-outline';
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_buttons.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_buttons.inc
index 560a01e4f1fffb9fc17b6c6e0e89f775bbd46ad8..ed553a67d3fdc9c5f04890a93fa0b75cb8149527 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_buttons.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_buttons.inc
@@ -17,7 +17,7 @@ $plugin = array(
 
 function ctools_node_form_buttons_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->title = '';
   $block->delta = 'buttons';
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_comment.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_comment.inc
index 352681cc61cf5fecf339c34d4bbf47f8b41b5083..eaf5c954c115d4b84c93bd52490d1c18ecaf9d54 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_comment.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_comment.inc
@@ -21,7 +21,7 @@ if (module_exists('comment')) {
 
 function ctools_node_form_comment_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->title = t('Comment options');
   $block->delta = 'comment-options';
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_language.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_language.inc
index 76ab3f8248b633023b1a75e2078f775b2eb9c41f..dfef295f511a504304091f0ed7efbb2c04dc27b4 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_language.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_language.inc
@@ -17,7 +17,7 @@ $plugin = array(
 
 function ctools_node_form_language_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->delta = 'language-options';
 
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_log.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_log.inc
index 329ea89903f9cbf6be131c3d03bf49ea8caea26d..aac42a6455ca1a67e723362ccaae2ad332b6929c 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_log.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_log.inc
@@ -17,7 +17,7 @@ $plugin = array(
 
 function ctools_node_form_log_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
   $block->title = t('Revision information');
 
   if (isset($context->form)) {
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_menu.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_menu.inc
index 576dcd43897c698313c4a6451705d7ce98ad8dce..9b4df43452e22196ecb4c9d4a58d6da580b1c2c1 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_menu.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_menu.inc
@@ -21,7 +21,7 @@ if (module_exists('menu')) {
 
 function ctools_node_form_menu_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->title = t('Menu options');
   $block->delta = 'menu-options';
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_path.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_path.inc
index 9aeeba00af12b0285278275a233b74b114365824..262851f0ad17e7e3a8031cd14c002c7b5eb20746 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_path.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_path.inc
@@ -21,7 +21,7 @@ if (module_exists('path')) {
 
 function ctools_node_form_path_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->title = t('URL path options');
   $block->delta = 'url-path-options';
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_publishing.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_publishing.inc
index f8b92aec9b217a18feeb568c094b2b474df02ae2..53b584f90908944046645b41376ce3dce82a39eb 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_publishing.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_publishing.inc
@@ -23,7 +23,7 @@ function ctools_node_form_publishing_content_type_render($subtype, $conf, $panel
   $block = new stdClass();
 
   $block->title = t('Publishing options');
-  $block->module = t('node_form');
+  $block->module = 'node_form';
   $block->delta = 'publishing-options';
 
   if (isset($context->form)) {
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_title.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_title.inc
index 2f58ee309e6f0e9c047f9f7923c2c13a9de829fb..14e79abae89cd4427ba9a2f66ef72d449a770083 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_title.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/content_types/node_form/node_form_title.inc
@@ -17,7 +17,7 @@ $plugin = array(
 
 function ctools_node_form_title_content_type_render($subtype, $conf, $panel_args, &$context) {
   $block = new stdClass();
-  $block->module = t('node_form');
+  $block->module = 'node_form';
 
   $block->delta = 'title-options';
 
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/contexts/user.inc b/profiles/wcm_base/modules/contrib/ctools/plugins/contexts/user.inc
index 362cfe3b12813db271ffb4e7f1317c2961d0925b..bbdbd1a274e354dc19191b2e280bf0b83a4d92e7 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/contexts/user.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/contexts/user.inc
@@ -152,6 +152,7 @@ function ctools_context_user_settings_form_submit($form, &$form_state) {
  */
 function ctools_context_user_convert_list() {
   $tokens = token_info();
+  $list = array();
   foreach ($tokens['tokens']['user'] as $id => $info) {
     if (!isset($list[$id])) {
       $list[$id] = $info['name'];
diff --git a/profiles/wcm_base/modules/contrib/ctools/plugins/export_ui/ctools_export_ui.class.php b/profiles/wcm_base/modules/contrib/ctools/plugins/export_ui/ctools_export_ui.class.php
index 3e1aed4bae91b8f1e7ce77a59eaa53143c17c8d0..0c8145e4bfb51e20c39850be2e8eb2e37e731539 100644
--- a/profiles/wcm_base/modules/contrib/ctools/plugins/export_ui/ctools_export_ui.class.php
+++ b/profiles/wcm_base/modules/contrib/ctools/plugins/export_ui/ctools_export_ui.class.php
@@ -644,6 +644,7 @@ class ctools_export_ui {
   }
 
   public function add_page($js, $input, $step = NULL) {
+    $args = func_get_args();
     drupal_set_title($this->get_page_title('add'), PASS_THROUGH);
 
     // If a step not set, they are trying to create a new item. If a step
@@ -666,7 +667,7 @@ class ctools_export_ui {
       'no_redirect' => TRUE,
       'step' => $step,
       // Store these in case additional args are needed.
-      'function args' => func_get_args(),
+      'function args' => $args,
     );
 
     $output = $this->edit_execute_form($form_state);
@@ -681,6 +682,7 @@ class ctools_export_ui {
    * Main entry point to edit an item.
    */
   public function edit_page($js, $input, $item, $step = NULL) {
+    $args = func_get_args();
     drupal_set_title($this->get_page_title('edit', $item), PASS_THROUGH);
 
     // Check to see if there is a cached item to get if we're using the wizard.
@@ -702,7 +704,7 @@ class ctools_export_ui {
       'no_redirect' => TRUE,
       'step' => $step,
       // Store these in case additional args are needed.
-      'function args' => func_get_args(),
+      'function args' => $args,
     );
 
     $output = $this->edit_execute_form($form_state);
@@ -717,6 +719,7 @@ class ctools_export_ui {
    * Main entry point to clone an item.
    */
   public function clone_page($js, $input, $original, $step = NULL) {
+    $args = func_get_args();
     drupal_set_title($this->get_page_title('clone', $original), PASS_THROUGH);
 
     // If a step not set, they are trying to create a new clone. If a step
@@ -756,7 +759,7 @@ class ctools_export_ui {
       'no_redirect' => TRUE,
       'step' => $step,
       // Store these in case additional args are needed.
-      'function args' => func_get_args(),
+      'function args' => $args,
     );
 
     $output = $this->edit_execute_form($form_state);
@@ -1237,6 +1240,7 @@ class ctools_export_ui {
    * Page callback to import information for an exportable item.
    */
   public function import_page($js, $input, $step = NULL) {
+    $args = func_get_args();
     drupal_set_title($this->get_page_title('import'), PASS_THROUGH);
     // Import is basically a multi step wizard form, so let's go ahead and
     // use CTools' wizard.inc for it.
@@ -1261,7 +1265,7 @@ class ctools_export_ui {
       'no_redirect' => TRUE,
       'step' => $step,
       // Store these in case additional args are needed.
-      'function args' => func_get_args(),
+      'function args' => $args,
     );
 
     // import always uses the wizard.
diff --git a/profiles/wcm_base/modules/contrib/ctools/stylizer/stylizer.info b/profiles/wcm_base/modules/contrib/ctools/stylizer/stylizer.info
index f8b442e497503435cc970dbcdc6e3e2da5cea679..90204a3def5112b4855efb71ff8cf7d73c6b5671 100644
--- a/profiles/wcm_base/modules/contrib/ctools/stylizer/stylizer.info
+++ b/profiles/wcm_base/modules/contrib/ctools/stylizer/stylizer.info
@@ -2,13 +2,11 @@ name = Stylizer
 description = Create custom styles for applications such as Panels.
 core = 7.x
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 dependencies[] = ctools
 dependencies[] = color
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/term_depth/term_depth.info b/profiles/wcm_base/modules/contrib/ctools/term_depth/term_depth.info
index 326b2aef3ca75aaa73beffa34648991a79dd299c..4baee51d898802f4ceb34ac60633e8aa3f40ae58 100644
--- a/profiles/wcm_base/modules/contrib/ctools/term_depth/term_depth.info
+++ b/profiles/wcm_base/modules/contrib/ctools/term_depth/term_depth.info
@@ -3,11 +3,9 @@ description = Controls access to context based upon term depth
 core = 7.x
 dependencies[] = ctools
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/tests/ctools_export_test/ctools_export_test.info b/profiles/wcm_base/modules/contrib/ctools/tests/ctools_export_test/ctools_export_test.info
index 7abac58cb48ef4ce2939cc088c34518abd722fba..a2d0efbd92e9d0b3508516e588750097abc1be08 100644
--- a/profiles/wcm_base/modules/contrib/ctools/tests/ctools_export_test/ctools_export_test.info
+++ b/profiles/wcm_base/modules/contrib/ctools/tests/ctools_export_test/ctools_export_test.info
@@ -2,15 +2,13 @@ name = CTools export test
 description = CTools export test module
 core = 7.x
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 dependencies[] = ctools
 hidden = TRUE
 
 files[] = ctools_export.test
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/tests/ctools_plugin_test.info b/profiles/wcm_base/modules/contrib/ctools/tests/ctools_plugin_test.info
index 649e8912bf63e718810a313f32f26a276bbf88f5..d51a526148b707b3775deed6d6a8a191ea8cf86c 100644
--- a/profiles/wcm_base/modules/contrib/ctools/tests/ctools_plugin_test.info
+++ b/profiles/wcm_base/modules/contrib/ctools/tests/ctools_plugin_test.info
@@ -1,14 +1,12 @@
 name = Chaos tools plugins test
 description = Provides hooks for testing ctools plugins.
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 core = 7.x
 dependencies[] = ctools
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/ctools/views_content/plugins/relationships/node_from_view.inc b/profiles/wcm_base/modules/contrib/ctools/views_content/plugins/relationships/node_from_view.inc
index 0f3fa8e0a81a579b5abb8785583a97bcaa3d13db..f54cbc6b5f904b77aeb5deaefcef800fb241ede3 100644
--- a/profiles/wcm_base/modules/contrib/ctools/views_content/plugins/relationships/node_from_view.inc
+++ b/profiles/wcm_base/modules/contrib/ctools/views_content/plugins/relationships/node_from_view.inc
@@ -33,7 +33,7 @@ function views_content_node_from_view_context($context, $conf, $placeholder = FA
   views_content_context_get_output($context);
 
   $row = intval($conf['row']) - 1;
-  if (isset($view->result[$row])) {
+  if (isset($view->result[$row]) && isset($view->base_field) && isset($view->result[$row]->{$view->base_field})) {
     $nid = $view->result[$row]->{$view->base_field};
     if ($nid) {
       $node = node_load($nid);
diff --git a/profiles/wcm_base/modules/contrib/ctools/views_content/views_content.info b/profiles/wcm_base/modules/contrib/ctools/views_content/views_content.info
index 254d474b80585bf32a075d93693fedf2dae01084..68f4d875688629782a7d0633c88af925cfce72db 100644
--- a/profiles/wcm_base/modules/contrib/ctools/views_content/views_content.info
+++ b/profiles/wcm_base/modules/contrib/ctools/views_content/views_content.info
@@ -5,14 +5,12 @@ dependencies[] = ctools
 dependencies[] = views
 core = 7.x
 package = Chaos tool suite
-version = CTOOLS_MODULE_VERSION
 files[] = plugins/views/views_content_plugin_display_ctools_context.inc
 files[] = plugins/views/views_content_plugin_display_panel_pane.inc
 files[] = plugins/views/views_content_plugin_style_ctools_context.inc
 
-; Information added by Drupal.org packaging script on 2018-02-24
-version = "7.x-1.14"
+; Information added by Drupal.org packaging script on 2019-02-08
+version = "7.x-1.15"
 core = "7.x"
 project = "ctools"
-datestamp = "1519455788"
-
+datestamp = "1549603691"
diff --git a/profiles/wcm_base/modules/contrib/features/features.admin.inc b/profiles/wcm_base/modules/contrib/features/features.admin.inc
index 46752c891dde94c59196f96d3230a4ec811f38f7..2dea423efa401485032bf9bb2e81dd12be69a34d 100644
--- a/profiles/wcm_base/modules/contrib/features/features.admin.inc
+++ b/profiles/wcm_base/modules/contrib/features/features.admin.inc
@@ -871,7 +871,7 @@ function features_export_build_form_submit($form, &$form_state) {
   $feature = $form['#feature'];
   $export = _features_export_build($feature, $form_state);
   $export = _features_export_generate($export, $form_state, $feature);
-  $generate = ($form_state['values']['op'] == $form_state['values']['generate']);
+  $generate = isset($form_state['values']['generate']) && ($form_state['values']['op'] == $form_state['values']['generate']);
   $module_name = $form_state['values']['module_name'];
 
   if ($generate && !user_access('generate features')) {
@@ -1628,9 +1628,9 @@ function _features_get_features_list() {
 
   $cache = cache_get('features:features_list');
   if ($cache) {
-    $features = $cache->data;  
+    $features = $cache->data;
   }
-  
+
   if (empty($features)) {
     // Clear & rebuild key caches
     features_get_info(NULL, NULL, TRUE);
diff --git a/profiles/wcm_base/modules/contrib/features/features.drush.inc b/profiles/wcm_base/modules/contrib/features/features.drush.inc
index 696825d6b12064d66fc3880f160f680ee6271686..4be5d98cea948c7cedf37ba2e5aed8cb76640996 100644
--- a/profiles/wcm_base/modules/contrib/features/features.drush.inc
+++ b/profiles/wcm_base/modules/contrib/features/features.drush.inc
@@ -519,7 +519,7 @@ function drush_features_export() {
         drush_die('Aborting.');
       }
       $export = _drush_features_generate_export($items, $module);
-      _features_populate($items, $export[info], $export[name]);
+      _features_populate($items, $export['info'], $export['name']);
       _drush_features_export($export['info'], $module, $directory);
     }
   }
diff --git a/profiles/wcm_base/modules/contrib/features/features.export.inc b/profiles/wcm_base/modules/contrib/features/features.export.inc
index 5045b13119e9ebaea99dcc385e8cfdf55d2edeeb..340818b30c41ae0f48d9b996aead4f5e53ae9357 100644
--- a/profiles/wcm_base/modules/contrib/features/features.export.inc
+++ b/profiles/wcm_base/modules/contrib/features/features.export.inc
@@ -315,7 +315,7 @@ function features_export_render($export, $module_name, $reset = FALSE) {
   $code = array_filter($code);
   foreach ($code as $filename => $contents) {
     if ($filename != '_files') {
-      $code[$filename] = "<?php\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
+      $code[$filename] = "<?php\n\n/**\n * @file\n * {$module_name}.{$filename}.inc\n */\n\n". implode("\n\n", $contents) ."\n";
     }
   }
 
@@ -332,8 +332,8 @@ function features_export_render($export, $module_name, $reset = FALSE) {
   $code['info'] = features_export_info($export);
 
   // Used to create or manipulate the generated .module for features.inc.
-  $modulefile_features_inc = "<?php\n/**\n * @file\n * Code for the {$export['name']} feature.\n */\n\ninclude_once '{$module_name}.features.inc';\n";
-  $modulefile_blank = "<?php\n/**\n * @file\n * Drupal needs this blank file.\n */\n";
+  $modulefile_features_inc = "<?php\n\n/**\n * @file\n * Code for the {$export['name']} feature.\n */\n\ninclude_once '{$module_name}.features.inc';\n";
+  $modulefile_blank = "<?php\n\n/**\n * @file\n * Drupal needs this blank file.\n */\n";
 
   // Prepare the module
   // If module exists, let it be and include it in the files
@@ -1088,13 +1088,6 @@ function _features_is_assoc($array) {
  *   returns a copy of the object or array with recursion removed
  */
 function features_remove_recursion($o) {
-  static $replace;
-  if (!isset($replace)) {
-    $replace = create_function(
-      '$m',
-      '$r="\x00{$m[1]}ecursion_features";return \'s:\'.strlen($r.$m[2]).\':"\'.$r.$m[2].\'";\';'
-    );
-  }
   if (is_array($o) || is_object($o)) {
     $re = '#(r|R):([0-9]+);#';
     $serialize = serialize($o);
@@ -1104,7 +1097,7 @@ function features_remove_recursion($o) {
         $chunk = substr($serialize, $last, $pos - $last);
         if (preg_match($re, $chunk)) {
           $length = strlen($chunk);
-          $chunk = preg_replace_callback($re, $replace, $chunk);
+          $chunk = preg_replace_callback($re, '_features_remove_recursion', $chunk);
           $serialize = substr($serialize, 0, $last) . $chunk . substr($serialize, $last + ($pos - $last));
           $pos += strlen($chunk) - $length;
         }
@@ -1114,13 +1107,21 @@ function features_remove_recursion($o) {
         $last += 4 + $length;
         $pos = $last;
       }
-      $serialize = substr($serialize, 0, $last) . preg_replace_callback($re, $replace, substr($serialize, $last));
+      $serialize = substr($serialize, 0, $last) . preg_replace_callback($re, '_features_remove_recursion', substr($serialize, $last));
       $o = unserialize($serialize);
     }
   }
   return $o;
 }
 
+/**
+ * Callback function for preg_replace_callback() to remove recursion.
+ */
+function _features_remove_recursion($m) {
+  $r = "\x00{$m[1]}ecursion_features";
+  return 's:' . strlen($r . $m[2]) . ':"' . $r . $m[2] . '";';
+}
+
 /**
  * Helper to removes a set of keys an object/array.
  *
diff --git a/profiles/wcm_base/modules/contrib/features/features.info b/profiles/wcm_base/modules/contrib/features/features.info
index d28a617b93a1c4842a620c598fef99f9f87339c3..366a862519be0178cb03a746d408ccb2422768b0 100644
--- a/profiles/wcm_base/modules/contrib/features/features.info
+++ b/profiles/wcm_base/modules/contrib/features/features.info
@@ -10,9 +10,8 @@ test_dependencies[] = views
 
 configure = admin/structure/features/settings
 
-; Information added by Drupal.org packaging script on 2016-04-18
-version = "7.x-2.10"
+; Information added by Drupal.org packaging script on 2018-11-01
+version = "7.x-2.11"
 core = "7.x"
 project = "features"
-datestamp = "1461011641"
-
+datestamp = "1541050686"
diff --git a/profiles/wcm_base/modules/contrib/features/features.module b/profiles/wcm_base/modules/contrib/features/features.module
index 123e94f08a4c5c0abee1e8ad97518a54e25dfdd8..ec2a6789394a1eba5371a355debf59a394bbae3b 100644
--- a/profiles/wcm_base/modules/contrib/features/features.module
+++ b/profiles/wcm_base/modules/contrib/features/features.module
@@ -503,6 +503,7 @@ function features_load_feature($name, $reset = FALSE) {
         $features[$name]->name = $name;
         $features[$name]->filename = drupal_get_path('module', $name) . '/' . $name . '.module';
         $features[$name]->type = 'module';
+        $features[$name]->status = module_exists($name);
         $features[$name]->info = $info + $defaults;
       }
     }
diff --git a/profiles/wcm_base/modules/contrib/features/includes/features.field.inc b/profiles/wcm_base/modules/contrib/features/includes/features.field.inc
index 849081ddac46cbe2ff3f893de7a0f52e47cd9b86..ca305ae1ec70c7d6d78710ce299de20336fad02d 100644
--- a/profiles/wcm_base/modules/contrib/features/includes/features.field.inc
+++ b/profiles/wcm_base/modules/contrib/features/includes/features.field.inc
@@ -274,7 +274,7 @@ function field_base_features_rebuild($module) {
       // Create or update field.
       if (isset($existing_fields[$field['field_name']])) {
         $existing_field = $existing_fields[$field['field_name']];
-        $array_diff_result = drupal_array_diff_assoc_recursive($field + $existing_field, $existing_field);
+        $array_diff_result = features_array_diff_assoc_recursive($field + $existing_field, $existing_field);
         if (!empty($array_diff_result)) {
           try {
             field_update_field($field);
@@ -319,7 +319,7 @@ function field_instance_features_rebuild($module) {
       // Create or update field instance.
       if (isset($existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']])) {
         $existing_instance = $existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']];
-        if ($field_instance + $existing_instance !== $existing_instance) {
+        if ($field_instance + $existing_instance != $existing_instance) {
           try {
             field_update_instance($field_instance);
           }
@@ -494,7 +494,7 @@ function field_features_rebuild($module) {
       $field_config = $field['field_config'];
       if (isset($existing_fields[$field_config['field_name']])) {
         $existing_field = $existing_fields[$field_config['field_name']];
-        $array_diff_result = drupal_array_diff_assoc_recursive($field_config + $existing_field, $existing_field);
+        $array_diff_result = features_array_diff_assoc_recursive($field_config + $existing_field, $existing_field);
         if (!empty($array_diff_result)) {
           try {
             field_update_field($field_config);
@@ -518,7 +518,7 @@ function field_features_rebuild($module) {
       $field_instance = $field['field_instance'];
       if (isset($existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']])) {
         $existing_instance = $existing_instances[$field_instance['entity_type']][$field_instance['bundle']][$field_instance['field_name']];
-        if ($field_instance + $existing_instance !== $existing_instance) {
+        if ($field_instance + $existing_instance != $existing_instance) {
           field_update_instance($field_instance);
         }
       }
diff --git a/profiles/wcm_base/modules/contrib/features/includes/features.image.inc b/profiles/wcm_base/modules/contrib/features/includes/features.image.inc
index b2058b7c48eb5f904975b91629e759a7e691f57b..bf89191d4731ed125eaf91acf8040f67d8147cb5 100644
--- a/profiles/wcm_base/modules/contrib/features/includes/features.image.inc
+++ b/profiles/wcm_base/modules/contrib/features/includes/features.image.inc
@@ -73,11 +73,17 @@ function image_features_export_render($module_name, $data, $export = NULL) {
  */
 function image_features_revert($module) {
   if ($default_styles = features_get_default('image', $module)) {
-    foreach (array_keys($default_styles) as $default_style) {
-      if ($style = image_style_load($default_style)) {
+    foreach ($default_styles as $default_style_name => $default_style) {
+      if ($style = image_style_load($default_style_name)) {
         if ($style['storage'] != IMAGE_STORAGE_DEFAULT) {
           image_default_style_revert($style);
         }
+        else {
+          // Verify that the loaded style still matches what's in code.
+          if ($default_style['effects'] !== $style['effects']) {
+            image_default_style_revert($style);
+          }
+        }
       }
     }
   }
diff --git a/profiles/wcm_base/modules/contrib/features/includes/features.menu.inc b/profiles/wcm_base/modules/contrib/features/includes/features.menu.inc
index edd475142f199a5fcf89e10c6e828b946db2cfd5..84af6231e0ef3cbc5a318ccd9267e11e10cbf494 100644
--- a/profiles/wcm_base/modules/contrib/features/includes/features.menu.inc
+++ b/profiles/wcm_base/modules/contrib/features/includes/features.menu.inc
@@ -420,8 +420,12 @@ function features_menu_link_load($identifier) {
  * Returns a lowercase clean string with only letters, numbers and dashes
  */
 function features_clean_title($str) {
-  return strtolower(preg_replace_callback('/(\s)|([^a-zA-Z\-0-9])/i', create_function(
-          '$matches',
-          'return $matches[1]?"-":"";'
-      ), $str));
+  return strtolower(preg_replace_callback('/(\s)|([^a-zA-Z\-0-9])/i', '_features_clean_title', $str));
+}
+
+/**
+ * Callback function for preg_replace_callback() to clean a string.
+ */
+function _features_clean_title($matches) {
+  return $matches[1] ? '-' : '';
 }
diff --git a/profiles/wcm_base/modules/contrib/features/tests/features.test b/profiles/wcm_base/modules/contrib/features/tests/features.test
index 025ef23c97e03fb2669fb0fb9d1a76d395fdf3c3..743cc1bf998a530db73d7fb22ee047950046af75 100644
--- a/profiles/wcm_base/modules/contrib/features/tests/features.test
+++ b/profiles/wcm_base/modules/contrib/features/tests/features.test
@@ -14,6 +14,7 @@ class FeaturesUserTestCase extends DrupalWebTestCase {
       'name' => t('Component tests'),
       'description' => t('Run tests for components of Features.') ,
       'group' => t('Features'),
+      'dependencies' => array('views', 'strongarm'),
     );
   }
 
@@ -182,6 +183,7 @@ class FeaturesEnableTestCase extends DrupalWebTestCase {
       'name' => t('Features enable tests'),
       'description' => t('Run tests for enabling of features.') ,
       'group' => t('Features'),
+      'dependencies' => array('views', 'strongarm'),
     );
   }
 
@@ -231,6 +233,7 @@ class FeaturesCtoolsIntegrationTest extends DrupalWebTestCase {
       'name' => t('Features Chaos Tools integration'),
       'description' => t('Run tests for ctool integration of features.') ,
       'group' => t('Features'),
+      'dependencies' => array('views', 'strongarm'),
     );
   }
 
diff --git a/profiles/wcm_base/modules/contrib/features/tests/features_test/features_test.info b/profiles/wcm_base/modules/contrib/features/tests/features_test/features_test.info
index 04f01d33b4753aa7b324ab9e729eea5dafd26a73..cd414ec6498576a4ca37b7df4451d6b608c92b3d 100644
--- a/profiles/wcm_base/modules/contrib/features/tests/features_test/features_test.info
+++ b/profiles/wcm_base/modules/contrib/features/tests/features_test/features_test.info
@@ -21,9 +21,8 @@ features[user_permission][] = create features_test content
 features[views_view][] = features_test
 hidden = 1
 
-; Information added by Drupal.org packaging script on 2016-04-18
-version = "7.x-2.10"
+; Information added by Drupal.org packaging script on 2018-11-01
+version = "7.x-2.11"
 core = "7.x"
 project = "features"
-datestamp = "1461011641"
-
+datestamp = "1541050686"
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/CHANGELOG.txt b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/CHANGELOG.txt
index 7f2c9b51fdeaff16c38d7559d76890693a73cf84..438bfca1675f91e45e692b68bb62cb272db585af 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/CHANGELOG.txt
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/CHANGELOG.txt
@@ -1,3 +1,29 @@
+Fieldable Panels Panes 7.x-1.11, 2016-11-05
+-------------------------------------------
+#2688577 by rigoucr: Wrong function name for update 7114.
+#2778265 by DamienMcKenna: Fixed tests due to permission changes in core.
+#2233363 by DamienMcKenna: Removed some legacy paths that were causing problems
+  on some sites.
+#2798963 by tbfisher: Typo on Migrate integration could lead to dataloss.
+#2798965 by tbfisher: Migrate class for use as a data source.
+#2730465 by joelpittet: Fixed integration with Panel Nodes.
+#2548883 by Ahmad Abbad: Title field was shown twice in certain circumstances.
+#2637740 by joelstein, Chris Burge: Add the entity's bundle name, to make some
+  of the hooks easier to work with.
+#2494733 by DamienMcKenna: Added a test dependency on Pathauto.
+#2494733 by DamienMcKenna: Added a test for path handling when Pathauto is
+  installed.
+#2801605 by scuba_fly, DamienMcKenna, mrmikedewolf: Allow 'reuseable' option to
+  be changed after initial FPP creation.
+#2814117 by cboyden, lucas.constantino, dsnopek: Added
+  hook_fieldable_panels_panes_access() to allow other modules to control access
+  to Fieldable Panels Pane objects.
+#2801353 by ChaseOnTheWeb, DamienMcKenna: Duplicate logic of update_7112 and
+  update_7113 for when using UUID.
+#2723655 by DamienMcKenna: Fixed usage of POSITION() in update_7112 and
+  update_7113.
+
+
 Fieldable Panels Panes 7.x-1.10, 2016-05-04
 -------------------------------------------
 By cboyden, DamienMcKenna, dsnopek: Admin title formatting.
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.api.php b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.api.php
index 2e1057f9ddd0e4e7e34846dace9824924ae6f4ae..a8f0b810d796ee09c2bbe47e2bbce0cfb789c926 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.api.php
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.api.php
@@ -106,6 +106,30 @@ function hook_fieldable_panels_panes_content_types_alter(&$types, $bundle, $enti
   }
 }
 
+/**
+ * Allow other modules to control access to Fieldable Panels Pane objects.
+ *
+ * @param string $op
+ *   The operation to be performed.
+ * @param $entity
+ *   The fieldable panels pane that is being accessed.
+ * @param $account
+ *   The user account whose access should be checked.
+ *
+ * @return TRUE|FALSE|NULL
+ *   Returns TRUE to allow access, FALSE to deny, or NULL to pass the access
+ *   decision off to the next hook or the module itself.
+ *
+ * @ingroup fieldable_panels_pane_api_hooks
+ */
+function hook_fieldable_panels_panes_access($op, $entity = NULL, $account = NULL) {
+  // Example implementation which restricts access to edit reusable panes.
+  if ($op == 'update' && !empty($entity) && $entity->reusable && !user_access('administer fieldable panels panes')) {
+    return FALSE;
+  }
+  return NULL;
+}
+
 /**
  * @} End of "addtogroup hooks".
  */
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.info b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.info
index 7313e8b357deefcb5c4cb8b46d81d33cec9e32d0..b9d3520ba42b748d22090cef4ebf34e8c0709dba 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.info
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.info
@@ -56,13 +56,17 @@ files[] = tests/fpp.with_panels_and_entity.test
 test_dependencies[] = panelizer
 files[] = tests/fpp.with_panelizer.test
 
+; Test the interaction with the Pathauto module.
+test_dependencies[] = pathauto
+files[] = tests/fpp.with_pathauto.test
+
 ; Test the node revisions workflow with Revisioning module.
 test_dependencies[] = revisioning
 files[] = tests/fpp.with_revisioning.test
 
-; Information added by Drupal.org packaging script on 2016-05-04
-version = "7.x-1.10"
+; Information added by Drupal.org packaging script on 2016-11-05
+version = "7.x-1.11"
 core = "7.x"
 project = "fieldable_panels_panes"
-datestamp = "1462374853"
+datestamp = "1478371741"
 
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.install b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.install
index 37b18e4555650d556ebe79f81993e10e5418f269..efe1ea943e309f18a35205158d0d1f87772fd69f 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.install
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.install
@@ -31,6 +31,7 @@ function fieldable_panels_panes_uninstall() {
 
   // Variables.
   variable_del('fieldable_panels_panes_skip_default_type');
+  variable_del('fpp_allow_reusable_access');
   variable_del('fpp_blocks_expose');
   variable_del('fpp_hide_contextual_links');
   variable_del('fpp_revision_locking');
@@ -517,7 +518,7 @@ function fieldable_panels_panes_update_7112(&$sandbox) {
     $sandbox['max'] = db_query("SELECT DISTINCT fpp.fpid as fpid, fpp.vid as vid
       FROM {fieldable_panels_panes} fpp
       INNER JOIN {panels_pane} pp
-        ON fpp.fpid = SUBSTRING_INDEX(pp.subtype, ':', -1)
+        ON fpp.fpid = SUBSTRING(pp.subtype FROM (POSITION(':' IN pp.subtype) + 1))
       INNER JOIN {panelizer_entity} pe
         ON pe.did = pp.did
       WHERE (pp.subtype LIKE 'fpid:%' OR pp.subtype LIKE 'current:%')
@@ -537,7 +538,7 @@ function fieldable_panels_panes_update_7112(&$sandbox) {
   $results = db_query_range("SELECT DISTINCT fpp.fpid as fpid, fpp.vid as vid
     FROM {fieldable_panels_panes} fpp
     INNER JOIN {panels_pane} pp
-      ON fpp.fpid = SUBSTRING_INDEX(pp.subtype, ':', -1)
+      ON fpp.fpid = SUBSTRING(pp.subtype FROM (POSITION(':' IN pp.subtype) + 1))
     INNER JOIN {panelizer_entity} pe
       ON pe.did = pp.did
     WHERE (pp.subtype LIKE 'fpid:%' OR pp.subtype LIKE 'current:%')
@@ -643,7 +644,7 @@ function fieldable_panels_panes_update_7113() {
     $sandbox['max'] = db_query("SELECT DISTINCT fpp.fpid as fpid, fpp.vid as vid
       FROM {fieldable_panels_panes} fpp
       INNER JOIN {panels_pane} pp
-        ON fpp.fpid = SUBSTRING_INDEX(pp.subtype, ':', -1)
+        ON fpp.fpid = SUBSTRING(pp.subtype FROM (POSITION(':' IN pp.subtype) + 1))
       INNER JOIN {panels_node} pn
         ON pn.did = pp.did
       WHERE (pp.subtype LIKE 'fpid:%' OR pp.subtype LIKE 'current:%')
@@ -663,7 +664,7 @@ function fieldable_panels_panes_update_7113() {
   $results = db_query_range("SELECT DISTINCT fpp.fpid as fpid, fpp.vid as vid
     FROM {fieldable_panels_panes} fpp
     INNER JOIN {panels_pane} pp
-      ON fpp.fpid = SUBSTRING_INDEX(pp.subtype, ':', -1)
+      ON fpp.fpid = SUBSTRING(pp.subtype FROM (POSITION(':' IN pp.subtype) + 1))
     INNER JOIN {panels_node} pn
       ON pn.did = pp.did
     WHERE (pp.subtype LIKE 'fpid:%' OR pp.subtype LIKE 'current:%')
@@ -699,11 +700,11 @@ function fieldable_panels_panes_update_7113() {
 /**
  * Restore the default FPP bundle that may have been purged in update 7108.
  */
-function incae_custom_panes_update_7114() {
+function fieldable_panels_panes_update_7114() {
   if (!variable_get('fieldable_panels_panes_skip_default_type', FALSE)) {
     // Check if the FPP type exists.
     $found = db_select('fieldable_panels_pane_type', 'fppt')
-      ->fields('fppt', 'name')
+      ->fields('fppt', array('name'))
       ->condition('name', 'fieldable_panels_pane')
       ->execute()
       ->fetchField();
@@ -717,3 +718,257 @@ function incae_custom_panes_update_7114() {
     }
   }
 }
+
+/**
+ * Update all Panelizer displays to point non-reusable FPPs to the vuuid instead
+ * of the uuid.
+ */
+function fieldable_panels_panes_update_7115(&$sandbox) {
+  if (!module_exists('panelizer')) {
+    return t('Panelizer is not installed, so nothing to do.');
+  }
+
+  // This won't as-is work for many sites because the 'fpp_revision_locking'
+  // variable won't be defined before it's checked for. The simplest approach
+  // is to manually rerun the updates.
+  //
+  // /**
+  //  * Enable the FPP revision locking feature.
+  //  */
+  // function MYMODULE_update_7100() {
+  //   variable_set('fpp_revision_locking', 'lock');
+  // }
+  //
+  // /**
+  //  * Rerun Fieldable Panels Panes update 7115.
+  //  */
+  // function MYMODULE_update_7101(&$sandbox) {
+  //   return fieldable_panels_panes_update_7115($sandbox);
+  // }
+  //
+  //
+  // A more correct approach is to use hook_update_dependencies to ensure that
+  // the updates happen in the correct order. This may not work because if the
+  // hook implementation is newly added to the module it won't be recognized
+  // until the caches are cleared, which may not happen until after the updates
+  // are ran, thus the updates not running in the correct order.
+  //
+  // /**
+  //  * Implements hook_update_dependencies().
+  //  */
+  // function MYMODULE_update_dependencies() {
+  //   // Make sure that update 7100 below runs *after* Fieldable Panels Pane
+  //   // update 7111 and *before* Fieldable Panels Pane update 7112, that way
+  //   // the FPP 'lock' option will be enabled when the Panelizer updates are
+  //   // started.
+  //   $dependencies['MYMODULE'][7100] = array(
+  //     'fieldable_panels_panes' => 7111,
+  //   );
+  //   $dependencies['fieldable_panels_panes'][7115] = array(
+  //     'MYMODULE' => 7100,
+  //   );
+  //   return $dependencies;
+  // }
+  //
+  // /**
+  //  * Enable the FPP revision locking feature.
+  //  */
+  // function MYMODULE_update_7100() {
+  //   variable_set('fpp_revision_locking', 'lock');
+  // }
+  //
+  //
+  // An alternative solution would be to rerun the updates.
+
+  if (variable_get('fpp_revision_locking', 'lock') != 'lock') {
+    return t('Pane locking is not enabled, so nothing to do.');
+  }
+
+  // Update all Panelizer displays.
+
+  // The first time through, work out how many records need to be updated.
+  if (!isset($sandbox['progress'])) {
+    $sandbox['progress'] = 0;
+
+    // Total records that must be processed.
+    $sandbox['max'] = db_query("SELECT DISTINCT fpp.uuid as uuid, fppr.vuuid as vuuid
+      FROM {fieldable_panels_panes} fpp
+      INNER JOIN {fieldable_panels_panes_revision} fppr
+        ON fpp.vid = fppr.vid
+      INNER JOIN {panels_pane} pp
+        ON fpp.uuid = SUBSTRING(pp.subtype FROM (POSITION(':' IN pp.subtype) + 1))
+      INNER JOIN {panelizer_entity} pe
+        ON pe.did = pp.did
+      WHERE (pp.subtype LIKE 'uuid:%')
+        AND fpp.reusable != 1
+      ORDER BY fpp.uuid")->rowCount();
+
+    // If there's nothing to do, bail early.
+    if (empty($sandbox['max'])) {
+      return t('No panels needed to be updated.');
+    }
+  }
+
+  // Do the updates in small batches.
+  $limit = 10;
+
+  // Get a list of all UUIDs for Panelizer displays.
+  $results = db_query_range("SELECT DISTINCT fpp.uuid as uuid, fppr.vuuid as vuuid
+    FROM {fieldable_panels_panes} fpp
+    INNER JOIN {fieldable_panels_panes_revision} fppr
+      ON fpp.vid = fppr.vid
+    INNER JOIN {panels_pane} pp
+      ON fpp.uuid = SUBSTRING(pp.subtype FROM (POSITION(':' IN pp.subtype) + 1))
+    INNER JOIN {panelizer_entity} pe
+      ON pe.did = pp.did
+    WHERE (pp.subtype LIKE 'uuid:%')
+      AND fpp.reusable != 1
+    ORDER BY fpp.uuid", 0, $limit);
+
+  // Loop through the FPPs.
+  foreach ($results as $record) {
+    // Update the 'uuid:' records to use 'vuuid:'; don't bother checking if the
+    // record exists first because there's no down side to running this query
+    // immediately.
+    $query = db_update('panels_pane')
+      ->fields(array('subtype' => 'vuuid:' . $record->vuuid))
+      ->condition('subtype', 'uuid:' . $record->uuid)
+      ->execute();
+
+    // Increment the progress counter.
+    $sandbox['progress']++;
+  }
+
+  // Done yet?
+  $sandbox['#finished'] = empty($sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($sandbox['#finished'] === TRUE) {
+    return t('Updated @count record(s) to use the new locking system.', array('@count' => $sandbox['max']));
+  }
+}
+
+/**
+ * Update all Panel Nodes displays to point non-reusable FPPs to the vuuid
+ * instead of the uuid.
+ */
+function fieldable_panels_panes_update_7116(&$sandbox) {
+  if (!module_exists('panels_node')) {
+    return t('Panel Nodes is not installed, so nothing to do.');
+  }
+
+  // This won't as-is work for many sites because the 'fpp_revision_locking'
+  // variable won't be defined before it's checked for. The simplest approach
+  // is to manually rerun the updates.
+  //
+  // /**
+  //  * Enable the FPP revision locking feature.
+  //  */
+  // function MYMODULE_update_7100() {
+  //   variable_set('fpp_revision_locking', 'lock');
+  // }
+  //
+  // /**
+  //  * Rerun Fieldable Panels Panes update 7116.
+  //  */
+  // function MYMODULE_update_7101(&$sandbox) {
+  //   return fieldable_panels_panes_update_7116($sandbox);
+  // }
+  //
+  //
+  // A more correct approach is to use hook_update_dependencies to ensure that
+  // the updates happen in the correct order. This may not work because if the
+  // hook implementation is newly added to the module it won't be recognized
+  // until the caches are cleared, which may not happen until after the updates
+  // are ran, thus the updates not running in the correct order.
+  //
+  // /**
+  //  * Implements hook_update_dependencies().
+  //  */
+  // function MYMODULE_update_dependencies() {
+  //   // Make sure that update 7100 below runs *after* Fieldable Panels Pane
+  //   // update 7111 and *before* Fieldable Panels Pane update 7112, that way
+  //   // the FPP 'lock' option will be enabled when the Panel Nodes updates are
+  //   // started.
+  //   $dependencies['MYMODULE'][7100] = array(
+  //     'fieldable_panels_panes' => 7111,
+  //   );
+  //   $dependencies['fieldable_panels_panes'][7116] = array(
+  //     'MYMODULE' => 7100,
+  //   );
+  //   return $dependencies;
+  // }
+  //
+  // /**
+  //  * Enable the FPP revision locking feature.
+  //  */
+  // function MYMODULE_update_7100() {
+  //   variable_set('fpp_revision_locking', 'lock');
+  // }
+  //
+  //
+  // An alternative solution would be to rerun the updates.
+
+  if (variable_get('fpp_revision_locking', 'lock') != 'lock') {
+    return t('Pane locking is not enabled, so nothing to do.');
+  }
+
+  // The first time through, work out how many records need to be updated.
+  if (!isset($sandbox['progress'])) {
+    $sandbox['progress'] = 0;
+
+    // Total records that must be processed.
+    $sandbox['max'] = db_query("SELECT DISTINCT fpp.uuid as uuid, fppr.vuuid as vuuid
+      FROM {fieldable_panels_panes} fpp
+      INNER JOIN {fieldable_panels_panes_revision} fppr
+        ON fpp.vid = fppr.vid
+      INNER JOIN {panels_pane} pp
+        ON fpp.uuid = SUBSTRING(pp.subtype FROM (POSITION(':' IN pp.subtype) + 1))
+      INNER JOIN {panels_node} pn
+        ON pn.did = pp.did
+      WHERE (pp.subtype LIKE 'uuid:%')
+        AND fpp.reusable != 1
+      ORDER BY fpp.uuid")->rowCount();
+
+    // If there's nothing to do, bail early.
+    if (empty($sandbox['max'])) {
+      return t('No panels needed to be updated.');
+    }
+  }
+
+  // Do the updates in small batches.
+  $limit = 10;
+
+  // Get a list of all UUIDs for Panel Nodes displays.
+  $results = db_query_range("SELECT DISTINCT fpp.uuid as uuid, fppr.vuuid as vuuid
+    FROM {fieldable_panels_panes} fpp
+    INNER JOIN {fieldable_panels_panes_revision} fppr
+      ON fpp.vid = fppr.vid
+    INNER JOIN {panels_pane} pp
+      ON fpp.uuid = SUBSTRING(pp.subtype FROM (POSITION(':' IN pp.subtype) + 1))
+    INNER JOIN {panels_node} pn
+      ON pn.did = pp.did
+    WHERE (pp.subtype LIKE 'uuid:%')
+      AND fpp.reusable != 1
+    ORDER BY fpp.uuid", 0, $limit);
+
+  // Loop through the FPPs.
+  foreach ($results as $record) {
+    // Update the 'uuid:' records to use 'vuuid:'; don't bother checking if the
+    // record exists first because there's no down side to running this query
+    // immediately.
+    $query = db_update('panels_pane')
+      ->fields(array('subtype' => 'vuuid:' . $record->vuuid))
+      ->condition('subtype', 'uuid:' . $record->uuid)
+      ->execute();
+
+    // Increment the progress counter.
+    $sandbox['progress']++;
+  }
+
+  // Done yet?
+  $sandbox['#finished'] = empty($sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
+
+  if ($sandbox['#finished'] === TRUE) {
+    return t('Updated @count record(s) to use the new locking system.', array('@count' => $sandbox['max']));
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.module b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.module
index 6c2f9b7e46fbb1c8701e79ef38ac3a9c48f98c23..8f190adecf9e36a330db124fb76f770b8abe7a64 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.module
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/fieldable_panels_panes.module
@@ -73,7 +73,7 @@ function fieldable_panels_panes_entity_info() {
     'save callback' => 'fieldable_panels_panes_save',
     'deletion callback' => 'fieldable_panels_panes_delete',
 
-    // Entity translation support
+    // Entity translation support.
     'translation' => array(
       'entity_translation' => array(
         'class' => 'EntityTranslationFieldablePanelsPaneHandler',
@@ -83,7 +83,7 @@ function fieldable_panels_panes_entity_info() {
       ),
     ),
 
-    // Title module support
+    // Title module support.
     'field replacement' => array(
       'title' => array(
         'field' => array(
@@ -224,7 +224,8 @@ function fieldable_panels_panes_menu() {
     'file' => 'includes/admin.inc',
   );
 
-  // Legacy paths to support the old method of providing bundles via entity_hook_info().
+  // Legacy paths to support the old method of providing bundles via
+  // entity_hook_info().
   $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes'] = array(
     'title callback' => 'fieldable_panels_panes_entity_title',
     'title arguments' => array(4),
@@ -255,7 +256,7 @@ function fieldable_panels_panes_menu() {
     'weight' => -9,
   ) + $base;
 
-  // Access control
+  // Access control.
   $items['admin/structure/fieldable-panels-panes/view/%fieldable_panels_panes/access'] = array(
     'title' => 'Access control',
     'type' => MENU_LOCAL_TASK,
@@ -391,27 +392,20 @@ function fieldable_panels_panes_menu() {
     );
   }
 
-  $items['admin/structure/fieldable-panels-panes/manage/%fieldable_panels_pane_type'] = array(
+  $items['admin/structure/fieldable-panels-panes/%fieldable_panels_pane_type'] = array(
     'title callback' => 'fieldable_panels_panes_entities_title',
-    'title arguments' => array(4),
+    'title arguments' => array(3),
     'page callback' => 'fieldable_panels_panes_entities_list_page',
-    'page arguments' => array(4),
+    'page arguments' => array(3),
   ) + $base;
 
-  $items['admin/structure/fieldable-panels-panes/manage/%fieldable_panels_pane_type/list'] = array(
-    'title' => 'List',
-    'type' => MENU_LOCAL_TASK,
-    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
-    'weight' => -10,
-  );
-
-  $items['admin/structure/fieldable-panels-panes/manage/%fieldable_panels_pane_type/add'] = array(
-    'title' => 'Add fieldable panel pane',
+  $items['admin/structure/fieldable-panels-panes/%fieldable_panels_pane_type/add'] = array(
+    'title' => 'Add fieldable panels pane',
     'page callback' => 'fieldable_panels_panes_entities_add_page',
-    'page arguments' => array(4),
+    'page arguments' => array(3),
     'access callback' => 'fieldable_panels_panes_access',
-    'access arguments' => array('create', 4),
-    'type' => MENU_LOCAL_TASK,
+    'access arguments' => array('create', 3),
+    'type' => MENU_LOCAL_ACTION,
     'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
   ) + $base;
 
@@ -421,20 +415,11 @@ function fieldable_panels_panes_menu() {
     'page arguments' => array(3),
     'access callback' => 'fieldable_panels_panes_access_callback',
     'access arguments' => array(),
-    'type' => MENU_LOCAL_TASK,
+    'type' => MENU_DEFAULT_LOCAL_TASK,
     'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
     'weight' => -10,
   ) + $base;
 
-  $items['admin/structure/fieldable-panels-panes/%fieldable_panels_pane_type/add'] = array(
-    'title' => 'Add',
-    'page callback' => 'fieldable_panels_panes_entities_add_page',
-    'page arguments' => array(3),
-    'access callback' => 'fieldable_panels_panes_access',
-    'access arguments' => array('create', 3),
-    'type' => MENU_LOCAL_TASK,
-    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
-  ) + $base;
 
   // Settings.
   $items['admin/structure/fieldable-panels-panes/settings'] = array(
@@ -632,6 +617,19 @@ function fieldable_panels_pane_type_load($type) {
   if (isset($entity_info['bundles'][$type])) {
     return $type;
   }
+  else {
+    // Special handling for two legacy paths:
+    // * admin/structure/fieldable-panels-panes/manage/FPPTYPE/fields
+    // * admin/structure/fieldable-panels-panes/manage/FPPTYPE/display
+    // Redirect these paths as appropriate.
+    if ($type == 'manage' && (arg(5) == 'fields' || arg(5) == 'display')) {
+      drupal_goto('admin/structure/fieldable-panels-panes/' . arg(4) . '/' . arg(5));
+    }
+
+    // If nothing else was found, redirect to the main FPP admin page.
+    drupal_set_message(t('Unable to load the "@type" Fieldable Panels Pane type.', array('@type' => $type)));
+    drupal_goto('admin/structure/fieldable-panels-panes');
+  }
 }
 
 /**
@@ -993,7 +991,7 @@ function fieldable_panels_panes_load_from_subtype($subtype_name) {
   $subtype_info = ctools_content_get_subtype($plugin, $subtype_name);
 
   // This means we're probably in the process of creating a new one.
-  if (isset($subtype_info['bundle'])) {
+  if (!isset($subtype_info['entity_id'])) {
     return fieldable_panels_panes_create(array('bundle' => $subtype_info['bundle']));
   }
 
@@ -1420,6 +1418,14 @@ function fieldable_panels_panes_entity_edit_form($form, &$form_state) {
     ),
   );
 
+  // Override reusable pane settings to be changed after the pane is created.
+  if (variable_get('fpp_allow_reusable_access', FALSE)) {
+    // Reset field access.
+    unset($form['reusable']['#access']);
+    $form['reusable']['reusable']['#description'] = t('A reusable pane may be used multiple times on the same page or on other pages. A non-reusable pane may not be added to another page once it is created and added to this page. Once this option has been checked it cannot be turned off.');
+    $form['reusable']['reusable']['#disabled'] = !empty($form_state['entity']->reusable);
+  }
+
   $language = NULL;
   if (function_exists('entity_language')) {
     // entity_language() was added in Drupal 7.15.
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/PanelsPaneController.class.php b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/PanelsPaneController.class.php
index 6c7f1ea817a722c6752a2bbbac0ce8dd8aedf4ea..e1c034ac2560fa59a8283c20c8840cb255c7d7b5 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/PanelsPaneController.class.php
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/PanelsPaneController.class.php
@@ -70,6 +70,13 @@ class PanelsPaneController extends DrupalDefaultEntityController {
       return TRUE;
     }
 
+    // On the first FALSE we return, otherwise we use our own access check.
+    foreach (module_invoke_all('fieldable_panels_panes_access', $op, $entity, $account) as $result) {
+      if ($result === FALSE) {
+        return $result;
+      }
+    }
+
     $bundle = is_string($entity) ? $entity : $entity->bundle;
 
     if ($op == 'create') {
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/admin.inc b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/admin.inc
index 234f8a2c46cef21ca4b4d3e0a980e65cd50b0b93..7986502c663bed4585bb26d4ffb04c4ec4f2bea4 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/admin.inc
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/admin.inc
@@ -36,6 +36,14 @@ function fieldable_panels_panes_settings() {
     '#description' => t('Fieldable panels panes that are reusable will be made available as blocks.'),
     '#default_value' => variable_get('fpp_blocks_expose', FALSE),
   );
+
+  $form['fpp_allow_reusable_access'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Allow access to the "reusable" option on the pane edit form after initial creation'),
+    '#description' => t('This only allows the pane te be made reusable. Reusable panes cannot be changed back to non reusable panes.<br />Warning: enabling this option could break the editorial workflows using e.g. the Workbench Moderation or Revisioning modules, and could cause other problems too.'),
+    '#default_value' => variable_get('fpp_allow_reusable_access', FALSE),
+  );
+
   $form['bundles'] = array(
     '#type' => 'fieldset',
     '#title' => t('FPP Types Exposed as Blocks'),
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/fieldable_panels_pane.migrate.inc b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/fieldable_panels_pane.migrate.inc
index a7c9682503d66b65936f7d40b52d3466ae88e090..57052bcd93b03614ca2ff29623cc22f606d7c6d4 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/fieldable_panels_pane.migrate.inc
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/includes/fieldable_panels_pane.migrate.inc
@@ -144,7 +144,7 @@ class MigrateDestinationFieldablePanelsPanes extends MigrateDestinationEntity {
       if (!isset($fieldable_panels_pane->fpid)) {
         throw new MigrateException(t('System-of-record is DESTINATION, but no destination fpid provided'));
       }
-      $old_fieldable_panels_panes = entity_load_single('fieldable_panels_panes', $fieldable_panels_pane->fpid);
+      $old_fieldable_panels_panes = entity_load_single('fieldable_panels_pane', $fieldable_panels_pane->fpid);
       if (empty($old_fieldable_panels_panes)) {
         throw new MigrateException(t('System-of-record is DESTINATION, but fieldable panels pane !fpid does not exist',
           array('!fpid' => $fieldable_panels_pane->fpid)));
@@ -258,3 +258,190 @@ class MigrateDestinationFieldablePanelsPanes extends MigrateDestinationEntity {
     return $return;
   }
 }
+
+/**
+ * Handling specific to a Drupal 7 source for Fieldable Panels Panes.
+ */
+class DrupalFieldablePanelsPanesMigration extends DrupalMigration {
+
+  /**
+   * The source and destination content types (bundles) we're dealing with.
+   */
+  protected $destinationType;
+
+  /**
+   * Default language to apply to the node and it's body field.
+   *
+   * @var string
+   */
+  protected $defaultLanguage = LANGUAGE_NONE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $arguments) {
+    $this->destinationType = $arguments['destination_type'];
+    $this->sourceType = $arguments['source_type'];
+    if (!empty($arguments['default_language'])) {
+      $this->defaultLanguage = $arguments['default_language'];
+    }
+    $arguments['source_version'] = 7;
+    parent::__construct($arguments);
+
+    // Document known core fields.
+    $this->sourceFields += array(
+      'fpid' => t('The primary identifier for the entity.'),
+      'vid' => t('The current version in use for this entity.'),
+      'bundle' => t('The bundle of the entity.'),
+      'title' => t('The title of the entity.'),
+      'link' => t('Whether or not this entity title will link to another page.'),
+      'path' => t('The path the title should link to.'),
+      'reusable' => t('Whether or not this entity will appear in the Add Content dialog.'),
+      'admin_title' => t('The title it will appear in the Add Content dialog as.'),
+      'admin_description' => t('The description it will appear in the Add Content dialog with.'),
+      'category' => t('The category it will appear in the Add Content dialog under.'),
+      'view_access' => t('Access rules to describe if the user has view access to this entity.'),
+      'edit_access' => t('Access rules to describe if the user has view access to this entity.'),
+      'created' => t('The Unix timestamp when the entity was created.'),
+      'changed' => t('The Unix timestamp when the entity was most recently saved.'),
+      'uuid' => t('The Universally Unique Identifier.'),
+      'language' => t('The languages.language of this entity.'),
+      'uid' => t('The users.uid of the current revision.'),
+      'log' => t('The log message of the current revision.'),
+      'vuuid' => t('The Universally Unique Identifier of the current revision.'),
+    );
+
+    $this->sourceFields += $this->version->getSourceFields('fieldable_panels_pane', $this->sourceType);
+
+    $this->source = new MigrateSourceSQL($this->query(), $this->sourceFields, NULL, $this->sourceOptions);
+
+    $this->destination = new MigrateDestinationFieldablePanelsPanes($this->destinationType);
+
+    $this->map = new MigrateSQLMap($this->machineName,
+      array(
+        'fpid' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'Source entity ID',
+          'alias' => 'fpp',
+        ),
+      ),
+      MigrateDestinationNode::getKeySchema(),
+      $this->mapConnection
+    );
+
+    if (!$this->newOnly) {
+      $this->highwaterField = array(
+        'name' => 'changed',
+        'alias' => 'fpp',
+        'type' => 'int',
+      );
+    }
+
+    // Setup common mappings.
+    $this->addSimpleMappings(array(
+      'title',
+      'link',
+      'path',
+      'reusable',
+      'admin_title',
+      'admin_description',
+      'category',
+      'view_access',
+      'edit_access',
+      'created',
+      'changed',
+      'uuid',
+      'language',
+    ));
+
+    $this->addUnmigratedSources(array('vid'));
+    $this->addUnmigratedDestinations(array());
+
+    if (isset($arguments['default_uid'])) {
+      $default_uid = $arguments['default_uid'];
+    }
+    else {
+      $default_uid = 1;
+    }
+    /*
+    if (isset($user_migration)) {
+      $this->addFieldMapping('uid', 'uid')
+        ->sourceMigration($user_migration)
+        ->defaultValue($default_uid);
+    }
+    else {
+      $this->addFieldMapping('uid', NULL, FALSE)
+        ->defaultValue($default_uid);
+    }
+    */
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function query() {
+    $query = Database::getConnection('default', $this->sourceConnection)
+      ->select('fieldable_panels_panes', 'fpp')
+      ->fields('fpp', array(
+        'fpid',
+        'vid',
+        'title',
+        'link',
+        'path',
+        'reusable',
+        'admin_title',
+        'admin_description',
+        'category',
+        'view_access',
+        'edit_access',
+        'created',
+        'changed',
+        'uuid',
+        'language',
+      ));
+    $query->join('fieldable_panels_panes_revision', 'fppv',
+      'fppv.fpid = fpp.fpid and fppv.vid = fpp.vid');
+    $query->fields('fppv', array(
+      'uid',
+      'log',
+      'vuuid',
+    ));
+    $query->condition('fpp.bundle', $this->sourceType);
+    $query->orderBy($this->newOnly ? 'fpp.fpid' : 'fpp.changed');
+    return $query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareRow($row) {
+    if (parent::prepareRow($row) === FALSE) {
+      return FALSE;
+    }
+
+    $this->version->getSourceValues($row, $row->fpid);
+
+    return TRUE;
+  }
+
+  /**
+   * Implementation of Migration::createStub().
+   */
+  protected function createStub($migration) {
+    migrate_instrument_start('DrupalNodeMigration::createStub');
+    $entity = new stdClass();
+    $entity->title = t('Stub');
+    $entity->type = $this->destination->getBundle();
+    fieldable_panels_panes_save($entity);
+    migrate_instrument_stop('DrupalFieldablePanelsPanesMigration::createStub');
+    if (isset($entity->fpid)) {
+      return array($entity->fpid);
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/plugins/content_types/fieldable_panels_pane.inc b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/plugins/content_types/fieldable_panels_pane.inc
index ee10fd630df7ba81742697acb59a4306dc021d4b..174f290536cc247db6195f6781be121b75b0bda1 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/plugins/content_types/fieldable_panels_pane.inc
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/plugins/content_types/fieldable_panels_pane.inc
@@ -104,9 +104,9 @@ function fieldable_panels_panes_fieldable_panels_pane_content_type_content_types
   // The current function is called as part of the standard panel pane rendering
   // pipeline. To prevent a full list of FPP entities from being generated every
   // time that a panel is rendered, generate the list only when a request is
-  // made from an administrative URL.
+  // made from an administrative URL or Panel Nodes pages.
   // @see https://www.drupal.org/node/2374577
-  if (path_is_admin(current_path()) || preg_match('/panels/i', current_path())) {
+  if (path_is_admin(current_path()) || preg_match('#(panels|panel_content$)#i', current_path())) {
     $fpp_info = entity_get_info('fieldable_panels_pane');
     foreach ($fpp_info['bundles'] as $bundle_name => $bundle) {
       $ids = db_query('SELECT fpid FROM {fieldable_panels_panes} WHERE reusable = 1 AND bundle = :bundle', array(':bundle' => $bundle_name))->fetchCol();
@@ -179,14 +179,16 @@ function fieldable_panels_panes_fieldable_panels_pane_content_type_render($subty
       // Is this view mode configured?
       $is_view_mode_set = isset($settings['extra_fields']['display']['title'][$view_mode]['visible']);
 
-      // Is the title configured for this view mode?
-      $show_view_mode_title = ($is_view_mode_set && $settings['extra_fields']['display']['title'][$view_mode]['visible']);
-
       // Should the default view mode show the title?
       $show_default_title = !empty($settings['extra_fields']['display']['title']['default']['visible']);
 
+      // Is the title configured for this view mode?
+      $show_view_mode_title = ($is_view_mode_set && $settings['extra_fields']['display']['title'][$view_mode]['visible']);
+
       // Combine all of the above logic.
-      if (empty($settings['extra_fields']['display']) || (!$is_view_mode_set && $show_default_title) || $show_view_mode_title) {
+      if (empty($settings['extra_fields']['display'])
+          || (!$is_view_mode_set && $show_default_title)
+          || !$show_view_mode_title) {
         $block->title = filter_xss_admin($entity->title);
       }
     }
@@ -436,6 +438,7 @@ function _fieldable_panels_panes_custom_content_type($entity) {
   }
 
   $info['entity_id'] = $info['name'];
+  $info['bundle'] = $entity->bundle;
   return $info;
 }
 
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/plugins/export_ui/fieldable_panels_pane.class.php b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/plugins/export_ui/fieldable_panels_pane.class.php
index 2e7907624d2175fd4dedc32c0b54319828d32c6f..b7b1ec0e440ec022c1bb9fd3c7c9aa54af307b30 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/plugins/export_ui/fieldable_panels_pane.class.php
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/plugins/export_ui/fieldable_panels_pane.class.php
@@ -178,13 +178,13 @@ class fieldable_panels_pane extends ctools_export_ui {
         if (user_access('administer fieldable panels panes') || user_access('access fieldable panels panes master list')) {
           $operations['list'] = array(
             'title' => t('list'),
-            'href' => 'admin/structure/fieldable-panels-panes/manage/' . $bundle,
+            'href' => 'admin/structure/fieldable-panels-panes/' . $bundle,
           );
         }
         if (user_access('administer fieldable panels panes')) {
           $operations['add'] = array(
             'title' => t('add'),
-            'href' => 'admin/structure/fieldable-panels-panes/manage/' . $bundle . '/add',
+            'href' => 'admin/structure/fieldable-panels-panes/' . $bundle . '/add',
           );
           $operations['edit'] = array(
             'title' => t('edit'),
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.entity_form.test b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.entity_form.test
index 79970c7ad5114e839da22dafebd80267a7031d12..6270420e739b8966e55f77eefbf46f096847631b 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.entity_form.test
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.entity_form.test
@@ -169,4 +169,52 @@ class FppEntityFormTest extends FppTestHelper {
     $this->assertFieldByName('log');
   }
 
+  /**
+   * Confirm that the edit form has the correct fields for non-reusable FPPs
+   * when the allow_reusable_access is enabled.
+   */
+  function testAllowReusableAccessOption() {
+    // Create a user with the admin permission.
+    $this->adminUser = $this->createAdminUser();
+
+    $this->drupalLogin($this->adminUser);
+
+    // Create a non-reusable FPP.
+    $fpp = new StdClass();
+    $fpp->bundle = $this->bundle;
+    $fpp->title = $this->title;
+    $fpp->reusable = 0;
+    $saved_fpp = fieldable_panels_panes_save($fpp);
+
+    // Load the fpp-add form.
+    $this->drupalGet('admin/structure/fieldable-panels-panes/view/' . $fpp->fpid . '/edit');
+    $this->assertResponse(200);
+
+    // The 'reusable' option is not changable after the FPP's initial creation,
+    // and these fields will be hidden for non-reusable FPPs.
+    $this->assertNoFieldByName('reusable');
+    $this->assertNoFieldByName('category');
+
+    // When editing a non-reusable FPP the revision option may not be disabled
+    // but the log field will be available.
+    $this->assertNoFieldByName('revision');
+    $this->assertFieldByName('log');
+
+    // Change the 'allow_reusable_access' option.
+    variable_set('fpp_allow_reusable_access', TRUE);
+
+    // Load the fpp-add form.
+    $this->drupalGet('admin/structure/fieldable-panels-panes/view/' . $fpp->fpid . '/edit');
+    $this->assertResponse(200);
+
+    // The 'reusable' option is now changable again because the
+    // "allow_reusable_access" option has been enabled.
+    $this->assertFieldByName('reusable');
+    $this->assertFieldByName('category');
+
+    // The revision fields should be visible when editing a reusable FPP.
+    $this->assertFieldByName('revision');
+    $this->assertFieldByName('log');
+  }
+
 }
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.helper.test b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.helper.test
index 53013570589be51006c6cf7c5b27bac6384694ea..aac9489461e483bb2441ed105589b07ab5336414 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.helper.test
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.helper.test
@@ -4,7 +4,7 @@
  * A base class for the Fieldable Panels Panes tests, provides shared methods.
  */
 
-class FppTestHelper extends DrupalWebTestCase {
+abstract class FppTestHelper extends DrupalWebTestCase {
 
   /**
    * Admin user.
@@ -24,21 +24,42 @@ class FppTestHelper extends DrupalWebTestCase {
   /**
    * Create an admin-level user.
    *
+   * @param array $perms
+   *   Any permissions to be added to the user.
+   *
    * @return object
    *   A standard Drupal user object.
    */
-  function createAdminUser() {
+  function createAdminUser(array $perms = array()) {
     // Create a user with the admin permission.
     $permissions = array(
+      // The master permission for FPP.
       'administer fieldable panels panes',
+
+      // Required for Drupal core 7.50+, otherwise the user won't have access to
+      // the field settings.
+      'administer fields',
     );
 
+    return $this->createUser(array_merge($permissions, $perms));
+  }
+
+  /**
+   * Create a new user account with limited permissions.
+   *
+   * @param array $perms
+   *   Any permissions to be added to the user.
+   *
+   * @return object
+   *   A standard Drupal user object.
+   */
+  function createUser(array $perms = array()) {
     // Reset the static variable used to identify permissions, otherwise it's
     // possible the permissions check in drupalCreateUser will fail.
     $this->checkPermissions(array(), TRUE);
     cache_clear_all();
 
-    return $this->drupalCreateUser($permissions);
+    return $this->drupalCreateUser($perms);
   }
 
   /**
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.permissions.test b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.permissions.test
index 3dad9d11b702fc8da199c28e9a78bdda42ea2149..7e502e5683fd849fa5288a15ffbcc04ab30ddf5e 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.permissions.test
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.permissions.test
@@ -33,16 +33,7 @@ class FppPermissionsTest extends FppTestHelper {
    */
   function testAdminPermission() {
     // Create a user with the admin permission.
-    $permissions = array(
-      'administer fieldable panels panes',
-    );
-
-    // Reset the static variable used to identify permissions, otherwise it's
-    // possible the permissions check in drupalCreateUser will fail.
-    $this->checkPermissions(array(), TRUE);
-    cache_clear_all();
-
-    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->adminUser = $this->createAdminUser();
 
     $this->drupalLogin($this->adminUser);
 
@@ -86,14 +77,13 @@ class FppPermissionsTest extends FppTestHelper {
     $this->assertResponse(200, 'Loaded the Manage Display page for the default FPP type.');
     $this->assertText(t('Use custom display settings for the following view modes'));
 
-    // These two paths don't exist.
+    // These two legacy paths should redirect to the correct paths.
     $this->drupalGet('admin/structure/fieldable-panels-panes/manage/' . $this->bundle . '/fields');
     $this->assertResponse(200);
-    $this->assertText(t('There are currently no entities of this type.'), 'The old(?) FPP manage fields URL no longer works.');
-
+    $this->assertEqual($this->getUrl(), url('admin/structure/fieldable-panels-panes/' . $this->bundle . '/fields', array('absolute' => TRUE)));
     $this->drupalGet('admin/structure/fieldable-panels-panes/manage/' . $this->bundle . '/display');
     $this->assertResponse(200);
-    $this->assertText(t('There are currently no entities of this type.'), 'The old(?) FPP manage display URL no longer works.');
+    $this->assertEqual($this->getUrl(), url('admin/structure/fieldable-panels-panes/' . $this->bundle . '/display', array('absolute' => TRUE)));
   }
 
   /**
@@ -104,15 +94,8 @@ class FppPermissionsTest extends FppTestHelper {
     $permissions = array(
       'access fieldable panels panes master list',
     );
-
-    // Reset the static variable used to identify permissions, otherwise it's
-    // possible the permissions check in drupalCreateUser will fail.
-    $this->checkPermissions(array(), TRUE);
-    cache_clear_all();
-
-    $this->adminUser = $this->drupalCreateUser($permissions);
-
-    $this->drupalLogin($this->adminUser);
+    $this->user = $this->createUser($permissions);
+    $this->drupalLogin($this->user);
 
     // Load the 'list' page.
     $this->drupalGet('admin/structure/fieldable-panels-panes/' . $this->bundle . '/list');
@@ -126,17 +109,10 @@ class FppPermissionsTest extends FppTestHelper {
   function testCreateBundlePermission() {
     // Create a user with the admin permission.
     $permissions = array(
-      'create fieldable ' . $this->bundle,
+      "create fieldable {$this->bundle}",
     );
-
-    // Reset the static variable used to identify permissions, otherwise it's
-    // possible the permissions check in drupalCreateUser will fail.
-    $this->checkPermissions(array(), TRUE);
-    cache_clear_all();
-
-    $this->adminUser = $this->drupalCreateUser($permissions);
-
-    $this->drupalLogin($this->adminUser);
+    $this->user = $this->createUser($permissions);
+    $this->drupalLogin($this->user);
 
     // Load the 'add' page.
     $this->drupalGet('admin/structure/fieldable-panels-panes/' . $this->bundle . '/add');
@@ -173,17 +149,10 @@ class FppPermissionsTest extends FppTestHelper {
   function testEditBundlePermission() {
     // Create a user with edit permission.
     $permissions = array(
-      'edit fieldable ' . $this->bundle,
+      "edit fieldable {$this->bundle}",
     );
-
-    // Reset the static variable used to identify permissions, otherwise it's
-    // possible the permissions check in drupalCreateUser will fail.
-    $this->checkPermissions(array(), TRUE);
-    cache_clear_all();
-
-    $this->adminUser = $this->drupalCreateUser($permissions);
-
-    $this->drupalLogin($this->adminUser);
+    $this->user = $this->createUser($permissions);
+    $this->drupalLogin($this->user);
 
     // Load the 'add' page.
     $this->drupalGet('admin/structure/fieldable-panels-panes/' . $this->bundle . '/add');
@@ -196,18 +165,11 @@ class FppPermissionsTest extends FppTestHelper {
   function testCreateEditBundlePermission() {
     // Create a user with create & edit permission.
     $permissions = array(
-      'create fieldable ' . $this->bundle,
-      'edit fieldable ' . $this->bundle,
+      "create fieldable {$this->bundle}",
+      "edit fieldable {$this->bundle}",
     );
-
-    // Reset the static variable used to identify permissions, otherwise it's
-    // possible the permissions check in drupalCreateUser will fail.
-    $this->checkPermissions(array(), TRUE);
-    cache_clear_all();
-
-    $this->adminUser = $this->drupalCreateUser($permissions);
-
-    $this->drupalLogin($this->adminUser);
+    $this->user = $this->createUser($permissions);
+    $this->drupalLogin($this->user);
 
     // Load the 'add' page.
     $this->drupalGet('admin/structure/fieldable-panels-panes/' . $this->bundle . '/add');
@@ -237,18 +199,11 @@ class FppPermissionsTest extends FppTestHelper {
   function testDeleteBundlePermission() {
     // Create a user with the admin permission.
     $permissions = array(
-      'create fieldable ' . $this->bundle,
-      'delete fieldable ' . $this->bundle,
+      "create fieldable {$this->bundle}",
+      "delete fieldable {$this->bundle}",
     );
-
-    // Reset the static variable used to identify permissions, otherwise it's
-    // possible the permissions check in drupalCreateUser will fail.
-    $this->checkPermissions(array(), TRUE);
-    cache_clear_all();
-
-    $this->adminUser = $this->drupalCreateUser($permissions);
-
-    $this->drupalLogin($this->adminUser);
+    $this->user = $this->createUser($permissions);
+    $this->drupalLogin($this->user);
 
     // Load the 'add' page.
     $this->drupalGet('admin/structure/fieldable-panels-panes/' . $this->bundle . '/add');
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.with_panels.test b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.with_panels.test
index 0bf39969161e1ec5c2a0828008fc35c756c57bca..126fcbc0c39d4aab8946a5e6e8061eb1ad82e34c 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.with_panels.test
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.with_panels.test
@@ -33,19 +33,11 @@ class FppWithPanelsTest extends FppTestHelper {
 
     // Create a user with all the permissions
     $permissions = array(
-      'administer fieldable panels panes',
-
       // Needed for Page Manager.
       'administer page manager',
       'use page manager',
     );
-
-    // Reset the static variable used to identify permissions, otherwise it's
-    // possible the permissions check in drupalCreateUser will fail.
-    $this->checkPermissions(array(), TRUE);
-    cache_clear_all();
-
-    $this->adminUser = $this->drupalCreateUser($permissions);
+    $this->adminUser = $this->createAdminUser($permissions);
     $this->drupalLogin($this->adminUser);
   }
 
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.with_pathauto.test b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.with_pathauto.test
new file mode 100644
index 0000000000000000000000000000000000000000..626355d101bdd16d6e1bdceb1e093ade4b392e1c
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp.with_pathauto.test
@@ -0,0 +1,75 @@
+<?php
+/**
+ * @file
+ * Tests for the Fieldable Panels Panes module with Pathauto.
+ */
+
+class FppWithPathautoTest extends FppTestHelper {
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'FPP tests for Pathauto',
+      'description' => 'Confirm that FPP does not break when using Pathauto.',
+      'group' => 'FPP',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp(array $modules = array()) {
+    $modules[] = 'fieldable_panels_panes';
+
+    // 
+    $modules[] = 'pathauto';
+
+    parent::setUp($modules);
+
+    // Some default values to work with.
+    $this->bundle = 'fieldable_panels_pane';
+  }
+
+  function testPathauto() {
+    // Create a test node.
+    $node = $this->drupalCreateNode();
+
+    // Create a user with the admin permission.
+    $permissions = array(
+      "create fieldable {$this->bundle}",
+      'view the administration theme',
+    );
+    $this->user = $this->createUser($permissions);
+    $this->drupalLogin($this->user);
+
+    // Load the 'add' page.
+    $this->drupalGet('admin/structure/fieldable-panels-panes/' . $this->bundle . '/add');
+    $this->assertResponse(200, 'Loaded the Add page for the default FPP type.');
+
+    // Save the record with a path that points to the node.
+    $args = array(
+      'title' => t('Test'),
+      'link' => TRUE,
+      'path' => 'node/1',
+    );
+    $this->drupalPost(NULL, $args, t('Save'));
+    $this->assertResponse(200);
+    $this->assertText(t('The entity has been saved.'));
+
+    // Confirm the record was saved.
+    $this->drupalGet('admin/structure/fieldable-panels-panes/' . $this->bundle);
+    $this->assertNoText(t('There are currently no entities of this type.'));
+
+    // Try loading it.
+    $this->drupalGet('admin/structure/fieldable-panels-panes/view/1');
+    $this->assertResponse(200, 'Loaded the FPP object.');
+
+    // Load the FPP.
+    $fpp = fieldable_panels_panes_load(1);
+
+    // Confirm there's a link with the FPP's title and the node's URL.
+    $this->assertRaw(l($fpp->title, 'node/' . $node->nid));
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp_with_panels_test.info b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp_with_panels_test.info
index 0dc5dadf14aca1d30daaab854cbf99679cc92025..02383526ced0724617d4b3d150b0467631e3895e 100644
--- a/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp_with_panels_test.info
+++ b/profiles/wcm_base/modules/contrib/fieldable_panels_panes/tests/fpp_with_panels_test.info
@@ -9,9 +9,9 @@ dependencies[] = ctools
 dependencies[] = page_manager
 dependencies[] = fieldable_panels_panes
 
-; Information added by Drupal.org packaging script on 2016-05-04
-version = "7.x-1.10"
+; Information added by Drupal.org packaging script on 2016-11-05
+version = "7.x-1.11"
 core = "7.x"
 project = "fieldable_panels_panes"
-datestamp = "1462374853"
+datestamp = "1478371741"
 
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/README.txt b/profiles/wcm_base/modules/contrib/google_analytics/README.txt
index 25fd22aa79445177501575a3eccb1209659b42f9..8ef9818fdcb555b33796f589774115e3c37747b0 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/README.txt
+++ b/profiles/wcm_base/modules/contrib/google_analytics/README.txt
@@ -1,6 +1,6 @@
 
 Module: Google Analytics
-Author: Alexander Hass <http://drupal.org/user/85918>
+Author: Alexander Hass <https://drupal.org/user/85918>
 
 
 Description
@@ -53,15 +53,15 @@ user with 'Administer Google Analytics' permission.
 Like the blocks visibility settings in Drupal core, there is a choice for
 "Add if the following PHP code returns TRUE." Sample PHP snippets that can be
 used in this textarea can be found on the handbook page "Overview-approach to
-block visibility" at http://drupal.org/node/64135.
+block visibility" at https://drupal.org/node/64135.
 
 Custom dimensions and metrics
 =============================
 One example for custom dimensions tracking is the "User roles" tracking.
 
-1. In the Google Analytics Management Interface (http://www.google.com/analytics/)
-   you need to setup Dimension #1 with name e.g. "User roles". This step is
-   required. Do not miss it, please.
+1. In the Google Analytics (https://marketingplatform.google.com/about/analytics/)
+   Management Interface you need to setup Dimension #1 with name 
+   e.g. "User roles". This step is required. Do not miss it, please.
 
 2. Enter the below configuration data into the Drupal custom dimensions settings
    form under admin/config/system/googleanalytics. You can also choose another
@@ -77,7 +77,7 @@ Advanced Settings
 =================
 You can include additional JavaScript snippets in the custom javascript
 code textarea. These can be found on the official Google Analytics pages
-and a few examples at http://drupal.org/node/248699. Support is not
+and a few examples at https://drupal.org/node/248699. Support is not
 provided for any customisations you include.
 
 To speed up page loading you may also cache the Google Analytics "analytics.js"
@@ -97,7 +97,7 @@ Body:
   <li><a href="mailto:foo@example.com">Mailto</a></li>
   <li><a href="/files/test.txt">Download file</a></li>
   <li><a class="colorbox" href="#">Open colorbox</a></li>
-  <li><a href="http://example.com/">External link</a></li>
+  <li><a href="https://example.com/">External link</a></li>
   <li><a href="/go/test">Go link</a></li>
 </ul>
 
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.inc b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.inc
index 20abb5c36fa60d9d6bd8766056fb17f07ee922d5..2d8afa35b823b37997b702e334fb1d245509f59c 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.inc
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.inc
@@ -21,7 +21,7 @@ function googleanalytics_admin_settings_form($form_state) {
     '#size' => 20,
     '#maxlength' => 20,
     '#required' => TRUE,
-    '#description' => t('This ID is unique to each site you want to track separately, and is in the form of UA-xxxxxxx-yy. To get a Web Property ID, <a href="@analytics">register your site with Google Analytics</a>, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. <a href="@webpropertyid">Find more information in the documentation</a>.', array('@analytics' => 'http://www.google.com/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty')))),
+    '#description' => t('This ID is unique to each site you want to track separately, and is in the form of UA-xxxxxxx-yy. To get a Web Property ID, <a href="@analytics">register your site with Google Analytics</a>, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. <a href="@webpropertyid">Find more information in the documentation</a>.', array('@analytics' => 'https://marketingplatform.google.com/about/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty')))),
   );
 
   $form['account']['googleanalytics_premium'] = array(
@@ -316,7 +316,7 @@ function googleanalytics_admin_settings_form($form_state) {
   $form['tracking']['privacy']['googleanalytics_privacy_donottrack'] = array(
     '#type' => 'checkbox',
     '#title' => t('Universal web tracking opt-out'),
-    '#description' => t('If enabled and your server receives the <a href="@donottrack">Do-Not-Track</a> header from the client browser, the Google Analytics module will not embed any tracking code into your site. Compliance with Do Not Track could be purely voluntary, enforced by industry self-regulation, or mandated by state or federal law. Please accept your visitors privacy. If they have opt-out from tracking and advertising, you should accept their personal decision. This feature is currently limited to logged in users and disabled page caching.', array('@donottrack' => 'http://donottrack.us/')),
+    '#description' => t('If enabled and your server receives the <a href="@donottrack">Do-Not-Track</a> header from the client browser, the Google Analytics module will not embed any tracking code into your site. Compliance with Do Not Track could be purely voluntary, enforced by industry self-regulation, or mandated by state or federal law. Please accept your visitors privacy. If they have opt-out from tracking and advertising, you should accept their personal decision. This feature is currently limited to logged in users and disabled page caching.', array('@donottrack' => 'https://www.eff.org/issues/do-not-track')),
     '#default_value' => variable_get('googleanalytics_privacy_donottrack', 1),
   );
 
@@ -363,7 +363,7 @@ function googleanalytics_admin_settings_form($form_state) {
 
   $form['googleanalytics_custom_dimension']['googleanalytics_description'] = array(
     '#type' => 'item',
-    '#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom dimensions. Section 7 of the <a href="@ga_tos">Google Analytics terms of service</a> requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'http://www.google.com/analytics/terms/gb.html')),
+    '#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom dimensions. Section 7 of the <a href="@ga_tos">Google Analytics terms of service</a> requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'https://www.google.com/analytics/terms/gb.html')),
   );
   if (module_exists('token')) {
     $form['googleanalytics_custom_dimension']['googleanalytics_token_tree'] = array(
@@ -415,7 +415,7 @@ function googleanalytics_admin_settings_form($form_state) {
 
   $form['googleanalytics_custom_metric']['googleanalytics_description'] = array(
     '#type' => 'item',
-    '#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom metrics. Section 7 of the <a href="@ga_tos">Google Analytics terms of service</a> requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'http://www.google.com/analytics/terms/gb.html')),
+    '#description' => t('You can supplement Google Analytics\' basic IP address tracking of visitors by segmenting users based on custom metrics. Section 7 of the <a href="@ga_tos">Google Analytics terms of service</a> requires that You will not (and will not allow any third party to) use the Service to track, collect or upload any data that personally identifies an individual (such as a name, userid, email address or billing information), or other data which can be reasonably linked to such information by Google. You will have and abide by an appropriate Privacy Policy and will comply with all applicable laws and regulations relating to the collection of information from Visitors. You must post a Privacy Policy and that Privacy Policy must provide notice of Your use of cookies that are used to collect traffic data, and You must not circumvent any privacy features (e.g., an opt-out) that are part of the Service.', array('@ga_tos' => 'https://www.google.com/analytics/terms/gb.html')),
   );
   if (module_exists('token')) {
     $form['googleanalytics_custom_metric']['googleanalytics_token_tree'] = array(
@@ -457,7 +457,7 @@ function googleanalytics_admin_settings_form($form_state) {
     '#title' => t('Custom JavaScript code'),
     '#collapsible' => TRUE,
     '#collapsed' => TRUE,
-    '#description' => t('You can add custom Google Analytics <a href="@snippets">code snippets</a> here. These will be added every time tracking is in effect. Before you add your custom code, you should read the <a href="@ga_concepts_overview">Google Analytics Tracking Code - Functional Overview</a> and the <a href="@ga_js_api">Google Analytics Tracking API</a> documentation. <strong>Do not include the &lt;script&gt; tags</strong>, and always end your code with a semicolon (;).', array('@snippets' => 'http://drupal.org/node/248699', '@ga_concepts_overview' => 'https://developers.google.com/analytics/resources/concepts/gaConceptsTrackingOverview', '@ga_js_api' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/method-reference')),
+    '#description' => t('You can add custom Google Analytics <a href="@snippets">code snippets</a> here. These will be added every time tracking is in effect. Before you add your custom code, you should read the <a href="@ga_concepts_overview">Google Analytics Tracking Code - Functional Overview</a> and the <a href="@ga_js_api">Google Analytics Tracking API</a> documentation. <strong>Do not include the &lt;script&gt; tags</strong>, and always end your code with a semicolon (;).', array('@snippets' => 'https://drupal.org/node/248699', '@ga_concepts_overview' => 'https://developers.google.com/analytics/resources/concepts/gaConceptsTrackingOverview', '@ga_js_api' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/method-reference')),
   );
   $form['advanced']['codesnippet']['googleanalytics_codesnippet_create'] = array(
     '#type' => 'textarea',
@@ -807,7 +807,6 @@ function _googleanalytics_validate_create_field_name($name) {
     'siteSpeedSampleRate',
     'storage',
     'useAmpClientId',
-    'userId',
   );
 
   if ($name == 'name') {
@@ -816,6 +815,9 @@ function _googleanalytics_validate_create_field_name($name) {
   if ($name == 'allowLinker') {
     return t('Create only field name %name is a disallowed field name. Please select <em>Multiple top-level domains</em> under <em>What are you tracking</em> to enable cross domain tracking.', array('%name' => $name));
   }
+  if ($name == 'userId') {
+    return t('Create only field name %name is a disallowed field name. Please enable <em>Track User ID</em> under <em>Tracking scope > Users</em>.', array('%name' => $name));
+  }
   if (!in_array($name, $create_only_fields)) {
     return t('Create only field name %name is an unknown field name. Field names are case sensitive. Please see <a href="@url">create only fields</a> documentation for supported field names.', array('%name' => $name, '@url' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#create'));
   }
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.debug.js b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.debug.js
index a0be81ae8ead8e60701f5ec2979f6330abd9eda9..7186230494bf44d611265ecdd6edcc2efe00a14c 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.debug.js
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.debug.js
@@ -122,7 +122,7 @@ $(document).ready(function() {
 Drupal.googleanalytics.isCrossDomain = function (hostname, crossDomains) {
   /**
    * jQuery < 1.6.3 bug: $.inArray crushes IE6 and Chrome if second argument is
-   * `null` or `undefined`, http://bugs.jquery.com/ticket/10076,
+   * `null` or `undefined`, https://bugs.jquery.com/ticket/10076,
    * https://github.com/jquery/jquery/commit/a839af034db2bd934e4d4fa6758a3fed8de74174
    *
    * @todo: Remove/Refactor in D8
@@ -181,8 +181,8 @@ Drupal.googleanalytics.isInternalSpecial = function (url) {
  * Extract the relative internal URL from an absolute internal URL.
  *
  * Examples:
- * - http://mydomain.com/node/1 -> /node/1
- * - http://example.com/foo/bar -> http://example.com/foo/bar
+ * - https://mydomain.com/node/1 -> /node/1
+ * - https://example.com/foo/bar -> https://example.com/foo/bar
  *
  * @param string url
  *   The web url to check.
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.info b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.info
index 9232102f03d355978395b60d5816dd6b24d7a9c8..591816a33a17821e51d348ef7ac98cc21f87b686 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.info
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.info
@@ -5,8 +5,8 @@ package = Statistics
 configure = admin/config/system/googleanalytics
 files[] = googleanalytics.test
 test_dependencies[] = token
-; Information added by Drupal.org packaging script on 2018-07-13
-version = "7.x-2.5"
+; Information added by Drupal.org packaging script on 2019-01-31
+version = "7.x-2.6"
 core = "7.x"
 project = "google_analytics"
-datestamp = "1531469026"
+datestamp = "1548968597"
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.install b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.install
index 12f1c55e6eade1ed02d417675d146974191173a1..08b4e05cef89109c727ec72c746c1f66f76890e1 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.install
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.install
@@ -104,7 +104,7 @@ function googleanalytics_update_6001() {
   variable_set('googleanalytics_visibility', 0);
 
   // Remove tracking from all administrative pages, see:
-  // http://drupal.org/node/34970.
+  // https://drupal.org/node/34970.
   $pages = array(
     'admin*',
     'user*',
@@ -237,7 +237,7 @@ function googleanalytics_update_6006() {
  *
  * This is a data protection and privacy law change. For more information see
  * Google Analytics terms of use section 8.1:
- * http://www.google.com/analytics/en-GB/tos.html
+ * https://www.google.com/analytics/en-GB/tos.html
  */
 function googleanalytics_update_6007() {
   $profile_fields = variable_get('googleanalytics_segmentation', array());
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.js b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.js
index 8d5bde1b251f1a408564dd8d34329374b6bb6680..5ba42ca934b8a8157b42a1345f043c1bdd228ee8 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.js
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.js
@@ -104,7 +104,7 @@ $(document).ready(function() {
 Drupal.googleanalytics.isCrossDomain = function (hostname, crossDomains) {
   /**
    * jQuery < 1.6.3 bug: $.inArray crushes IE6 and Chrome if second argument is
-   * `null` or `undefined`, http://bugs.jquery.com/ticket/10076,
+   * `null` or `undefined`, https://bugs.jquery.com/ticket/10076,
    * https://github.com/jquery/jquery/commit/a839af034db2bd934e4d4fa6758a3fed8de74174
    *
    * @todo: Remove/Refactor in D8
@@ -163,8 +163,8 @@ Drupal.googleanalytics.isInternalSpecial = function (url) {
  * Extract the relative internal URL from an absolute internal URL.
  *
  * Examples:
- * - http://mydomain.com/node/1 -> /node/1
- * - http://example.com/foo/bar -> http://example.com/foo/bar
+ * - https://mydomain.com/node/1 -> /node/1
+ * - https://example.com/foo/bar -> https://example.com/foo/bar
  *
  * @param string url
  *   The web url to check.
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.module b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.module
index 275262be7d074a141186b11bbe2be7ba398eb3b7..0026703a504cfc5fdc9fee59b445c1a89e5e8544 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.module
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.module
@@ -7,7 +7,7 @@
  * Adds the required Javascript to all your Drupal pages to allow tracking by
  * the Google Analytics statistics package.
  *
- * @author: Alexander Hass <http://drupal.org/user/85918>
+ * @author: Alexander Hass <https://drupal.org/user/85918>
  */
 
 /**
@@ -17,7 +17,7 @@ define('GOOGLEANALYTICS_TRACKFILES_EXTENSIONS', '7z|aac|arc|arj|asf|asx|avi|bin|
 
 /**
  * Define default path exclusion list to remove tracking from admin pages,
- * see http://drupal.org/node/34970 for more information.
+ * see https://drupal.org/node/34970 for more information.
  */
 define('GOOGLEANALYTICS_PAGES', "admin\nadmin/*\nbatch\nnode/add*\nnode/*/*\nuser/*/*");
 
@@ -36,7 +36,7 @@ function googleanalytics_api() {
 function googleanalytics_help($path, $arg) {
   switch ($path) {
     case 'admin/config/system/googleanalytics':
-      return t('<a href="@ga_url">Google Analytics</a> is a free (registration required) website traffic and marketing effectiveness service.', array('@ga_url' => 'http://www.google.com/analytics/'));
+      return t('<a href="@ga_url">Google Analytics</a> is a free (registration required) website traffic and marketing effectiveness service.', array('@ga_url' => 'https://marketingplatform.google.com/about/analytics/'));
   }
 }
 
@@ -201,7 +201,7 @@ function googleanalytics_page_alter(&$page) {
 
     // Track access denied (403) and file not found (404) pages.
     if ($status == '403 Forbidden') {
-      // See http://www.google.com/support/analytics/bin/answer.py?answer=86927
+      // See https://www.google.com/support/analytics/bin/answer.py?answer=86927
       $url_custom = '"' . $base_path . '403.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
     }
     elseif ($status == '404 Not Found') {
@@ -496,7 +496,7 @@ function googleanalytics_preprocess_search_results(&$variables) {
 /**
  * Helper function for grabbing search keys. Function is missing in D7.
  *
- * http://api.drupal.org/api/function/search_get_keys/6
+ * https://api.drupal.org/api/function/search_get_keys/6
  */
 function googleanalytics_search_get_keys() {
   static $return;
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test
index 3db117fe16d35a22957d6b47c2b876550a08b575..52c31489e02d868a0f9dd8b2d4ecf5a3b2335b23 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test
@@ -558,7 +558,7 @@ class GoogleAnalyticsStatusMessagesTest extends DrupalWebTestCase {
     //drupal_set_message('Example status message.', 'status');
     //drupal_set_message('Example warning message.', 'warning');
     //drupal_set_message('Example error message.', 'error');
-    //drupal_set_message('Example error <em>message</em> with html tags and <a href="http://example.com/">link</a>.', 'error');
+    //drupal_set_message('Example error <em>message</em> with html tags and <a href="https://example.com/">link</a>.', 'error');
     //$this->drupalGet('');
     //$this->assertNoRaw('ga("send", "event", "Messages", "Status message", "Example status message.");', '[testGoogleAnalyticsStatusMessages]: Example status message is not enabled for tracking.');
     //$this->assertNoRaw('ga("send", "event", "Messages", "Warning message", "Example warning message.");', '[testGoogleAnalyticsStatusMessages]: Example warning message is not enabled for tracking.');
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test.js b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test.js
index b3893501d669cf28d0641c631adcb01632b4d462..f22d6f1ae8b11ecb84ac916826005ac13f05f986 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test.js
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test.js
@@ -77,7 +77,7 @@ $(document).ready(function() {
   Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isInternal(base_url + Drupal.settings.basePath + 'node/1?foo=bar'), "Link '" + base_url + Drupal.settings.basePath + "node/1?foo=bar' has been detected as internal link.");
   Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isInternal(base_url + Drupal.settings.basePath + 'node/1?foo=bar#foo'), "Link '" + base_url + Drupal.settings.basePath + "node/1?foo=bar#foo' has been detected as internal link.");
   Drupal.googleanalytics.test.assertTrue(Drupal.googleanalytics.isInternal(base_url + Drupal.settings.basePath + 'go/foo'), "Link '" + base_url + Drupal.settings.basePath + "go/foo' has been detected as internal link.");
-  Drupal.googleanalytics.test.assertFalse(Drupal.googleanalytics.isInternal('http://example.com/node/3'), "Link 'http://example.com/node/3' has been detected as external link.");
+  Drupal.googleanalytics.test.assertFalse(Drupal.googleanalytics.isInternal('https://example.com/node/3'), "Link 'https://example.com/node/3' has been detected as external link.");
   console.groupEnd();
 
   console.group("Test 'isInternalSpecial':");
@@ -90,7 +90,7 @@ $(document).ready(function() {
   Drupal.google_analytics.test.assertSame(base_path, Drupal.google_analytics.getPageUrl(base_path), "Absolute internal URL '" + base_path + "' has been extracted from absolute url '" + base_path + "'.");
   //Drupal.googleanalytics.test.assertSame(base_path, Drupal.googleanalytics.getPageUrl(base_url + Drupal.settings.basePath + 'node/1'), "Absolute internal URL '" +  Drupal.settings.basePath + "node/1' has been extracted from full qualified url '" + base_url + base_path + "'.");
   //Drupal.googleanalytics.test.assertSame(base_path, Drupal.googleanalytics.getPageUrl(Drupal.settings.basePath + 'node/1'), "Absolute internal URL '" +  Drupal.settings.basePath + "node/1' has been extracted from absolute url '" +  base_path + "'.");
-  Drupal.googleanalytics.test.assertSame('http://example.com/node/2', Drupal.googleanalytics.getPageUrl('http://example.com/node/2'), "Full qualified external url 'http://example.com/node/2' has been extracted.");
+  Drupal.googleanalytics.test.assertSame('https://example.com/node/2', Drupal.googleanalytics.getPageUrl('https://example.com/node/2'), "Full qualified external url 'https://example.com/node/2' has been extracted.");
   Drupal.googleanalytics.test.assertSame('//example.com/node/2', Drupal.googleanalytics.getPageUrl('//example.com/node/2'), "Full qualified external url '//example.com/node/2' has been extracted.");
   console.groupEnd();
 
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.variable.inc b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.variable.inc
index 9d142a2750311ce27cf452d5db4e134a8bc1767e..5b88b1ff86e7c1fa05412d66d574e83a64929747 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.variable.inc
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.variable.inc
@@ -13,7 +13,7 @@ function googleanalytics_variable_info($options) {
     'type' => 'string',
     'title' => t('Web Property ID', array(), $options),
     'default' => 'UA-',
-    'description' => t('This ID is unique to each site you want to track separately, and is in the form of UA-xxxxxxx-yy. To get a Web Property ID, <a href="@analytics">register your site with Google Analytics</a>, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. <a href="@webpropertyid">Find more information in the documentation</a>.', array('@analytics' => 'http://www.google.com/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty'))), $options),
+    'description' => t('This ID is unique to each site you want to track separately, and is in the form of UA-xxxxxxx-yy. To get a Web Property ID, <a href="@analytics">register your site with Google Analytics</a>, or if you already have registered your site, go to your Google Analytics Settings page to see the ID next to every site profile. <a href="@webpropertyid">Find more information in the documentation</a>.', array('@analytics' => 'https://marketingplatform.google.com/about/analytics/', '@webpropertyid' => url('https://developers.google.com/analytics/resources/concepts/gaConceptsAccounts', array('fragment' => 'webProperty'))), $options),
     'required' => TRUE,
     'group' => 'googleanalytics',
     'localize' => TRUE,
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/LICENSE.txt b/profiles/wcm_base/modules/contrib/logging_alerts/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/LICENSE.txt
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.admin.inc b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.admin.inc
new file mode 100644
index 0000000000000000000000000000000000000000..d44804f501daa49ad5fa41fbb1453c6b73bffaff
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.admin.inc
@@ -0,0 +1,221 @@
+<?php
+
+/**
+ * @file
+ *   Admin callbacks for the Email Logging and Alerts module.
+ */
+
+/**
+ * Returns admin settings form.
+ */
+function emaillog_admin_settings() {
+  $severity_levels = watchdog_severity_levels();
+  $form['emaillog'] = array(
+    '#type'           => 'fieldset',
+    '#title'          => t('Email addresses for each severity level'),
+    '#description'    => t('Enter an email address for each severity level. For example, you may want to get emergency and critical levels to your pager or mobile phone, while notice level messages can go to a regular email. If you leave the email address blank for a severity level, no email messages will be sent for that severity level.'),
+    '#collapsible'    => TRUE,
+    '#collapsed'      => FALSE,
+  );
+  foreach ($severity_levels as $severity => $description) {
+    $key = 'emaillog_' . $severity;
+    $form['emaillog'][$key] = array(
+      '#type'           => 'textfield',
+      '#title'          => t('Email address for severity %description', array('%description' => drupal_ucfirst($description))),
+      '#default_value'  => variable_get($key),
+      '#description'    => t('The email address to send log entries of severity %description to.', array('%description' => drupal_ucfirst($description))),
+    );
+  }
+
+  $form['debug_info'] = array(
+    '#type'           => 'fieldset',
+    '#title'          => t('Additional debug info'),
+    '#description'    => t('Additional debug information that should be attached to email alerts. Note that this information could be altered by other modules using <em>hook_emaillog_debug_info_alter(&$debug_info)</em>'),
+    '#collapsible'    => TRUE,
+    '#collapsed'      => TRUE,
+    '#tree'           => TRUE,
+  );
+  $debug_info_settings = variable_get('emaillog_debug_info');
+  $status = array();
+  foreach (_emaillog_get_debug_info_callbacks() as $debug_info_key => $debug_info_callback) {
+    $options[$debug_info_key] = '';
+    $form['debug_info']['variable'][$debug_info_key] = array(
+      '#type' => 'item',
+      '#markup' => $debug_info_callback,
+    );
+    foreach (array_keys($severity_levels) as $level_id) {
+      // Builds arrays for checked boxes for each role
+      if (!empty($debug_info_settings[$level_id][$debug_info_key])) {
+        $status[$level_id][] = $debug_info_key;
+      }
+    }
+  }
+  foreach ($severity_levels as $level_id => $description) {
+    $form['debug_info'][$level_id] = array(
+      '#title' => check_plain($description),
+      '#type' => 'checkboxes',
+      '#options' => $options,
+      '#default_value' => isset($status[$level_id]) ? $status[$level_id] : array(),
+    );
+  }
+  $form['debug_info']['emaillog_backtrace_replace_args'] = array(
+    '#type'           => 'checkbox',
+    '#title'          => t('Replace debug_backtrace() argument values with types'),
+    '#description'    => t('By default <em>debug_backtrace()</em> will return full variable information in the stack traces that it produces. Variable information can take quite a bit of resources, both while collecting and adding to the alert email, therefore here by default all variable values are replaced with their types only. Warning - unchecking this option could cause your site to crash when it tries to send an alert email with too big stack trace!'),
+    '#default_value'  => variable_get('emaillog_backtrace_replace_args', TRUE),
+    '#weight'         => 1,
+  );
+
+  $form['limits'] = array(
+    '#type' => 'fieldset',
+    '#title'          => t('Email sending limits'),
+    '#collapsible'    => TRUE,
+    '#collapsed'      => TRUE,
+  );
+  $form['limits']['emaillog_max_similar_emails'] = array(
+    '#type'           => 'textfield',
+    '#title'          => t('Maximum number of allowed consecutive similar email alerts'),
+    '#description'    => t('Upper limit of email alerts sent consecutively with the same or very similar message. Leave empty for no limit.'),
+    '#default_value'  => variable_get('emaillog_max_similar_emails'),
+  );
+  $form['limits']['emaillog_max_consecutive_timespan'] = array(
+    '#type'           => 'textfield',
+    '#title'          => t('Email alerts should be considered "consecutive" if sent within'),
+    '#field_suffix'   => t('minutes from each other'),
+    '#description'    => t('Longest possible period between two email alerts being sent to still be considered consecutive. Leave empty for no limit.'),
+    '#default_value'  => variable_get('emaillog_max_consecutive_timespan'),
+  );
+  $form['limits']['emaillog_max_similarity_level'] = array(
+    '#type'           => 'textfield',
+    '#title'          => t('Maximum allowed similarity level between consecutive email alerts'),
+    '#description'    => '<p>' . t('Highest similarity level above which new email alerts will not be sent anymore if "Maximum number of allowed consecutive similar email alerts" has been reached and email alerts are considered "consecutive" (time period between each previous and next one is smaller than defined above). Possible values range from 0 to 1, where 1 stands for two identical emails.') . '</p>'
+                       . '<p>' . t('For example setting "Maximum number of allowed consecutive similar email alerts" to 5, "Email alerts should be considered consecutive if sent within" to 5 minutes and "Similarity level" to 0.9 would mean that only 5 email alerts would be sent within 5 minutes if Watchdog entries are similar in at least 90%.') . '</p>'
+                       . '<p>' . t("(Note that similarity level is calculated using PHP's <a href='@similar_text_url'>similar_text()</a> function, with all its complexity and implications.)", array('@similar_text_url' => url('http://php.net/similar_text'))) . '</p>',
+    '#default_value'  => variable_get('emaillog_max_similarity_level'),
+  );
+
+  $form['legacy'] = array(
+    '#type' => 'fieldset',
+    '#title'          => t('Legacy settings'),
+    '#collapsible'    => TRUE,
+    '#collapsed'      => TRUE,
+  );
+  $form['legacy']['emaillog_legacy_subject'] = array(
+    '#type'           => 'checkbox',
+    '#title'          => t('Use legacy email subject'),
+    '#description'    => t('Older versions of this module were using email subject "%subject", while currently it is being set to beginning of Watchdog message. This option allows to switch back to previous version of email subject.', array(
+      '%subject'        => t('[@site_name] @severity_desc: Alert from your web site'),
+    )),
+    '#default_value'  => variable_get('emaillog_legacy_subject'),
+  );
+
+  // We need our own theme function to put all checkboxes in the table.
+  $form['#theme'] = 'emaillog_admin_settings';
+  $form['#validate'][] = 'emaillog_admin_settings_validate';
+  $form['#submit'][] = 'emaillog_admin_settings_submit';
+  return system_settings_form($form);
+}
+
+/**
+ * Themes admin settings form.
+ */
+function theme_emaillog_admin_settings($variables) {
+  $form = $variables['form'];
+
+  $severity_levels = watchdog_severity_levels();
+  foreach (element_children($form['debug_info']['variable']) as $key) {
+    $row = array();
+    // Permission row.
+    $row[] = array(
+      'data' => drupal_render($form['debug_info']['variable'][$key]),
+      'class' => array('variable'),
+    );
+    foreach (array_keys($severity_levels) as $level_id) {
+      $element = $form['debug_info'][$level_id];
+      $form['debug_info'][$level_id][$key]['#title'] = $element['#title'] . ' : ' . $form['debug_info']['variable'][$key]['#markup'];
+      $form['debug_info'][$level_id][$key]['#title_display'] = 'invisible';
+      $row[] = array(
+        'data' => drupal_render($form['debug_info'][$level_id][$key]),
+        'class' => array('checkbox'),
+        'title' => $element['#title'] . ' : ' . $form['debug_info']['variable'][$key]['#markup'],
+      );
+    }
+    $rows[] = $row;
+  }
+  $header = array('');
+  foreach (array_keys($severity_levels) as $level_id) {
+    $element = $form['debug_info'][$level_id];
+    $header[] = array('data' => $element['#title'], 'class' => array('checkbox'));
+
+    // Mark the checkboxes parent as rendered
+    $form['debug_info'][$level_id]['#printed'] = TRUE;
+  }
+
+  // Put the table with all checkboxes inside a fieldset.
+  $form['debug_info']['variables'] = array(
+    '#type'   => 'markup',
+    '#markup' => theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'permissions'))),
+  );
+
+  // Render and return all remaining elements.
+  return drupal_render_children($form);
+}
+
+/**
+ * Validates admin settings form.
+ */
+function emaillog_admin_settings_validate($form, &$form_state) {
+  if ($form_state['values']['emaillog_max_similar_emails'] && !$form_state['values']['emaillog_max_similarity_level']) {
+    form_set_error('emaillog_max_similarity_level', t('You need to provide value for %field1 field when specifying %field2.', array(
+      '%field1' => 'Maximum allowed similarity level between consecutive email alerts',
+      '%field2' => 'Maximum number of allowed consecutive similar emails',
+    )));
+  }
+  if ($form_state['values']['emaillog_max_similarity_level'] && !$form_state['values']['emaillog_max_similar_emails']) {
+    form_set_error('emaillog_max_similar_emails', t('You need to provide value for %field1 field when specifying %field2.', array(
+      '%field1' => 'Maximum number of allowed consecutive similar emails',
+      '%field2' => 'Maximum allowed similarity level between consecutive email alerts',
+    )));
+  }
+  if ($form_state['values']['emaillog_max_consecutive_timespan'] && !$form_state['values']['emaillog_max_similar_emails']) {
+    form_set_error('emaillog_max_similar_emails', t('You need to provide value for %field1 field when specifying %field2.', array(
+      '%field1' => 'Maximum number of allowed consecutive similar emails',
+      '%field2' => 'Email alerts should be considered "consecutive" if sent within',
+    )));
+  }
+  if ($form_state['values']['emaillog_max_consecutive_timespan'] && !$form_state['values']['emaillog_max_similarity_level']) {
+    form_set_error('emaillog_max_similarity_level', t('You need to provide value for %field1 field when specifying %field2.', array(
+      '%field1' => 'Maximum allowed similarity level between consecutive email alerts',
+      '%field2' => 'Email alerts should be considered "consecutive" if sent within',
+    )));
+  }
+  if ($form_state['values']['emaillog_max_similarity_level']) {
+    if (!is_numeric($form_state['values']['emaillog_max_similarity_level'])) {
+      form_set_error('emaillog_max_similarity_level', t('Value of %field cannot contain any non-numeric characters.', array(
+        '%field' => 'Maximum allowed similarity level between consecutive email alerts',
+      )));
+    }
+    if ($form_state['values']['emaillog_max_similarity_level'] < 0 || $form_state['values']['emaillog_max_similarity_level'] > 1) {
+      form_set_error('emaillog_max_similarity_level', t('Value of %field needs to be in [0-1] range.', array(
+        '%field' => 'Maximum allowed similarity level between consecutive email alerts',
+      )));
+    }
+  }
+}
+
+/**
+ * Saves additional debug info from submitted admin settings form.
+ */
+function emaillog_admin_settings_submit($form, &$form_state) {
+  $severity_levels = watchdog_severity_levels();
+  $debug_info = array();
+  foreach (array_keys($severity_levels) as $level_id) {
+    foreach (array_keys(_emaillog_get_debug_info_callbacks()) as $variable_id) {
+      if (!empty($form_state['values']['debug_info'][$level_id][$variable_id])) {
+        $debug_info[$level_id][$variable_id] = 1;
+      }
+    }
+  }
+  unset($form_state['values']['debug_info']);
+  variable_set('emaillog_debug_info', $debug_info);
+}
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.info b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.info
new file mode 100644
index 0000000000000000000000000000000000000000..88cad2c87e49d1c50217ab733ea88954f00250b9
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.info
@@ -0,0 +1,12 @@
+name = Email logging and alerts
+description = Sends logs and alerts to email addresses, with different severity going to different emails.
+package = Logging and alerts
+core = 7.x
+configure = admin/config/system/emaillog
+
+; Information added by drupal.org packaging script on 2013-06-19
+version = "7.x-2.2"
+core = "7.x"
+project = "logging_alerts"
+datestamp = "1371630053"
+
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.install b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.install
new file mode 100644
index 0000000000000000000000000000000000000000..c7ee3f3eb41ab6dae3660eb6e8678506be9c6d88
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.install
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the emaillog module.
+ */
+
+/**
+ * Implements hook_uninstall().
+ * Deletes all emaillog variables and clears variable cache.
+ */
+function emaillog_uninstall() {
+  db_query("DELETE FROM {variable} WHERE name LIKE 'emaillog_%'");
+  cache_clear_all('variables', 'cache');
+}
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.module b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.module
new file mode 100644
index 0000000000000000000000000000000000000000..eff62151cfe1e4a809349c1092267f93318ba00d
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.module
@@ -0,0 +1,247 @@
+<?php
+
+/**
+ * @file
+ * Drupal Module: Email Logging and Alerts
+ *
+ * Sends logs and alerts to email addresses.
+ *
+ * @Author: Khalid Baheyeldin http://2bits.com (version 6.x-1.x)
+ * @Author: Maciej Zgadzaj http://zgadzaj.com (versions x.x-2.x)
+ */
+
+/**
+ * Implements hook_help().
+ */
+function emaillog_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#emaillog':
+      return '<p>' . t('Sends logs and alerts to email addresses, with different severity going to different emails.') . '</p>';
+    case 'admin/reports/dblog':
+      return '<p>' . t('Sends logs and alerts to email addresses, with different severity going to different emails.') . '</p>';
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function emaillog_menu() {
+  $items['admin/config/system/emaillog'] = array(
+    'title'             => 'Email logging and alerts',
+    'description'       => 'Settings for logging and alerts to email addresses.',
+    'page callback'     => 'drupal_get_form',
+    'page arguments'    => array('emaillog_admin_settings'),
+    'access arguments'  => array('administer site configuration'),
+    'type'              => MENU_NORMAL_ITEM,
+    'file'              => 'emaillog.admin.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_theme().
+ */
+function emaillog_theme() {
+  return array(
+    'emaillog' => array(
+      'render element' => 'elements',
+      'template' => 'emaillog',
+      'variables' => array('log' => NULL),
+    ),
+    'emaillog_admin_settings' => array(
+      'render element' => 'form',
+      'file' => 'emaillog.admin.inc',
+    ),
+  );
+}
+
+/**
+ * Implements hook_watchdog().
+ */
+function emaillog_watchdog($log) {
+  global $language;
+
+  // Send email only if there is an email address.
+  // Otherwise the message is ignored by this module.
+  if (!$to = variable_get('emaillog_' . $log['severity'])) {
+    return;
+  }
+
+  // Assume that current message is not a repetition.
+  $message_count = 1;
+
+  // Check if main email repetition restricting options are set.
+  // It's enough to check only emaillog_max_similar_emails variable,
+  // as setting it requires emaillog_max_similarity_level to be set as well.
+  // Saving few unnecessary database queries this way if it's not set.
+  $max_similar_emails = variable_get('emaillog_max_similar_emails');
+  if ($max_similar_emails) {
+    $max_similarity_level = variable_get('emaillog_max_similarity_level');
+
+    // Get previously sent message data and compare its content with current one.
+    $last_message = variable_get('emaillog_last_message');
+    $max_length = isset($last_message['message']) ? max(strlen($log['message']), strlen($last_message['message'])) : strlen($log['message']);
+    $similarity = 0;
+    if ($max_length > 0) {
+      similar_text($log['type'] . $log['message'], $last_message['message'], $similarity);
+      $similarity /= 100;
+    }
+
+    // If similarity level is higher than allowed in module configuration,
+    // and if maximum number of similar messages to sent was reached,
+    // stop execution and return - no email should be sent in such case.
+    if ($similarity > $max_similarity_level) {
+      if ($last_message['count'] >= $max_similar_emails) {
+        // Also make sure that those similar emails are consecutive,
+        // ie. were sent during a specific period of time (if defined).
+        $max_consecutive_timespan = variable_get('emaillog_max_consecutive_timespan');
+        if (!$max_consecutive_timespan || $last_message['time'] >= time() - $max_consecutive_timespan * 60) {
+          // No email should be sent - stop function execution.
+          return;
+        }
+        // Reset last message count if max consecutive time has already passed.
+        $last_message['count'] = 0;
+      }
+      // Email should and will be sent, so increase counter for this message.
+      $message_count = ++$last_message['count'];
+    }
+  }
+
+  // Add additional debug info (PHP predefined variables, debug backtrace etc.)
+  $log['debug_info'] = array();
+  $debug_info_settings = variable_get('emaillog_debug_info');
+  foreach (_emaillog_get_debug_info_callbacks() as $debug_info_key => $debug_info_callback) {
+    if (isset($debug_info_settings[$log['severity']][$debug_info_key]) && $debug_info_settings[$log['severity']][$debug_info_key]) {
+      eval('$log[\'debug_info\'][\'' . $debug_info_callback . '\'] = ' . $debug_info_callback . ';');
+    }
+  }
+  drupal_alter('emaillog_debug_info', $log['debug_info']);
+
+  // Make sure that $log['variables'] is always an array to avoid
+  // errors like in issue http://drupal.org/node/1325938
+  if (!is_array($log['variables'])) {
+    $log['variables'] = array();
+  }
+  // Send email alert.
+  drupal_mail('emaillog', 'alert', $to, $language->language, $log);
+
+  // Update email repetition restricting variables if needed.
+  if ($max_similar_emails) {
+    $last_message = array(
+      'message' => $log['type'] . $log['message'],
+      'time'    => time(),
+      'count'   => $message_count,
+    );
+    variable_set('emaillog_last_message', $last_message);
+  }
+}
+
+/**
+ * Implements hook_mail().
+ */
+function emaillog_mail($key, &$message, $params) {
+  if ($key == 'alert') {
+    $severity_levels = watchdog_severity_levels();
+    $vars = array(
+      '@site_name'      => variable_get('site_name', 'Drupal'),
+      '@severity_desc'  => drupal_ucfirst($severity_levels[$params['severity']]),
+      '@dblog_message'  => truncate_utf8(strip_tags(t($params['message'], $params['variables'])), 60, TRUE, TRUE),
+    );
+    // Legacy email subject.
+    if (variable_get('emaillog_legacy_subject')) {
+      $message['subject'] = t('[@site_name] @severity_desc: Alert from your web site', $vars);
+    }
+    // New version of email subject, with beginning of Watchdog message.
+    else {
+      $message['subject'] = t('[@site_name] @severity_desc: @dblog_message', $vars);
+    }
+    $message['body'][] = theme('emaillog', array('log' => $params));
+  }
+}
+
+/**
+ * Process variables for emaillog.tpl.php.
+ */
+function template_preprocess_emaillog(&$variables) {
+  global $base_url;
+  $variables['base_url'] = $base_url;
+
+  $severity_list                     = watchdog_severity_levels();
+  $variables['log']['severity_desc'] = drupal_ucfirst($severity_list[$variables['log']['severity']]);
+  $variables['log']['datetime']      = date('Y-m-d H:i:s', $variables['log']['timestamp']);
+  $variables['log']['uid']           = $variables['log']['user']->uid;
+  $variables['log']['name']          = format_username($variables['log']['user']);
+  $variables['log']['link']          = strip_tags($variables['log']['link']);
+  $variables['log']['message']       = strip_tags(t($variables['log']['message'], $variables['log']['variables']));
+
+  $severity = _emaillog_system_string($severity_list[$variables['log']['severity']]);
+  $variables['theme_hook_suggestions'][] = 'emaillog__' . $severity;
+  $variables['theme_hook_suggestions'][] = 'emaillog__' . _emaillog_system_string($variables['log']['type']);
+  $variables['theme_hook_suggestions'][] = 'emaillog__' . $severity . '__' . _emaillog_system_string($variables['log']['type']);
+}
+
+/**
+ * Formats string as safe system string.
+ */
+function _emaillog_system_string($string, $replacement = '_') {
+  return preg_replace('/[^a-z0-9]+/', $replacement, drupal_strtolower($string));
+}
+
+/**
+ * Returns array of available additional debug information for email alerts.
+ */
+function _emaillog_get_debug_info_callbacks() {
+  return array(
+    'server'    => '$_SERVER',
+    'env'       => '$_ENV',
+    'request'   => '$_REQUEST',
+    'cookie'    => '$_COOKIE',
+    'get'       => '$_GET',
+    'post'      => '$_POST',
+    'session'   => '$_SESSION',
+    'backtrace' => 'debug_backtrace()',
+  );
+}
+
+/**
+ * Replaces backtrace argument values with their types.
+ */
+function emaillog_emaillog_debug_info_alter(&$debug_info) {
+  if (
+    isset($debug_info['debug_backtrace()'])
+    && is_array($debug_info['debug_backtrace()'])
+    && variable_get('emaillog_backtrace_replace_args', TRUE)
+  ) {
+    foreach ($debug_info['debug_backtrace()'] as $trace_key => $trace) {
+      $args = array();
+      if (isset($trace['args']) && is_array($trace['args'])) {
+        foreach ($trace['args'] as $key => $value) {
+          $args[$key] = sprintf('%s(%s)', gettype($value), _emaillog_get_variable_size($value));
+        }
+        $debug_info['debug_backtrace()'][$trace_key]['args'] = implode(', ', $args);
+      }
+    }
+  }
+}
+
+/**
+ * Returns size of a variable.
+ */
+function _emaillog_get_variable_size($variable) {
+  switch (gettype($variable)) {
+    case 'array':
+    case 'object':
+      return count((array)$variable);
+    case 'integer':
+    case 'int':
+    case 'boolean':
+    case 'bool':
+    case 'float':
+    case 'double':
+    case 'real':
+    case 'string':
+      return strlen((string)$variable);
+    default:
+      return '?';
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.tpl.php b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.tpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..326d46ccbd45ef7c17b327cfb08e23bf3a147881
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/emaillog/emaillog.tpl.php
@@ -0,0 +1,18 @@
+Site:           <?php print $base_url; ?> 
+Severity:       <?php print $log['severity_desc']; ?> (<?php print $log['severity']; ?>)
+Timestamp:      <?php print $log['datetime']; ?> 
+Type:           <?php print $log['type']; ?> 
+IP Address:     <?php print $log['ip']; ?> 
+Request URI:    <?php print $log['request_uri']; ?> 
+Referrer URI:   <?php print $log['referer']; ?> 
+User:           <?php print $log['name']; ?> (<?php print $log['uid']; ?>)
+Link:           <?php print $log['link']; ?> 
+Message:
+
+<?php print $log['message']; ?> 
+
+
+<?php foreach ($log['debug_info'] as $name => $value): ?>
+<?php print $name; ?> => <?php print_r($value); ?> 
+
+<?php endforeach; ?>
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.admin.inc b/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.admin.inc
new file mode 100644
index 0000000000000000000000000000000000000000..f2f92263f0163d1f8b2dd9d6fb2f520d7d0812a8
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.admin.inc
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Admin callbacks for the Web Server Logging and Alerts module.
+ */
+
+/**
+ * Returns admin settings form.
+ */
+function errorlog_admin_settings() {
+  $form['errorlog'] = array(
+    '#type'         => 'fieldset',
+    '#title'        => t('Error logging for each severity level.'),
+    '#description'  => t('Check each severity level you want to get logged to the error log.'),
+  );
+  foreach (watchdog_severity_levels() as $severity => $description) {
+    $key = 'errorlog_' . $severity;
+    $form['errorlog'][$key] = array(
+      '#type'          => 'checkbox',
+      '#title'         => t('Severity: @description', array('@description' => drupal_ucfirst($description))),
+      '#default_value' => variable_get($key, FALSE),
+    );
+  }
+  return system_settings_form($form);
+}
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.info b/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.info
new file mode 100644
index 0000000000000000000000000000000000000000..46b9c92c0847cf4aad5989446ad643c5b2682482
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.info
@@ -0,0 +1,12 @@
+name = Web server logging and alerts
+description = Sends logs and alerts to the web server's error log.
+package = Logging and alerts
+core = 7.x
+configure = admin/config/system/errorlog
+
+; Information added by drupal.org packaging script on 2013-06-19
+version = "7.x-2.2"
+core = "7.x"
+project = "logging_alerts"
+datestamp = "1371630053"
+
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.install b/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.install
new file mode 100644
index 0000000000000000000000000000000000000000..043b81a454fafbb1e3d59af264c5b36c756dfcf1
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.install
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the errorlog module.
+ */
+
+/**
+ * Implements hook_uninstall().
+ */
+function errorlog_uninstall() {
+  $severities = array_keys(watchdog_severity_levels());
+  foreach ($severities as $severity) {
+    variable_del('errorlog_' . $severity);
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.module b/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.module
new file mode 100644
index 0000000000000000000000000000000000000000..4965c9ebd73139d3a44473d91bfe796d69bd4b39
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/errorlog/errorlog.module
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * @file
+ * Drupal Module: Web Server Logging and Alerts
+ *
+ * Sends logs and alerts to the web server's log.
+ *
+ * @Author: Khalid Baheyeldin http://2bits.com (version 6.x)
+ * @Author: Maciej Zgadzaj http://zgadzaj.com (version 7.x)
+ */
+
+/**
+ * Implements hook_help().
+ */
+function errorlog_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#errorlog':
+    case 'admin/reports/errorlog':
+      return '<p>' . t('Sends logs and alerts to the web server\'s error log.') . '</p>';
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function errorlog_menu() {
+  $items['admin/config/system/errorlog'] = array(
+    'title'             => 'Web server logging and alerts',
+    'description'       => "Settings for logging and alerts to server's error log.",
+    'page callback'     => 'drupal_get_form',
+    'page arguments'    => array('errorlog_admin_settings'),
+    'access arguments'  => array('administer site configuration'),
+    'type'              => MENU_NORMAL_ITEM,
+    'file'              => 'errorlog.admin.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_watchdog().
+ */
+function errorlog_watchdog($log) {
+  if (variable_get('errorlog_' . $log['severity'], FALSE)) {
+    // Make sure that $log['variables'] is always an array to avoid
+    // errors like in issue http://drupal.org/node/1325938
+    if (!is_array($log['variables'])) {
+      $log['variables'] = array();
+    }
+
+    // Send themed alert to the web server's log.
+    if (drupal_get_bootstrap_phase() >= DRUPAL_BOOTSTRAP_FULL) {
+      $message = theme('errorlog_format', $log);
+    }
+    // On earlier bootstrap stages not all theme functions are available.
+    else {
+      $message = theme_errorlog_format($log);
+    }
+    error_log($message);
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function errorlog_theme() {
+  return array(
+    'errorlog_format' => array(
+      'arguments' => array('log_msg' => NULL),
+    ),
+  );
+}
+
+/**
+ * Themes error log message.
+ */
+function theme_errorlog_format($log_msg = array()) {
+  global $base_root;
+  $severity_list = watchdog_severity_levels();
+  $message  = variable_get('site_name', 'Drupal');
+  $message .= '|' . $base_root;
+  $message .= '|severity=' . $severity_list[$log_msg['severity']];
+  $message .= '|type=' . $log_msg['type'];
+  $message .= '|ip=' . $log_msg['ip'];
+  $message .= '|uri=' . $log_msg['request_uri'];
+  $message .= '|referer=' . $log_msg['referer'];
+  $message .= '|uid=' . $log_msg['user']->uid;
+  $message .= '|link=' . strip_tags($log_msg['link']);
+  $message .= '|message=' . strip_tags(t($log_msg['message'], $log_msg['variables']));
+  return $message;
+}
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/README.txt b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7194636fc9bbd7c588bfb2ff3648447f40063e05
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/README.txt
@@ -0,0 +1,21 @@
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * Introduction
+ * Installation
+
+INTRODUCTION
+------------
+
+Current Maintainer: Bastlynn <bastlynn@gmail.com>
+
+The Watchdog Rules module allows you to trigger Rules in response to watchdog events. 
+
+INSTALLATION
+------------
+
+1. Copy the logging_alerts directory to your sites/SITENAME/modules directory. 
+
+2. Enable either watchdog_triggers or watchdog_rules, whichever is suitable for your
+site according to your use of either the Actions module or the Rules module.
\ No newline at end of file
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.info b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.info
new file mode 100644
index 0000000000000000000000000000000000000000..9159053f372f25c799be523e624fbecb356ba725
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.info
@@ -0,0 +1,12 @@
+name = Watchdog Rules
+description = Provides Rules integration to watchdog events.
+package = Logging and alerts
+core = 7.x
+dependencies[] = rules
+
+; Information added by drupal.org packaging script on 2013-06-19
+version = "7.x-2.2"
+core = "7.x"
+project = "logging_alerts"
+datestamp = "1371630053"
+
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.module b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.module
new file mode 100644
index 0000000000000000000000000000000000000000..46dd7f8259599a88cf52b0285621a726d260a9b6
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.module
@@ -0,0 +1,99 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Provides a Rules integration for wachdog events.
+ */
+
+/**
+ * Implements hook_watchdog().
+ */
+function watchdog_rules_watchdog($watchdog_message) {
+  // Make sure we do not provide NULL values, as Rules module doesn't like them.
+  // @see http://drupal.org/node/1497534
+  foreach ($watchdog_message as $key => $value) {
+    if (is_null($value)) {
+      $watchdog_message[$key] = '';
+    }
+  }
+  rules_invoke_event('watchdog_rules', 
+    $watchdog_message['user'],
+    $watchdog_message['type'],
+    $watchdog_message['request_uri'],
+    $watchdog_message['referer'],
+    $watchdog_message['ip'],
+    $watchdog_message['timestamp'],
+    $watchdog_message['severity'],
+    $watchdog_message['link'],
+    $watchdog_message['message']
+  );
+}
+
+/**
+ * Implements hook_token_info().
+ */
+function watchdog_rules_token_info() {
+  $type = array(
+    'name' => t('Watchdog tokens'),
+    'description' => t('Watchdog tokens'),
+    'needs-data' => 'watchdog',
+  );
+  $watchdog['type'] = array(
+    'name' => t("Watchdog type"), 
+    'description' => t("The type of the watchdog event."),
+  );
+  $watchdog['request_uri'] = array(
+    'name' => t("Watchdog request URI"), 
+    'description' => t('The request URI for the page the event happened in.'),
+  );
+  $watchdog['referer'] = array(
+    'name' => t("Watchdog referer"), 
+    'description' => t('The page that referred the user to the page where the event occurred.'),
+  );
+  $watchdog['ip'] = array(
+    'name' => t("Watchdog ip"), 
+    'description' => t('The IP address where the request for the page came from.'),
+  );
+  $watchdog['timestamp'] = array(
+    'name' => t("Watchdog timestamp"), 
+    'description' => t('The UNIX timetamp of the date/time the event occurred.'),
+  );
+  $watchdog['severity'] = array(
+    'name' => t("Watchdog severity"), 
+    'description' => t('The degree of severity of the event.'),
+  );
+  $watchdog['link'] = array(
+    'name' => t("Watchdog link"), 
+    'description' => t('An informational link provided by the module calling watchdog.'),
+  );
+  $watchdog['message'] = array(
+    'name' => t("Watchdog message"), 
+    'description' => t('The text of the message to be logged.'),
+  );
+
+  return array(
+    'types' => array('watchdog' => $type), 
+    'tokens' => array('watchdog' => $watchdog),
+  );
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function watchdog_rules_tokens($type, $object = NULL, $options = array()) {
+  $tokens = array();
+  if ($type == 'watchdog' || $type == 'all') {
+    $tokens = array(
+      'type' => $object['type'],
+      'request_uri' => $object['request_uri'],
+      'referer' => $object['referer'],
+      'ip' => $object['ip'],
+      'timestamp' => $object['timestamp'],
+      'severity' => $object['severity'],
+      'link' => $object['link'],
+      'message' => $object['message'],
+    );
+    return $tokens;
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.rules.inc b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..a13e7ad6d78e3754031debfefdb6befdf1cdabc6
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_rules/watchdog_rules.rules.inc
@@ -0,0 +1,63 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Provides a Rules integration for watchdog events.
+ */
+
+/**
+ * Implements hook_rules_event_info().
+ *
+ * Register rules events used by this module.
+ */
+function watchdog_rules_rules_event_info() {
+  $defaults = array(
+    'group' => t('Watchdog'),
+    'module' => 'watchdog_rules',
+  );
+  return array(
+    'watchdog_rules' => $defaults + array(
+      'label' => t('Watchdog logs an event'),
+      'variables' => array(
+        'watchdog_user' => array(
+          'type' => 'user', 
+          'label' => t('The logged in user when watchdog was called'),
+        ),
+        'watchdog_type' => array(
+          'type' => 'text', 
+          'label' => t('The watchdog log type'),
+        ),
+        'watchdog_request_uri' => array(
+          'type' => 'text', 
+          'label' => t('The watchdog log request URI'),
+        ),
+        'watchdog_referer' => array(
+          'type' => 'text', 
+          'label' => t('The watchdog log referer'),
+        ),
+        'watchdog_ip' => array(
+          'type' => 'text', 
+          'label' => t('The watchdog log ip'),
+        ),
+        'watchdog_timestamp' => array(
+          'type' => 'text', 
+          'label' => t('The watchdog log timestamp'),
+        ),
+        'watchdog_severity' => array(
+          'type' => 'text', 
+          'label' => t('The watchdog log severity'),
+        ),
+        'watchdog_link' => array(
+          'type' => 'text', 
+          'label' => t('The watchdog log link'),
+        ),
+        'watchdog_message' => array(
+          'type' => 'text', 
+          'label' => t('The watchdog log message'),
+        ),
+      ),
+    ),
+  );
+}
+
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/README.txt b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..37f0f5d0e3367d7ed2a0a04cb1f4b1d6324127e6
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/README.txt
@@ -0,0 +1,21 @@
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * Introduction
+ * Installation
+
+INTRODUCTION
+------------
+
+Current Maintainer: Bastlynn <bastlynn@gmail.com>
+
+The Wactchdog Triggers module suite allows you to trigger Actions in response to watchdog events.
+
+INSTALLATION
+------------
+
+1. Copy the logging_alerts directory to your sites/SITENAME/modules directory. 
+
+2. Enable either watchdog_triggers or watchdog_rules, whichever is suitable for your
+site according to your use of either the Actions module or the Rules module.
\ No newline at end of file
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/watchdog_triggers.info b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/watchdog_triggers.info
new file mode 100644
index 0000000000000000000000000000000000000000..e9c946b13135f88941afcdaa8f9f626f960b5948
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/watchdog_triggers.info
@@ -0,0 +1,13 @@
+name = Watchdog Triggers
+description = Provides Trigger and Actions integration to watchdog events.
+package = Logging and alerts
+core = 7.x
+configure = admin/config/system/watchdog_triggers
+dependencies[] = trigger
+
+; Information added by drupal.org packaging script on 2013-06-19
+version = "7.x-2.2"
+core = "7.x"
+project = "logging_alerts"
+datestamp = "1371630053"
+
diff --git a/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/watchdog_triggers.module b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/watchdog_triggers.module
new file mode 100644
index 0000000000000000000000000000000000000000..ac59a6a1866c6f4e7d79d5db539e68c2d082304a
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/logging_alerts/watchdog_triggers/watchdog_triggers.module
@@ -0,0 +1,283 @@
+<?php
+
+/**
+ * @file
+ * Provides a Trigger and Action integration for watchdog events.
+ */
+
+/**
+ * Implements hook_trigger_info().
+ *
+ * Register triggered hooks used by this module.
+ */
+function watchdog_triggers_trigger_info() {
+  return array(
+    'watchdog_triggers' => array(
+      'watchdog_logged' => array(
+        'label' => t('Watchdog logs an event'),
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_watchdog_triggers().
+ */
+function watchdog_triggers_watchdog_triggers($op, $user) {
+  if (!in_array($op, array('watchdog_logged'))) {
+    return;
+  }
+  $aids = trigger_get_assigned_actions('watchdog_logged', $op);
+  $context = array(
+    'hook' => 'watchdog_logged',
+    'op' => $op,
+    'user' => $user,
+  );
+  actions_do(array_keys($aids), $user, $context);
+}
+
+/**
+ * Implements hook_action_info_alter().
+ */
+function watchdog_triggers_action_info_alter(&$info) {
+  foreach ($info as $type => $data) {
+    if (stripos($type, "user_") === 0 || stripos($type, "system_") === 0) {
+      if (isset($info[$type]['hooks']['application'])) {
+        $info[$type]['hooks']['watchdog_logged'] = array_merge($info[$type]['hooks']['watchdog_logged'], array('watchdog_logged'));
+      }
+      else {
+        $info[$type]['hooks']['watchdog_logged'] = array('watchdog_logged');
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_watchdog().
+ */
+function watchdog_triggers_watchdog($watchdog_message) {
+  global $user;
+
+  $types = trim(variable_get('watchdog_triggers_type', ''));
+  $users = trim(variable_get('watchdog_triggers_user', ''));
+  $request_uris = trim(variable_get('watchdog_triggers_request_uri', ''));
+  $referers = trim(variable_get('watchdog_triggers_referer', ''));
+  $ips = trim(variable_get('watchdog_triggers_ip', ''));
+  $severities = variable_get('watchdog_triggers_severity', array(WATCHDOG_CRITICAL, WATCHDOG_ALERT, WATCHDOG_EMERGENCY));
+  if (array_key_exists('-100', $severities)) {
+    unset($severities['-100']);
+  }
+  if ($found_key = array_search('-100', $severities)) {
+    unset($severities[$found_key]);
+  }
+  $messages = trim(variable_get('watchdog_triggers_message', ''));
+
+  // Check each value.
+  // These are cumulative filters, so if one fails - all fail.
+  $triggered = TRUE;
+
+  if (!empty($types)) {
+    $types = _watchdog_triggers_clean_array(explode(',', $types));
+    if (!in_array($watchdog_message['type'], $types)) {
+      $triggered = FALSE;
+    }
+  }
+
+  if (!empty($users)) {
+    $users = _watchdog_triggers_clean_array(explode(',', $users));
+    if (!in_array($watchdog_message['user']->name, $users)) {
+      $triggered = FALSE;
+    }
+  }
+  if (!empty($request_uris)) {
+    $request_uris = _watchdog_triggers_clean_array(explode(',', $request_uris));
+    $match_results = _watchdog_triggers_preg_match_patterns($request_uris, $watchdog_message['request_uri']);
+    if ($match_results == FALSE) {
+      $triggered = FALSE;
+    }
+  }
+  if (!empty($referers)) {
+    $referers = _watchdog_triggers_clean_array(explode(',', $referers));
+    $match_results = _watchdog_triggers_preg_match_patterns($referers, $watchdog_message['referer']);
+    if ($match_results == FALSE) {
+      $triggered = FALSE;
+    }
+  }
+  if (!empty($ips)) {
+    $ips = _watchdog_triggers_clean_array(explode(',', $ips));
+    $match_results = _watchdog_triggers_preg_match_patterns($ips, $watchdog_message['ip']);
+    if ($match_results == FALSE) {
+      $triggered = FALSE;
+    }
+  }
+  if (count($severities) > 0) {
+    $severities = _watchdog_triggers_clean_array($severities);
+    if (!in_array($watchdog_message['severity'], $severities)) {
+      $triggered = FALSE;
+    }
+  }
+  if (!empty($messages)) {
+    $messages = _watchdog_triggers_clean_array(explode(',', $messages));
+    $match_results = _watchdog_triggers_preg_match_patterns($messages, $watchdog_message['message']);
+    if ($match_results == FALSE) {
+      $triggered = FALSE;
+    }
+  }
+
+  if ($triggered === TRUE) {
+    module_invoke_all('watchdog_triggers', 'watchdog_logged', $user);
+  }
+}
+
+/**
+ * Implements hook_menu_alter().
+ *
+ * Work around loss of menu local task inheritance.
+ */
+function onthisdate_menu_alter(&$callbacks) {
+  if (module_exists('trigger') & isset($callbacks['admin/structure/trigger/watchdog_triggers'])) {
+    $callbacks['admin/structure/trigger/watchdog_triggers']['access callback'] = 'trigger_access_check';
+  }
+}
+
+/**
+ * Implements hook_permission().
+ */
+function watchdog_triggers_permission() {
+  return array(
+    'administer watchdog triggers' => array(
+      'title' => t('administer watchdog triggers'),
+      'description' => t('Administer triggers for watchdog events.'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function watchdog_triggers_menu() {
+  $items = array();
+  $items['admin/config/system/watchdog_triggers'] = array(
+    'title' => 'Watchdog triggers',
+    'description' => 'Configure watchdog triggers settings',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('watchdog_triggers_admin_settings'),
+    'access arguments' => array('administer watchdog triggers'),
+  );
+
+  return $items;
+}
+
+/**
+ * Implements form page callback for module admin settings page
+ */
+function watchdog_triggers_admin_settings($form, &$form_state) {
+  $form = array();
+  // Check against the following features of watchdog.
+  $form['watchdog_triggers_info'] = array(
+    '#type' => 'item',
+    '#description' => t('The settings set here apply to all watchdog actions. These are cumulative filters. ' .
+    'The more filters you set the narrower your selection of watchdog events will be. If you need more fine ' .
+    'control or multiple configurations then you need to upgrade to the Rules integration for Watchdog.'),
+  );
+
+  // type (module name)
+  $form['watchdog_triggers_type'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Message type'),
+    '#default_value' => variable_get('watchdog_triggers_type', ''),
+    '#description' => t('Enter each type to trigger against, usually the module of origin, separated by a comma.'),
+  );
+
+  // user
+  $form['watchdog_triggers_user'] = array(
+    '#type' => 'textarea',
+    '#title' => t('User generating message'),
+    '#default_value' => variable_get('watchdog_triggers_user', ''),
+    '#description' => t('Enter each user name to trigger against, separated by a comma.'),
+  );
+
+  // request uri
+  $form['watchdog_triggers_request_uri'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Message request uri'),
+    '#default_value' => variable_get('watchdog_triggers_request_uri', ''),
+    '#description' => t('Enter each regular expression to match the requesting uri against, separated by a comma.'),
+  );
+
+  // referer
+  $form['watchdog_triggers_referer'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Message referer'),
+    '#default_value' => variable_get('watchdog_triggers_referer', ''),
+    '#description' => t('Enter each regular expression to match the refering page against, separated by a comma.'),
+  );
+
+  // ip
+  $form['watchdog_triggers_ip'] = array(
+    '#type' => 'textarea',
+    '#title' => t('IP generating message'),
+    '#default_value' => variable_get('watchdog_triggers_ip', ''),
+    '#description' => t('Enter each regular expression to match the IP against, separated by a comma.'),
+  );
+
+  // severity
+  $array_options = array(
+    -100 => 'none',
+    WATCHDOG_EMERGENCY => 'Emergency: system is unusable',
+    WATCHDOG_ALERT => 'Alert: action must be taken immediately',
+    WATCHDOG_CRITICAL => 'Critical: critical conditions',
+    WATCHDOG_ERROR => 'Error: error conditions',
+    WATCHDOG_WARNING => 'Warning: warning conditions',
+    WATCHDOG_NOTICE => 'Notice: normal but significant condition',
+    WATCHDOG_INFO => 'Informational: informational messages',
+    WATCHDOG_DEBUG => 'Debug: debug-level messages',
+  );
+
+  $form['watchdog_triggers_severity'] = array(
+    '#type' => 'select',
+    '#title' => t('Message severity'),
+    '#default_value' => variable_get('watchdog_triggers_severity', array(WATCHDOG_CRITICAL, WATCHDOG_ALERT, WATCHDOG_EMERGENCY)),
+    '#options' => $array_options,
+    '#description' => t('Select each severity to trigger against.'),
+    '#multiple' => TRUE,
+  );
+
+  // message
+  $form['watchdog_triggers_message'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Message pattern'),
+    '#default_value' => variable_get('watchdog_triggers_message', ''),
+    '#description' => t('Enter each regular expression to match the message against, separated by a comma.'),
+  );
+
+  return system_settings_form($form);
+}
+
+/**
+ * Helper function, prepares user data in an array for matching.
+ */
+function _watchdog_triggers_clean_array($this_array) {
+  foreach ($this_array as $key => $value) {
+    $this_array[$key] = trim($value);
+  }
+  return $this_array;
+}
+
+/**
+ * Helper function, preg_matches contents of an array.
+ */
+function _watchdog_triggers_preg_match_patterns($array, $target) {
+  $match_results = FALSE;
+  foreach ($array as $pattern) {
+    $pattern = trim($pattern);
+    $matches = array();
+    if (!empty($pattern)) {
+      $number_matches = preg_match_all($pattern, $target, $matches);
+      if ($number_matches > 0 && $number_matches !== FALSE) {
+        $match_results = TRUE;
+      }
+    }
+  }
+  return $match_results;
+}
diff --git a/profiles/wcm_base/modules/contrib/media/includes/media.fields.inc b/profiles/wcm_base/modules/contrib/media/includes/media.fields.inc
index 6240b5c9012fa44503a62667acffaaa49776734c..ce750afe132dcb3a375b89f349769b3f7d4b82d5 100644
--- a/profiles/wcm_base/modules/contrib/media/includes/media.fields.inc
+++ b/profiles/wcm_base/modules/contrib/media/includes/media.fields.inc
@@ -127,9 +127,11 @@ function media_field_widget_form(&$form, &$form_state, $field, $instance, $langc
   // on the elements for further usage in media_element_process().
   if (module_invoke('entity_translation', 'enabled', $element['#entity_type'], $element['#entity'])) {
     $translation_handler = entity_translation_get_handler($element['#entity_type'], $element['#entity']);
-    $element['#media_parent_entity_form_langcode'] = $translation_handler->getActiveLanguage();
-    if ($source_langcode = $translation_handler->getSourceLanguage()) {
-      $element['#media_parent_entity_source_langcode'] = $source_langcode;
+    if ($translation_handler) {
+      $element['#media_parent_entity_form_langcode'] = $translation_handler->getActiveLanguage();
+      if ($source_langcode = $translation_handler->getSourceLanguage()) {
+        $element['#media_parent_entity_source_langcode'] = $source_langcode;
+      }
     }
   }
   elseif (module_exists('translation') && $element['#entity_type'] == 'node' && translation_supported_type($element['#entity']->type)) {
diff --git a/profiles/wcm_base/modules/contrib/media/media.info b/profiles/wcm_base/modules/contrib/media/media.info
index ba436958967ef6149670ed689fbef07e75201f19..211f4b7dfe56e7b351336d8b0948a154f1d736b3 100644
--- a/profiles/wcm_base/modules/contrib/media/media.info
+++ b/profiles/wcm_base/modules/contrib/media/media.info
@@ -24,8 +24,8 @@ configure = admin/config/media/browser
 ; We have to add a fake version so Git checkouts do not fail Media dependencies
 version = 7.x-2.x-dev
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media/media.module b/profiles/wcm_base/modules/contrib/media/media.module
index cccf8797c9042ef907d27c293bf39e36fd00770b..1315dff9e41862e8ec022aa4bbae642cb04d4401 100644
--- a/profiles/wcm_base/modules/contrib/media/media.module
+++ b/profiles/wcm_base/modules/contrib/media/media.module
@@ -668,12 +668,20 @@ function media_parse_to_file($url, $params = array()) {
 }
 
 /**
- * Utility function to recursively run check_plain on an array.
+ * Custom implementation of array_walk_recursive() that works around a crash
+ * some users have been experiencing with that function in PHP 7.
  *
- * @todo There is probably something in core I am not aware of that does this.
+ * @see https://www.drupal.org/project/media/issues/2998097
  */
-function media_recursive_check_plain(&$value, $key) {
-  $value = check_plain($value);
+function media_array_walk_recursive(&$array) {
+  foreach ($array as $key => $value) {
+    if (is_array($array[$key])) {
+      media_array_walk_recursive($array[$key]);
+    }
+    else {
+      $array[$key] = check_plain($array[$key]);
+    }
+  }
 }
 
 /**
@@ -1193,6 +1201,9 @@ function media_file_displays_alter(&$displays, $file, $view_mode) {
   if (!empty($title)) {
     $file->title = decode_entities(token_replace($title, array('file' => $file), $replace_options));
   }
+
+  // Reduce memory footprint and response size in media browser.
+  $file->file_contents = '';
 }
 
 /**
@@ -1260,7 +1271,7 @@ function media_set_browser_params() {
       }
     }
 
-    array_walk_recursive($params, 'media_recursive_check_plain');
+    media_array_walk_recursive($params);
 
     // Provide some default parameters.
     $params += array(
diff --git a/profiles/wcm_base/modules/contrib/media/media.views.inc b/profiles/wcm_base/modules/contrib/media/media.views.inc
index 5fd2c02f2001fde097c56810f3ef12ce535d680d..bf22d49192aa483867c0d9e0d411584ac275a520 100644
--- a/profiles/wcm_base/modules/contrib/media/media.views.inc
+++ b/profiles/wcm_base/modules/contrib/media/media.views.inc
@@ -127,7 +127,7 @@ function template_preprocess_media_views_view_media_browser(&$vars) {
   $vars['wrapper_suffix'] = '</div>';
   $vars['list_type_prefix'] = '<' . $handler->options['type'] . ' id="media-browser-library-list" class="' . implode(' ', $class) . '">';
   $vars['list_type_suffix'] = '</' . $handler->options['type'] . '>';
-  $vars['aria_role'] = $params['multiselect'] ? 'checkbox' : 'radio';
+  $vars['aria_role'] = (isset($params['multiselect']) && $params['multiselect']) ? 'checkbox' : 'radio';
 
   // Run theming variables through a standard Views preprocess function.
   template_preprocess_views_view_unformatted($vars);
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_bulk_upload/includes/media_bulk_upload.pages.inc b/profiles/wcm_base/modules/contrib/media/modules/media_bulk_upload/includes/media_bulk_upload.pages.inc
index 41287ebb011f287da7c311aee6e746839be36262..f595dc66b15cc65be9fcb7677a5cb02b6d63e549 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_bulk_upload/includes/media_bulk_upload.pages.inc
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_bulk_upload/includes/media_bulk_upload.pages.inc
@@ -38,6 +38,7 @@ function media_bulk_upload_file_page_edit_multiple($files) {
 
     // Remove the 'replace file' functionality.
     $form['multiform'][$key]['replace_upload']['#access'] = FALSE;
+    $form['multiform'][$key]['replace_keep_original_filename']['#access'] = FALSE;
 
     // Remove any actions.
     $form['multiform'][$key]['actions']['#access'] = FALSE;
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_bulk_upload/media_bulk_upload.info b/profiles/wcm_base/modules/contrib/media/modules/media_bulk_upload/media_bulk_upload.info
index 95ab5467f079b2e65ced49f01bc0603f08ed519d..fb18da26851bb46165138a796cb4e1fd004c865e 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_bulk_upload/media_bulk_upload.info
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_bulk_upload/media_bulk_upload.info
@@ -15,8 +15,8 @@ test_dependencies[] = plupload
 files[] = includes/MediaBrowserBulkUpload.inc
 files[] = tests/media_bulk_upload.test
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_internet/media_internet.info b/profiles/wcm_base/modules/contrib/media/modules/media_internet/media_internet.info
index a27a34c3c5cf6e1f5f33ea052dda87c8844a0240..d328c766ae4fd91775e6848ddff174c7347dd470 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_internet/media_internet.info
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_internet/media_internet.info
@@ -12,8 +12,8 @@ files[] = includes/MediaInternetNoHandlerException.inc
 files[] = includes/MediaInternetValidationException.inc
 files[] = tests/media_internet.test
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_internet/tests/media_internet_test.info b/profiles/wcm_base/modules/contrib/media/modules/media_internet/tests/media_internet_test.info
index b8e3a6bee2926b0020dc61237d1578405d1fc01e..364ac0f58c1e4a41862270e8ea4f3ca024cc4635 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_internet/tests/media_internet_test.info
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_internet/tests/media_internet_test.info
@@ -7,8 +7,8 @@ hidden = TRUE
 files[] = includes/MediaInternetTestStreamWrapper.inc
 files[] = includes/MediaInternetTestHandler.inc
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_migrate_file_types/media_migrate_file_types.info b/profiles/wcm_base/modules/contrib/media/modules/media_migrate_file_types/media_migrate_file_types.info
index 4d836facc1156a260cb9469ce49d494c35766046..0c91735ddf075206017567f7f5b246c76220a115 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_migrate_file_types/media_migrate_file_types.info
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_migrate_file_types/media_migrate_file_types.info
@@ -8,8 +8,8 @@ dependencies[] = media
 
 configure = admin/structure/file-types/upgrade
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.filter.inc b/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.filter.inc
index a307c2f2810d0cdd5f2ac081cf01c00ce2dbb880..79e3ba6659efbe919f6c7151d1cce4705a0c7349 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.filter.inc
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.filter.inc
@@ -5,7 +5,7 @@
  * Functions related to the WYSIWYG editor and the media input filter.
  */
 
-define('MEDIA_WYSIWYG_TOKEN_REGEX', '/\[\[\{.*?"type":"media".+?\}\]\]/s');
+define('MEDIA_WYSIWYG_TOKEN_REGEX', '/\[\[\{.*?"type":"media".*?\}\]\]/s');
 
 /**
  * Filter callback for media markup filter.
@@ -140,6 +140,16 @@ function media_wysiwyg_token_to_markup($match, $wysiwyg = FALSE, $langcode = NUL
   $match = str_replace("]]", "", $match);
   $tag = $match[0];
 
+  // Drupal modules with email support often include site name in the subject line
+  // wrapped in brackets. With a token, this is rendered as "[[site:name]]". Such a
+  // format will cause a conflict with media_wysiwyg, which is looking for the same.
+  if (module_exists('token_filter')) {
+    $token_filter = _token_filter_filter_tokens('[' . $tag . ']', '', '', $langcode, NULL, NULL);
+    if ($token_filter != '[' . $tag . ']') {
+      return '[[' . $tag . ']]';
+    }
+  }
+
   try {
     if (!is_string($tag)) {
       throw new Exception('Unable to find matching tag');
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.pages.inc b/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.pages.inc
index c126058a4327e93cea66264228d608ac7ac105a3..a3404de879ce1d1fcbaf12ec70c26a50bc00e0ef 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.pages.inc
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/includes/media_wysiwyg.pages.inc
@@ -180,6 +180,7 @@ function media_wysiwyg_format_form_view_mode(&$form, $form_state, $file) {
       'class' => 'button',
       'title' => t('Use for replace fox or edit file fields.'),
     ),
+    'query' => drupal_get_destination(),
   );
   if (!empty($_GET['render'])) {
     $link_options['query']['render'] = $_GET['render'];
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.info b/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.info
index 88a03774761c412d7413597b3d27f83a66308f06..ce687f67f43e4f587ef41327c528241244090b9e 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.info
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg/media_wysiwyg.info
@@ -16,8 +16,8 @@ files[] = tests/media_wysiwyg.paragraph_fix_filter.test
 
 configure = admin/config/media/browser
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg_view_mode/media_wysiwyg_view_mode.info b/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg_view_mode/media_wysiwyg_view_mode.info
index bbeff2411e9b260d7c8d2d880a564b732b57f800..5fc3f6f85b3b7b628063c635b1908081859c2746 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg_view_mode/media_wysiwyg_view_mode.info
+++ b/profiles/wcm_base/modules/contrib/media/modules/media_wysiwyg_view_mode/media_wysiwyg_view_mode.info
@@ -3,8 +3,8 @@ description = DEPRECATED, this folder is only here so that the module can be uni
 package = Media
 core = 7.x
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media/modules/mediafield/mediafield.info b/profiles/wcm_base/modules/contrib/media/modules/mediafield/mediafield.info
index 61c26869b87c71531771574669321c956e81f995..af92f57e0643f42df4231b789f84043c296b8e7f 100644
--- a/profiles/wcm_base/modules/contrib/media/modules/mediafield/mediafield.info
+++ b/profiles/wcm_base/modules/contrib/media/modules/mediafield/mediafield.info
@@ -4,8 +4,8 @@ package = Media
 core = 7.x
 dependencies[] = media
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media/tests/media_module_test.info b/profiles/wcm_base/modules/contrib/media/tests/media_module_test.info
index c7360b8e6fc8e137c542fa25af35a3460d2c1430..d3541be5dd8578f36338787989747047c908c798 100644
--- a/profiles/wcm_base/modules/contrib/media/tests/media_module_test.info
+++ b/profiles/wcm_base/modules/contrib/media/tests/media_module_test.info
@@ -6,8 +6,8 @@ hidden = TRUE
 
 files[] = includes/MediaModuleTest.inc
 
-; Information added by Drupal.org packaging script on 2018-04-25
-version = "7.x-2.19"
+; Information added by Drupal.org packaging script on 2018-10-24
+version = "7.x-2.21"
 core = "7.x"
 project = "media"
-datestamp = "1524677887"
+datestamp = "1540363086"
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/includes/MediaInternetYouTubeHandler.inc b/profiles/wcm_base/modules/contrib/media_youtube/includes/MediaInternetYouTubeHandler.inc
index a58097338f17b4919a1dc7df4ed37589d5d859f6..b63621579090b48cf925ebc3b800eff2c051fb11 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/includes/MediaInternetYouTubeHandler.inc
+++ b/profiles/wcm_base/modules/contrib/media_youtube/includes/MediaInternetYouTubeHandler.inc
@@ -25,12 +25,12 @@ class MediaInternetYouTubeHandler extends MediaInternetBaseHandler {
         return file_stream_wrapper_uri_normalize('youtube://l/' . $matches[1]);
       }
     }
-    // http://youtube.com/watch/*
-    // http://youtube.com/embed/*
-    // http://youtube.com/v/*
-    // http://youtube.com/?v=*
-    // http://youtu.be/*
-    // http://gdata.youtube.com/feeds/api/videos/*
+    // https://youtube.com/watch/*
+    // https://youtube.com/embed/*
+    // https://youtube.com/v/*
+    // https://youtube.com/?v=*
+    // https://youtu.be/*
+    // https://gdata.youtube.com/feeds/api/videos/*
     $patterns = array(
       '@youtube\.com/watch[#\?].*?v=([^"#\& ]+).*&list=([^"#\& ]+)@i',
       '@youtu\.be/([^"#\&\? ]+)\?list=([^"#\& ]+)@i',
@@ -88,7 +88,7 @@ class MediaInternetYouTubeHandler extends MediaInternetBaseHandler {
   public function getOEmbed() {
     $uri = $this->parse($this->embedCode);
     $external_url = file_create_url($uri);
-    $oembed_url = url('http://www.youtube.com/oembed', array('query' => array('url' => $external_url, 'format' => 'json')));
+    $oembed_url = url('https://www.youtube.com/oembed', array('query' => array('url' => $external_url, 'format' => 'json')));
     $response = drupal_http_request($oembed_url);
 
     if (!isset($response->error)) {
@@ -110,7 +110,7 @@ class MediaInternetYouTubeHandler extends MediaInternetBaseHandler {
   public function validId($id, $type = 'v') {
     $uri = file_stream_wrapper_uri_normalize('youtube://' . $type . '/' . check_plain($id));
     $external_url = file_create_url($uri);
-    $oembed_url = url('http://www.youtube.com/oembed', array('query' => array('url' => $external_url, 'format' => 'json')));
+    $oembed_url = url('https://www.youtube.com/oembed', array('query' => array('url' => $external_url, 'format' => 'json')));
     $response = drupal_http_request($oembed_url, array('method' => 'HEAD'));
 
     if ($response->code == 401) {
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/includes/MediaYouTubeStreamWrapper.inc b/profiles/wcm_base/modules/contrib/media_youtube/includes/MediaYouTubeStreamWrapper.inc
index 46c08b6a5788e68d1180802b391d1dfedf14fd9e..ea7b2d050da730f7500a6fabdd9e407f9ba74173 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/includes/MediaYouTubeStreamWrapper.inc
+++ b/profiles/wcm_base/modules/contrib/media_youtube/includes/MediaYouTubeStreamWrapper.inc
@@ -27,6 +27,9 @@ class MediaYouTubeStreamWrapper extends MediaReadOnlyStreamWrapper {
     if (!isset($response->error)) {
       return $thumbnail_url;
     }
+    elseif ($response->code == -110) {
+      throw new MediaInternetValidationException("Connection timed out.");
+    }
     elseif ($response->code == 401) {
       throw new MediaInternetValidationException("Embedding has been disabled for this video.");
     }
@@ -99,7 +102,7 @@ class MediaYouTubeStreamWrapper extends MediaReadOnlyStreamWrapper {
   }
 
   /**
-   * Returns a url in the format "http://www.youtube.com/watch?v=qsPQN4MiTeE".
+   * Returns a url in the format "https://www.youtube.com/watch?v=qsPQN4MiTeE".
    *
    * Overrides interpolateUrl() defined in MediaReadOnlyStreamWrapper.
    */
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/includes/media_youtube.formatters.inc b/profiles/wcm_base/modules/contrib/media_youtube/includes/media_youtube.formatters.inc
index 639a3395874e97bb08b1146f3c96c134e7ef0520..f1f988871357d00e640ac3fb6c0108fd1c37c5ea 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/includes/media_youtube.formatters.inc
+++ b/profiles/wcm_base/modules/contrib/media_youtube/includes/media_youtube.formatters.inc
@@ -184,9 +184,8 @@ function media_youtube_file_formatter_video_settings($form, &$form_state, $setti
   $element['protocol'] = array(
     '#title' => t('Iframe protocol'),
     '#type' => 'radios',
-    '#default_value' => $settings['protocol'],
+    '#default_value' => 'https:',
     '#options' => array(
-      'http:' => 'http://',
       'https:' => 'https://',
     ),
     '#states' => array(
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/media_youtube.info b/profiles/wcm_base/modules/contrib/media_youtube/media_youtube.info
index e6d8620341282c29b036d4f46ae9d62c75862f01..3a5ff93c9794592915602b7cd7055ba003275efd 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/media_youtube.info
+++ b/profiles/wcm_base/modules/contrib/media_youtube/media_youtube.info
@@ -11,9 +11,8 @@ files[] = media_youtube.test
 files[] = includes/MediaYouTubeStreamWrapper.inc
 files[] = includes/MediaInternetYouTubeHandler.inc
 
-; Information added by Drupal.org packaging script on 2017-08-14
-version = "7.x-3.5"
+; Information added by Drupal.org packaging script on 2019-02-14
+version = "7.x-3.8"
 core = "7.x"
 project = "media_youtube"
-datestamp = "1502719245"
-
+datestamp = "1550118784"
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/media_youtube.test b/profiles/wcm_base/modules/contrib/media_youtube/media_youtube.test
index ca63c86af83e51b531a1b8fd511ccd82c2c6e63b..78b1729f5795db93f0ca2fddcbf9fa16dc41013a 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/media_youtube.test
+++ b/profiles/wcm_base/modules/contrib/media_youtube/media_youtube.test
@@ -78,7 +78,8 @@ class MediaInternetYouTubeTestCase extends MediaYouTubeTestHelper {
         'origin' => '',
         'protocol' => 'https:',
         'protocol_specify' => FALSE,
-        'rel' => TRUE,
+        'rel' => FALSE,
+        'controls' => FALSE,
         'showinfo' => TRUE,
         'theme' => 'dark',
         'captions' => FALSE,
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/tests/media_youtube_test.info b/profiles/wcm_base/modules/contrib/media_youtube/tests/media_youtube_test.info
index a1cc74b9939892d920b6b6c3aa6ddce69014cb3d..0624d1c10a444565072bd770053336f0e63c46a1 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/tests/media_youtube_test.info
+++ b/profiles/wcm_base/modules/contrib/media_youtube/tests/media_youtube_test.info
@@ -8,9 +8,8 @@ hidden = TRUE
 files[] = includes/MediaYouTubeTestStreamWrapper.inc
 files[] = includes/MediaYouTubeTestHandler.inc
 
-; Information added by Drupal.org packaging script on 2017-08-14
-version = "7.x-3.5"
+; Information added by Drupal.org packaging script on 2019-02-14
+version = "7.x-3.8"
 core = "7.x"
 project = "media_youtube"
-datestamp = "1502719245"
-
+datestamp = "1550118784"
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/tests/media_youtube_test.module b/profiles/wcm_base/modules/contrib/media_youtube/tests/media_youtube_test.module
index 44f3c4ba337706c7a63444ba5eb707d2cd0bb024..863dc87895c58e3de7216d8d5ff2c71131072f0b 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/tests/media_youtube_test.module
+++ b/profiles/wcm_base/modules/contrib/media_youtube/tests/media_youtube_test.module
@@ -32,7 +32,7 @@ function media_youtube_test_oembed() {
     'thumbnail_width' => 480,
     'author_name' => 'YouTube Help',
     'height' => 270,
-    'provider_url' => 'http://www.youtube.com/',
+    'provider_url' => 'https://www.youtube.com/',
     'html' => '<iframe width="480" height="270" src="https://www.youtube.com/embed/' . $params['v'] . '?feature=oembed" frameborder="0" allowfullscreen></iframe>',
     'thumbnail_height' => 360,
     'title' => 'YouTube Content ID',
@@ -40,7 +40,7 @@ function media_youtube_test_oembed() {
     'type' => 'video',
     'width' => 480,
     'version' => '1.0',
-    'author_url' => 'http://www.youtube.com/user/YouTubeHelp',
+    'author_url' => 'https://www.youtube.com/user/YouTubeHelp',
   );
 
   drupal_json_output($data);
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/themes/media-youtube-video.tpl.php b/profiles/wcm_base/modules/contrib/media_youtube/themes/media-youtube-video.tpl.php
index 86ce00ed922efcdf4c224d6546c05787e1442895..54140f53f1a85fc1b0b23c07cad4a0aa1a082043 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/themes/media-youtube-video.tpl.php
+++ b/profiles/wcm_base/modules/contrib/media_youtube/themes/media-youtube-video.tpl.php
@@ -23,5 +23,5 @@
 
 ?>
 <div class="<?php print $classes; ?> media-youtube-<?php print $id; ?>">
-  <iframe class="media-youtube-player" <?php print $api_id_attribute; ?>width="<?php print $width; ?>" height="<?php print $height; ?>" title="<?php print $title; ?>" src="<?php print $url; ?>" frameborder="0" allowfullscreen><?php print $alternative_content; ?></iframe>
+  <iframe class="media-youtube-player" <?php print $api_id_attribute; ?>width="<?php print $width; ?>" height="<?php print $height; ?>" title="<?php print $title; ?>" src="<?php print $url; ?>" name="<?php print $title; ?>" frameborder="0" allowfullscreen><?php print $alternative_content; ?></iframe>
 </div>
diff --git a/profiles/wcm_base/modules/contrib/media_youtube/themes/media_youtube.theme.inc b/profiles/wcm_base/modules/contrib/media_youtube/themes/media_youtube.theme.inc
index 85aa6205002d9a3e3ab831958f524c5d6c07a16f..4470da8eb8ca2dd7b303e69bb1846d756f95364e 100644
--- a/profiles/wcm_base/modules/contrib/media_youtube/themes/media_youtube.theme.inc
+++ b/profiles/wcm_base/modules/contrib/media_youtube/themes/media_youtube.theme.inc
@@ -35,7 +35,7 @@ function media_youtube_preprocess_media_youtube_video(&$variables) {
   // Checked existing function.
   if(function_exists('file_uri_to_object')) {
     // Make the file object available.
-    $file_object = file_uri_to_object($variables['uri']);
+    $file_object = file_uri_to_object($variables['uri'], TRUE);
   }
   else {
     $file_object = media_youtube_file_uri_to_object($variables['uri']);
@@ -60,7 +60,7 @@ function media_youtube_preprocess_media_youtube_video(&$variables) {
   }
   // These queries default to 0. If the option is true, set value to 1.
   foreach (array('autoplay', 'enablejsapi', 'loop', 'modestbranding') as $option) {
-    if ($variables['options'][$option]) {
+    if (isset($variables['options'][$option]) && $variables['options'][$option]) {
       $query[$option] = 1;
     }
   }
diff --git a/profiles/wcm_base/modules/contrib/menu_block/menu-block-background-display-options.png b/profiles/wcm_base/modules/contrib/menu_block/css/display-options-background.png
similarity index 100%
rename from profiles/wcm_base/modules/contrib/menu_block/menu-block-background-display-options.png
rename to profiles/wcm_base/modules/contrib/menu_block/css/display-options-background.png
diff --git a/profiles/wcm_base/modules/contrib/menu_block/menu-block.admin.css b/profiles/wcm_base/modules/contrib/menu_block/css/menu-block.admin.css
similarity index 96%
rename from profiles/wcm_base/modules/contrib/menu_block/menu-block.admin.css
rename to profiles/wcm_base/modules/contrib/menu_block/css/menu-block.admin.css
index 14c36615f7bc20dd26085f86554609d2761218e3..d23c87715cb785bc72340e64db2f066f2b4661b7 100644
--- a/profiles/wcm_base/modules/contrib/menu_block/menu-block.admin.css
+++ b/profiles/wcm_base/modules/contrib/menu_block/css/menu-block.admin.css
@@ -38,7 +38,7 @@ label#item-label {
   border: 1px solid #666;
   color: #666;
   font-weight: bold;
-  background-image: url(menu-block-background-display-options.png);
+  background-image: url(display-options-background.png);
   background-position: left top;
   background-repeat: no-repeat;
 }
diff --git a/profiles/wcm_base/modules/contrib/menu_block/menu-block.js b/profiles/wcm_base/modules/contrib/menu_block/js/menu-block.js
similarity index 95%
rename from profiles/wcm_base/modules/contrib/menu_block/menu-block.js
rename to profiles/wcm_base/modules/contrib/menu_block/js/menu-block.js
index 73a99adbb93f8a908f5d73d698bc1f8f1259dc6a..2d7d85e628d8a4d983392ae5e05d5c38650a6508 100644
--- a/profiles/wcm_base/modules/contrib/menu_block/menu-block.js
+++ b/profiles/wcm_base/modules/contrib/menu_block/js/menu-block.js
@@ -22,7 +22,7 @@ Drupal.behaviors.menu_block = {
       }
     });
 
-    // Syncronize the display of menu and parent item selects.
+    // Synchronize the display of menu and parent item selects.
     $('.menu-block-parent-mlid', context).change( function() {
       var menuItem = $(this).val().split(':');
       $('.menu-block-menu-name').val(menuItem[0]);
diff --git a/profiles/wcm_base/modules/contrib/menu_block/menu_block.admin.inc b/profiles/wcm_base/modules/contrib/menu_block/menu_block.admin.inc
index d3952bbfdb2731d8fc77a4f806870bd8d6be127e..94e446d3d6fc6b3c7b476ce7d86443f110a2dc47 100644
--- a/profiles/wcm_base/modules/contrib/menu_block/menu_block.admin.inc
+++ b/profiles/wcm_base/modules/contrib/menu_block/menu_block.admin.inc
@@ -248,8 +248,8 @@ function menu_block_configure_form($form, &$form_state) {
   }
 
   // Build the standard form.
-  $form['#attached']['js'][] = drupal_get_path('module', 'menu_block') . '/menu-block.js';
-  $form['#attached']['css'][] = drupal_get_path('module', 'menu_block') . '/menu-block.admin.css';
+  $form['#attached']['js'][] = drupal_get_path('module', 'menu_block') . '/js/menu-block.js';
+  $form['#attached']['css'][] = drupal_get_path('module', 'menu_block') . '/css/menu-block.admin.css';
   $form['#attached']['library'][] = array('system', 'ui.button');
 
   $form['menu-block-wrapper-start'] = array(
@@ -286,6 +286,12 @@ function menu_block_configure_form($form, &$form_state) {
       ),
     );
   }
+  $form['display_empty'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Always display title'),
+    '#description' => t('Display the block with its title even if the block content is empty.'),
+    '#default_value' => $config['display_empty'],
+  );
   $form['admin_title'] = array(
     '#type' => 'textfield',
     '#default_value' => $config['admin_title'],
@@ -410,10 +416,12 @@ function menu_block_configure_form($form, &$form_state) {
   $form['menu-block-wrapper-close'] = array('#markup' => '</div>');
 
   // Set visibility of advanced options.
-  foreach (array('title_link', 'follow', 'depth_relative', 'follow_parent', 'expanded', 'sort', 'parent') as $key) {
+  foreach (array('title_link', 'display_empty', 'follow', 'depth_relative', 'follow_parent', 'expanded', 'sort', 'parent') as $key) {
     $form[$key]['#states']['visible'][':input[name=display_options]'] = array('value' => 'advanced');
   }
-  if ($config['title_link'] || $follow || $config['expanded'] || $config['sort'] || $config['parent_mlid']) {
+  // depth_relative and follow_parent aren't listed below because they require
+  // $follow to be true.
+  if ($config['title_link'] || $config['display_empty'] || $follow || $config['expanded'] || $config['sort'] || $config['parent_mlid']) {
     $form['display_options']['#default_value'] = 'advanced';
   }
 
@@ -482,6 +490,7 @@ function _menu_block_block_save($delta = '', $edit = array()) {
       variable_set("menu_block_{$delta}_parent", $edit['parent']);
       variable_set("menu_block_{$delta}_level", $edit['level']);
       variable_set("menu_block_{$delta}_follow", $edit['follow']);
+      variable_set("menu_block_{$delta}_display_empty", $edit['display_empty']);
       variable_set("menu_block_{$delta}_depth", $edit['depth']);
       variable_set("menu_block_{$delta}_depth_relative", $edit['depth_relative']);
       variable_set("menu_block_{$delta}_expanded", $edit['expanded']);
diff --git a/profiles/wcm_base/modules/contrib/menu_block/menu_block.info b/profiles/wcm_base/modules/contrib/menu_block/menu_block.info
index f64bcf4da5bbb1741f8c97c69c2620688676d333..5a845ab34bdd631fad580a2dfa04b74fe2dec97d 100644
--- a/profiles/wcm_base/modules/contrib/menu_block/menu_block.info
+++ b/profiles/wcm_base/modules/contrib/menu_block/menu_block.info
@@ -6,9 +6,8 @@ dependencies[] = menu (>7.11)
 
 configure = admin/config/user-interface/menu-block
 
-; Information added by Drupal.org packaging script on 2015-06-30
-version = "7.x-2.7"
+; Information added by Drupal.org packaging script on 2018-12-04
+version = "7.x-2.8"
 core = "7.x"
 project = "menu_block"
-datestamp = "1435676232"
-
+datestamp = "1543950483"
diff --git a/profiles/wcm_base/modules/contrib/menu_block/menu_block.module b/profiles/wcm_base/modules/contrib/menu_block/menu_block.module
index 425338155a55145026f2c4a301b96d362c6fd664..1f6a960693bafa877c6a0bedc6f666b4bbd3e5f7 100644
--- a/profiles/wcm_base/modules/contrib/menu_block/menu_block.module
+++ b/profiles/wcm_base/modules/contrib/menu_block/menu_block.module
@@ -5,7 +5,7 @@
  */
 
 /**
- * Denotes that the tree should use the menu picked by the curent page.
+ * Denotes that the tree should use the menu picked by the current page.
  */
 define('MENU_TREE__CURRENT_PAGE_MENU', '_active');
 
@@ -241,6 +241,7 @@ function menu_block_default_config() {
     'admin_title' => '',
     'level' => 1,
     'follow' => 0,
+    'display_empty' => 0,
     'depth' => 0,
     'depth_relative' => 0,
     'expanded' => 0,
@@ -532,13 +533,18 @@ function menu_tree_build(array &$config) {
   $data['subject_array'] = $title;
   $data['subject'] = drupal_render($title);
   $data['content'] = array();
-  if (!empty($tree) && $output = menu_block_tree_output($tree, $config)) {
-    $data['content']['#content'] = $output;
-    $data['content']['#theme'] = array(
-      'menu_block_wrapper__' . str_replace('-', '_', $config['delta']),
-      'menu_block_wrapper__' . str_replace('-', '_', $config['menu_name']),
-      'menu_block_wrapper'
-    );
+  if (!empty($tree) || !empty($config['display_empty'])) {
+    if ($output = menu_block_tree_output($tree, $config)) {
+      $data['content']['#content'] = $output;
+      $data['content']['#theme'] = array(
+        'menu_block_wrapper__' . str_replace('-', '_', $config['delta']),
+        'menu_block_wrapper__' . str_replace('-', '_', $config['menu_name']),
+        'menu_block_wrapper'
+      );
+    }
+    else {
+      $data['content']['#content'] = NULL;
+    }
     $data['content']['#config'] = $config;
     $data['content']['#delta'] = $config['delta'];
   }
diff --git a/profiles/wcm_base/modules/contrib/menu_block/menu_block_export.info b/profiles/wcm_base/modules/contrib/menu_block/menu_block_export.info
index 996c9ac54c83ccd984cfac1b77883a2a68f4b064..167a62c0fe81fd76b6b67a56e627c499981be5a0 100644
--- a/profiles/wcm_base/modules/contrib/menu_block/menu_block_export.info
+++ b/profiles/wcm_base/modules/contrib/menu_block/menu_block_export.info
@@ -9,9 +9,8 @@ files[] = menu_block_export.admin.inc
 
 configure = admin/config/user-interface/menu-block/export
 
-; Information added by Drupal.org packaging script on 2015-06-30
-version = "7.x-2.7"
+; Information added by Drupal.org packaging script on 2018-12-04
+version = "7.x-2.8"
 core = "7.x"
 project = "menu_block"
-datestamp = "1435676232"
-
+datestamp = "1543950483"
diff --git a/profiles/wcm_base/modules/contrib/menu_block/plugins/content_types/menu_tree/menu_tree.inc b/profiles/wcm_base/modules/contrib/menu_block/plugins/content_types/menu_tree/menu_tree.inc
index 71ba2b89818aacd564a1f2580baf6326d7c4720b..128ef550aa15a171eef66183845af9981149a186 100644
--- a/profiles/wcm_base/modules/contrib/menu_block/plugins/content_types/menu_tree/menu_tree.inc
+++ b/profiles/wcm_base/modules/contrib/menu_block/plugins/content_types/menu_tree/menu_tree.inc
@@ -33,8 +33,8 @@ function menu_block_menu_tree_content_type_content_types() {
     'defaults' => menu_block_get_config(),
 
     // JavaScript and CSS for the config form.
-    'js' => array(drupal_get_path('module', 'menu_block') . '/menu-block.js'),
-    'css' => array(drupal_get_path('module', 'menu_block') . '/menu-block-admin.css'),
+    'js' => array(drupal_get_path('module', 'menu_block') . '/js/menu-block.js'),
+    'css' => array(drupal_get_path('module', 'menu_block') . '/css/menu-block.admin.css'),
   );
 
   $menus = menu_block_get_all_menus();
diff --git a/profiles/wcm_base/modules/contrib/realname/realname.info b/profiles/wcm_base/modules/contrib/realname/realname.info
index 24217817be991627bda2c6ba2d8564be075cc4cb..0cbe4e01c399145d62f7fb1306efabc5d0fa655d 100644
--- a/profiles/wcm_base/modules/contrib/realname/realname.info
+++ b/profiles/wcm_base/modules/contrib/realname/realname.info
@@ -9,9 +9,8 @@ test_dependencies[] = entity
 files[] = tests/realname.test
 files[] = tests/realname_entity.test
 
-; Information added by Drupal.org packaging script on 2016-07-13
-version = "7.x-1.3"
+; Information added by Drupal.org packaging script on 2019-01-31
+version = "7.x-1.4"
 core = "7.x"
 project = "realname"
-datestamp = "1468435219"
-
+datestamp = "1548970389"
diff --git a/profiles/wcm_base/modules/contrib/realname/realname.module b/profiles/wcm_base/modules/contrib/realname/realname.module
index 6384e7d57792d929f722b729cc9dab5d3f67686b..9b43a4ee433796052067ac72dec4ad7aaadc8560 100644
--- a/profiles/wcm_base/modules/contrib/realname/realname.module
+++ b/profiles/wcm_base/modules/contrib/realname/realname.module
@@ -331,13 +331,13 @@ function realname_update($account) {
   // Remove double spaces (if a token had no value).
   $realname = preg_replace('/ {2,}/', ' ', $realname);
 
+  // Allow other modules to alter the generated realname.
+  drupal_alter('realname', $realname, $account);
+
   // The name must be trimmed to 255 characters before inserting into the
   // database.
   $realname = truncate_utf8(trim($realname), 255);
 
-  // Allow other modules to alter the generated realname.
-  drupal_alter('realname', $realname, $account);
-
   // Save to the database and the static cache.
   db_merge('realname')
     ->key(array('uid' => $account->uid))
@@ -350,7 +350,14 @@ function realname_update($account) {
   // Allow modules to react to the realname being updated.
   module_invoke_all('realname_update', $realname, $account);
 
-  $account->realname = $realname;
+  // Only if the real name is a non-empty string the name is altered.
+  if (drupal_strlen($realname)) {
+    $account->realname = $realname;
+  }
+
+  // Clear the entity cache.
+  entity_get_controller('user')->resetCache(array($account->uid));
+
   return $realname;
 }
 
diff --git a/profiles/wcm_base/modules/contrib/realname/realname.views.inc b/profiles/wcm_base/modules/contrib/realname/realname.views.inc
index 96c5648e7218ae889972e0ddd2543537cf0e52fd..89deb00c473a09b0676fd890237746b1c879cb4c 100644
--- a/profiles/wcm_base/modules/contrib/realname/realname.views.inc
+++ b/profiles/wcm_base/modules/contrib/realname/realname.views.inc
@@ -9,7 +9,7 @@
  * Implements hook_views_data().
  */
 function realname_views_data() {
-  $data['realname']['table']['group']  = t('Realname');
+  $data['realname']['table']['group'] = t('Realname');
   $data['realname']['table']['join'] = array(
     'users' => array(
       'left_field' => 'uid',
diff --git a/profiles/wcm_base/modules/contrib/rules/DEVELOPER.txt b/profiles/wcm_base/modules/contrib/rules/DEVELOPER.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d5d0f478151d3aab6d5fd7c9e09e39a2e6864ca4
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/DEVELOPER.txt
@@ -0,0 +1,26 @@
+---------------------------------------------------
+Some notes for developers working on the rules code
+---------------------------------------------------
+
+Terminology & Overview
+-----------------------
+  * Rules plugins extend the "rules language". Thus conditions and actions are
+    implemented with a plugin, but also loops or ORs are plugins. Each plugin is
+    declared to be used in the condition or the action part - specified by the
+    interface.
+  * The action and condition plugin are a so called "AbstractPlugin" which means
+    they have to be implemented by modules to be actually usable. In fact the
+    callbacks provided by the action or condition implementation are
+    incorporated in the plugin object using faces. That way an action or
+    condition element behaves exactly like any other plugin instance. 
+  * Any rule element is an instance of a RulesPlugin. 
+  * A rules configuration consists of multiple rule elements while one is the
+    root element, which may be a 'rule' but also any other plugin.
+  * Each rules configuration may be persistently saved to the db using the
+    entity CRUD API. Using the API on contained rule elements is working too and
+    results in the whole configuration being updated.
+  * Rules provides per plugin UI components, what makes the UI parts re-usable
+    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.
diff --git a/profiles/wcm_base/modules/contrib/rules/LICENSE.txt b/profiles/wcm_base/modules/contrib/rules/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/LICENSE.txt
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/profiles/wcm_base/modules/contrib/rules/README.txt b/profiles/wcm_base/modules/contrib/rules/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ee5360e62be31b008d5c85d2051f1871a87ab068
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/README.txt
@@ -0,0 +1,91 @@
+
+--------------------------------------------------------------------------------
+                                 Rules
+--------------------------------------------------------------------------------
+
+Maintainers:
+ * Wolfgang Ziegler (fago), nuppla@zites.net
+
+The Rules module allows site administrators to define conditionally executed
+actions based on occurring events (ECA-rules).
+
+Project homepage: https://www.drupal.org/project/rules
+
+
+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:
+
+                 https://www.drupal.org/node/298480
+
+ * Rules depends on the Entity API module, download and install it from
+   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.
+ * The administrative user interface can be found at admin/config/workflow/rules
+
+
+Documentation
+-------------
+* 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
+---------------
+
+ * If you enable the Rules scheduler module, you get new actions that allow you
+   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
+   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
+--------------------------------------------
+
+ * In order to upgrade Rules from 6.x-1.x to 7.x-2.x just run "update.php". This
+   is going to make sure Rules 2.x is properly installed, but it will leave your
+   Rules 1.x configurations untouched. Thus, your rules won't be upgraded yet.
+ * To convert your Rules 1.x configurations to Rules 2.x go to
+   'admin/config/workflow/rules/upgrade'.
+     * At this page, you may choose the Rules 1.x rules and rule sets to upgrade
+       and whether the converted configurations should be immediately saved to
+       your database or whether the configuration export should be generated.
+     * 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.
+     * 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
+       before you convert the rule.
+     * If all required modules are installed and have been upgraded but the rule
+       conversion still fails, the cause might be that a module has not yet
+       upgraded its Rules integration or does not implement the Rules conversion
+       functionality. In that case, file an issue for the module that provided
+       the action or condition causing the conversion to fail.
+     * Note that any rule configurations containing token replacements or PHP
+       input evaluations might need some manual corrections in order to stay
+       working. This is, as some used token replacements might not be available
+       in Drupal 7 any more and the PHP code might need to be updated in order
+       to be compatible with Drupal 7.
+     * Once the upgrade was successful, you may delete the left over Rules 1.x
+       configurations by going to 'admin/config/workflow/rules/upgrade/clear'.
+  * The Rules Scheduler module also comes with an upgrade routine that is
+    invoked as usual via "update.php". Its actions can be upgraded via the usual
+    Rules upgrade tool, see above.
+    However, there is currently no support for upgrading already scheduled
+    tasks. That means, all previously on Drupal 6 scheduled tasks won't apply
+    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 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.
diff --git a/profiles/wcm_base/modules/contrib/rules/includes/faces.inc b/profiles/wcm_base/modules/contrib/rules/includes/faces.inc
new file mode 100644
index 0000000000000000000000000000000000000000..d0fe8ce199a6ec454f8c15668575e36f583b71f4
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/includes/faces.inc
@@ -0,0 +1,323 @@
+<?php
+
+/**
+ * @file
+ * Extendable Object Faces API. Provided by the faces module.
+ */
+
+if (!interface_exists('FacesExtenderInterface', FALSE)) {
+
+  /**
+   * Interface for extenders.
+   */
+  interface FacesExtenderInterface {
+
+    /**
+     * Constructs an instance of the extender.
+     */
+    public function __construct(FacesExtendable $object);
+
+    /**
+     * Returns the extended object.
+     */
+    public function getExtendable();
+
+  }
+
+  /**
+   * The Exception thrown by the FacesExtendable.
+   */
+  class FacesExtendableException extends ErrorException {}
+
+}
+
+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.
+   */
+  abstract class FacesExtender implements FacesExtenderInterface {
+
+    /**
+     * @var FacesExtendable
+     */
+    protected $object;
+
+    public function __construct(FacesExtendable $object) {
+      $this->object = $object;
+    }
+
+    /**
+     * Returns the extended object.
+     */
+    public function getExtendable() {
+      return $this->object;
+    }
+
+    /**
+     * Makes protected properties of the extendable accessible.
+     */
+    protected function &property($name) {
+      $var =& $this->object->property($name);
+      return $var;
+    }
+
+    /**
+     * Invokes any method on the extended object, including protected methods.
+     *
+     * @param string $name
+     *   The method name.
+     * @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);
+    }
+
+  }
+}
+
+
+if (!class_exists('FacesExtendable', FALSE)) {
+
+  /**
+   * An extendable base class.
+   */
+  abstract class FacesExtendable {
+
+    protected $facesMethods = array();
+    protected $faces = array();
+    protected $facesIncludes = array();
+    protected $facesClassInstances = array();
+    static protected $facesIncluded = array();
+
+    /**
+     * Wraps calls to module_load_include() to prevent multiple inclusions.
+     *
+     * @see module_load_include()
+     */
+    protected static function load_include($args) {
+      $args += array('type' => 'inc', 'module' => '', 'name' => NULL);
+      $key = implode(':', $args);
+      if (!isset(self::$facesIncluded[$key])) {
+        self::$facesIncluded[$key] = TRUE;
+        module_load_include($args['type'], $args['module'], $args['name']);
+      }
+    }
+
+    /**
+     * Magic method: Invoke the dynamically implemented methods.
+     */
+    public function __call($name, $arguments = array()) {
+      if (isset($this->facesMethods[$name])) {
+        $method = $this->facesMethods[$name];
+        // Include code, if necessary.
+        if (isset($this->facesIncludes[$name])) {
+          self::load_include($this->facesIncludes[$name]);
+          $this->facesIncludes[$name] = NULL;
+        }
+        if (isset($method[0])) {
+          // We always pass the object reference and the name of the method.
+          $arguments[] = $this;
+          $arguments[] = $name;
+          return call_user_func_array($method[0], $arguments);
+        }
+        // Call the method on the extender object, but don't use extender()
+        // for performance reasons.
+        if (!isset($this->facesClassInstances[$method[1]])) {
+          $this->facesClassInstances[$method[1]] = new $method[1]($this);
+        }
+        return call_user_func_array(array($this->facesClassInstances[$method[1]], $name), $arguments);
+      }
+      $class = check_plain(get_class($this));
+      throw new FacesExtendableException("There is no method $name for this instance of the class $class.");
+    }
+
+    /**
+     * 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])) {
+        $this->facesClassInstances[$class] = new $class($this);
+      }
+      return $this->facesClassInstances[$class];
+    }
+
+    /**
+     * 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 object has been extended by an appropriate
+     * implementation.
+     *
+     * @param $interface
+     *   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.
+     */
+    public function facesAs($interface = NULL) {
+      if (!isset($interface)) {
+        return array_values($this->faces);
+      }
+      return in_array($interface, $this->faces) || $this instanceof $interface;
+    }
+
+    /**
+     * Extend the object by a class to implement the given interfaces.
+     *
+     * @param $interface
+     *   The interface name or an array of interface names.
+     * @param $className
+     *   The extender class, which has to implement the FacesExtenderInterface.
+     * @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
+     *   required as 'type' defaults to 'inc' and 'name' to NULL.
+     */
+    public function extendByClass($interface, $className, array $includes = array()) {
+      $parents = class_implements($className);
+      if (!in_array('FacesExtenderInterface', $parents)) {
+        throw new FacesExtendableException("The class " . check_plain($className) . " doesn't implement the FacesExtenderInterface.");
+      }
+      $interfaces = is_array($interface) ? $interface : array($interface);
+
+      foreach ($interfaces as $interface) {
+        if (!in_array($interface, $parents)) {
+          throw new FacesExtendableException("The class " . check_plain($className) . " doesn't implement the interface " . check_plain($interface) . ".");
+        }
+        $this->faces[$interface] = $interface;
+        $this->faces += class_implements($interface);
+        $face_methods = get_class_methods($interface);
+        $this->addIncludes($face_methods, $includes);
+        foreach ($face_methods as $method) {
+          $this->facesMethods[$method] = array(1 => $className);
+        }
+      }
+    }
+
+    /**
+     * Extend the object by the given functions to implement the given
+     * interface. There has to be an implementation function for each method of
+     * the interface.
+     *
+     * @param $interface
+     *   The interface name or FALSE to extend the object without a given
+     *   interface.
+     * @param array $callbacks
+     *   An array, where the keys are methods of the given interface and the
+     *   values the callback functions to use.
+     * @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
+     *   extendByClass() $include parameter for more details about how to
+     *   describe a single file.
+     */
+    public function extend($interface, array $callbacks = array(), array $includes = array()) {
+      $face_methods = $interface ? get_class_methods($interface) : array_keys($callbacks);
+      if ($interface) {
+        if (array_diff($face_methods, array_keys($callbacks))) {
+          throw new FacesExtendableException("Missing methods for implementing the interface " . check_plain($interface) . ".");
+        }
+        $this->faces[$interface] = $interface;
+        $this->faces += class_implements($interface);
+      }
+      $this->addIncludes($face_methods, $includes);
+      foreach ($face_methods as $method) {
+        $this->facesMethods[$method] = array(0 => $callbacks[$method]);
+      }
+    }
+
+    /**
+     * Override the implementation of an extended method.
+     *
+     * @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 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
+     *   extendByClass() $include parameter for more details about how to
+     *   describe a single file.
+     */
+    public function override(array $callbacks = array(), array $includes = array()) {
+      if (array_diff_key($callbacks, $this->facesMethods)) {
+        throw new FacesExtendableException("A not implemented method is to be overridden.");
+      }
+      $this->addIncludes(array_keys($callbacks), $includes);
+      foreach ($callbacks as $method => $callback) {
+        $this->facesMethods[$method] = array(0 => $callback);
+      }
+    }
+
+    /**
+     * 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) {
+      $includes = isset($includes['module']) && is_string($includes['module']) ? array_fill_keys($methods, $includes) : $includes;
+      $this->facesIncludes = $includes + array_diff_key($this->facesIncludes, array_flip($methods));
+    }
+
+    /**
+     * Only serialize what is really necessary.
+     */
+    public function __sleep() {
+      return array('facesMethods', 'faces', 'facesIncludes');
+    }
+
+    /**
+     * 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.
+     */
+    public function destroy() {
+      // Avoid circular references.
+      $this->facesClassInstances = array();
+    }
+
+    /**
+     * Makes protected properties accessible.
+     */
+    public function &property($name) {
+      if (property_exists($this, $name)) {
+        return $this->$name;
+      }
+    }
+
+    /**
+     * Invokes any method.
+     *
+     * This also allows to pass arguments by reference, so it may be used to
+     * pass arguments by reference to dynamically extended methods.
+     *
+     * @param string $name
+     *   The method name.
+     * @param array $args
+     *   An array of arguments to pass to the method.
+     */
+    public function call($name, array $args = array()) {
+      if (method_exists($this, $name)) {
+        return call_user_func_array(array($this, $name), $args);
+      }
+      return $this->__call($name, $args);
+    }
+
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/includes/rules.core.inc b/profiles/wcm_base/modules/contrib/rules/includes/rules.core.inc
new file mode 100644
index 0000000000000000000000000000000000000000..b00ed410eaaf8baab246b28aac897a591948ee22
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/includes/rules.core.inc
@@ -0,0 +1,2919 @@
+<?php
+
+/**
+ * @file
+ * Rules base classes and interfaces needed for any rule evaluation.
+ */
+
+// This is not necessary as the classes are autoloaded via the registry. However
+// it saves some possible update headaches until the registry is rebuilt.
+// @todo Remove for a future release.
+require_once dirname(__FILE__) . '/faces.inc';
+
+/**
+ * Make sure loaded rule configs are instantiated right.
+ */
+class RulesEntityController extends EntityAPIControllerExportable {
+
+  /**
+   * Overridden.
+   *
+   * @see EntityAPIController::create()
+   */
+  public function create(array $values = array()) {
+    // Default to rules as owning module.
+    $values += array('owner' => 'rules');
+    return parent::create($values);
+  }
+
+  /**
+   * Overridden.
+   *
+   * @see DrupalDefaultEntityController::attachLoad()
+   */
+  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
+    // Retrieve stdClass records and store them as rules objects in 'data'.
+    $ids = array_keys($queried_entities);
+    $result = db_select('rules_tags')
+      ->fields('rules_tags', array('id', 'tag'))
+      ->condition('id', $ids, 'IN')
+      ->execute();
+    foreach ($result as $row) {
+      $tags[$row->id][] = $row->tag;
+    }
+    $result = db_select('rules_dependencies')
+      ->fields('rules_dependencies', array('id', 'module'))
+      ->condition('id', $ids, 'IN')
+      ->execute();
+    foreach ($result as $row) {
+      $modules[$row->id][] = $row->module;
+    }
+
+    $entities = array();
+    foreach ($queried_entities as $record) {
+      $entity = $record->data;
+      // Set the values of the other columns.
+      foreach ($this->entityInfo['schema_fields_sql']['base table'] as $field) {
+        $entity->$field = $record->$field;
+      }
+      unset($entity->data, $entity->plugin);
+      // Add any tags or dependencies.
+      $entity->dependencies = isset($modules[$entity->id]) ? $modules[$entity->id] : array();
+      $entity->tags = isset($tags[$entity->id]) ? $tags[$entity->id] : array();
+      $entities[$entity->id] = $entity;
+    }
+    $queried_entities = $entities;
+    parent::attachLoad($queried_entities, $revision_id);
+  }
+
+  /**
+   * Override to support having events and tags as conditions.
+   *
+   * @see EntityAPIController::applyConditions()
+   * @see rules_query_rules_config_load_multiple_alter()
+   */
+  protected function applyConditions($entities, $conditions = array()) {
+    if (isset($conditions['event']) || isset($conditions['plugin'])) {
+      foreach ($entities as $key => $entity) {
+        if (isset($conditions['event']) && (!($entity instanceof RulesTriggerableInterface) || !in_array($conditions['event'], $entity->events()))) {
+          unset($entities[$key]);
+        }
+        if (isset($conditions['plugin']) && !is_array($conditions['plugin'])) {
+          $conditions['plugin'] = array($conditions['plugin']);
+        }
+        if (isset($conditions['plugin']) && !in_array($entity->plugin(), $conditions['plugin'])) {
+          unset($entities[$key]);
+        }
+      }
+      unset($conditions['event'], $conditions['plugin']);
+    }
+    if (!empty($conditions['tags'])) {
+      foreach ($entities as $key => $entity) {
+        foreach ($conditions['tags'] as $tag) {
+          if (in_array($tag, $entity->tags)) {
+            continue 2;
+          }
+        }
+        unset($entities[$key]);
+      }
+      unset($conditions['tags']);
+    }
+    return parent::applyConditions($entities, $conditions);
+  }
+
+  /**
+   * Overridden to work with Rules' custom export format.
+   *
+   * @param $export
+   *   A serialized string in JSON format as produced by the
+   *   RulesPlugin::export() method, or the PHP export as usual PHP array.
+   * @param string $error_msg
+   *   The error message.
+   */
+  public function import($export, &$error_msg = '') {
+    $export = is_array($export) ? $export : drupal_json_decode($export);
+    if (!is_array($export)) {
+      $error_msg = t('Unable to parse the pasted export.');
+      return FALSE;
+    }
+    // The key is the configuration name and the value the actual export.
+    $name = key($export);
+    $export = current($export);
+    if (!isset($export['PLUGIN'])) {
+      $error_msg = t('Export misses plugin information.');
+      return FALSE;
+    }
+    // Create an empty configuration, re-set basic keys and import.
+    $config = rules_plugin_factory($export['PLUGIN']);
+    $config->name = $name;
+    foreach (array('label', 'active', 'weight', 'tags', 'access_exposed', 'owner') as $key) {
+      if (isset($export[strtoupper($key)])) {
+        $config->$key = $export[strtoupper($key)];
+      }
+    }
+    if (!empty($export['REQUIRES'])) {
+      foreach ($export['REQUIRES'] as $module) {
+        if (!module_exists($module)) {
+          $error_msg = t('Missing the required module %module.', array('%module' => $module));
+          return FALSE;
+        }
+      }
+      $config->dependencies = $export['REQUIRES'];
+    }
+    $config->import($export);
+    return $config;
+  }
+
+  public function save($rules_config, DatabaseTransaction $transaction = NULL) {
+    $transaction = isset($transaction) ? $transaction : db_transaction();
+
+    // Load the stored entity, if any.
+    if (!isset($rules_config->original) && $rules_config->{$this->idKey}) {
+      $rules_config->original = entity_load_unchanged($this->entityType, $rules_config->{$this->idKey});
+    }
+    $original = isset($rules_config->original) ? $rules_config->original : NULL;
+
+    $return = parent::save($rules_config, $transaction);
+    $this->storeTags($rules_config);
+    if ($rules_config instanceof RulesTriggerableInterface) {
+      $this->storeEvents($rules_config);
+    }
+    $this->storeDependencies($rules_config);
+
+    // See if there are any events that have been removed.
+    if ($original && $rules_config->plugin == 'reaction rule') {
+      foreach (array_diff($original->events(), $rules_config->events()) as $event_name) {
+        // Check if the event handler implements the event dispatcher interface.
+        $handler = rules_get_event_handler($event_name, $rules_config->getEventSettings($event_name));
+        if (!$handler instanceof RulesEventDispatcherInterface) {
+          continue;
+        }
+
+        // Only stop an event dispatcher if there are no rules for it left.
+        if (!rules_config_load_multiple(FALSE, array('event' => $event_name, 'plugin' => 'reaction rule', 'active' => TRUE)) && $handler->isWatching()) {
+          $handler->stopWatching();
+        }
+      }
+    }
+
+    return $return;
+  }
+
+  /**
+   * Save tagging information to the rules_tags table.
+   */
+  protected function storeTags($rules_config) {
+    db_delete('rules_tags')
+      ->condition('id', $rules_config->id)
+      ->execute();
+    if (!empty($rules_config->tags)) {
+      foreach ($rules_config->tags as $tag) {
+        db_insert('rules_tags')
+          ->fields(array('id', 'tag'), array($rules_config->id, $tag))
+          ->execute();
+      }
+    }
+  }
+
+  /**
+   * Save event information to the rules_trigger table.
+   */
+  protected function storeEvents(RulesTriggerableInterface $rules_config) {
+    db_delete('rules_trigger')
+      ->condition('id', $rules_config->id)
+      ->execute();
+    foreach ($rules_config->events() as $event) {
+      db_insert('rules_trigger')
+        ->fields(array(
+          'id' => $rules_config->id,
+          'event' => $event,
+        ))
+        ->execute();
+    }
+  }
+
+  protected function storeDependencies($rules_config) {
+    db_delete('rules_dependencies')
+      ->condition('id', $rules_config->id)
+      ->execute();
+    if (!empty($rules_config->dependencies)) {
+      foreach ($rules_config->dependencies as $dependency) {
+        db_insert('rules_dependencies')
+          ->fields(array(
+            'id' => $rules_config->id,
+            'module' => $dependency,
+          ))
+          ->execute();
+      }
+    }
+  }
+
+  /**
+   * Overridden to support tags and events in $conditions.
+   *
+   * @see EntityAPIControllerExportable::buildQuery()
+   */
+  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
+    $query = parent::buildQuery($ids, $conditions, $revision_id);
+    $query_conditions =& $query->conditions();
+    foreach ($query_conditions as &$condition) {
+      // One entry in $query_conditions is a string with key '#conjunction'.
+      // @see QueryConditionInterface::conditions()
+      if (is_array($condition)) {
+        // Support using 'tags' => array('tag1', 'tag2') as condition.
+        if ($condition['field'] == 'base.tags') {
+          $query->join('rules_tags', 'rt', 'base.id = rt.id');
+          $condition['field'] = 'rt.tag';
+        }
+        // Support using 'event' => $name as condition.
+        if ($condition['field'] == 'base.event') {
+          $query->join('rules_trigger', 'tr', "base.id = tr.id");
+          $condition['field'] = 'tr.event';
+          // Use like operator to support % wildcards also.
+          $condition['operator'] = 'LIKE';
+        }
+      }
+    }
+    return $query;
+  }
+
+  /**
+   * Overridden to also delete tags and events.
+   *
+   * @see EntityAPIControllerExportable::delete()
+   */
+  public function delete($ids, DatabaseTransaction $transaction = NULL) {
+    $transaction = isset($transaction) ? $transaction : db_transaction();
+    // Use entity-load as ids may be the names as well as the ids.
+    $configs = $ids ? entity_load('rules_config', $ids) : array();
+    if ($configs) {
+      foreach ($configs as $config) {
+        db_delete('rules_trigger')
+          ->condition('id', $config->id)
+          ->execute();
+        db_delete('rules_tags')
+          ->condition('id', $config->id)
+          ->execute();
+        db_delete('rules_dependencies')
+          ->condition('id', $config->id)
+          ->execute();
+      }
+    }
+    $return = parent::delete($ids, $transaction);
+
+    // Stop event dispatchers when deleting the last rule of an event set.
+    $processed = array();
+    foreach ($configs as $config) {
+      if ($config->getPluginName() != 'reaction rule') {
+        continue;
+      }
+
+      foreach ($config->events() as $event_name) {
+        // Only process each event once.
+        if (!empty($processed[$event_name])) {
+          continue;
+        }
+        $processed[$event_name] = TRUE;
+
+        // Check if the event handler implements the event dispatcher interface.
+        $handler = rules_get_event_handler($event_name, $config->getEventSettings($event_name));
+        if (!$handler instanceof RulesEventDispatcherInterface) {
+          continue;
+        }
+
+        // Only stop an event dispatcher if there are no rules for it left.
+        if ($handler->isWatching() && !rules_config_load_multiple(FALSE, array('event' => $event_name, 'plugin' => 'reaction rule', 'active' => TRUE))) {
+          $handler->stopWatching();
+        }
+      }
+    }
+
+    return $return;
+  }
+
+}
+
+/**
+ * Base class for RulesExtendables.
+ *
+ * The RulesExtendable uses the rules cache to setup the defined extenders
+ * and overrides automatically.
+ * As soon faces is used the faces information is autoloaded using setUp().
+ */
+abstract class RulesExtendable extends FacesExtendable {
+
+  /**
+   * The name of the info definitions associated with info about this class.
+   *
+   * This would be defined abstract, if possible. Common rules hooks with class
+   * info are e.g. plugin_info and data_info.
+   */
+  protected $hook;
+
+  /**
+   * The name of the item this class represents in the info hook.
+   */
+  protected $itemName;
+
+  protected $cache;
+  protected $itemInfo = array();
+
+  public function __construct() {
+    $this->setUp();
+  }
+
+  protected function setUp() {
+    // Keep a reference on the cache, so elements created during cache
+    // rebuilding end up with a complete cache in the end too.
+    $this->cache = &rules_get_cache();
+    if (isset($this->cache[$this->hook][$this->itemName])) {
+      $this->itemInfo = &$this->cache[$this->hook][$this->itemName];
+    }
+    // Set up the Faces Extenders.
+    if (!empty($this->itemInfo['faces_cache'])) {
+      list($this->facesMethods, $this->facesIncludes, $this->faces) = $this->itemInfo['faces_cache'];
+    }
+  }
+
+  /**
+   * Forces the object to be setUp, this executes setUp() if not done yet.
+   */
+  public function forceSetUp() {
+    if (!isset($this->cache) || (!empty($this->itemInfo['faces_cache']) && !$this->faces)) {
+      $this->setUp();
+    }
+  }
+
+  /**
+   * Magic method: Invoke the dynamically implemented methods.
+   */
+  public function __call($name, $arguments = array()) {
+    $this->forceSetUp();
+    return parent::__call($name, $arguments);
+  }
+
+  public function facesAs($interface = NULL) {
+    $this->forceSetUp();
+    return parent::facesAs($interface);
+  }
+
+  /**
+   * Allows items to add something to the rules cache.
+   */
+  public function rebuildCache(&$itemInfo, &$cache) {
+    // Speed up setting up items by caching the faces methods.
+    if (!empty($itemInfo['extenders'])) {
+      // Apply extenders and overrides.
+      $itemInfo += array('overrides' => array());
+      foreach ($itemInfo['extenders'] as $face => $data) {
+        $data += array('file' => array());
+        if (isset($data['class'])) {
+          $this->extendByClass($face, $data['class'], $data['file']);
+        }
+        elseif (isset($data['methods'])) {
+          $this->extend($face, $data['methods'], $data['file']);
+        }
+      }
+      foreach ($itemInfo['overrides'] as $data) {
+        $data += array('file' => array());
+        $this->override($data['methods'], $data['file']);
+      }
+      $itemInfo['faces_cache'] = array($this->facesMethods, $this->facesIncludes, $this->faces);
+      // We don't need that any more.
+      unset($itemInfo['extenders'], $itemInfo['overrides']);
+    }
+  }
+
+  /**
+   * Returns whether the a RuleExtendable supports the given interface.
+   *
+   * @param $itemInfo
+   *   The info about the item as specified in the hook.
+   * @param $interface
+   *   The interface to check for.
+   *
+   * @return bool
+   *   Whether it supports the given interface.
+   */
+  public static function itemFacesAs(&$itemInfo, $interface) {
+    return in_array($interface, class_implements($itemInfo['class'])) || isset($itemInfo['faces_cache'][2][$interface]);
+  }
+
+}
+
+/**
+ * Base class for rules plugins.
+ *
+ * We cannot inherit from EntityDB at the same time, so we implement our own
+ * entity related methods. Any CRUD related actions performed on contained
+ * plugins are applied and the root element representing the configuration is
+ * saved.
+ */
+abstract class RulesPlugin extends RulesExtendable {
+
+  /**
+   * If this is a configuration saved to the db, the id of it.
+   */
+  public $id = NULL;
+  public $weight = 0;
+  public $name = NULL;
+
+  /**
+   * An array of settings for this element.
+   */
+  public $settings = array();
+
+  /**
+   * Info about this element. Usage depends on the plugin.
+   */
+  protected $info = array();
+
+  /**
+   * The parent element, if any.
+   *
+   * @var RulesContainerPlugin
+   */
+  protected $parent = NULL;
+
+  protected $cache = NULL;
+  protected $hook = 'plugin_info';
+
+  /**
+   * Identifies an element inside a configuration.
+   */
+  protected $elementId = NULL;
+
+  /**
+   * Static cache for availableVariables().
+   */
+  protected $availableVariables;
+
+  /**
+   * Sets a new parent element.
+   */
+  public function setParent(RulesContainerPlugin $parent) {
+    if ($this->parent == $parent) {
+      return;
+    }
+    if (isset($this->parent) && ($key = array_search($this, $this->parent->children)) !== FALSE) {
+      // Remove element from any previous parent.
+      unset($this->parent->children[$key]);
+      $this->parent->resetInternalCache();
+    }
+    // Make sure the interface matches the type of the container.
+    if (($parent instanceof RulesActionContainer && $this instanceof RulesActionInterface) ||
+       ($parent instanceof RulesConditionContainer && $this instanceof RulesConditionInterface)) {
+
+      $this->parent = $parent;
+      $parent->children[] = $this;
+      $this->parent->resetInternalCache();
+    }
+    else {
+      throw new RulesEvaluationException('The given container is incompatible with this element.', array(), $this, RulesLog::ERROR);
+    }
+  }
+
+  /**
+   * Gets the root element of the configuration.
+   */
+  public function root() {
+    $element = $this;
+    while (!$element->isRoot()) {
+      $element = $element->parent;
+    }
+    return $element;
+  }
+
+  /**
+   * Returns whether the element is the root of the configuration.
+   */
+  public function isRoot() {
+    return empty($this->parent) || isset($this->name);
+  }
+
+  /**
+   * Returns the element's parent.
+   */
+  public function parentElement() {
+    return $this->parent;
+  }
+
+  /**
+   * Returns the element id, which identifies the element inside the config.
+   */
+  public function elementId() {
+    if (!isset($this->elementId)) {
+      $this->elementMap()->index();
+    }
+    return $this->elementId;
+  }
+
+  /**
+   * Gets the element map helper object, which helps mapping elements to ids.
+   *
+   * @return RulesElementMap
+   */
+  public function elementMap() {
+    $config = $this->root();
+    if (empty($config->map)) {
+      $config->map = new RulesElementMap($config);
+    }
+    return $config->map;
+  }
+
+  /**
+   * Iterate over all elements nested below the current element.
+   *
+   * This helper can be used to recursively iterate over all elements of a
+   * configuration. To iterate over the children only, just regularly iterate
+   * over the object.
+   *
+   * @param $mode
+   *   (optional) The iteration mode used. See
+   *   RecursiveIteratorIterator::construct(). Defaults to SELF_FIRST.
+   *
+   * @return RecursiveIteratorIterator
+   */
+  public function elements($mode = RecursiveIteratorIterator::SELF_FIRST) {
+    return new RecursiveIteratorIterator($this, $mode);
+  }
+
+  /**
+   * Do a deep clone.
+   */
+  public function __clone() {
+    // Make sure the element map is cleared.
+    // @see self::elementMap()
+    unset($this->map);
+  }
+
+  /**
+   * Returns the depth of this element in the configuration.
+   */
+  public function depth() {
+    $element = $this;
+    $i = 0;
+    while (!empty($element->parent)) {
+      $element = $element->parent;
+      $i++;
+    }
+    return $i;
+  }
+
+  /**
+   * Execute the configuration.
+   *
+   * @param ...
+   *   Arguments to pass to the configuration.
+   */
+  public function execute() {
+    return $this->executeByArgs(func_get_args());
+  }
+
+  /**
+   * Execute the configuration by passing arguments in a single array.
+   */
+  abstract public function executeByArgs($args = array());
+
+  /**
+   * Evaluate the element on a given rules evaluation state.
+   */
+  abstract public function evaluate(RulesState $state);
+
+  protected static function compare(RulesPlugin $a, RulesPlugin $b) {
+    if ($a->weight == $b->weight) {
+      return 0;
+    }
+    return ($a->weight < $b->weight) ? -1 : 1;
+  }
+
+  /**
+   * Returns info about parameters needed by the plugin.
+   *
+   * Note that not necessarily all parameters are needed when executing the
+   * plugin, as values for the parameter might have been already configured via
+   * the element settings.
+   *
+   * @see self::parameterInfo()
+   */
+  public function pluginParameterInfo() {
+    return isset($this->info['parameter']) ? $this->info['parameter'] : array();
+  }
+
+  /**
+   * Returns info about parameters needed for executing the configured plugin.
+   *
+   * @param bool $optional
+   *   Whether optional parameters should be included.
+   *
+   * @see self::pluginParameterInfo()
+   */
+  public function parameterInfo($optional = FALSE) {
+    // We have to filter out parameters that are already configured.
+    foreach ($this->pluginParameterInfo() as $name => $info) {
+      if (!isset($this->settings[$name . ':select']) && !isset($this->settings[$name]) && ($optional || (empty($info['optional']) && $info['type'] != 'hidden'))) {
+        $vars[$name] = $info;
+      }
+    }
+    return isset($vars) ? $vars : array();
+  }
+
+  /**
+   * Returns the about variables the plugin provides for later evaluated elements.
+   *
+   * Note that this method returns info about the provided variables as defined
+   * by the plugin. Thus this resembles the original info, which may be
+   * adapted via configuration.
+   *
+   * @see self::providesVariables()
+   */
+  public function pluginProvidesVariables() {
+    return isset($this->info['provides']) ? $this->info['provides'] : array();
+  }
+
+  /**
+   * Returns info about all variables provided for later evaluated elements.
+   *
+   * @see self::pluginProvidesVariables()
+   */
+  public function providesVariables() {
+    foreach ($this->pluginProvidesVariables() as $name => $info) {
+      $info['source name'] = $name;
+      $info['label'] = isset($this->settings[$name . ':label']) ? $this->settings[$name . ':label'] : $info['label'];
+      if (isset($this->settings[$name . ':var'])) {
+        $name = $this->settings[$name . ':var'];
+      }
+      $provides[$name] = $info;
+    }
+    return isset($provides) ? $provides : array();
+  }
+
+  /**
+   * Returns the info of the plugin.
+   */
+  public function info() {
+    return $this->info;
+  }
+
+  /**
+   * When converted to a string, just use the export format.
+   */
+  public function __toString() {
+    return $this->isRoot() ? $this->export() : entity_var_json_export($this->export());
+  }
+
+  /**
+   * Gets variables to return once the configuration has been executed.
+   */
+  protected function returnVariables(RulesState $state, $result = NULL) {
+    $var_info = $this->providesVariables();
+    foreach ($var_info as $name => $info) {
+      try {
+        $vars[$name] = $this->getArgument($name, $info, $state);
+      }
+      catch (RulesEvaluationException $e) {
+        // Ignore not existing variables.
+        $vars[$name] = NULL;
+      }
+      $var_info[$name] += array('allow null' => TRUE);
+    }
+    return isset($vars) ? array_values(rules_unwrap_data($vars, $var_info)) : array();
+  }
+
+  /**
+   * Sets up the execution state for the given arguments.
+   */
+  public function setUpState(array $args) {
+    $state = new RulesState();
+    $vars = $this->setUpVariables();
+    // Fix numerically indexed args to start with 0.
+    if (!isset($args[rules_array_key($vars)])) {
+      $args = array_values($args);
+    }
+    $offset = 0;
+    foreach (array_keys($vars) as $i => $name) {
+      $info = $vars[$name];
+      if (!empty($info['handler']) || (isset($info['parameter']) && $info['parameter'] === FALSE)) {
+        $state->addVariable($name, NULL, $info);
+        // Count the variables that are not passed as parameters.
+        $offset++;
+      }
+      // Support numerically indexed arrays as well as named parameter style.
+      // The index is reduced to exclude non-parameter variables.
+      elseif (isset($args[$i - $offset])) {
+        $state->addVariable($name, $args[$i - $offset], $info);
+      }
+      elseif (isset($args[$name])) {
+        $state->addVariable($name, $args[$name], $info);
+      }
+      elseif (empty($info['optional']) && $info['type'] != 'hidden') {
+        throw new RulesEvaluationException('Argument %name is missing.', array('%name' => $name), $this, RulesLog::ERROR);
+      }
+    }
+    return $state;
+  }
+
+  /**
+   * Returns info about all variables that have to be setup in the state.
+   */
+  protected function setUpVariables() {
+    return $this->parameterInfo(TRUE);
+  }
+
+  /**
+   * Returns info about variables available to be used as arguments for this element.
+   *
+   * As this is called very often, e.g. during integrity checks, we statically
+   * cache the results.
+   *
+   * @see RulesPlugin::resetInternalCache()
+   */
+  public function availableVariables() {
+    if (!isset($this->availableVariables)) {
+      $this->availableVariables = !$this->isRoot() ? $this->parent->stateVariables($this) : RulesState::defaultVariables();
+    }
+    return $this->availableVariables;
+  }
+
+  /**
+   * Returns asserted additions to the available variable info.
+   *
+   * Any returned info is merged into the variable info, in case the execution
+   * flow passes the element.
+   * E.g. this is used to assert the content type of a node if the condition
+   * is met, such that the per-node type properties are available.
+   */
+  protected function variableInfoAssertions() {
+    return array();
+  }
+
+  /**
+   * Gets the name of this plugin instance.
+   *
+   * The returned name should identify the code which drives this plugin.
+   */
+  public function getPluginName() {
+    return $this->itemName;
+  }
+
+  /**
+   * Calculates an array of required modules.
+   *
+   * You can use $this->dependencies to access dependencies for saved
+   * configurations.
+   */
+  public function dependencies() {
+    $this->processSettings();
+    $modules = isset($this->itemInfo['module']) && $this->itemInfo['module'] != 'rules' ? array($this->itemInfo['module'] => 1) : array();
+    foreach ($this->pluginParameterInfo() as $name => $info) {
+      if (isset($this->settings[$name . ':process']) && $this->settings[$name . ':process'] instanceof RulesDataProcessor) {
+        $modules += array_flip($this->settings[$name . ':process']->dependencies());
+      }
+    }
+    return array_keys($modules);
+  }
+
+  /**
+   * Whether the currently logged in user has access to all configured elements.
+   *
+   * Note that this only checks whether the current user has permission to all
+   * configured elements, but not whether a user has access to configure Rule
+   * configurations in general. Use rules_config_access() for that.
+   *
+   * Use this to determine access permissions for configuring or triggering the
+   * execution of certain configurations independent of the Rules UI.
+   *
+   * @see rules_config_access()
+   */
+  public function access() {
+    $this->processSettings();
+    foreach ($this->pluginParameterInfo() as $name => $info) {
+      if (isset($this->settings[$name . ':select']) && $wrapper = $this->applyDataSelector($this->settings[$name . ':select'])) {
+        if ($wrapper->access('view') === FALSE) {
+          return FALSE;
+        }
+      }
+      // Incorporate access checks for data processors and input evaluators.
+      if (isset($this->settings[$name . ':process']) && $this->settings[$name . ':process'] instanceof RulesDataProcessor && !$this->settings[$name . ':process']->editAccess()) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+
+  /**
+   * Processes the settings e.g. to prepare input evaluators.
+   *
+   * Usually settings get processed automatically, however if $this->settings
+   * has been altered manually after element construction, it needs to be
+   * invoked explicitly with $force set to TRUE.
+   */
+  public function processSettings($force = FALSE) {
+    // Process if not done yet.
+    if ($force || !empty($this->settings['#_needs_processing'])) {
+      $var_info = $this->availableVariables();
+      foreach ($this->pluginParameterInfo() as $name => $info) {
+        // Prepare input evaluators.
+        if (isset($this->settings[$name])) {
+          $this->settings[$name . ':process'] = $this->settings[$name];
+          RulesDataInputEvaluator::prepareSetting($this->settings[$name . ':process'], $info, $var_info);
+        }
+        // Prepare data processors.
+        elseif (isset($this->settings[$name . ':select']) && !empty($this->settings[$name . ':process'])) {
+          RulesDataProcessor::prepareSetting($this->settings[$name . ':process'], $info, $var_info);
+        }
+        // Clean up.
+        if (empty($this->settings[$name . ':process'])) {
+          unset($this->settings[$name . ':process']);
+        }
+      }
+      unset($this->settings['#_needs_processing']);
+    }
+  }
+
+  /**
+   * Makes sure the plugin is configured right.
+   *
+   * "Configured right" means all needed variables are available in the
+   * element's scope and dependent modules are enabled.
+   *
+   * @return $this
+   *
+   * @throws RulesIntegrityException
+   *   In case of a failed integrity check, a RulesIntegrityException exception
+   *   is thrown.
+   */
+  public function integrityCheck() {
+    // First process the settings if not done yet.
+    $this->processSettings();
+    // Check dependencies using the pre-calculated dependencies stored in
+    // $this->dependencies. Fail back to calculation them on the fly, e.g.
+    // during creation.
+    $dependencies = empty($this->dependencies) ? $this->dependencies() : $this->dependencies;
+    foreach ($dependencies as $module) {
+      if (!module_exists($module)) {
+        throw new RulesDependencyException(t('Missing required module %name.', array('%name' => $module)));
+      }
+    }
+    // Check the parameter settings.
+    $this->checkParameterSettings();
+    // Check variable names for provided variables to be valid.
+    foreach ($this->pluginProvidesVariables() as $name => $info) {
+      if (isset($this->settings[$name . ':var'])) {
+        $this->checkVarName($this->settings[$name . ':var']);
+      }
+    }
+    return $this;
+  }
+
+  protected function checkVarName($name) {
+    if (!preg_match('/^[0-9a-zA-Z_]*$/', $name)) {
+      throw new RulesIntegrityException(t('%plugin: The variable name %name contains not allowed characters.', array('%plugin' => $this->getPluginName(), '%name' => $name)), $this);
+    }
+  }
+
+  /**
+   * Checks whether parameters are correctly configured.
+   */
+  protected function checkParameterSettings() {
+    foreach ($this->pluginParameterInfo() as $name => $info) {
+      if (isset($info['restriction']) && $info['restriction'] == 'selector' && isset($this->settings[$name])) {
+        throw new RulesIntegrityException(t("The parameter %name may only be configured using a selector.", array('%name' => $name)), array($this, 'parameter', $name));
+      }
+      elseif (isset($info['restriction']) && $info['restriction'] == 'input' && isset($this->settings[$name . ':select'])) {
+        throw new RulesIntegrityException(t("The parameter %name may not be configured using a selector.", array('%name' => $name)), array($this, 'parameter', $name));
+      }
+      elseif (!empty($this->settings[$name . ':select']) && !$this->applyDataSelector($this->settings[$name . ':select'])) {
+        throw new RulesIntegrityException(t("Data selector %selector for parameter %name is invalid.", array('%selector' => $this->settings[$name . ':select'], '%name' => $name)), array($this, 'parameter', $name));
+      }
+      elseif ($arg_info = $this->getArgumentInfo($name)) {
+        // If we have enough metadata, check whether the types match.
+        if (!RulesData::typesMatch($arg_info, $info)) {
+          throw new RulesIntegrityException(t("The data type of the configured argument does not match the parameter's %name requirement.", array('%name' => $name)), array($this, 'parameter', $name));
+        }
+      }
+      elseif (!$this->isRoot() && !isset($this->settings[$name]) && empty($info['optional']) && $info['type'] != 'hidden') {
+        throw new RulesIntegrityException(t('Missing configuration for parameter %name.', array('%name' => $name)), array($this, 'parameter', $name));
+      }
+      // @todo Make sure used values are allowed.
+      // (key/value pairs + allowed values).
+    }
+  }
+
+  /**
+   * Returns the argument for the parameter $name described with $info.
+   *
+   * Returns the argument as configured in the element settings for the
+   * parameter $name described with $info.
+   *
+   * @param string $name
+   *   The name of the parameter for which to get the argument.
+   * @param $info
+   *   Info about the parameter.
+   * @param RulesState $state
+   *   The current evaluation state.
+   * @param string $langcode
+   *   (optional) The language code used to get the argument value if the
+   *   argument value should be translated. By default (NULL) the current
+   *   interface language will be used.
+   *
+   * @return
+   *   The argument, possibly wrapped.
+   *
+   * @throws RulesEvaluationException
+   *   In case the argument cannot be retrieved an exception is thrown.
+   */
+  protected function getArgument($name, $info, RulesState $state, $langcode = NULL) {
+    // Only apply the langcode if the parameter has been marked translatable.
+    if (empty($info['translatable'])) {
+      $langcode = LANGUAGE_NONE;
+    }
+    elseif (!isset($langcode)) {
+      $langcode = $GLOBALS['language']->language;
+    }
+
+    if (!empty($this->settings[$name . ':select'])) {
+      $arg = $state->applyDataSelector($this->settings[$name . ':select'], $langcode);
+    }
+    elseif (isset($this->settings[$name])) {
+      $arg = rules_wrap_data($this->settings[$name], $info);
+      // We don't sanitize directly specified values.
+      $skip_sanitize = TRUE;
+    }
+    elseif ($state->varinfo($name)) {
+      $arg = $state->get($name);
+    }
+    elseif (empty($info['optional']) && $info['type'] != 'hidden') {
+      throw new RulesEvaluationException('Required parameter %name is missing.', array('%name' => $name), $this, RulesLog::ERROR);
+    }
+    else {
+      $arg = isset($info['default value']) ? $info['default value'] : NULL;
+      $skip_sanitize = TRUE;
+      $info['allow null'] = TRUE;
+    }
+    // Make sure the given value is set if required (default).
+    if (!isset($arg) && empty($info['allow null'])) {
+      throw new RulesEvaluationException('The provided argument for parameter %name is empty.', array('%name' => $name), $this);
+    }
+
+    // Support passing already sanitized values.
+    if ($info['type'] == 'text' && !isset($skip_sanitize) && !empty($info['sanitize']) && !($arg instanceof EntityMetadataWrapper)) {
+      $arg = check_plain((string) $arg);
+    }
+
+    // Apply any configured data processors.
+    if (!empty($this->settings[$name . ':process'])) {
+      // For processing, make sure the data is unwrapped now.
+      $return = rules_unwrap_data(array($arg), array($info));
+      // @todo For Drupal 8: Refactor to add the name and language code as
+      // separate parameter to process().
+      $info['#name'] = $name;
+      $info['#langcode'] = $langcode;
+      return isset($return[0]) ? $this->settings[$name . ':process']->process($return[0], $info, $state, $this) : NULL;
+    }
+    return $arg;
+  }
+
+  /**
+   * Gets the right arguments for executing the element.
+   *
+   * @throws RulesEvaluationException
+   *   If case an argument cannot be retrieved an exception is thrown.
+   */
+  protected function getExecutionArguments(RulesState $state) {
+    $parameters = $this->pluginParameterInfo();
+    // If there is language parameter, get its value first so it can be used
+    // for getting other translatable values.
+    $langcode = NULL;
+    if (isset($parameters['language'])) {
+      $lang_arg = $this->getArgument('language', $parameters['language'], $state);
+      $langcode = $lang_arg instanceof EntityMetadataWrapper ? $lang_arg->value() : $lang_arg;
+    }
+    // Now get all arguments.
+    foreach ($parameters as $name => $info) {
+      $args[$name] = $name == 'language' ? $lang_arg : $this->getArgument($name, $info, $state, $langcode);
+    }
+    // Append the settings and the execution state. Faces will append $this.
+    $args['settings'] = $this->settings;
+    $args['state'] = $state;
+    // Make the wrapped variables for the arguments available in the state.
+    $state->currentArguments = $args;
+    return rules_unwrap_data($args, $parameters);
+  }
+
+  /**
+   * Applies the given data selector.
+   *
+   * Applies the given data selector by using the info about available
+   * variables. Thus it doesn't require an actual evaluation state.
+   *
+   * @param string $selector
+   *   The selector string, e.g. "node:author:mail".
+   *
+   * @return EntityMetadataWrapper
+   *   An empty wrapper for the given selector or FALSE if the selector couldn't
+   *   be applied.
+   */
+  public function applyDataSelector($selector) {
+    $parts = explode(':', str_replace('-', '_', $selector), 2);
+    if (($vars = $this->availableVariables()) && isset($vars[$parts[0]]['type'])) {
+      $wrapper = rules_wrap_data(NULL, $vars[$parts[0]], TRUE);
+      if (count($parts) > 1 && $wrapper instanceof EntityMetadataWrapper) {
+        try {
+          foreach (explode(':', $parts[1]) as $name) {
+            if ($wrapper instanceof EntityListWrapper || $wrapper instanceof EntityStructureWrapper) {
+              $wrapper = $wrapper->get($name);
+            }
+            else {
+              return FALSE;
+            }
+          }
+        }
+        // Return FALSE if there is no wrappper or we get an exception.
+        catch (EntityMetadataWrapperException $e) {
+          return FALSE;
+        }
+      }
+    }
+    return isset($wrapper) ? $wrapper : FALSE;
+  }
+
+  /**
+   * Returns info about the configured argument.
+   *
+   * @return
+   *   The determined info. If it's not known NULL is returned.
+   */
+  public function getArgumentInfo($name) {
+    $vars = $this->availableVariables();
+    if (!empty($this->settings[$name . ':select']) && !empty($vars[$this->settings[$name . ':select']])) {
+      return $vars[$this->settings[$name . ':select']];
+    }
+    elseif (!empty($this->settings[$name . ':select'])) {
+      if ($wrapper = $this->applyDataSelector($this->settings[$name . ':select'])) {
+        return $wrapper->info();
+      }
+      return;
+    }
+    elseif (isset($this->settings[$name . ':type'])) {
+      return array('type' => $this->settings[$name . ':type']);
+    }
+    elseif (!isset($this->settings[$name]) && isset($vars[$name])) {
+      return $vars[$name];
+    }
+  }
+
+  /**
+   * Saves the configuration to the database.
+   *
+   * The configuration is saved regardless whether this method is invoked on
+   * the rules configuration or a contained rule element.
+   */
+  public function save($name = NULL, $module = 'rules') {
+    if (isset($this->parent)) {
+      $this->parent->sortChildren();
+      return $this->parent->save($name, $module);
+    }
+    else {
+      // Update the dirty flag before saving.
+      // However, this operation depends on a fully built Rules-cache, so skip
+      // it when entities in code are imported to the database.
+      // @see _rules_rebuild_cache()
+      if (empty($this->is_rebuild)) {
+        rules_config_update_dirty_flag($this, FALSE);
+        // In case the config is not dirty, pre-calculate the dependencies for
+        // later checking. Note that this also triggers processing settings if
+        // necessary.
+        // @see rules_modules_enabled()
+        if (empty($this->dirty)) {
+          $this->dependencies = $this->dependencies();
+        }
+      }
+
+      $this->plugin = $this->itemName;
+      $this->name = isset($name) ? $name : $this->name;
+      // Module stores the module via which the rule is configured and is used
+      // for generating machine names with the right prefix. However, for
+      // default configurations 'module' points to the module providing the
+      // default configuration, so the module via which the rules is configured
+      // is stored in the "owner" property.
+      // @todo For Drupal 8 use "owner" for generating machine names also and
+      // module only for the modules providing default configurations.
+      $this->module = !isset($this->module) || $module != 'rules' ? $module : $this->module;
+      if (!isset($this->owner)) {
+        $this->owner = 'rules';
+      }
+      $this->ensureNameExists();
+      $this->data = $this;
+      $return = entity_get_controller('rules_config')->save($this);
+      unset($this->data);
+
+      // Care about clearing necessary caches.
+      if (!empty($this->is_rebuild)) {
+        rules_clear_cache();
+      }
+      else {
+        $plugin_info = $this->pluginInfo();
+        if (!empty($plugin_info['component'])) {
+          // When component variables changes rebuild the complete cache so the
+          // changes to the provided action/condition take affect.
+          if (empty($this->original) || $this->componentVariables() != $this->original->componentVariables()) {
+            rules_clear_cache();
+          }
+          // Clear components cached for evaluation.
+          cache_clear_all('comp_', 'cache_rules', TRUE);
+        }
+        elseif ($this->plugin == 'reaction rule') {
+          // Clear event sets cached for evaluation.
+          cache_clear_all('event_', 'cache_rules', TRUE);
+          // Clear event whitelist for rebuild.
+          cache_clear_all('rules_event_whitelist', 'cache_rules', TRUE);
+        }
+        drupal_static_reset('rules_get_cache');
+        drupal_static_reset('rules_config_update_dirty_flag');
+      }
+
+      return $return;
+    }
+  }
+
+  /**
+   * Ensure the configuration has a name. If not, generate one.
+   */
+  protected function ensureNameExists() {
+    if (!isset($this->module)) {
+      $this->module = 'rules';
+    }
+    if (!isset($this->name)) {
+      // Find a unique name for this configuration.
+      $this->name = $this->module . '_';
+      for ($i = 0; $i < 8; $i++) {
+        // Alphanumeric name generation.
+        $rnd = mt_rand(97, 122);
+        $this->name .= chr($rnd);
+      }
+    }
+  }
+
+  public function __sleep() {
+    // Keep the id always as we need it for the recursion prevention.
+    $array = drupal_map_assoc(array('parent', 'id', 'elementId', 'weight', 'settings'));
+    // Keep properties related to configurations if they are there.
+    $info = entity_get_info('rules_config');
+    $fields = array_merge($info['schema_fields_sql']['base table'], array('recursion', 'tags'));
+    foreach ($fields as $key) {
+      if (isset($this->$key)) {
+        $array[$key] = $key;
+      }
+    }
+    return $array;
+  }
+
+  /**
+   * Optimizes a rule configuration in order to speed up evaluation.
+   *
+   * Additional optimization methods may be inserted by an extender
+   * implementing the RulesOptimizationInterface. By default, there is no
+   * optimization extender.
+   *
+   * An optimization method may rearrange the internal structure of a
+   * configuration in order to speed up the evaluation. As the configuration may
+   * change optimized configurations should not be saved permanently, except
+   * when saving it temporary, for later execution only.
+   *
+   * @see RulesOptimizationInterface
+   */
+  public function optimize() {
+    // Make sure settings are processed before configs are cached.
+    $this->processSettings();
+    if ($this->facesAs('RulesOptimizationInterface')) {
+      $this->__call('optimize');
+    }
+  }
+
+  /**
+   * Deletes configuration from database.
+   *
+   * If invoked on a rules configuration it is deleted from database. If
+   * invoked on a contained rule element, it's removed from the configuration.
+   */
+  public function delete() {
+    if (isset($this->parent)) {
+      foreach ($this->parent->children as $key => $child) {
+        if ($child === $this) {
+          unset($this->parent->children[$key]);
+          break;
+        }
+      }
+    }
+    elseif (isset($this->id)) {
+      entity_get_controller('rules_config')->delete(array($this->name));
+      rules_clear_cache();
+    }
+  }
+
+  public function internalIdentifier() {
+    return isset($this->id) ? $this->id : NULL;
+  }
+
+  /**
+   * Returns the config name.
+   */
+  public function identifier() {
+    return isset($this->name) ? $this->name : NULL;
+  }
+
+  public function entityInfo() {
+    return entity_get_info('rules_config');
+  }
+
+  public function entityType() {
+    return 'rules_config';
+  }
+
+  /**
+   * Checks if the configuration has a certain exportable status.
+   *
+   * @param $status
+   *   A status constant, i.e. one of ENTITY_CUSTOM, ENTITY_IN_CODE,
+   *   ENTITY_OVERRIDDEN or ENTITY_FIXED.
+   *
+   * @return bool
+   *   TRUE if the configuration has the status, else FALSE.
+   *
+   * @see entity_has_status()
+   */
+  public function hasStatus($status) {
+    return $this->isRoot() && isset($this->status) && ($this->status & $status) == $status;
+  }
+
+  /**
+   * Removes circular object references so PHP garbage collector can work.
+   */
+  public function destroy() {
+    parent::destroy();
+    $this->parent = NULL;
+  }
+
+  /**
+   * Seamlessly invokes the method implemented via faces.
+   *
+   * Frees the caller from having to think about references.
+   */
+  public function form(&$form, &$form_state, array $options = array()) {
+    $this->__call('form', array(&$form, &$form_state, $options));
+  }
+
+  public function form_validate($form, &$form_state) {
+    $this->__call('form_validate', array($form, &$form_state));
+  }
+
+  public function form_submit($form, &$form_state) {
+    $this->__call('form_submit', array($form, &$form_state));
+  }
+
+  /**
+   * Returns the label of the element.
+   */
+  public function label() {
+    if (!empty($this->label) && $this->label != t('unlabeled')) {
+      return $this->label;
+    }
+    $info = $this->info();
+    return isset($info['label']) ? $info['label'] : (!empty($this->name) ? $this->name : t('unlabeled'));
+  }
+
+  /**
+   * Returns the name of the element's plugin.
+   */
+  public function plugin() {
+    return $this->itemName;
+  }
+
+  /**
+   * Returns info about the element's plugin.
+   */
+  public function pluginInfo() {
+    $this->forceSetUp();
+    return $this->itemInfo;
+  }
+
+  /**
+   * Applies the given export.
+   */
+  public function import(array $export) {
+    $this->importSettings($export[strtoupper($this->plugin())]);
+  }
+
+  protected function importSettings($export) {
+    // Import parameter settings.
+    $export += array('USING' => array(), 'PROVIDE' => array());
+    foreach ($export['USING'] as $name => $param_export) {
+      $this->importParameterSetting($name, $param_export);
+    }
+    foreach ($export['PROVIDE'] as $name => $var_export) {
+      // The key of $var_export is the variable name, the value the label.
+      $this->settings[$name . ':var'] = rules_array_key($var_export);
+      $this->settings[$name . ':label'] = reset($var_export);
+    }
+  }
+
+  protected function importParameterSetting($name, $export) {
+    if (is_array($export) && isset($export['select'])) {
+      $this->settings[$name . ':select'] = $export['select'];
+      if (count($export) > 1) {
+        // Add in processor settings.
+        unset($export['select']);
+        $this->settings[$name . ':process'] = $export;
+      }
+    }
+    // Convert back the [selector] strings being an array with one entry.
+    elseif (is_array($export) && count($export) == 1 && isset($export[0])) {
+      $this->settings[$name . ':select'] = $export[0];
+    }
+    elseif (is_array($export) && isset($export['value'])) {
+      $this->settings[$name] = $export['value'];
+    }
+    else {
+      $this->settings[$name] = $export;
+    }
+  }
+
+  /**
+   * Exports a rule configuration.
+   *
+   * @param string $prefix
+   *   An optional prefix for each line.
+   * @param bool $php
+   *   Optional. Set to TRUE to format the export using PHP arrays. By default
+   *   JSON is used.
+   *
+   * @return
+   *   The exported configuration.
+   *
+   * @see rules_import()
+   */
+  public function export($prefix = '', $php = FALSE) {
+    $export = $this->exportToArray();
+    return $this->isRoot() ? $this->returnExport($export, $prefix, $php) : $export;
+  }
+
+  protected function exportToArray() {
+    $export[strtoupper($this->plugin())] = $this->exportSettings();
+    return $export;
+  }
+
+  protected function exportSettings() {
+    $export = array();
+    if (!$this->isRoot()) {
+      foreach ($this->pluginParameterInfo() as $name => $info) {
+        if (($return = $this->exportParameterSetting($name, $info)) !== NULL) {
+          $export['USING'][$name] = $return;
+        }
+      }
+      foreach ($this->providesVariables() as $name => $info) {
+        if (!empty($info['source name'])) {
+          $export['PROVIDE'][$info['source name']][$name] = $info['label'];
+        }
+      }
+    }
+    return $export;
+  }
+
+  protected function exportParameterSetting($name, $info) {
+    if (isset($this->settings[$name]) && (empty($info['optional']) || !isset($info['default value']) || $this->settings[$name] != $info['default value'])) {
+      // In case of an array-value wrap the value into another array, such that
+      // the value cannot be confused with an exported data selector.
+      return is_array($this->settings[$name]) ? array('value' => $this->settings[$name]) : $this->settings[$name];
+    }
+    elseif (isset($this->settings[$name . ':select'])) {
+      if (isset($this->settings[$name . ':process']) && $processor = $this->settings[$name . ':process']) {
+        $export['select'] = $this->settings[$name . ':select'];
+        $export += $processor instanceof RulesDataProcessor ? $processor->getChainSettings() : $processor;
+        return $export;
+      }
+      // If there is no processor use a simple array to abbreviate this usual
+      // case. In JSON this turns to a nice [selector] string.
+      return array($this->settings[$name . ':select']);
+    }
+  }
+
+  /**
+   * Finalizes the configuration export.
+   *
+   * Adds general attributes regarding the configuration and returns it in the
+   * right format for export.
+   *
+   * @param $export
+   * @param string $prefix
+   *   An optional prefix for each line.
+   * @param bool $php
+   *   Optional. Set to TRUE to format the export using PHP arrays. By default
+   *   JSON is used.
+   */
+  protected function returnExport($export, $prefix = '', $php = FALSE) {
+    $this->ensureNameExists();
+    if (!empty($this->label) && $this->label != t('unlabeled')) {
+      $export_cfg[$this->name]['LABEL'] = $this->label;
+    }
+    $export_cfg[$this->name]['PLUGIN'] = $this->plugin();
+    if (!empty($this->weight)) {
+      $export_cfg[$this->name]['WEIGHT'] = $this->weight;
+    }
+    if (isset($this->active) && !$this->active) {
+      $export_cfg[$this->name]['ACTIVE'] = FALSE;
+    }
+    if (!empty($this->owner)) {
+      $export_cfg[$this->name]['OWNER'] = $this->owner;
+    }
+    if (!empty($this->tags)) {
+      $export_cfg[$this->name]['TAGS'] = $this->tags;
+    }
+    if ($modules = $this->dependencies()) {
+      $export_cfg[$this->name]['REQUIRES'] = $modules;
+    }
+    if (!empty($this->access_exposed)) {
+      $export_cfg[$this->name]['ACCESS_EXPOSED'] = $this->access_exposed;
+    };
+    $export_cfg[$this->name] += $export;
+    return $php ? entity_var_export($export_cfg, $prefix) : entity_var_json_export($export_cfg, $prefix);
+  }
+
+  /**
+   * Resets any internal static caches.
+   *
+   * This function does not reset regular caches as retrieved via
+   * rules_get_cache(). Usually, it's invoked automatically when a Rules
+   * configuration is modified.
+   *
+   * Static caches are reset for the element and any elements down the tree. To
+   * clear static caches of the whole configuration, invoke the function at the
+   * root.
+   *
+   * @see RulesPlugin::availableVariables()
+   */
+  public function resetInternalCache() {
+    $this->availableVariables = NULL;
+  }
+
+}
+
+/**
+ * Defines a common base class for so-called "Abstract Plugins" like actions.
+ *
+ * Modules have to provide the concrete plugin implementation.
+ */
+abstract class RulesAbstractPlugin extends RulesPlugin {
+
+  protected $elementName;
+  protected $info = array('parameter' => array(), 'provides' => array());
+  protected $infoLoaded = FALSE;
+
+  /**
+   * @param string $name
+   *   The plugin implementation's name.
+   * @param $settings
+   *   Further information provided about the plugin. Optional.
+   * @throws RulesException
+   *   If validation of the passed settings fails RulesExceptions are thrown.
+   */
+  public function __construct($name = NULL, $settings = array()) {
+    $this->elementName = $name;
+    $this->settings = (array) $settings + array('#_needs_processing' => TRUE);
+    $this->setUp();
+  }
+
+  protected function setUp() {
+    parent::setUp();
+    if (isset($this->cache[$this->itemName . '_info'][$this->elementName])) {
+      $this->info = $this->cache[$this->itemName . '_info'][$this->elementName];
+      // Remember that the info has been correctly setup.
+      // @see self::forceSetup()
+      $this->infoLoaded = TRUE;
+
+      // Register the defined class, if any.
+      if (isset($this->info['class'])) {
+        $this->faces['RulesPluginImplInterface'] = 'RulesPluginImplInterface';
+        $face_methods = get_class_methods('RulesPluginImplInterface');
+        $class_info = array(1 => $this->info['class']);
+        foreach ($face_methods as $method) {
+          $this->facesMethods[$method] = $class_info;
+        }
+      }
+      // Add in per-plugin implementation callbacks if any.
+      if (!empty($this->info['faces_cache'])) {
+        foreach ($this->info['faces_cache'] as $face => $data) {
+          list($methods, $file_names) = $data;
+          foreach ($methods as $method => $callback) {
+            $this->facesMethods[$method] = $callback;
+          }
+          foreach ((array) $file_names as $method => $name) {
+            $this->facesIncludes[$method] = array('module' => $this->info['module'], 'name' => $name);
+          }
+        }
+        // Invoke the info_alter callback, but only if it has been implemented.
+        if ($this->facesMethods['info_alter'] != $this->itemInfo['faces_cache'][0]['info_alter']) {
+          $this->__call('info_alter', array(&$this->info));
+        }
+      }
+    }
+    elseif (!empty($this->itemInfo['faces_cache']) && function_exists($this->elementName)) {
+      // We don't have any info, so just add the name as execution callback.
+      $this->override(array('execute' => $this->elementName));
+    }
+  }
+
+  public function forceSetUp() {
+    if (!isset($this->cache) || (!empty($this->itemInfo['faces_cache']) && !$this->faces)) {
+      $this->setUp();
+    }
+    // In case we have element specific information, which is not loaded yet,
+    // do so now. This might happen if the element has been initially loaded
+    // with an incomplete cache, i.e. during cache rebuilding.
+    elseif (!$this->infoLoaded && isset($this->cache[$this->itemName . '_info'][$this->elementName])) {
+      $this->setUp();
+    }
+  }
+
+  /**
+   * Returns the label of the element.
+   */
+  public function label() {
+    $info = $this->info();
+    return isset($info['label']) ? $info['label'] : t('@plugin "@name"', array('@name' => $this->elementName, '@plugin' => $this->plugin()));
+  }
+
+  public function access() {
+    $info = $this->info();
+    $this->loadBasicInclude();
+    if (!empty($info['access callback']) && !call_user_func($info['access callback'], $this->itemName, $this->getElementName())) {
+      return FALSE;
+    }
+    return parent::access() && $this->__call('access');
+  }
+
+  public function integrityCheck() {
+    // Do the usual integrity check first so the implementation's validation
+    // handler can rely on that already.
+    parent::integrityCheck();
+    // Make sure the element is known.
+    $this->forceSetUp();
+    if (!isset($this->cache[$this->itemName . '_info'][$this->elementName])) {
+      throw new RulesIntegrityException(t('Unknown @plugin %name.', array('@plugin' => $this->plugin(), '%name' => $this->elementName)));
+    }
+    $this->validate();
+    return $this;
+  }
+
+  public function processSettings($force = FALSE) {
+    // Process if not done yet.
+    if ($force || !empty($this->settings['#_needs_processing'])) {
+      $this->resetInternalCache();
+      // In case the element implements the info alteration callback, (re-)run
+      // the alteration so that any settings depending info alterations are
+      // applied.
+      if ($this->facesMethods && $this->facesMethods['info_alter'] != $this->itemInfo['faces_cache'][0]['info_alter']) {
+        $this->__call('info_alter', array(&$this->info));
+      }
+      // First let the plugin implementation do processing, so data types of the
+      // parameters are fixed when we process the settings.
+      $this->process();
+      parent::processSettings($force);
+    }
+  }
+
+  public function pluginParameterInfo() {
+    // Ensure the info alter callback has been executed.
+    $this->forceSetup();
+    return parent::pluginParameterInfo();
+  }
+
+  public function pluginProvidesVariables() {
+    // Ensure the info alter callback has been executed.
+    $this->forceSetup();
+    return parent::pluginProvidesVariables();
+  }
+
+  public function info() {
+    // Ensure the info alter callback has been executed.
+    $this->forceSetup();
+    return $this->info;
+  }
+
+  protected function variableInfoAssertions() {
+    // Get the implementation's assertions and map them to the variable names.
+    if ($assertions = $this->__call('assertions')) {
+      foreach ($assertions as $param_name => $data) {
+        $name = isset($this->settings[$param_name . ':select']) ? $this->settings[$param_name . ':select'] : $param_name;
+        $return[$name] = $data;
+      }
+      return $return;
+    }
+  }
+
+  public function import(array $export) {
+    // The key is the element name and the value the actual export.
+    $this->elementName = rules_array_key($export);
+    $export = reset($export);
+
+    // After setting the element name, setup the element again so the right
+    // element info is loaded.
+    $this->setUp();
+
+    if (!isset($export['USING']) && !isset($export['PROVIDES']) && !empty($export)) {
+      // The export has been abbreviated to skip "USING".
+      $export = array('USING' => $export);
+    }
+    $this->importSettings($export);
+  }
+
+  protected function exportToArray() {
+    $export = $this->exportSettings();
+    if (!$this->providesVariables()) {
+      // Abbreviate the export making "USING" implicit.
+      $export = isset($export['USING']) ? $export['USING'] : array();
+    }
+    return array($this->elementName => $export);
+  }
+
+  public function dependencies() {
+    $modules = array_flip(parent::dependencies());
+    $modules += array_flip((array) $this->__call('dependencies'));
+    return array_keys($modules + (!empty($this->info['module']) ? array($this->info['module'] => 1) : array()));
+  }
+
+  public function executeByArgs($args = array()) {
+    $replacements = array('%label' => $this->label(), '@plugin' => $this->itemName);
+    rules_log('Executing @plugin %label.', $replacements, RulesLog::INFO, $this, TRUE);
+    $this->processSettings();
+    // If there is no element info, just pass through the passed arguments.
+    // That way we support executing actions without any info at all.
+    if ($this->info()) {
+      $state = $this->setUpState($args);
+      module_invoke_all('rules_config_execute', $this);
+
+      $result = $this->evaluate($state);
+      $return = $this->returnVariables($state, $result);
+    }
+    else {
+      rules_log('Unable to execute @plugin %label.', $replacements, RulesLog::ERROR, $this);
+    }
+    $state->cleanUp();
+    rules_log('Finished executing of @plugin %label.', $replacements, RulesLog::INFO, $this, FALSE);
+    return $return;
+  }
+
+  /**
+   * Execute the configured execution callback and log that.
+   */
+  abstract protected function executeCallback(array $args, RulesState $state = NULL);
+
+  public function evaluate(RulesState $state) {
+    $this->processSettings();
+    try {
+      // Get vars as needed for execute and call it.
+      return $this->executeCallback($this->getExecutionArguments($state), $state);
+    }
+    catch (RulesEvaluationException $e) {
+      rules_log($e->msg, $e->args, $e->severity);
+      rules_log('Unable to evaluate %name.', array('%name' => $this->getPluginName()), RulesLog::WARN, $this);
+    }
+    // Catch wrapper exceptions that might occur due to failures loading an
+    // entity or similar.
+    catch (EntityMetadataWrapperException $e) {
+      rules_log('Unable to get a data value. Error: !error', array('!error' => $e->getMessage()), RulesLog::WARN);
+      rules_log('Unable to evaluate %name.', array('%name' => $this->getPluginName()), RulesLog::WARN, $this);
+    }
+  }
+
+  public function __sleep() {
+    return parent::__sleep() + array('elementName' => 'elementName');
+  }
+
+  public function getPluginName() {
+    return $this->itemName . " " . $this->elementName;
+  }
+
+  /**
+   * Gets the name of the configured action or condition.
+   */
+  public function getElementName() {
+    return $this->elementName;
+  }
+
+  /**
+   * Add in the data provided by the info hooks to the cache.
+   */
+  public function rebuildCache(&$itemInfo, &$cache) {
+    parent::rebuildCache($itemInfo, $cache);
+
+    // Include all declared files so we can find all implementations.
+    self::includeFiles();
+
+    // Get the plugin's own info data.
+    $cache[$this->itemName . '_info'] = rules_fetch_data($this->itemName . '_info');
+    foreach ($cache[$this->itemName . '_info'] as $name => &$info) {
+      $info += array(
+        'parameter' => isset($info['arguments']) ? $info['arguments'] : array(),
+        'provides' => isset($info['new variables']) ? $info['new variables'] : array(),
+        'base' => $name,
+        'callbacks' => array(),
+      );
+      unset($info['arguments'], $info['new variables']);
+
+      if (function_exists($info['base'])) {
+        $info['callbacks'] += array('execute' => $info['base']);
+      }
+
+      // We do not need to build a faces cache for RulesPluginHandlerInterface,
+      // which gets added in automatically as its a parent of
+      // RulesPluginImplInterface.
+      unset($this->faces['RulesPluginHandlerInterface']);
+
+      // Build up the per-plugin implementation faces cache.
+      foreach ($this->faces as $interface) {
+        $methods = $file_names = array();
+        $includes = self::getIncludeFiles($info['module']);
+
+        foreach (get_class_methods($interface) as $method) {
+          if (isset($info['callbacks'][$method]) && ($function = $info['callbacks'][$method])) {
+            $methods[$method][0] = $function;
+            $file_names[$method] = $this->getFileName($function, $includes);
+          }
+          // Note that this skips RulesPluginImplInterface, which is not
+          // implemented by plugin handlers.
+          elseif (isset($info['class']) && is_subclass_of($info['class'], $interface)) {
+            $methods[$method][1] = $info['class'];
+          }
+          elseif (function_exists($function = $info['base'] . '_' . $method)) {
+            $methods[$method][0] = $function;
+            $file_names[$method] = $this->getFileName($function, $includes);
+          }
+        }
+        // Cache only the plugin implementation specific callbacks.
+        $info['faces_cache'][$interface] = array($methods, array_filter($file_names));
+      }
+      // Filter out interfaces with no overridden methods.
+      $info['faces_cache'] = rules_filter_array($info['faces_cache'], 0, TRUE);
+      // We don't need that any more.
+      unset($info['callbacks'], $info['base']);
+    }
+  }
+
+  /**
+   * Loads this module's .rules.inc file.
+   *
+   * Makes sure the providing modules' .rules.inc file is included, as diverse
+   * callbacks may reside in that file.
+   */
+  protected function loadBasicInclude() {
+    static $included = array();
+
+    if (isset($this->info['module']) && !isset($included[$this->info['module']])) {
+      $module = $this->info['module'];
+      module_load_include('inc', $module, $module . '.rules');
+      $included[$module] = TRUE;
+    }
+  }
+
+  /**
+   * Makes sure all supported destinations are included.
+   */
+  public static function includeFiles() {
+    static $included;
+
+    if (!isset($included)) {
+      foreach (module_implements('rules_file_info') as $module) {
+        // rules.inc are already included thanks to the rules_hook_info() group.
+        foreach (self::getIncludeFiles($module, FALSE) as $name) {
+          module_load_include('inc', $module, $name);
+        }
+      }
+      $dirs = array();
+      foreach (module_implements('rules_directory') as $module) {
+        // Include all files once, so the discovery can find them.
+        $result = module_invoke($module, 'rules_directory');
+        if (!is_array($result)) {
+          $result = array($module => $result);
+        }
+        $dirs += $result;
+      }
+      foreach ($dirs as $module => $directory) {
+        $module_path = drupal_get_path('module', $module);
+        foreach (array('inc', 'php') as $extension) {
+          foreach (glob("$module_path/$directory/*.$extension") as $filename) {
+            include_once $filename;
+          }
+        }
+      }
+      $included = TRUE;
+    }
+  }
+
+  /**
+   * Returns all include files for a module.
+   *
+   * @param string $module
+   *   The module name.
+   * @param bool $all
+   *   If FALSE, the $module.rules.inc file isn't added.
+   *
+   * @return string[]
+   *   An array containing the names of all the include files for a module.
+   */
+  protected static function getIncludeFiles($module, $all = TRUE) {
+    $files = (array) module_invoke($module, 'rules_file_info');
+    // Automatically add "$module.rules_forms.inc" and "$module.rules.inc".
+    $files[] = $module . '.rules_forms';
+    if ($all) {
+      $files[] = $module . '.rules';
+    }
+    return $files;
+  }
+
+  protected function getFileName($function, $includes) {
+    static $filenames;
+    if (!isset($filenames) || !array_key_exists($function, $filenames)) {
+      $filenames[$function] = NULL;
+      $reflector = new ReflectionFunction($function);
+      // On windows the path contains backslashes instead of slashes, fix that.
+      $file = str_replace('\\', '/', $reflector->getFileName());
+      foreach ($includes as $include) {
+        $pos = strpos($file, $include . '.inc');
+        // Test whether the file ends with the given filename.inc.
+        if ($pos !== FALSE && strlen($file) - $pos == strlen($include) + 4) {
+          $filenames[$function] = $include;
+          return $include;
+        }
+      }
+    }
+    return $filenames[$function];
+  }
+
+}
+
+/**
+ * Interface for objects that can be used as actions.
+ */
+interface RulesActionInterface {
+
+  /**
+   * @return
+   *   As specified.
+   *
+   * @throws RulesEvaluationException
+   *   Throws an exception if not all necessary arguments have been provided.
+   */
+  public function execute();
+
+}
+
+/**
+ * Interface for objects that can be used as conditions.
+ */
+interface RulesConditionInterface {
+
+  /**
+   * @return bool
+   *
+   * @throws RulesEvaluationException
+   *   Throws an exception if not all necessary arguments have been provided.
+   */
+  public function execute();
+
+  /**
+   * Negate the result.
+   */
+  public function negate($negate = TRUE);
+
+  /**
+   * Returns whether the element is configured to negate the result.
+   */
+  public function isNegated();
+
+}
+
+/**
+ * Interface for objects that are triggerable.
+ */
+interface RulesTriggerableInterface {
+
+  /**
+   * Returns the array of (configured) event names associated with this object.
+   */
+  public function events();
+
+  /**
+   * Removes an event from the rule configuration.
+   *
+   * @param string $event_name
+   *   The name of the (configured) event to remove.
+   *
+   * @return RulesTriggerableInterface
+   *   The object instance itself, to allow chaining.
+   */
+  public function removeEvent($event_name);
+
+  /**
+   * Adds the specified event.
+   *
+   * @param string $event_name
+   *   The base name of the event to add.
+   * @param array $settings
+   *   (optional) The event settings. If there are no event settings, pass an
+   *   empty array (default).
+   *
+   * @return RulesTriggerableInterface
+   */
+  public function event($event_name, array $settings = array());
+
+  /**
+   * Gets the event settings associated with the given (configured) event.
+   *
+   * @param string $event_name
+   *   The (configured) event's name.
+   *
+   * @return array|null
+   *   The array of event settings, or NULL if there are no settings.
+   */
+  public function getEventSettings($event_name);
+
+}
+
+/**
+ * Provides the base interface for implementing abstract plugins via classes.
+ */
+interface RulesPluginHandlerInterface {
+
+  /**
+   * Validates $settings independent from a form submission.
+   *
+   * @throws RulesIntegrityException
+   *   In case of validation errors, RulesIntegrityExceptions are thrown.
+   */
+  public function validate();
+
+  /**
+   * Processes settings independent from a form submission.
+   *
+   * Processing results may be stored and accessed on execution time
+   * in $settings.
+   */
+  public function process();
+
+  /**
+   * Allows altering of the element's action/condition info.
+   *
+   * Note that this method is also invoked on evaluation time, thus any costly
+   * operations should be avoided.
+   *
+   * @param $element_info
+   *   A reference on the element's info as returned by RulesPlugin::info().
+   */
+  public function info_alter(&$element_info);
+
+  /**
+   * Checks whether the user has access to configure this element.
+   *
+   * Note that this only covers access for already created elements. In order to
+   * control access for creating or using elements specify an 'access callback'
+   * in the element's info array.
+   *
+   * @see hook_rules_action_info()
+   */
+  public function access();
+
+  /**
+   * Returns an array of required modules.
+   */
+  public function dependencies();
+
+  /**
+   * Alters the generated configuration form of the element.
+   *
+   * Validation and processing of the settings should be untied from the form
+   * and implemented in validate() and process() wherever it makes sense.
+   * For the remaining cases where form tied validation and processing is needed
+   * make use of the form API #element_validate and #value_callback properties.
+   */
+  public function form_alter(&$form, $form_state, $options);
+
+  /**
+   * Returns an array of info assertions for the specified parameters.
+   *
+   * This allows conditions to assert additional metadata, such as info about
+   * the fields of a bundle.
+   *
+   * @see RulesPlugin::variableInfoAssertions()
+   */
+  public function assertions();
+
+}
+
+/**
+ * Interface for implementing conditions via classes.
+ *
+ * In addition to the interface an execute() and a static getInfo() method must
+ * be implemented. The static getInfo() method has to return the info as
+ * returned by hook_rules_condition_info() but including an additional 'name'
+ * key, specifying the plugin name.
+ * The execute method is the equivalent to the usual execution callback and
+ * gets the parameters passed as specified in the info array.
+ *
+ * See RulesNodeConditionType for an example and rules_discover_plugins()
+ * for information about class discovery.
+ */
+interface RulesConditionHandlerInterface extends RulesPluginHandlerInterface {}
+
+/**
+ * Interface for implementing actions via classes.
+ *
+ * In addition to the interface an execute() and a static getInfo() method must
+ * be implemented. The static getInfo() method has to return the info as
+ * returned by hook_rules_action_info() but including an additional 'name' key,
+ * specifying the plugin name.
+ * The execute method is the equivalent to the usual execution callback and
+ * gets the parameters passed as specified in the info array.
+ *
+ * See RulesNodeConditionType for an example and rules_discover_plugins()
+ * for information about class discovery.
+ */
+interface RulesActionHandlerInterface extends RulesPluginHandlerInterface {}
+
+/**
+ * Interface used for implementing an abstract plugin via Faces.
+ *
+ * Provides the interface used for implementing an abstract plugin by using
+ * the Faces extension mechanism.
+ */
+interface RulesPluginImplInterface extends RulesPluginHandlerInterface {
+
+  /**
+   * Executes the action or condition making use of the parameters as specified.
+   */
+  public function execute();
+
+}
+
+/**
+ * Interface for optimizing evaluation.
+ *
+ * @see RulesContainerPlugin::optimize()
+ */
+interface RulesOptimizationInterface {
+
+  /**
+   * Optimizes a rule configuration in order to speed up evaluation.
+   */
+  public function optimize();
+
+}
+
+/**
+ * Base class for implementing abstract plugins via classes.
+ */
+abstract class RulesPluginHandlerBase extends FacesExtender implements RulesPluginHandlerInterface {
+
+  /**
+   * @var RulesAbstractPlugin
+   */
+  protected $element;
+
+  /**
+   * Overridden to provide $this->element to make the code more meaningful.
+   */
+  public function __construct(FacesExtendable $object) {
+    $this->object = $object;
+    $this->element = $object;
+  }
+
+  /**
+   * Implements RulesPluginImplInterface.
+   */
+  public function access() {
+    return TRUE;
+  }
+
+  public function validate() {}
+
+  public function process() {}
+
+  public function info_alter(&$element_info) {}
+
+  public function dependencies() {}
+
+  public function form_alter(&$form, $form_state, $options) {}
+
+  public function assertions() {}
+
+}
+
+/**
+ * Base class for implementing conditions via classes.
+ */
+abstract class RulesConditionHandlerBase extends RulesPluginHandlerBase implements RulesConditionHandlerInterface {}
+
+/**
+ * Base class for implementing actions via classes.
+ */
+abstract class RulesActionHandlerBase extends RulesPluginHandlerBase implements RulesActionHandlerInterface {}
+
+/**
+ * Provides default implementations of all RulesPluginImplInterface methods.
+ *
+ * If a plugin implementation does not provide a function for a method, the
+ * default method of this class will be invoked.
+ *
+ * @see RulesPluginImplInterface
+ * @see RulesAbstractPlugin
+ */
+class RulesAbstractPluginDefaults extends RulesPluginHandlerBase implements RulesPluginImplInterface {
+
+  public function execute() {
+    throw new RulesEvaluationException($this->object->getPluginName() . ": Execution implementation is missing.", array(), $this->object, RulesLog::ERROR);
+  }
+
+}
+
+/**
+ * A RecursiveIterator for rule elements.
+ */
+class RulesRecursiveElementIterator extends ArrayIterator implements RecursiveIterator {
+
+  public function getChildren() {
+    return $this->current()->getIterator();
+  }
+
+  public function hasChildren() {
+    return $this->current() instanceof IteratorAggregate;
+  }
+
+}
+
+/**
+ * Base class for ContainerPlugins like Rules, Logical Operations or Loops.
+ */
+abstract class RulesContainerPlugin extends RulesPlugin implements IteratorAggregate {
+
+  protected $children = array();
+
+  public function __construct($variables = array()) {
+    $this->setUp();
+    if (!empty($variables) && $this->isRoot()) {
+      $this->info['variables'] = $variables;
+    }
+  }
+
+  /**
+   * Returns the specified variables, in case the plugin is used as component.
+   */
+  public function &componentVariables() {
+    if ($this->isRoot()) {
+      $this->info += array('variables' => array());
+      return $this->info['variables'];
+    }
+    // We have to return a reference in any case.
+    $return = NULL;
+    return $return;
+  }
+
+  /**
+   * Allows access to the children through the iterator.
+   *
+   * @return RulesRecursiveElementIterator
+   */
+  public function getIterator() {
+    return new RulesRecursiveElementIterator($this->children);
+  }
+
+  /**
+   * @return RulesContainerPlugin
+   */
+  public function integrityCheck() {
+    if (!empty($this->info['variables']) && !$this->isRoot()) {
+      throw new RulesIntegrityException(t('%plugin: Specifying state variables is not possible for child elements.', array('%plugin' => $this->getPluginName())), $this);
+    }
+    parent::integrityCheck();
+    foreach ($this->children as $child) {
+      $child->integrityCheck();
+    }
+    return $this;
+  }
+
+  public function dependencies() {
+    $modules = array_flip(parent::dependencies());
+    foreach ($this->children as $child) {
+      $modules += array_flip($child->dependencies());
+    }
+    return array_keys($modules);
+  }
+
+  public function parameterInfo($optional = FALSE) {
+    $params = parent::parameterInfo($optional);
+    if (isset($this->info['variables'])) {
+      foreach ($this->info['variables'] as $name => $var_info) {
+        if (empty($var_info['handler']) && (!isset($var_info['parameter']) || $var_info['parameter'])) {
+          $params[$name] = $var_info;
+          // For lists allow empty variables by default.
+          if (entity_property_list_extract_type($var_info['type'])) {
+            $params[$name] += array('allow null' => TRUE);
+          }
+        }
+      }
+    }
+    return $params;
+  }
+
+  public function availableVariables() {
+    if (!isset($this->availableVariables)) {
+      if ($this->isRoot()) {
+        $this->availableVariables = RulesState::defaultVariables();
+        if (isset($this->info['variables'])) {
+          $this->availableVariables += $this->info['variables'];
+        }
+      }
+      else {
+        $this->availableVariables = $this->parent->stateVariables($this);
+      }
+    }
+    return $this->availableVariables;
+  }
+
+  /**
+   * Returns available state variables for an element.
+   *
+   * Returns info about variables available in the evaluation state for any
+   * children elements or if given for a special child element.
+   *
+   * @param $element
+   *   The element for which the available state variables should be returned.
+   *   If NULL is given, the variables available before any children are invoked
+   *   are returned. If set to TRUE, the variables available after evaluating
+   *   all children will be returned.
+   */
+  protected function stateVariables($element = NULL) {
+    $vars = $this->availableVariables();
+    if (isset($element)) {
+      // Add in variables provided by siblings executed before the element.
+      foreach ($this->children as $child) {
+        if ($child === $element) {
+          break;
+        }
+        $vars += $child->providesVariables();
+        // Take variable info assertions into account.
+        if ($assertions = $child->variableInfoAssertions()) {
+          $vars = RulesData::addMetadataAssertions($vars, $assertions);
+        }
+      }
+    }
+    return $vars;
+  }
+
+  protected function variableInfoAssertions() {
+    $assertions = array();
+    foreach ($this->children as $child) {
+      if ($add = $child->variableInfoAssertions()) {
+        $assertions = rules_update_array($assertions, $add);
+      }
+    }
+    return $assertions;
+  }
+
+  protected function setUpVariables() {
+    return isset($this->info['variables']) ? parent::parameterInfo(TRUE) + $this->info['variables'] : $this->parameterInfo(TRUE);
+  }
+
+  /**
+   * Executes container with the given arguments.
+   *
+   * Condition containers just return a boolean while action containers return
+   * the configured provided variables as an array of variables.
+   */
+  public function executeByArgs($args = array()) {
+    $replacements = array('%label' => $this->label(), '@plugin' => $this->itemName);
+    rules_log('Executing @plugin %label.', $replacements, RulesLog::INFO, $this, TRUE);
+    $this->processSettings();
+    $state = $this->setUpState($args);
+
+    // Handle recursion prevention.
+    if ($state->isBlocked($this)) {
+      return rules_log('Not evaluating @plugin %label to prevent recursion.', array('%label' => $this->label(), '@plugin' => $this->plugin()), RulesLog::INFO);
+    }
+    // Block the config to prevent any future recursion.
+    $state->block($this);
+
+    module_invoke_all('rules_config_execute', $this);
+    $result = $this->evaluate($state);
+    $return = $this->returnVariables($state, $result);
+
+    $state->unblock($this);
+    $state->cleanUp();
+    rules_log('Finished executing of @plugin %label.', $replacements, RulesLog::INFO, $this, FALSE);
+    return $return;
+  }
+
+  public function access() {
+    foreach ($this->children as $key => $child) {
+      if (!$child->access()) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+
+  public function destroy() {
+    foreach ($this->children as $key => $child) {
+      $child->destroy();
+    }
+    parent::destroy();
+  }
+
+  /**
+   * By default we do a deep clone.
+   */
+  public function __clone() {
+    parent::__clone();
+    foreach ($this->children as $key => $child) {
+      $this->children[$key] = clone $child;
+      $this->children[$key]->parent = $this;
+    }
+  }
+
+  /**
+   * Overrides delete to keep the children alive, if possible.
+   */
+  public function delete($keep_children = TRUE) {
+    if (isset($this->parent) && $keep_children) {
+      foreach ($this->children as $child) {
+        $child->setParent($this->parent);
+      }
+    }
+    parent::delete();
+  }
+
+  public function __sleep() {
+    return parent::__sleep() + array('children' => 'children', 'info' => 'info');
+  }
+
+  /**
+   * Sorts all child elements by their weight.
+   *
+   * @param bool $deep
+   *   If enabled a deep sort is performed, thus the whole element tree below
+   *   this element is sorted.
+   */
+  public function sortChildren($deep = FALSE) {
+    // Make sure the array order is kept in case two children have the same
+    // weight by ensuring later children would have higher weights.
+    foreach (array_values($this->children) as $i => $child) {
+      $child->weight += $i / 1000;
+    }
+    usort($this->children, array('RulesPlugin', 'compare'));
+
+    // Fix up the weights afterwards to be unique integers.
+    foreach (array_values($this->children) as $i => $child) {
+      $child->weight = $i;
+    }
+
+    if ($deep) {
+      foreach (new ParentIterator($this->getIterator()) as $child) {
+        $child->sortChildren(TRUE);
+      }
+    }
+    $this->resetInternalCache();
+  }
+
+  protected function exportChildren($key = NULL) {
+    $key = isset($key) ? $key : strtoupper($this->plugin());
+    $export[$key] = array();
+    foreach ($this->children as $child) {
+      $export[$key][] = $child->export();
+    }
+    return $export;
+  }
+
+  /**
+   * Determines whether the element should be exported in flat style.
+   *
+   * Flat style means that the export keys are written directly into the export
+   * array, whereas else the export is written into a sub-array.
+   */
+  protected function exportFlat() {
+    // By default we always use flat style for plugins without any parameters
+    // or provided variables, as then only children have to be exported. E.g.
+    // this applies to the OR and AND plugins.
+    return $this->isRoot() || (!$this->pluginParameterInfo() && !$this->providesVariables());
+  }
+
+  protected function exportToArray() {
+    $export = array();
+    if (!empty($this->info['variables'])) {
+      $export['USES VARIABLES'] = $this->info['variables'];
+    }
+    if ($this->exportFlat()) {
+      $export += $this->exportSettings() + $this->exportChildren();
+    }
+    else {
+      $export[strtoupper($this->plugin())] = $this->exportSettings() + $this->exportChildren();
+    }
+    return $export;
+  }
+
+  public function import(array $export) {
+    if (!empty($export['USES VARIABLES'])) {
+      $this->info['variables'] = $export['USES VARIABLES'];
+    }
+    // Care for exports having the export array nested in a sub-array.
+    if (!$this->exportFlat()) {
+      $export = reset($export);
+    }
+    $this->importSettings($export);
+    $this->importChildren($export);
+  }
+
+  protected function importChildren($export, $key = NULL) {
+    $key = isset($key) ? $key : strtoupper($this->plugin());
+    foreach ($export[$key] as $child_export) {
+      $plugin = _rules_import_get_plugin(rules_array_key($child_export), $this instanceof RulesActionInterface ? 'action' : 'condition');
+      $child = rules_plugin_factory($plugin);
+      $child->setParent($this);
+      $child->import($child_export);
+    }
+  }
+
+  public function resetInternalCache() {
+    $this->availableVariables = NULL;
+    foreach ($this->children as $child) {
+      $child->resetInternalCache();
+    }
+  }
+
+  /**
+   * Overrides optimize().
+   */
+  public function optimize() {
+    parent::optimize();
+    // Now let the children optimize itself.
+    foreach ($this as $element) {
+      $element->optimize();
+    }
+  }
+
+}
+
+/**
+ * Base class for all action containers.
+ */
+abstract class RulesActionContainer extends RulesContainerPlugin implements RulesActionInterface {
+
+  public function __construct($variables = array(), $providesVars = array()) {
+    parent::__construct($variables);
+    // The provided vars of a component are the names of variables, which should
+    // be provided to the caller. See rule().
+    if ($providesVars) {
+      $this->info['provides'] = $providesVars;
+    }
+  }
+
+  /**
+   * Adds an action to the container.
+   *
+   * Pass in either an instance of the RulesActionInterface or the arguments
+   * as needed by rules_action().
+   *
+   * @return $this
+   */
+  public function action($name, $settings = array()) {
+    $action = (is_object($name) && $name instanceof RulesActionInterface) ? $name : rules_action($name, $settings);
+    $action->setParent($this);
+    return $this;
+  }
+
+  /**
+   * Evaluate, whereas by default new vars are visible in the parent's scope.
+   */
+  public function evaluate(RulesState $state) {
+    foreach ($this->children as $action) {
+      $action->evaluate($state);
+    }
+  }
+
+  public function pluginProvidesVariables() {
+    return array();
+  }
+
+  public function providesVariables() {
+    $provides = parent::providesVariables();
+    if (isset($this->info['provides']) && $vars = $this->componentVariables()) {
+      // Determine the full variable info for the provided variables. Note that
+      // we only support providing variables list in the component vars.
+      $provides += array_intersect_key($vars, array_flip($this->info['provides']));
+    }
+    return $provides;
+  }
+
+  /**
+   * Returns an array of provided variable names.
+   *
+   * Returns an array of variable names, which are provided by passing through
+   * the provided variables of the children.
+   */
+  public function &componentProvidesVariables() {
+    $this->info += array('provides' => array());
+    return $this->info['provides'];
+  }
+
+  protected function exportToArray() {
+    $export = parent::exportToArray();
+    if (!empty($this->info['provides'])) {
+      $export['PROVIDES VARIABLES'] = $this->info['provides'];
+    }
+    return $export;
+  }
+
+  public function import(array $export) {
+    parent::import($export);
+    if (!empty($export['PROVIDES VARIABLES'])) {
+      $this->info['provides'] = $export['PROVIDES VARIABLES'];
+    }
+  }
+
+}
+
+/**
+ * Base class for all condition containers.
+ */
+abstract class RulesConditionContainer extends RulesContainerPlugin implements RulesConditionInterface {
+
+  protected $negate = FALSE;
+
+  /**
+   * Adds a condition to the container.
+   *
+   * Pass in either an instance of the RulesConditionInterface or the arguments
+   * as needed by rules_condition().
+   *
+   * @return $this
+   */
+  public function condition($name, $settings = array()) {
+    $condition = (is_object($name) && $name instanceof RulesConditionInterface) ? $name : rules_condition($name, $settings);
+    $condition->setParent($this);
+    return $this;
+  }
+
+  /**
+   * Negate this condition.
+   *
+   * @return RulesConditionContainer
+   */
+  public function negate($negate = TRUE) {
+    $this->negate = (bool) $negate;
+    return $this;
+  }
+
+  public function isNegated() {
+    return $this->negate;
+  }
+
+  public function __sleep() {
+    return parent::__sleep() + array('negate' => 'negate');
+  }
+
+  /**
+   * Just return the condition container's result.
+   */
+  protected function returnVariables(RulesState $state, $result = NULL) {
+    return $result;
+  }
+
+  protected function exportChildren($key = NULL) {
+    $key = isset($key) ? $key : strtoupper($this->plugin());
+    return parent::exportChildren($this->negate ? 'NOT ' . $key : $key);
+  }
+
+  protected function importChildren($export, $key = NULL) {
+    $key = isset($key) ? $key : strtoupper($this->plugin());
+    // Care for negated elements.
+    if (!isset($export[$key]) && isset($export['NOT ' . $key])) {
+      $this->negate = TRUE;
+      $key = 'NOT ' . $key;
+    }
+    parent::importChildren($export, $key);
+  }
+
+  /**
+   * Overridden to exclude variable assertions of negated conditions.
+   */
+  protected function stateVariables($element = NULL) {
+    $vars = $this->availableVariables();
+    if (isset($element)) {
+      // Add in variables provided by siblings executed before the element.
+      foreach ($this->children as $child) {
+        if ($child === $element) {
+          break;
+        }
+        $vars += $child->providesVariables();
+        // Take variable info assertions into account.
+        if (!$this->negate && !$child->isNegated() && ($assertions = $child->variableInfoAssertions())) {
+          $vars = RulesData::addMetadataAssertions($vars, $assertions);
+        }
+      }
+    }
+    return $vars;
+  }
+
+}
+
+/**
+ * The rules default logging class.
+ */
+class RulesLog {
+
+  const INFO  = 1;
+  const WARN  = 2;
+  const ERROR = 3;
+
+  static protected $logger;
+
+  /**
+   * @return RulesLog
+   *   Returns the rules logger instance.
+   */
+  public static function logger() {
+    if (!isset(self::$logger)) {
+      $class = __CLASS__;
+      self::$logger = new $class(variable_get('rules_log_level', self::INFO));
+    }
+    return self::$logger;
+  }
+
+  protected $log = array();
+  protected $logLevel;
+  protected $line = 0;
+
+  /**
+   * This is a singleton.
+   */
+  protected function __construct($logLevel = self::WARN) {
+    $this->logLevel = $logLevel;
+  }
+
+  public function __clone() {
+    throw new Exception("Cannot clone the logger.");
+  }
+
+  /**
+   * Logs a log message.
+   *
+   * @see rules_log()
+   */
+  public function log($msg, $args = array(), $logLevel = self::INFO, $scope = NULL, $path = NULL) {
+    if ($logLevel >= $this->logLevel) {
+      $this->log[] = array($msg, $args, $logLevel, microtime(TRUE), $scope, $path);
+    }
+  }
+
+  /**
+   * Checks the log and throws an exception if there were any problems.
+   */
+  public function checkLog($logLevel = self::WARN) {
+    foreach ($this->log as $entry) {
+      if ($entry[2] >= $logLevel) {
+        throw new Exception($this->render());
+      }
+    }
+  }
+
+  /**
+   * Checks the log for error messages.
+   *
+   * @param int $logLevel
+   *   Lowest log level to return. Values lower than $logLevel will not be
+   *   returned.
+   *
+   * @return bool
+   *   Whether the an error has been logged.
+   */
+  public function hasErrors($logLevel = self::WARN) {
+    foreach ($this->log as $entry) {
+      if ($entry[2] >= $logLevel) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Gets an array of logged messages.
+   */
+  public function get() {
+    return $this->log;
+  }
+
+  /**
+   * Renders the whole log.
+   */
+  public function render() {
+    $line = 0;
+    $output = array();
+    while (isset($this->log[$line])) {
+      $vars['head'] = t($this->log[$line][0], $this->log[$line][1]);
+      $vars['log'] = $this->renderHelper($line);
+      $output[] = theme('rules_debug_element', $vars);
+      $line++;
+    }
+    return implode('', $output);
+  }
+
+  /**
+   * Renders the log of one event invocation.
+   */
+  protected function renderHelper(&$line = 0) {
+    $startTime = isset($this->log[$line][3]) ? $this->log[$line][3] : 0;
+    $output = array();
+    while ($line < count($this->log)) {
+      if ($output && !empty($this->log[$line][4])) {
+        // The next entry stems from another evaluated set, add in its log
+        // messages here.
+        $vars['head'] = t($this->log[$line][0], $this->log[$line][1]);
+        if (isset($this->log[$line][5])) {
+          $vars['link'] = '[' . l(t('edit'), $this->log[$line][5]) . ']';
+        }
+        $vars['log'] = $this->renderHelper($line);
+        $output[] = theme('rules_debug_element', $vars);
+      }
+      else {
+        $formatted_diff = round(($this->log[$line][3] - $startTime) * 1000, 3) . ' ms';
+        $msg = $formatted_diff . ' ' . t($this->log[$line][0], $this->log[$line][1]);
+        if ($this->log[$line][2] >= RulesLog::WARN) {
+          $level = $this->log[$line][2] == RulesLog::WARN ? 'warn' : 'error';
+          $msg = '<span class="rules-debug-' . $level . '">' . $msg . '</span>';
+        }
+        if (isset($this->log[$line][5]) && !isset($this->log[$line][4])) {
+          $msg .= ' [' . l(t('edit'), $this->log[$line][5]) . ']';
+        }
+        $output[] = $msg;
+
+        if (isset($this->log[$line][4]) && !$this->log[$line][4]) {
+          // This was the last log entry of this set.
+          return theme('item_list', array('items' => $output));
+        }
+      }
+      $line++;
+    }
+    return theme('item_list', array('items' => $output));
+  }
+
+  /**
+   * Clears the logged messages.
+   */
+  public function clear() {
+    $this->log = array();
+  }
+
+}
+
+/**
+ * A base exception class for Rules.
+ *
+ * This class can be used to catch all exceptions thrown by Rules, and it
+ * may be subclassed to describe more specific exceptions.
+ */
+abstract class RulesException extends Exception {}
+
+/**
+ * An exception that is thrown during evaluation.
+ *
+ * Messages are prepared to be logged to the watchdog, thus not yet translated.
+ *
+ * @see watchdog()
+ */
+class RulesEvaluationException extends RulesException {
+
+  public $msg;
+  public $args;
+  public $severity;
+  public $element;
+  public $keys = array();
+
+  /**
+   * Constructor.
+   *
+   * @param string $msg
+   *   The exception message containing placeholder as t().
+   * @param array $args
+   *   Replacement arguments such as for t().
+   * @param $element
+   *   The element of a configuration causing the exception or an array
+   *   consisting of the element and keys specifying a setting value causing
+   *   the exception.
+   * @param int $severity
+   *   The RulesLog severity. Defaults to RulesLog::WARN.
+   */
+  public function __construct($msg, array $args = array(), $element = NULL, $severity = RulesLog::WARN) {
+    $this->element = is_array($element) ? array_shift($element) : $element;
+    $this->keys = is_array($element) ? $element : array();
+    $this->msg = $msg;
+    $this->args = $args;
+    $this->severity = $severity;
+    // If an error happened, run the integrity check on the rules configuration
+    // and mark it as dirty if it the check fails.
+    if ($severity == RulesLog::ERROR && isset($this->element)) {
+      $rules_config = $this->element->root();
+      rules_config_update_dirty_flag($rules_config);
+      // If we discovered a broken configuration, exclude it in future.
+      if ($rules_config->dirty) {
+        rules_clear_cache();
+      }
+    }
+    // @todo Fix _drupal_decode_exception() to use __toString() and override it.
+    $this->message = t($this->msg, $this->args);
+  }
+
+}
+
+/**
+ * Indicates the Rules configuration failed the integrity check.
+ *
+ * @see RulesPlugin::integrityCheck()
+ */
+class RulesIntegrityException extends RulesException {
+
+  public $msg;
+  public $element;
+  public $keys = array();
+
+  /**
+   * Constructs a RulesIntegrityException object.
+   *
+   * @param string $msg
+   *   The exception message, already translated.
+   * @param $element
+   *   The element of a configuration causing the exception or an array
+   *   consisting of the element and keys specifying a parameter or provided
+   *   variable causing the exception, e.g.
+   *   @code array($element, 'parameter', 'node') @endcode
+   */
+  public function __construct($msg, $element = NULL) {
+    $this->element = is_array($element) ? array_shift($element) : $element;
+    $this->keys = is_array($element) ? $element : array();
+    parent::__construct($msg);
+  }
+
+}
+
+/**
+ * An exception that is thrown for missing module dependencies.
+ */
+class RulesDependencyException extends RulesIntegrityException {}
+
+/**
+ * Determines the plugin to be used for importing a child element.
+ *
+ * @param string $key
+ *   The key to look for, e.g. 'OR' or 'DO'.
+ * @param string $default
+ *   The default to return if no special plugin can be found.
+ */
+function _rules_import_get_plugin($key, $default = 'action') {
+  $map = &drupal_static(__FUNCTION__);
+  if (!isset($map)) {
+    $cache = rules_get_cache();
+    foreach ($cache['plugin_info'] as $name => $info) {
+      if (!empty($info['embeddable'])) {
+        $info += array('import keys' => array(strtoupper($name)));
+        foreach ($info['import keys'] as $k) {
+          $map[$k] = $name;
+        }
+      }
+    }
+  }
+  // Cut off any leading NOT from the key.
+  if (strpos($key, 'NOT ') === 0) {
+    $key = substr($key, 4);
+  }
+  if (isset($map[$key])) {
+    return $map[$key];
+  }
+  return $default;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/includes/rules.event.inc b/profiles/wcm_base/modules/contrib/rules/includes/rules.event.inc
new file mode 100644
index 0000000000000000000000000000000000000000..f97dec1eda38379188591262a1641d12025f20e8
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/includes/rules.event.inc
@@ -0,0 +1,421 @@
+<?php
+
+/**
+ * @file
+ * Contains event handler interface and base classes.
+ */
+
+/**
+ * Interface for handling rules events.
+ *
+ * Configurable events (i.e. events making use of settings) have a custom
+ * event suffix, which gets appended to the base event name. The configured
+ * event name of, e.g. the event for viewing an article node, would be
+ * node_view--article, whereas "node_view" is the base event name and "article"
+ * the event suffix as returned from
+ * RulesEventHandlerInterface::getEventNameSuffix(). The event suffix is
+ * generated based upon the event settings and must map to this settings, i.e.
+ * each set of event settings must always generate the same suffix.
+ * For a configurable event to be invoked, rules_invoke_event() has to be called
+ * with the configured event name, e.g.
+ * @code
+ * rules_invoke_event('node_view--' . $node->type, $node, $view_mode);
+ * @endcode
+ * If the event settings are optional, both events have to be invoked whereas
+ * usually the more general event is invoked last. E.g.:
+ * @code
+ * rules_invoke_event('node_view--' . $node->type, $node, $view_mode);
+ * rules_invoke_event('node_view', $node, $view_mode);
+ * @endcode
+ *
+ * Rules event handlers have to be declared using the 'class' key in
+ * hook_rules_event_info(), or may be discovered automatically, see
+ * rules_discover_plugins() for details.
+ *
+ * @see RulesEventHandlerBase
+ * @see RulesEventDefaultHandler
+ */
+interface RulesEventHandlerInterface {
+
+  /**
+   * Constructs the event handler.
+   *
+   * @param string $event_name
+   *   The base event string.
+   * @param array $info
+   *   The event info of the given event.
+   */
+  public function __construct($event_name, $info);
+
+  /**
+   * Sets the event settings.
+   *
+   * @param array $settings
+   *   An array of settings to set.
+   *
+   * @return RulesEventHandlerInterface
+   *   The handler itself for chaining.
+   */
+  public function setSettings(array $settings);
+
+  /**
+   * Gets the event settings.
+   *
+   * @return array
+   *   The array of settings.
+   */
+  public function getSettings();
+
+  /**
+   * Returns an array of default settings.
+   *
+   * @return array
+   *   The array of default settings.
+   */
+  public function getDefaults();
+
+  /**
+   * Returns a user-facing summary of the settings.
+   *
+   * @return string
+   *   The summary in HTML, i.e. properly escaped or filtered.
+   */
+  public function summary();
+
+  /**
+   * Builds the event settings form.
+   *
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   *
+   * @return array
+   *   The form structure.
+   */
+  public function buildForm(array &$form_state);
+
+  /**
+   * Validate the event settings independent from a form submission.
+   *
+   * @throws RulesIntegrityException
+   *   In case of validation errors, RulesIntegrityExceptions are thrown.
+   */
+  public function validate();
+
+  /**
+   * Extract the form values and update the event settings.
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   */
+  public function extractFormValues(array &$form, array &$form_state);
+
+  /**
+   * Returns the suffix to be added to the base event named based upon settings.
+   *
+   * If event settings are used, the event name Rules uses for the configured
+   * event is {EVENT_NAME}--{SUFFIX}.
+   *
+   * @return string
+   *   The suffix string. Return an empty string for not appending a suffix.
+   */
+  public function getEventNameSuffix();
+
+  /**
+   * Returns info about the variables provided by this event.
+   *
+   * @return array
+   *   An array of provided variables, keyed by variable names and with the
+   *   variable info array as value.
+   */
+  public function availableVariables();
+
+  /**
+   * Returns the base name of the event the event handler belongs to.
+   *
+   * @return string
+   *   The name of the event the event handler belongs to.
+   */
+  public function getEventName();
+
+  /**
+   * Returns the info array of the event the event handler belongs to.
+   *
+   * @return string
+   *   The info array of the event the event handler belongs to.
+   */
+  public function getEventInfo();
+
+}
+
+/**
+ * Interface for event dispatchers.
+ */
+interface RulesEventDispatcherInterface extends RulesEventHandlerInterface {
+
+  /**
+   * Starts the event watcher.
+   */
+  public function startWatching();
+
+  /**
+   * Stops the event watcher.
+   */
+  public function stopWatching();
+
+  /**
+   * Returns whether the event dispatcher is currently active.
+   *
+   * @return bool
+   *   TRUE if the event dispatcher is currently active, FALSE otherwise.
+   */
+  public function isWatching();
+
+}
+
+/**
+ * Base class for event handler.
+ */
+abstract class RulesEventHandlerBase implements RulesEventHandlerInterface {
+
+  /**
+   * The event name.
+   *
+   * @var string
+   */
+  protected $eventName;
+
+  /**
+   * The event info.
+   *
+   * @var array
+   */
+  protected $eventInfo;
+
+  /**
+   * The event settings.
+   *
+   * @var array
+   */
+  protected $settings = array();
+
+  /**
+   * Implements RulesEventHandlerInterface::__construct().
+   */
+  public function __construct($event_name, $info) {
+    $this->eventName = $event_name;
+    $this->eventInfo = $info;
+    $this->settings = $this->getDefaults();
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::getSettings().
+   */
+  public function getSettings() {
+    return $this->settings;
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::setSettings().
+   */
+  public function setSettings(array $settings) {
+    $this->settings = $settings + $this->getDefaults();
+    return $this;
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::validate().
+   */
+  public function validate() {
+    // Nothing to check by default.
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::extractFormValues().
+   */
+  public function extractFormValues(array &$form, array &$form_state) {
+    foreach ($this->getDefaults() as $key => $setting) {
+      $this->settings[$key] = isset($form_state['values'][$key]) ? $form_state['values'][$key] : $setting;
+    }
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::availableVariables().
+   */
+  public function availableVariables() {
+    return isset($this->eventInfo['variables']) ? $this->eventInfo['variables'] : array();
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::getEventName().
+   */
+  public function getEventName() {
+    return $this->eventName;
+  }
+
+  /**
+   * 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 {
+
+  /**
+   * Implements RulesEventHandlerInterface::buildForm().
+   */
+  public function buildForm(array &$form_state) {
+    return array();
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::getConfiguredEventName().
+   */
+  public function getEventNameSuffix() {
+    return '';
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::summary().
+   */
+  public function summary() {
+    return check_plain($this->eventInfo['label']);
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::getDefaults().
+   */
+  public function getDefaults() {
+    return array();
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::getSettings().
+   */
+  public function getSettings() {
+    return NULL;
+  }
+
+}
+
+/**
+ * Exposes the bundle of an entity as event setting.
+ */
+class RulesEventHandlerEntityBundle extends RulesEventHandlerBase {
+
+  protected $entityType;
+  protected $entityInfo;
+  protected $bundleKey;
+
+  /**
+   * Implements RulesEventHandlerInterface::__construct().
+   */
+  public function __construct($event_name, $info) {
+    parent::__construct($event_name, $info);
+    // Cut off the suffix, e.g. remove 'view' from node_view.
+    $this->entityType = implode('_', explode('_', $event_name, -1));
+    $this->entityInfo = entity_get_info($this->entityType);
+    if (!$this->entityInfo) {
+      throw new InvalidArgumentException('Unsupported event name passed.');
+    }
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::summary().
+   */
+  public function summary() {
+    $bundle = &$this->settings['bundle'];
+    $bundle_label = isset($this->entityInfo['bundles'][$bundle]['label']) ? $this->entityInfo['bundles'][$bundle]['label'] : $bundle;
+    $suffix = isset($bundle) ? ' ' . t('of @bundle-key %name', array('@bundle-key' => $this->getBundlePropertyLabel(), '%name' => $bundle_label)) : '';
+    return check_plain($this->eventInfo['label']) . $suffix;
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::buildForm().
+   */
+  public function buildForm(array &$form_state) {
+    $form['bundle'] = array(
+      '#type' => 'select',
+      '#title' => t('Restrict by @bundle', array('@bundle' => $this->getBundlePropertyLabel())),
+      '#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'];
+    }
+    return $form;
+  }
+
+  /**
+   * 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().
+   */
+  public function extractFormValues(array &$form, array &$form_state) {
+    $this->settings['bundle'] = !empty($form_state['values']['bundle']) ? $form_state['values']['bundle'] : NULL;
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::validate().
+   */
+  public function validate() {
+    if ($this->settings['bundle'] && empty($this->entityInfo['bundles'][$this->settings['bundle']])) {
+      throw new RulesIntegrityException(t('The @bundle %bundle of %entity_type is not known.',
+        array(
+          '%bundle' => $this->settings['bundle'],
+          '%entity_type' => $this->entityInfo['label'],
+          '@bundle' => $this->getBundlePropertyLabel(),
+        )), array(NULL, 'bundle'));
+    }
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::getConfiguredEventName().
+   */
+  public function getEventNameSuffix() {
+    return $this->settings['bundle'];
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::getDefaults().
+   */
+  public function getDefaults() {
+    return array(
+      'bundle' => NULL,
+    );
+  }
+
+  /**
+   * Implements RulesEventHandlerInterface::availableVariables().
+   */
+  public function availableVariables() {
+    $variables = $this->eventInfo['variables'];
+    if ($this->settings['bundle']) {
+      // Add the bundle to all variables of the entity type.
+      foreach ($variables as $name => $variable_info) {
+        if ($variable_info['type'] == $this->entityType) {
+          $variables[$name]['bundle'] = $this->settings['bundle'];
+        }
+      }
+    }
+    return $variables;
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/includes/rules.plugins.inc b/profiles/wcm_base/modules/contrib/rules/includes/rules.plugins.inc
new file mode 100644
index 0000000000000000000000000000000000000000..62fda46908fb7bde522f182a68a82e79d7487f0a
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/includes/rules.plugins.inc
@@ -0,0 +1,899 @@
+<?php
+
+/**
+ * @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';
+
+  /**
+   * Execute the callback and update/save data as specified by the action.
+   */
+  protected function executeCallback(array $args, RulesState $state = NULL) {
+    rules_log('Evaluating the action %name.', array('%name' => $this->elementName), RulesLog::INFO, $this);
+    $return = $this->__call('execute', empty($this->info['named parameter']) ? $args : array($args));
+    // Get the (partially) wrapped arguments.
+    $args = $state->currentArguments;
+
+    if (is_array($return)) {
+      foreach ($return as $name => $data) {
+        // Add provided variables.
+        if (isset($this->info['provides'][$name])) {
+          $var_name = isset($this->settings[$name . ':var']) ? $this->settings[$name . ':var'] : $name;
+          if (!$state->varInfo($var_name)) {
+            $state->addVariable($var_name, $data, $this->info['provides'][$name]);
+            rules_log('Added the provided variable %name of type %type', array('%name' => $var_name, '%type' => $this->info['provides'][$name]['type']), RulesLog::INFO, $this);
+            if (!empty($this->info['provides'][$name]['save']) && $state->variables[$var_name] instanceof EntityMetadataWrapper) {
+              $state->saveChanges($var_name, $state->variables[$var_name]);
+            }
+          }
+        }
+        // Support updating variables by returning the values.
+        elseif (!isset($this->info['provides'][$name])) {
+          // Update the data value using the wrapper.
+          if (isset($args[$name]) && $args[$name] instanceof EntityMetadataWrapper) {
+            try {
+              $args[$name]->set($data);
+            }
+            catch (EntityMetadataWrapperException $e) {
+              throw new RulesEvaluationException('Unable to update the argument for parameter %name: %error', array('%name' => $name, '%error' => $e->getMessage()), $this);
+            }
+          }
+          elseif (array_key_exists($name, $args)) {
+            // Map back to the source variable name and update it.
+            $var_name = !empty($this->settings[$name . ':select']) ? str_replace('-', '_', $this->settings[$name . ':select']) : $name;
+            $state->variables[$var_name] = $data;
+          }
+        }
+      }
+    }
+    // Save parameters as defined in the parameter info.
+    if ($return !== FALSE) {
+      foreach ($this->info['parameter'] as $name => $info) {
+        if (!empty($info['save']) && $args[$name] instanceof EntityMetadataWrapper) {
+          if (isset($this->settings[$name . ':select'])) {
+            $state->saveChanges($this->settings[$name . ':select'], $args[$name]);
+          }
+          else {
+            // Wrapper has been configured via direct input, so just save.
+            rules_log('Saved argument of type %type for parameter %name.', array('%name' => $name, '%type' => $args[$name]->type()));
+            $args[$name]->save();
+          }
+        }
+      }
+    }
+  }
+
+}
+
+/**
+ * Implements a rules condition.
+ */
+class RulesCondition extends RulesAbstractPlugin implements RulesConditionInterface {
+
+  /**
+   * @var string
+   */
+  protected $itemName = 'condition';
+
+  /**
+   * @var bool
+   */
+  protected $negate = FALSE;
+
+  public function providesVariables() {
+    return array();
+  }
+
+  public function negate($negate = TRUE) {
+    $this->negate = (bool) $negate;
+    return $this;
+  }
+
+  public function isNegated() {
+    return $this->negate;
+  }
+
+  protected function executeCallback(array $args, RulesState $state = NULL) {
+    $return = (bool) $this->__call('execute', empty($this->info['named parameter']) ? $args : array($args));
+    rules_log('The condition %name evaluated to %bool', array('%name' => $this->elementName, '%bool' => $return ? 'TRUE' : 'FALSE'), RulesLog::INFO, $this);
+    return $this->negate ? !$return : $return;
+  }
+
+  public function __sleep() {
+    return parent::__sleep() + array('negate' => 'negate');
+  }
+
+  /**
+   * Just return the boolean result.
+   */
+  protected function returnVariables(RulesState $state, $result = NULL) {
+    return $result;
+  }
+
+  protected function exportToArray() {
+    $not = $this->negate ? 'NOT ' : '';
+    $export = $this->exportSettings();
+    // Abbreviate the export making "USING" implicit.
+    return array($not . $this->elementName => isset($export['USING']) ? $export['USING'] : array());
+  }
+
+  public function import(array $export) {
+    $this->elementName = rules_array_key($export);
+    if (strpos($this->elementName, 'NOT ') === 0) {
+      $this->elementName = substr($this->elementName, 4);
+      $this->negate = TRUE;
+    }
+    // After setting the element name, setup the element again so the right
+    // element info is loaded.
+    $this->setUp();
+
+    // Re-add 'USING' which has been removed for abbreviation.
+    $this->importSettings(array('USING' => reset($export)));
+  }
+
+  public function label() {
+    $label = parent::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()) {
+    parent::__construct($variables, $providesVars);
+
+    // Initialize the conditions container.
+    if (!isset($this->conditions)) {
+      $this->conditions = rules_and();
+      // Don't use setParent() to avoid having it added to the children.
+      $this->conditions->parent = $this;
+    }
+  }
+
+  /**
+   * Gets an iterator over all contained conditions.
+   *
+   * Note that this iterator also implements the ArrayAccess interface.
+   *
+   * @return RulesRecursiveElementIterator
+   */
+  public function conditions() {
+    return $this->conditions->getIterator();
+  }
+
+  /**
+   * Returns the "And" condition container, which contains all conditions of
+   * this rule.
+   *
+   * @return RulesAnd
+   */
+  public function conditionContainer() {
+    return $this->conditions;
+  }
+
+  public function __sleep() {
+    return parent::__sleep() + drupal_map_assoc(array('conditions', 'label'));
+  }
+
+  /**
+   * Gets an iterator over all contained actions.
+   *
+   * Note that this iterator also implements the ArrayAccess interface.
+   *
+   * @return RulesRecursiveElementIterator
+   */
+  public function actions() {
+    return parent::getIterator();
+  }
+
+  /**
+   * Adds a condition.
+   *
+   * Pass either an instance of the RulesConditionInterface or the arguments as
+   * needed by rules_condition().
+   *
+   * @return $this
+   */
+  public function condition($name, $settings = array()) {
+    $this->conditions->condition($name, $settings);
+    return $this;
+  }
+
+  public function sortChildren($deep = FALSE) {
+    $this->conditions->sortChildren($deep);
+    parent::sortChildren($deep);
+  }
+
+  public function evaluate(RulesState $state) {
+    rules_log('Evaluating conditions of rule %label.', array('%label' => $this->label), RulesLog::INFO, $this);
+    if ($this->conditions->evaluate($state)) {
+      rules_log('Rule %label fires.', array('%label' => $this->label), RulesLog::INFO, $this, TRUE);
+      parent::evaluate($state);
+      rules_log('Rule %label has fired.', array('%label' => $this->label), RulesLog::INFO, $this, FALSE);
+    }
+  }
+
+  /**
+   * Fires the rule, i.e. evaluates the rule without checking its conditions.
+   *
+   * @see RulesPlugin::evaluate()
+   */
+  public function fire(RulesState $state) {
+    rules_log('Firing rule %label.', array('%label' => $this->label), RulesLog::INFO, $this);
+    parent::evaluate($state);
+  }
+
+  public function integrityCheck() {
+    parent::integrityCheck();
+    $this->conditions->integrityCheck();
+    return $this;
+  }
+
+  public function access() {
+    return (!isset($this->conditions) || $this->conditions->access()) && parent::access();
+  }
+
+  public function dependencies() {
+    return array_keys(array_flip($this->conditions->dependencies()) + array_flip(parent::dependencies()));
+  }
+
+  public function destroy() {
+    $this->conditions->destroy();
+    parent::destroy();
+  }
+
+  /**
+   * @return RulesRecursiveElementIterator
+   */
+  public function getIterator() {
+    $array = array_merge(array($this->conditions), $this->children);
+    return new RulesRecursiveElementIterator($array);
+  }
+
+  protected function stateVariables($element = NULL) {
+    // Don't add in provided action variables for the conditions.
+    if (isset($element) && $element === $this->conditions) {
+      return $this->availableVariables();
+    }
+    $vars = parent::stateVariables($element);
+    // Take variable info assertions of conditions into account.
+    if ($assertions = $this->conditions->variableInfoAssertions()) {
+      $vars = RulesData::addMetadataAssertions($vars, $assertions);
+    }
+    return $vars;
+  }
+
+  protected function exportFlat() {
+    return $this->isRoot();
+  }
+
+  protected function exportToArray() {
+    $export = parent::exportToArray();
+    if (!$this->isRoot()) {
+      $export[strtoupper($this->plugin())]['LABEL'] = $this->label;
+    }
+    return $export;
+  }
+
+  protected function exportChildren($key = NULL) {
+    $export = array();
+    if ($this->conditions->children) {
+      $export = $this->conditions->exportChildren('IF');
+    }
+    return $export + parent::exportChildren('DO');
+  }
+
+  public function import(array $export) {
+    if (!$this->isRoot() && isset($export[strtoupper($this->plugin())]['LABEL'])) {
+      $this->label = $export[strtoupper($this->plugin())]['LABEL'];
+    }
+    parent::import($export);
+  }
+
+  protected function importChildren($export, $key = NULL) {
+    if (!empty($export['IF'])) {
+      $this->conditions->importChildren($export, 'IF');
+    }
+    parent::importChildren($export, 'DO');
+  }
+
+  public function __clone() {
+    parent::__clone();
+    $this->conditions = clone $this->conditions;
+    $this->conditions->parent = $this;
+  }
+
+  /**
+   * Overrides RulesPlugin::variableInfoAssertions().
+   *
+   * Rules may not provide any variable info assertions, as Rules are only
+   * conditionally executed.
+   */
+  protected function variableInfoAssertions() {
+    return array();
+  }
+
+  /**
+   * Overridden to ensure the whole Rule is deleted at once.
+   */
+  public function delete($keep_children = FALSE) {
+    parent::delete($keep_children);
+  }
+
+  /**
+   * Overridden to expose the variables of all actions for embedded rules.
+   */
+  public function providesVariables() {
+    $provides = parent::providesVariables();
+    if (!$this->isRoot()) {
+      foreach ($this->actions() as $action) {
+        $provides += $action->providesVariables();
+      }
+    }
+    return $provides;
+  }
+
+  public function resetInternalCache() {
+    parent::resetInternalCache();
+    $this->conditions->resetInternalCache();
+  }
+
+}
+
+/**
+ * Represents rules getting triggered by events.
+ */
+class RulesReactionRule extends Rule implements RulesTriggerableInterface {
+
+  /**
+   * @var string
+   */
+  protected $itemName = 'reaction rule';
+
+  /**
+   * @var array
+   */
+  protected $events = array();
+
+  /**
+   * @var array
+   */
+  protected $eventSettings = array();
+
+  /**
+   * Implements RulesTriggerableInterface::events().
+   */
+  public function events() {
+    return $this->events;
+  }
+
+  /**
+   * Implements RulesTriggerableInterface::removeEvent().
+   */
+  public function removeEvent($event) {
+    if (($id = array_search($event, $this->events)) !== FALSE) {
+      unset($this->events[$id]);
+    }
+    return $this;
+  }
+
+  /**
+   * Implements RulesTriggerableInterface::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;
+  }
+
+  /**
+   * Reaction rules can't add variables to the parent scope, so clone $state.
+   */
+  public function evaluate(RulesState $state) {
+    // Implement recursion prevention for reaction rules.
+    if ($state->isBlocked($this)) {
+      return rules_log('Not evaluating @plugin %label to prevent recursion.', array('%label' => $this->label(), '@plugin' => $this->plugin()), RulesLog::INFO, $this);
+    }
+    $state->block($this);
+    $copy = clone $state;
+    parent::evaluate($copy);
+    $state->unblock($this);
+  }
+
+  public function access() {
+    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;
+      }
+    }
+    return parent::access();
+  }
+
+  public function dependencies() {
+    $modules = array_flip(parent::dependencies());
+    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);
+  }
+
+  public function providesVariables() {
+    return array();
+  }
+
+  public function parameterInfo($optional = FALSE) {
+    // If executed directly, all variables as defined by the event need to
+    // be passed.
+    return rules_filter_array($this->availableVariables(), 'handler', FALSE);
+  }
+
+  public function availableVariables() {
+    if (!isset($this->availableVariables)) {
+      if (isset($this->parent)) {
+        // Return the event variables provided by the event set, once cached.
+        $this->availableVariables = $this->parent->stateVariables();
+      }
+      else {
+        // The intersection of the variables provided by the events are
+        // available.
+        foreach ($this->events as $event_name) {
+          $handler = rules_get_event_handler($event_name, $this->getEventSettings($event_name));
+
+          if (isset($this->availableVariables)) {
+            $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 = $handler->availableVariables();
+          }
+        }
+        $this->availableVariables = isset($this->availableVariables) ? RulesState::defaultVariables() + $this->availableVariables : RulesState::defaultVariables();
+      }
+    }
+    return $this->availableVariables;
+  }
+
+  public function __sleep() {
+    return parent::__sleep() + drupal_map_assoc(array('events', 'eventSettings'));
+  }
+
+  protected function exportChildren($key = 'ON') {
+    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') {
+    // 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();
+  }
+
+}
+
+/**
+ * A logical AND.
+ */
+class RulesAnd extends RulesConditionContainer {
+
+  /**
+   * @var string
+   */
+  protected $itemName = 'and';
+
+  public function evaluate(RulesState $state) {
+    foreach ($this->children as $condition) {
+      if (!$condition->evaluate($state)) {
+        rules_log('AND evaluated to FALSE.');
+        return $this->negate;
+      }
+    }
+    rules_log('AND evaluated to TRUE.');
+    return !$this->negate;
+  }
+
+  public function label() {
+    return !empty($this->label) ? $this->label : ($this->negate ? t('NOT AND') : t('AND'));
+  }
+
+}
+
+/**
+ * A logical OR.
+ */
+class RulesOr extends RulesConditionContainer {
+
+  /**
+   * @var string
+   */
+  protected $itemName = 'or';
+
+  public function evaluate(RulesState $state) {
+    foreach ($this->children as $condition) {
+      if ($condition->evaluate($state)) {
+        rules_log('OR evaluated to TRUE.');
+        return !$this->negate;
+      }
+    }
+    rules_log('OR evaluated to FALSE.');
+    return $this->negate;
+  }
+
+  public function label() {
+    return !empty($this->label) ? $this->label : ($this->negate ? t('NOT OR') : t('OR'));
+  }
+
+  /**
+   * Overrides RulesContainerPlugin::stateVariables().
+   *
+   * Overridden to exclude all variable assertions as in an OR we cannot assert
+   * the children are successfully evaluated.
+   */
+  protected function stateVariables($element = NULL) {
+    $vars = $this->availableVariables();
+    if (isset($element)) {
+      // Add in variables provided by siblings executed before the element.
+      foreach ($this->children as $child) {
+        if ($child === $element) {
+          break;
+        }
+        $vars += $child->providesVariables();
+      }
+    }
+    return $vars;
+  }
+
+}
+
+/**
+ * A loop element.
+ */
+class RulesLoop extends RulesActionContainer {
+
+  /**
+   * @var string
+   */
+  protected $itemName = 'loop';
+  protected $listItemInfo;
+
+  public function __construct($settings = array(), $variables = NULL) {
+    $this->setUp();
+    $this->settings = (array) $settings + array(
+      'item:var' => 'list_item',
+      'item:label' => t('Current list item'),
+    );
+    if (!empty($variables)) {
+      $this->info['variables'] = $variables;
+    }
+  }
+
+  public function pluginParameterInfo() {
+    $info['list'] = array(
+      'type' => 'list',
+      'restriction' => 'selector',
+      'label' => t('List'),
+      'description' => t('The list to loop over. The loop will step through each item in the list, allowing further actions on them. See <a href="@url"> the online handbook</a> for more information on how to use loops.',
+        array('@url' => rules_external_help('loops'))),
+    );
+    return $info;
+  }
+
+  public function integrityCheck() {
+    parent::integrityCheck();
+    $this->checkVarName($this->settings['item:var']);
+  }
+
+  public function listItemInfo() {
+    if (!isset($this->listItemInfo)) {
+      if ($info = $this->getArgumentInfo('list')) {
+        // Pass through the variable info keys like property info.
+        $this->listItemInfo = array_intersect_key($info, array_flip(array('type', 'property info', 'bundle')));
+        $this->listItemInfo['type'] = isset($info['type']) ? entity_property_list_extract_type($info['type']) : 'unknown';
+      }
+      else {
+        $this->listItemInfo = array('type' => 'unknown');
+      }
+      $this->listItemInfo['label'] = $this->settings['item:label'];
+    }
+    return $this->listItemInfo;
+  }
+
+  public function evaluate(RulesState $state) {
+    try {
+      $param_info = $this->pluginParameterInfo();
+      $list = $this->getArgument('list', $param_info['list'], $state);
+      $item_var_info = $this->listItemInfo();
+      $item_var_name = $this->settings['item:var'];
+
+      if (isset($this->settings['list:select'])) {
+        rules_log('Looping over the list items of %selector', array('%selector' => $this->settings['list:select']), RulesLog::INFO, $this);
+      }
+
+      // Loop over the list and evaluate the children for each list item.
+      foreach ($list as $key => $item) {
+        // Use a separate state so variables are available in the loop only.
+        $state2 = clone $state;
+        $state2->addVariable($item_var_name, $list[$key], $item_var_info);
+        parent::evaluate($state2);
+
+        // Update variables from parent scope.
+        foreach ($state->variables as $var_key => &$var_value) {
+          if (array_key_exists($var_key, $state2->variables)) {
+            $var_value = $state2->variables[$var_key];
+          }
+        }
+      }
+    }
+    catch (RulesEvaluationException $e) {
+      rules_log($e->msg, $e->args, $e->severity);
+      rules_log('Unable to evaluate %name.', array('%name' => $this->getPluginName()), RulesLog::WARN, $this);
+    }
+  }
+
+  protected function stateVariables($element = NULL) {
+    return array($this->settings['item:var'] => $this->listItemInfo()) + parent::stateVariables($element);
+  }
+
+  public function label() {
+    return !empty($this->label) ? $this->label : t('Loop');
+  }
+
+  protected function exportChildren($key = 'DO') {
+    return parent::exportChildren($key);
+  }
+
+  protected function importChildren($export, $key = 'DO') {
+    parent::importChildren($export, $key);
+  }
+
+  protected function exportSettings() {
+    $export = parent::exportSettings();
+    $export['ITEM'][$this->settings['item:var']] = $this->settings['item:label'];
+    return $export;
+  }
+
+  protected function importSettings($export) {
+    parent::importSettings($export);
+    if (isset($export['ITEM'])) {
+      $this->settings['item:var'] = rules_array_key($export['ITEM']);
+      $this->settings['item:label'] = reset($export['ITEM']);
+    }
+  }
+
+}
+
+/**
+ * An action set component.
+ */
+class RulesActionSet extends RulesActionContainer {
+
+  /**
+   * @var string
+   */
+  protected $itemName = 'action set';
+
+}
+
+/**
+ * A set of rules to execute upon defined variables.
+ */
+class RulesRuleSet extends RulesActionContainer {
+
+  /**
+   * @var string
+   */
+  protected $itemName = 'rule set';
+
+  /**
+   * @return RulesRuleSet
+   */
+  public function rule($rule) {
+    return $this->action($rule);
+  }
+
+  protected function exportChildren($key = 'RULES') {
+    return parent::exportChildren($key);
+  }
+
+  protected function importChildren($export, $key = 'RULES') {
+    parent::importChildren($export, $key);
+  }
+
+}
+
+/**
+ * This class is used for caching the rules to be evaluated per event.
+ */
+class RulesEventSet extends RulesRuleSet {
+
+  /**
+   * @var string
+   */
+  protected $itemName = 'event set';
+
+  /**
+   * Event sets may recurse as we block recursions on rule-level.
+   *
+   * @var bool
+   */
+  public $recursion = TRUE;
+
+  public function __construct($info = array()) {
+    $this->setup();
+    $this->info = $info;
+  }
+
+  public function executeByArgs($args = array()) {
+    rules_log('Reacting on event %label.', array('%label' => $this->info['label']), RulesLog::INFO, NULL, TRUE);
+    $state = $this->setUpState($args);
+    module_invoke_all('rules_config_execute', $this);
+    $this->evaluate($state);
+    $state->cleanUp($this);
+    rules_log('Finished reacting on event %label.', array('%label' => $this->info['label']), RulesLog::INFO, NULL, FALSE);
+  }
+
+  /**
+   * 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()
+   */
+  public static function rebuildEventCache() {
+    // Set up the per-event cache.
+    $events = rules_fetch_data('event_info');
+    $sets = array();
+    // Add all rules associated with this event to an EventSet for caching.
+    $rules = rules_config_load_multiple(FALSE, array('plugin' => 'reaction rule', 'active' => TRUE));
+
+    foreach ($rules as $name => $rule) {
+      foreach ($rule->events() as $event_name) {
+        $event_base_name = rules_get_event_base_name($event_name);
+        // Skip not defined events.
+        if (empty($events[$event_base_name])) {
+          continue;
+        }
+        // Create an event set if not yet done.
+        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.
+        if ($rule->dirty) {
+          rules_config_update_dirty_flag($rule);
+        }
+        if (!$rule->dirty) {
+          // Clone the rule to avoid modules getting the changed version from
+          // the static cache.
+          $sets[$event_name]->rule(clone $rule);
+        }
+      }
+    }
+
+    // Create cache items for all created sets.
+    foreach ($sets as $event_name => $set) {
+      $set->sortChildren();
+      $set->optimize();
+      // Allow modules to alter the cached event set.
+      drupal_alter('rules_event_set', $event_name, $set);
+      rules_set_cache('event_' . $event_name, $set);
+    }
+    // Cache a whitelist of configured events so we can use it to speed up later
+    // calls. See rules_invoke_event().
+    rules_set_cache('rules_event_whitelist', array_flip(array_keys($sets)));
+  }
+
+  protected function stateVariables($element = NULL) {
+    return $this->availableVariables();
+  }
+
+  /**
+   * Do not save since this class is for caching purposes only.
+   *
+   * @see RulesPlugin::save()
+   */
+  public function save($name = NULL, $module = 'rules') {
+    return FALSE;
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/includes/rules.processor.inc b/profiles/wcm_base/modules/contrib/rules/includes/rules.processor.inc
new file mode 100644
index 0000000000000000000000000000000000000000..42ca45d819bfccc0de98682740c5f0153c45149f
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/includes/rules.processor.inc
@@ -0,0 +1,387 @@
+<?php
+
+/**
+ * @file
+ * Contains classes for data processing.
+ *
+ * Data processors can be used to process element arguments on evaluation time,
+ * e.g. to apply input evaluators or to apply simple calculations to number
+ * arguments.
+ */
+
+/**
+ * Common base class for Rules data processors.
+ */
+abstract class RulesDataProcessor {
+
+  /**
+   * The processors' setting value.
+   */
+  protected $setting = NULL;
+
+  /**
+   * Allows chaining processors. If set, the next processor to invoke.
+   */
+  protected $processor = NULL;
+
+  /**
+   * Constructor.
+   */
+  protected function __construct($setting, $param_info, $var_info = array(), $processor = NULL) {
+    $this->setting = $setting;
+    $this->processor = $processor;
+  }
+
+  /**
+   * Return $this or skip this processor by returning the next processor.
+   */
+  protected function getPreparedValue() {
+    return isset($this->setting) && array_filter($this->setting) ? $this : $this->processor;
+  }
+
+  /**
+   * Determines whether the current user has permission to edit this chain of
+   * data processors.
+   *
+   * @return bool
+   *   Whether the current user has permission to edit this chain of data
+   *   processors.
+   */
+  public function editAccess() {
+    return $this->access() && (!isset($this->processor) || $this->processor->editAccess());
+  }
+
+  /**
+   * Prepares the processor for parameters.
+   *
+   * It turns the settings into a suitable processor object, which gets invoked
+   * on evaluation time.
+   *
+   * @param $setting
+   *   The processor settings which are to be prepared.
+   * @param $param_info
+   *   The info about the parameter to prepare the processor for.
+   * @param array $var_info
+   *   An array of info about the available variables.
+   */
+  public static function prepareSetting(&$setting, $param_info, $var_info = array()) {
+    $processor = NULL;
+    foreach (self::processors($param_info, FALSE) as $name => $info) {
+      if (!empty($setting[$name])) {
+        $object = new $info['class']($setting[$name], $param_info, $var_info, $processor);
+        $processor = $object->getPreparedValue();
+      }
+    }
+    $setting = $processor;
+  }
+
+  /**
+   * Attaches the form of applicable data processors.
+   */
+  public static function attachForm(&$form, $settings, $param_info, $var_info, $access_check = TRUE) {
+    // If $settings is already prepared get the settings from the processors.
+    if ($settings instanceof RulesDataProcessor) {
+      $settings = $settings->getChainSettings();
+    }
+    foreach (self::processors($param_info, $access_check) as $name => $info) {
+      $settings += array($name => array());
+      $form[$name] = call_user_func(array($info['class'], 'form'), $settings[$name], $var_info);
+      $form[$name]['#weight'] = $info['weight'];
+    }
+  }
+
+  /**
+   * Returns defined data processors applicable for the given parameter.
+   *
+   * Optionally also checks access to the processors.
+   *
+   * @param $param_info
+   *   If given, only processors valid for this parameter are returned.
+   * @param bool $access_check
+   * @param string $hook
+   */
+  public static function processors($param_info = NULL, $access_check = TRUE, $hook = 'data_processor_info') {
+    static $items = array();
+
+    if (!isset($items[$hook]['all'])) {
+      $items[$hook]['all'] = rules_fetch_data($hook);
+      if (isset($items[$hook]['all'])) {
+        uasort($items[$hook]['all'], array(__CLASS__, '_item_sort'));
+      }
+    }
+    // Data processing isn't supported for multiple types.
+    if (isset($param_info) && is_array($param_info['type'])) {
+      return array();
+    }
+    // Filter the items by type.
+    if (isset($param_info['type']) && !isset($items[$hook][$param_info['type']])) {
+      $items[$hook][$param_info['type']] = array();
+      foreach ($items[$hook]['all'] as $name => $info) {
+        // Check whether the parameter type matches the supported types.
+        $info += array('type' => 'text');
+        if (RulesData::typesMatch($param_info, $info, FALSE)) {
+          $items[$hook][$param_info['type']][$name] = $info;
+        }
+      }
+    }
+    // Apply the access check.
+    $return = isset($param_info['type']) ? $items[$hook][$param_info['type']] : $items[$hook]['all'];
+    if ($access_check) {
+      foreach ($return as $base => $info) {
+        if (!call_user_func(array($info['class'], 'access'))) {
+          unset($return[$base]);
+        }
+      }
+    }
+    return $return;
+  }
+
+  public static function _item_sort($a, $b) {
+    return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : 0);
+  }
+
+  /**
+   * Gets the settings array for this and all contained chained processors.
+   */
+  public function getChainSettings() {
+    foreach ($this->unchain() as $name => $processor) {
+      $settings[$name] = $processor->getSetting();
+    }
+    return isset($settings) ? $settings : array();
+  }
+
+  /**
+   * Returns an array of modules which we depend on.
+   */
+  public function dependencies() {
+    $used_processor_info = array_intersect_key($this->processors(), $this->unchain());
+    $modules = array();
+    foreach ($used_processor_info as $name => $info) {
+      $modules[] = $info['module'];
+    }
+    return array_filter($modules);
+  }
+
+  /**
+   * @return
+   *   An array of processors keyed by processor name.
+   */
+  protected function unchain() {
+    $processor = $this;
+    while ($processor instanceof RulesDataProcessor) {
+      $processors[get_class($processor)] = $processor;
+      $processor = $processor->processor;
+    }
+    // Note: Don't use the static context to call processors() here as we need a
+    // late binding to invoke the input evaluators version, if needed.
+    $return = array();
+    foreach ($this->processors() as $name => $info) {
+      if (isset($processors[$info['class']])) {
+        $return[$name] = $processors[$info['class']];
+      }
+    }
+    return $return;
+  }
+
+  /**
+   * Gets the settings of this processor.
+   */
+  public function getSetting() {
+    return $this->setting;
+  }
+
+  /**
+   * Processes the value.
+   *
+   * If $this->processor is set, invoke this processor first so chaining
+   * multiple processors is working.
+   *
+   * @param $value
+   *   The value to process.
+   * @param $info
+   *   Info about the parameter for which we process the value.
+   * @param RulesState $state
+   *   The rules evaluation state.
+   * @param RulesPlugin $element
+   *   The element for which we process the value.
+   *
+   * @return
+   *   The processed value.
+   */
+  abstract public function process($value, $info, RulesState $state, RulesPlugin $element);
+
+  /**
+   * Return whether the current user has permission to use the processor.
+   *
+   * @return bool
+   *   Whether the current user has permission to use the processor.
+   */
+  public static function access() {
+    return TRUE;
+  }
+
+  /**
+   * Defines the processor form element.
+   *
+   * @param $settings
+   *   The settings of the processor.
+   * @param array $var_info
+   *   An array of info about the available variables.
+   *
+   * @return
+   *   A form element structure.
+   */
+  protected static function form($settings, $var_info) {
+    return array();
+  }
+
+}
+
+
+/**
+ * A base processor for use by input evaluators.
+ *
+ * Input evaluators are not listed in hook_rules_data_processor_info(). Instead
+ * they use hook_rules_evaluator_info() and get attached to input forms.
+ */
+abstract class RulesDataInputEvaluator extends RulesDataProcessor {
+
+  /**
+   * Overridden to invoke prepare().
+   */
+  protected function __construct($setting, $param_info, $var_info = array(), $processor = NULL) {
+    $this->setting = TRUE;
+    $this->processor = $processor;
+    $this->prepare($setting, $var_info, $param_info);
+  }
+
+  /**
+   * Overridden to generate evaluator $options and invoke evaluate().
+   */
+  public function process($value, $info, RulesState $state, RulesPlugin $element, $options = NULL) {
+    $options = isset($options) ? $options : $this->getEvaluatorOptions($info, $state, $element);
+    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element, $options) : $value;
+    return $this->evaluate($value, $options, $state);
+  }
+
+  /**
+   * Generates the evaluator $options.
+   */
+  protected function getEvaluatorOptions($info, $state, $element) {
+    $cache = rules_get_cache();
+    $languages = language_list();
+    $info += array(
+      'cleaning callback' => isset($cache['data info'][$info['type']]['cleaning callback']) ? $cache['data info'][$info['type']]['cleaning callback'] : FALSE,
+      'sanitize' => FALSE,
+    );
+    $options = array_filter(array(
+      'language' => $info['#langcode'] != LANGUAGE_NONE && isset($languages[$info['#langcode']]) ? $languages[$info['#langcode']] : NULL,
+      'callback' => $info['cleaning callback'],
+      'sanitize' => $info['sanitize'],
+    ));
+    return $options;
+  }
+
+  /**
+   * Overridden to prepare input evaluator processors.
+   *
+   * The setting is expected to be the input value to be evaluated later on
+   * and is replaced by the suitable processor.
+   */
+  public static function prepareSetting(&$setting, $param_info, $var_info = array()) {
+    $processor = NULL;
+    foreach (self::evaluators($param_info, FALSE) as $name => $info) {
+      $object = new $info['class']($setting, $param_info, $var_info, $processor);
+      $processor = $object->getPreparedValue();
+    }
+    $setting = $processor;
+  }
+
+  protected function getPreparedValue() {
+    return isset($this->setting) ? $this : $this->processor;
+  }
+
+  /**
+   * Overrides RulesDataProcessor::attachForm().
+   *
+   * Overridden to just attach the help() of evaluators.
+   */
+  public static function attachForm(&$form, $settings, $param_info, $var_info, $access_check = TRUE) {
+    foreach (self::evaluators($param_info, $access_check) as $name => $info) {
+      $form['help'][$name] = call_user_func(array($info['class'], 'help'), $var_info, $param_info);
+      $form['help'][$name]['#weight'] = $info['weight'];
+    }
+  }
+
+  /**
+   * Returns all input evaluators that can be applied to the parameters type.
+   */
+  public static function evaluators($param_info = NULL, $access_check = TRUE) {
+    return parent::processors($param_info, $access_check, 'evaluator_info');
+  }
+
+  /**
+   * Overrides RulesDataProcessor::processors().
+   *
+   * Overridden to default to our hook, thus being equivalent to
+   * self::evaluators().
+   */
+  public static function processors($param_info = NULL, $access_check = TRUE, $hook = 'evaluator_info') {
+    return parent::processors($param_info, $access_check, $hook);
+  }
+
+  /**
+   * Prepares the evaluation.
+   *
+   * For example, to determine whether the input evaluator has been used.
+   * If this evaluator should be skipped just unset $this->setting.
+   *
+   * @param string $text
+   *   The text to evaluate later on.
+   * @param array $variables
+   *   An array of info about available variables.
+   * @param array $param_info
+   *   (optional) An array of information about the handled parameter value.
+   *   For backward compatibility, this parameter is not required.
+   */
+  abstract public function prepare($text, $variables);
+
+  /**
+   * Apply the input evaluator.
+   *
+   * @param string $text
+   *   The text to evaluate.
+   * @param array $options
+   *   A keyed array of settings and flags to control the processing.
+   *   Supported options are:
+   *   - language: A language object to be used when processing.
+   *   - callback: A callback function that will be used to post-process
+   *     replacements that might be incorporated, so they can be cleaned in a
+   *     certain way.
+   *   - sanitize: A boolean flag indicating whether incorporated replacements
+   *     should be sanitized.
+   * @param RulesState $state
+   *   The rules evaluation state.
+   *
+   * @return
+   *   The evaluated text.
+   */
+  abstract public function evaluate($text, $options, RulesState $state);
+
+  /**
+   * Provide some usage help for the evaluator.
+   *
+   * @param array $variables
+   *   An array of info about available variables.
+   * @param array $param_info
+   *   (optional) An array of information about the handled parameter value.
+   *   For backward compatibility, this parameter is not required.
+   *
+   * @return array
+   *   A renderable array.
+   */
+  public static function help($variables) {
+    return array();
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/includes/rules.state.inc b/profiles/wcm_base/modules/contrib/rules/includes/rules.state.inc
new file mode 100644
index 0000000000000000000000000000000000000000..2e9213e39828baae662d3c9c970abe85bd033d0c
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/includes/rules.state.inc
@@ -0,0 +1,804 @@
+<?php
+
+/**
+ * @file
+ * Contains the state and data related stuff.
+ */
+
+/**
+ * The rules evaluation state.
+ *
+ * A rule element may clone the state, so any added variables are only visible
+ * for elements in the current PHP-variable-scope.
+ */
+class RulesState {
+
+  /**
+   * Globally keeps the ids of rules blocked due to recursion prevention.
+   *
+   * @var array
+   */
+  static protected $blocked = array();
+
+  /**
+   * The known variables.
+   *
+   * @var array
+   */
+  public $variables = array();
+
+  /**
+   * Holds info about the variables.
+   *
+   * @var array
+   */
+  protected $info = array();
+
+  /**
+   * Keeps wrappers to be saved later on.
+   */
+  protected $save;
+
+  /**
+   * Holds the arguments while an element is executed.
+   *
+   * May be used by the element to easily access the wrapped arguments.
+   */
+  public $currentArguments;
+
+  /**
+   * Variable for saving currently blocked configs for serialization.
+   */
+  protected $currentlyBlocked;
+
+  /**
+   * Constructs a RulesState object.
+   */
+  public function __construct() {
+    // Use an object in order to ensure any cloned states reference the same
+    // save information.
+    $this->save = new ArrayObject();
+    $this->addVariable('site', FALSE, self::defaultVariables('site'));
+  }
+
+  /**
+   * Adds the given variable to the given execution state.
+   */
+  public function addVariable($name, $data, $info) {
+    $this->info[$name] = $info + array(
+      'skip save' => FALSE,
+      'type' => 'unknown',
+      'handler' => FALSE,
+    );
+    if (empty($this->info[$name]['handler'])) {
+      $this->variables[$name] = rules_wrap_data($data, $this->info[$name]);
+    }
+  }
+
+  /**
+   * Runs post-evaluation tasks, such as saving variables.
+   */
+  public function cleanUp() {
+    // Make changes permanent.
+    foreach ($this->save->getArrayCopy() as $selector => $wrapper) {
+      $this->saveNow($selector);
+    }
+    unset($this->currentArguments);
+  }
+
+  /**
+   * Block a rules configuration from execution.
+   */
+  public function block($rules_config) {
+    if (empty($rules_config->recursion) && $rules_config->id) {
+      self::$blocked[$rules_config->id] = TRUE;
+    }
+  }
+
+  /**
+   * Unblock a rules configuration from execution.
+   */
+  public function unblock($rules_config) {
+    if (empty($rules_config->recursion) && $rules_config->id) {
+      unset(self::$blocked[$rules_config->id]);
+    }
+  }
+
+  /**
+   * Returns whether a rules configuration should be blocked from execution.
+   */
+  public function isBlocked($rule_config) {
+    return !empty($rule_config->id) && isset(self::$blocked[$rule_config->id]);
+  }
+
+  /**
+   * Get the info about the state variables or a single variable.
+   */
+  public function varInfo($name = NULL) {
+    if (isset($name)) {
+      return isset($this->info[$name]) ? $this->info[$name] : FALSE;
+    }
+    return $this->info;
+  }
+
+  /**
+   * Returns whether the given wrapper is savable.
+   */
+  public function isSavable($wrapper) {
+    return ($wrapper instanceof EntityDrupalWrapper && entity_type_supports($wrapper->type(), 'save')) || $wrapper instanceof RulesDataWrapperSavableInterface;
+  }
+
+  /**
+   * Returns whether the variable with the given name is an entity.
+   */
+  public function isEntity($name) {
+    $entity_info = entity_get_info();
+    return isset($this->info[$name]['type']) && isset($entity_info[$this->info[$name]['type']]);
+  }
+
+  /**
+   * Gets a variable.
+   *
+   * If necessary, the specified handler is invoked to fetch the variable.
+   *
+   * @param string $name
+   *   The name of the variable to return.
+   *
+   * @return
+   *   The variable or a EntityMetadataWrapper containing the variable.
+   *
+   * @throws RulesEvaluationException
+   *   Throws a RulesEvaluationException in case we have info about the
+   *   requested variable, but it is not defined.
+   */
+  public function &get($name) {
+    if (!array_key_exists($name, $this->variables)) {
+      // If there is handler to load the variable, do it now.
+      if (!empty($this->info[$name]['handler'])) {
+        $data = call_user_func($this->info[$name]['handler'], rules_unwrap_data($this->variables), $name, $this->info[$name]);
+        $this->variables[$name] = rules_wrap_data($data, $this->info[$name]);
+        $this->info[$name]['handler'] = FALSE;
+        if (!isset($data)) {
+          throw new RulesEvaluationException('Unable to load variable %name, aborting.', array('%name' => $name), NULL, RulesLog::INFO);
+        }
+      }
+      else {
+        throw new RulesEvaluationException('Unable to get variable %name, it is not defined.', array('%name' => $name), NULL, RulesLog::ERROR);
+      }
+    }
+    return $this->variables[$name];
+  }
+
+  /**
+   * Apply permanent changes provided the wrapper's data type is savable.
+   *
+   * @param $selector
+   *   The data selector of the wrapper to save or just a variable name.
+   * @param $wrapper
+   * @param bool $immediate
+   *   Pass FALSE to postpone saving to later on. Else it's immediately saved.
+   */
+  public function saveChanges($selector, $wrapper, $immediate = FALSE) {
+    $info = $wrapper->info();
+    if (empty($info['skip save']) && $this->isSavable($wrapper)) {
+      $this->save($selector, $wrapper, $immediate);
+    }
+    // No entity, so try saving the parent.
+    elseif (empty($info['skip save']) && isset($info['parent']) && !($wrapper instanceof EntityDrupalWrapper)) {
+      // Cut of the last part of the selector.
+      $selector = implode(':', explode(':', $selector, -1));
+      $this->saveChanges($selector, $info['parent'], $immediate);
+    }
+    return $this;
+  }
+
+  /**
+   * Remembers to save the wrapper on cleanup or does it now.
+   */
+  protected function save($selector, EntityMetadataWrapper $wrapper, $immediate) {
+    // Convert variable names and selectors to both use underscores.
+    $selector = strtr($selector, '-', '_');
+    if (isset($this->save[$selector])) {
+      if ($this->save[$selector][0]->getIdentifier() == $wrapper->getIdentifier()) {
+        // The entity is already remembered. So do a combined save.
+        $this->save[$selector][1] += self::$blocked;
+      }
+      else {
+        // The wrapper is already in there, but wraps another entity. So first
+        // save the old one, then care about the new one.
+        $this->saveNow($selector);
+      }
+    }
+    if (!isset($this->save[$selector])) {
+      // In case of immediate saving don't clone the wrapper, so saving a new
+      // entity immediately makes the identifier available afterwards.
+      $this->save[$selector] = array($immediate ? $wrapper : clone $wrapper, self::$blocked);
+    }
+    if ($immediate) {
+      $this->saveNow($selector);
+    }
+  }
+
+  /**
+   * Saves the wrapper for the given selector.
+   */
+  protected function saveNow($selector) {
+    // Add the set of blocked elements for the recursion prevention.
+    $previously_blocked = self::$blocked;
+    self::$blocked += $this->save[$selector][1];
+
+    // Actually save!
+    $wrapper = $this->save[$selector][0];
+    $entity = $wrapper->value();
+    // When operating in hook_entity_insert() $entity->is_new might be still
+    // set. In that case remove the flag to avoid causing another insert instead
+    // of an update.
+    if (!empty($entity->is_new) && $wrapper->getIdentifier()) {
+      $entity->is_new = FALSE;
+    }
+    rules_log('Saved %selector of type %type.', array('%selector' => $selector, '%type' => $wrapper->type()));
+    $wrapper->save();
+
+    // Restore the state's set of blocked elements.
+    self::$blocked = $previously_blocked;
+    unset($this->save[$selector]);
+  }
+
+  /**
+   * Merges info from the given state into the existing state.
+   *
+   * Merges the info about to-be-saved variables from the given state into the
+   * existing state. Therefore we can aggregate saves from invoked components.
+   * Merged-in saves are removed from the given state, but not-mergeable saves
+   * remain there.
+   *
+   * @param $state
+   *   The state for which to merge the to be saved variables in.
+   * @param $component
+   *   The component which has been invoked, thus needs to be blocked for the
+   *   merged in saves.
+   * @param $settings
+   *   The settings of the element that invoked the component. Contains
+   *   information about variable/selector mappings between the states.
+   */
+  public function mergeSaveVariables(RulesState $state, RulesPlugin $component, $settings) {
+    // For any saves that we take over, also block the component.
+    $this->block($component);
+
+    foreach ($state->save->getArrayCopy() as $selector => $data) {
+      $parts = explode(':', $selector, 2);
+      // Adapt the selector to fit for the parent state and move the wrapper.
+      if (isset($settings[$parts[0] . ':select'])) {
+        $parts[0] = $settings[$parts[0] . ':select'];
+        $this->save(implode(':', $parts), $data[0], FALSE);
+        unset($state->save[$selector]);
+      }
+    }
+    $this->unblock($component);
+  }
+
+  /**
+   * Returns an entity metadata wrapper as specified in the selector.
+   *
+   * @param string $selector
+   *   The selector string, e.g. "node:author:mail".
+   * @param string $langcode
+   *   (optional) The language code used to get the argument value if the
+   *   argument value should be translated. Defaults to LANGUAGE_NONE.
+   *
+   * @return EntityMetadataWrapper
+   *   The wrapper for the given selector.
+   *
+   * @throws RulesEvaluationException
+   *   Throws a RulesEvaluationException in case the selector cannot be applied.
+   */
+  public function applyDataSelector($selector, $langcode = LANGUAGE_NONE) {
+    $parts = explode(':', str_replace('-', '_', $selector), 2);
+    $wrapper = $this->get($parts[0]);
+    if (count($parts) == 1) {
+      return $wrapper;
+    }
+    elseif (!$wrapper instanceof EntityMetadataWrapper) {
+      throw new RulesEvaluationException('Unable to apply data selector %selector. The specified variable is not wrapped correctly.', array('%selector' => $selector));
+    }
+    try {
+      foreach (explode(':', $parts[1]) as $name) {
+        if ($wrapper instanceof EntityListWrapper || $wrapper instanceof EntityStructureWrapper) {
+          // Make sure we are using the right language. Wrappers might be cached
+          // and have previous langcodes set, so always set the right language.
+          if ($wrapper instanceof EntityStructureWrapper) {
+            $wrapper->language($langcode);
+          }
+          $wrapper = $wrapper->get($name);
+        }
+        else {
+          throw new RulesEvaluationException('Unable to apply data selector %selector. The specified variable is not a list or a structure: %wrapper.', array('%selector' => $selector, '%wrapper' => $wrapper));
+        }
+      }
+    }
+    catch (EntityMetadataWrapperException $e) {
+      // In case of an exception, re-throw it.
+      throw new RulesEvaluationException('Unable to apply data selector %selector: %error', array('%selector' => $selector, '%error' => $e->getMessage()));
+    }
+    return $wrapper;
+  }
+
+  /**
+   * Magic method. Only serialize variables and their info.
+   *
+   * Additionally we remember currently blocked configs, so we can restore them
+   * upon deserialization using restoreBlocks().
+   */
+  public function __sleep() {
+    $this->currentlyBlocked = self::$blocked;
+    return array('info', 'variables', 'currentlyBlocked');
+  }
+
+  /**
+   * Magic method. Unserialize variables and their info.
+   */
+  public function __wakeup() {
+    $this->save = new ArrayObject();
+  }
+
+  /**
+   * Restores the before-serialization blocked configurations.
+   *
+   * Warning: This overwrites any possible currently blocked configs. Thus
+   * do not invoke this method if there might be evaluations active.
+   */
+  public function restoreBlocks() {
+    self::$blocked = $this->currentlyBlocked;
+  }
+
+  /**
+   * Defines always-available variables.
+   *
+   * @param $key
+   *   (optional)
+   */
+  public static function defaultVariables($key = NULL) {
+    // Add a variable for accessing site-wide data properties.
+    $vars['site'] = array(
+      'type' => 'site',
+      'label' => t('Site information'),
+      'description' => t("Site-wide settings and other global information."),
+      // Add the property info via a callback making use of the cached info.
+      'property info alter' => array('RulesData', 'addSiteMetadata'),
+      'property info' => array(),
+      'optional' => TRUE,
+    );
+    return isset($key) ? $vars[$key] : $vars;
+  }
+
+}
+
+/**
+ * A class holding static methods related to data.
+ */
+class RulesData {
+
+  /**
+   * Returns whether the type match. They match if type1 is compatible to type2.
+   *
+   * @param $var_info
+   *   The name of the type to check for whether it is compatible to type2.
+   * @param $param_info
+   *   The type expression to check for.
+   * @param bool $ancestors
+   *   (optional) Whether sub-type relationships for checking type compatibility
+   *   should be taken into account. Defaults to TRUE.
+   *
+   * @return bool
+   *   Whether the types match.
+   */
+  public static function typesMatch($var_info, $param_info, $ancestors = TRUE) {
+    $var_type = $var_info['type'];
+    $param_type = $param_info['type'];
+
+    if ($param_type == '*' || $param_type == 'unknown') {
+      return TRUE;
+    }
+
+    if ($var_type == $param_type) {
+      // Make sure the bundle matches, if specified by the parameter.
+      return !isset($param_info['bundles']) || isset($var_info['bundle']) && in_array($var_info['bundle'], $param_info['bundles']);
+    }
+
+    // Parameters may specify multiple types using an array.
+    $valid_types = is_array($param_type) ? $param_type : array($param_type);
+    if (in_array($var_type, $valid_types)) {
+      return TRUE;
+    }
+
+    // Check for sub-type relationships.
+    if ($ancestors && !isset($param_info['bundles'])) {
+      $cache = &rules_get_cache();
+      self::typeCalcAncestors($cache, $var_type);
+      // If one of the types is an ancestor return TRUE.
+      return (bool) array_intersect_key($cache['data_info'][$var_type]['ancestors'], array_flip($valid_types));
+    }
+    return FALSE;
+  }
+
+  protected static function typeCalcAncestors(&$cache, $type) {
+    if (!isset($cache['data_info'][$type]['ancestors'])) {
+      $cache['data_info'][$type]['ancestors'] = array();
+      if (isset($cache['data_info'][$type]['parent']) && $parent = $cache['data_info'][$type]['parent']) {
+        $cache['data_info'][$type]['ancestors'][$parent] = TRUE;
+        self::typeCalcAncestors($cache, $parent);
+        // Add all parent ancestors to our own ancestors.
+        $cache['data_info'][$type]['ancestors'] += $cache['data_info'][$parent]['ancestors'];
+      }
+      // For special lists like list<node> add in "list" as valid parent.
+      if (entity_property_list_extract_type($type)) {
+        $cache['data_info'][$type]['ancestors']['list'] = TRUE;
+      }
+    }
+  }
+
+  /**
+   * Returns data for the given info and the to-be-configured parameter.
+   *
+   * Returns matching data variables or properties for the given info and the
+   * to-be-configured parameter.
+   *
+   * @param $source
+   *   Either an array of info about available variables or a entity metadata
+   *   wrapper.
+   * @param $param_info
+   *   The information array about the to be configured parameter.
+   * @param string $prefix
+   *   An optional prefix for the data selectors.
+   * @param int $recursions
+   *   The number of recursions used to go down the tree. Defaults to 2.
+   * @param bool $suggestions
+   *   Whether possibilities to recurse are suggested as soon as the deepest
+   *   level of recursions is reached. Defaults to TRUE.
+   *
+   * @return array
+   *   An array of info about matching variables or properties that match, keyed
+   *   with the data selector.
+   */
+  public static function matchingDataSelector($source, $param_info, $prefix = '', $recursions = 2, $suggestions = TRUE) {
+    // If an array of info is given, get entity metadata wrappers first.
+    $data = NULL;
+    if (is_array($source)) {
+      foreach ($source as $name => $info) {
+        $source[$name] = rules_wrap_data($data, $info, TRUE);
+      }
+    }
+
+    $matches = array();
+    foreach ($source as $name => $wrapper) {
+      $info = $wrapper->info();
+      $name = str_replace('_', '-', $name);
+
+      if (self::typesMatch($info, $param_info)) {
+        $matches[$prefix . $name] = $info;
+        if (!is_array($source) && $source instanceof EntityListWrapper) {
+          // Add some more possible list items.
+          for ($i = 1; $i < 4; $i++) {
+            $matches[$prefix . $i] = $info;
+          }
+        }
+      }
+      // Recurse later on to get an improved ordering of the results.
+      if ($wrapper instanceof EntityStructureWrapper || $wrapper instanceof EntityListWrapper) {
+        $recurse[$prefix . $name] = $wrapper;
+        if ($recursions > 0) {
+          $matches += self::matchingDataSelector($wrapper, $param_info, $prefix . $name . ':', $recursions - 1, $suggestions);
+        }
+        elseif ($suggestions) {
+          // We may not recurse any more,
+          // but indicate the possibility to recurse.
+          $matches[$prefix . $name . ':'] = $wrapper->info();
+          if (!is_array($source) && $source instanceof EntityListWrapper) {
+            // Add some more possible list items.
+            for ($i = 1; $i < 4; $i++) {
+              $matches[$prefix . $i . ':'] = $wrapper->info();
+            }
+          }
+        }
+      }
+    }
+    return $matches;
+  }
+
+  /**
+   * Adds asserted metadata to the variable info.
+   *
+   * In case there are already assertions for a variable, the assertions are
+   * merged such that both apply.
+   *
+   * @see RulesData::applyMetadataAssertions()
+   */
+  public static function addMetadataAssertions($var_info, $assertions) {
+    foreach ($assertions as $selector => $assertion) {
+      // Convert the selector back to underscores, such it matches the varname.
+      $selector = str_replace('-', '_', $selector);
+
+      $parts = explode(':', $selector);
+      if (isset($var_info[$parts[0]])) {
+        // Apply the selector to determine the right target array. We build an
+        // array like
+        // $var_info['rules assertion']['property1']['property2']['#info'] = ..
+        $target = &$var_info[$parts[0]]['rules assertion'];
+        foreach (array_slice($parts, 1) as $part) {
+          $target = &$target[$part];
+        }
+
+        // In case the assertion is directly for a variable, we have to modify
+        // the variable info directly. In case the asserted property is nested
+        // the info-has to be altered by RulesData::applyMetadataAssertions()
+        // before the child-wrapper is created.
+        if (count($parts) == 1) {
+          // Support asserting a type in case of generic entity references only.
+          $var_type = &$var_info[$parts[0]]['type'];
+          if (isset($assertion['type']) && ($var_type == 'entity' || $var_type == 'list<entity>')) {
+            $var_type = $assertion['type'];
+            unset($assertion['type']);
+          }
+          // Add any single bundle directly to the variable info, so the
+          // variable fits as argument for parameters requiring the bundle.
+          if (isset($assertion['bundle']) && count($bundles = (array) $assertion['bundle']) == 1) {
+            $var_info[$parts[0]]['bundle'] = reset($bundles);
+          }
+        }
+
+        // Add the assertions, but merge them with any previously added
+        // assertions if necessary.
+        $target['#info'] = isset($target['#info']) ? rules_update_array($target['#info'], $assertion) : $assertion;
+
+        // Add in a callback that the entity metadata wrapper pick up for
+        // altering the property info, such that we can add in the assertions.
+        $var_info[$parts[0]] += array('property info alter' => array('RulesData', 'applyMetadataAssertions'));
+
+        // In case there is a VARNAME_unchanged variable as it is used in update
+        // hooks, assume the assertions are valid for the unchanged variable
+        // too.
+        if (isset($var_info[$parts[0] . '_unchanged'])) {
+          $name = $parts[0] . '_unchanged';
+          $var_info[$name]['rules assertion'] = $var_info[$parts[0]]['rules assertion'];
+          $var_info[$name]['property info alter'] = array('RulesData', 'applyMetadataAssertions');
+
+          if (isset($var_info[$parts[0]]['bundle']) && !isset($var_info[$name]['bundle'])) {
+            $var_info[$name]['bundle'] = $var_info[$parts[0]]['bundle'];
+          }
+        }
+      }
+    }
+    return $var_info;
+  }
+
+  /**
+   * Property info alter callback for the entity metadata wrapper.
+   *
+   * Used for applying the rules metadata assertions.
+   *
+   * @see RulesData::addMetadataAssertions()
+   */
+  public static function applyMetadataAssertions(EntityMetadataWrapper $wrapper, $property_info) {
+    $info = $wrapper->info();
+
+    if (!empty($info['rules assertion'])) {
+      $assertion = $info['rules assertion'];
+
+      // In case there are list-wrappers pass through the assertions of the item
+      // but make sure we only apply the assertions for the list items for
+      // which the conditions are executed.
+      if (isset($info['parent']) && $info['parent'] instanceof EntityListWrapper) {
+        $assertion = isset($assertion[$info['name']]) ? $assertion[$info['name']] : array();
+      }
+
+      // Support specifying multiple bundles, whereas the added properties are
+      // the intersection of the bundle properties.
+      if (isset($assertion['#info']['bundle'])) {
+        $bundles = (array) $assertion['#info']['bundle'];
+        foreach ($bundles as $bundle) {
+          $properties[] = isset($property_info['bundles'][$bundle]['properties']) ? $property_info['bundles'][$bundle]['properties'] : array();
+        }
+        // Add the intersection.
+        $property_info['properties'] += count($properties) > 1 ? call_user_func_array('array_intersect_key', $properties) : reset($properties);
+      }
+      // Support adding directly asserted property info.
+      if (isset($assertion['#info']['property info'])) {
+        $property_info['properties'] += $assertion['#info']['property info'];
+      }
+
+      // Pass through any rules assertion of properties to their info, so any
+      // derived wrappers apply it.
+      foreach (element_children($assertion) as $key) {
+        $property_info['properties'][$key]['rules assertion'] = $assertion[$key];
+        $property_info['properties'][$key]['property info alter'] = array('RulesData', 'applyMetadataAssertions');
+
+        // Apply any 'type' and 'bundle' assertion directly to the property
+        // info.
+        if (isset($assertion[$key]['#info']['type'])) {
+          $type = $assertion[$key]['#info']['type'];
+          // Support asserting a type in case of generic entity references only.
+          if ($property_info['properties'][$key]['type'] == 'entity' && entity_get_info($type)) {
+            $property_info['properties'][$key]['type'] = $type;
+          }
+        }
+        if (isset($assertion[$key]['#info']['bundle'])) {
+          $bundle = (array) $assertion[$key]['#info']['bundle'];
+          // Add any single bundle directly to the variable info, so the
+          // property fits as argument for parameters requiring the bundle.
+          if (count($bundle) == 1) {
+            $property_info['properties'][$key]['bundle'] = reset($bundle);
+          }
+        }
+      }
+    }
+    return $property_info;
+  }
+
+  /**
+   * Property info alter callback for the entity metadata wrapper.
+   *
+   * Used to inject metadata for the 'site' variable. In contrast to doing this
+   * via hook_rules_data_info() this callback makes use of the already existing
+   * property info cache for site information of entity metadata.
+   *
+   * @see RulesPlugin::availableVariables()
+   */
+  public static function addSiteMetadata(EntityMetadataWrapper $wrapper, $property_info) {
+    $site_info = entity_get_property_info('site');
+    $property_info['properties'] += $site_info['properties'];
+    // Also invoke the usual callback for altering metadata, in case actions
+    // have specified further metadata.
+    return RulesData::applyMetadataAssertions($wrapper, $property_info);
+  }
+
+}
+
+/**
+ * A wrapper class similar to the EntityDrupalWrapper, but for non-entities.
+ *
+ * This class is intended to serve as base for a custom wrapper classes of
+ * identifiable data types, which are non-entities. By extending this class only
+ * the extractIdentifier() and load() methods have to be defined.
+ * In order to make the data type savable implement the
+ * RulesDataWrapperSavableInterface.
+ *
+ * That way it is possible for non-entity data types to be work with Rules, i.e.
+ * one can implement a 'ui class' with a direct input form returning the
+ * identifier of the data. However, instead of that it is suggested to implement
+ * an entity type, such that the same is achieved via general API functions like
+ * entity_load().
+ */
+abstract class RulesIdentifiableDataWrapper extends EntityStructureWrapper {
+
+  /**
+   * Contains the id.
+   */
+  protected $id = FALSE;
+
+  /**
+   * Construct a new wrapper object.
+   *
+   * @param $type
+   *   The type of the passed data.
+   * @param $data
+   *   Optional. The data to wrap or its identifier.
+   * @param array $info
+   *   Optional. Used internally to pass info about properties down the tree.
+   */
+  public function __construct($type, $data = NULL, $info = array()) {
+    parent::__construct($type, $data, $info);
+    $this->setData($data);
+  }
+
+  /**
+   * Sets the data internally accepting both the data id and object.
+   */
+  protected function setData($data) {
+    if (isset($data) && $data !== FALSE && !is_object($data)) {
+      $this->id = $data;
+      $this->data = FALSE;
+    }
+    elseif (is_object($data)) {
+      // We got the data object passed.
+      $this->data = $data;
+      $id = $this->extractIdentifier($data);
+      $this->id = isset($id) ? $id : FALSE;
+    }
+  }
+
+  /**
+   * Returns the identifier of the wrapped data.
+   */
+  public function getIdentifier() {
+    return $this->dataAvailable() && $this->value() ? $this->id : NULL;
+  }
+
+  /**
+   * Overridden.
+   */
+  public function value(array $options = array()) {
+    $this->setData(parent::value());
+    if (!$this->data && !empty($this->id)) {
+      // Lazy load the data if necessary.
+      $this->data = $this->load($this->id);
+      if (!$this->data) {
+        throw new EntityMetadataWrapperException('Unable to load the ' . check_plain($this->type) . ' with the id ' . check_plain($this->id) . '.');
+      }
+    }
+    return $this->data;
+  }
+
+  /**
+   * Overridden to support setting the data by either the object or the id.
+   */
+  public function set($value) {
+    if (!$this->validate($value)) {
+      throw new EntityMetadataWrapperException('Invalid data value given. Be sure it matches the required data type and format.');
+    }
+    // As custom wrapper classes can only appear for Rules variables, but not
+    // as properties we don't have to care about updating the parent.
+    $this->clear();
+    $this->setData($value);
+    return $this;
+  }
+
+  /**
+   * Overridden.
+   */
+  public function clear() {
+    $this->id = NULL;
+    parent::clear();
+  }
+
+  /**
+   * Prepare for serialization.
+   */
+  public function __sleep() {
+    $vars = parent::__sleep();
+    // Don't serialize the loaded data, except for the case the data is not
+    // saved yet.
+    if (!empty($this->id)) {
+      unset($vars['data']);
+    }
+    return $vars;
+  }
+
+  /**
+   * Prepare for unserialization.
+   */
+  public function __wakeup() {
+    if ($this->id !== FALSE) {
+      // Make sure data is set, so the data will be loaded when needed.
+      $this->data = FALSE;
+    }
+  }
+
+  /**
+   * Extract the identifier of the given data object.
+   *
+   * @return
+   *   The extracted identifier.
+   */
+  abstract protected function extractIdentifier($data);
+
+  /**
+   * Load a data object given an identifier.
+   *
+   * @return
+   *   The loaded data object, or FALSE if loading failed.
+   */
+  abstract protected function load($id);
+
+}
+
+/**
+ * Used to declare custom wrapper classes as savable.
+ */
+interface RulesDataWrapperSavableInterface {
+
+  /**
+   * Save the currently wrapped data.
+   */
+  public function save();
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/includes/rules.upgrade.inc b/profiles/wcm_base/modules/contrib/rules/includes/rules.upgrade.inc
new file mode 100644
index 0000000000000000000000000000000000000000..2eb2cade9de6018ce7adff002a8c09b9f5be6a26
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/includes/rules.upgrade.inc
@@ -0,0 +1,745 @@
+<?php
+
+/**
+ * @file
+ * Contains code for upgrading rule configurations from 6.x-1.x to 7.x-2.x.
+ */
+
+/**
+ * Form builder for the upgrade page.
+ */
+function rules_upgrade_form($form, &$form_state) {
+
+  if (!empty($form_state['export'])) {
+    foreach ($form_state['export'] as $key => $export) {
+      // Rules have been already converted and exported, so show the export.
+      $form['export'][$key] = array(
+        '#type' => 'textarea',
+        '#title' => t('Export %name', array('%name' => $key)),
+        '#description' => t('For importing copy the content of the text area and paste it into the import page of the Rules admin UI. In case the export does not pass the integrity check during import, try using the save to database method instead and manually fix your configuration after conversion.'),
+        '#rows' => 10,
+        '#default_value' => $export,
+      );
+    }
+    return $form;
+  }
+
+  $form['help'] = array(
+    '#prefix' => '<p>',
+    '#suffix' => '</p>',
+    '#markup' => t('This form allows you to convert rules or rule sets from Rules 1.x to Rules 2.x.') . ' ' .
+      t('In order to convert a rule or rule set make sure you have all dependent modules installed and upgraded, i.e. modules which provide Rules integration that has been used in your rules or rule sets. In addition those modules may need to implement some Rules specific update hooks for the conversion to properly work.') . ' ' .
+      t('After conversion, the old rules and rule sets will stay in the database until you manually delete them. That way you can make sure the conversion has gone right before you delete the old rules and rule sets.'),
+  );
+
+  $option_rules = $option_sets = array();
+  if (!db_table_exists('rules_rules')) {
+    drupal_set_message('There are no Rules 1.x rules or rule sets left to convert.', 'error');
+  }
+  else {
+    foreach (_rules_upgrade_fetch_all_rules() as $name => $rule) {
+      if (!empty($rule['#set']) && strpos($rule['#set'], 'event_') === 0) {
+        $option_rules[$name] = $name . ': ' . $rule['#label'];
+      }
+    }
+    $query = db_select('rules_sets', 'r')->fields('r');
+    foreach ($query->execute() as $row) {
+      $set = unserialize($row->data);
+      $option_sets[$row->name] = $row->name . ': ' . $set['label'];
+    }
+
+    $form['clear'] = array(
+      '#prefix' => '<p>',
+      '#suffix' => '</p>',
+      '#markup' => t('Once you have successfully converted your configuration, you can clean up your database and <a href="!url">delete</a> all Rules 1.x configurations.', array('!url' => url('admin/config/workflow/rules/upgrade/clear'))),
+    );
+  }
+
+  $form['rules'] = array(
+    '#type' => 'select',
+    '#title' => t('Rules'),
+    '#options' => $option_rules,
+    '#multiple' => TRUE,
+  );
+
+  $form['sets'] = array(
+    '#type' => 'select',
+    '#title' => t('Rule sets'),
+    '#options' => $option_sets,
+    '#multiple' => TRUE,
+  );
+  $form['method'] = array(
+    '#type' => 'radios',
+    '#title' => t('Method'),
+    '#options' => array(
+      'export' => t('Convert configuration and export it.'),
+      'save' => t('Convert configuration and save it.'),
+    ),
+    '#default_value' => 'export',
+  );
+
+  $form['actions']['convert'] = array(
+    '#type' => 'submit',
+    '#value' => t('Convert'),
+    '#disabled' => !db_table_exists('rules_rules'),
+  );
+  return $form;
+}
+
+/**
+ * Submit handler for the form.
+ */
+function rules_upgrade_form_submit($form, &$form_state) {
+  // Load all rules includes and install files so modules may put there upgrade
+  // information in both locations.
+  module_load_all_includes('rules.inc');
+  module_load_all_includes('install');
+
+  $configs = array();
+
+  try {
+    foreach ($form_state['values']['rules'] as $name) {
+      drupal_set_message(t('Converting %plugin %name...', array('%plugin' => t('rule'), '%name' => $name)));
+      $configs[$name] = rules_upgrade_convert_rule($name, _rules_upgrade_fetch_item($name, 'rules_rules'));
+    }
+    foreach ($form_state['values']['sets'] as $name) {
+      drupal_set_message(t('Converting %plugin %name...', array('%plugin' => t('rule set'), '%name' => $name)));
+      $configs[$name] = rules_upgrade_convert_rule_set($name, _rules_upgrade_fetch_item($name, 'rules_sets'));
+    }
+    drupal_set_message(t('Completed.'));
+
+    if ($form_state['values']['method'] == 'save') {
+      foreach ($configs as $config) {
+        $config->save();
+      }
+      drupal_set_message(t('Converted configurations have been saved to the database and will appear in the Rules administration interface.'));
+    }
+    elseif ($form_state['values']['method'] == 'export') {
+      $export = array();
+      foreach ($configs as $name => $config) {
+        $export[$name] = $config->export();
+      }
+      $form_state['export'] = $export;
+      $form_state['rebuild'] = TRUE;
+    }
+  }
+  catch (RulesException $e) {
+    drupal_set_message($e->getMessage(), 'error');
+  }
+}
+
+/**
+ * Confirm form for deleting data.
+ */
+function rules_upgrade_confirm_clear_form($form, $form_state) {
+  $confirm_question = t('Are you sure you want to drop the Rules 1.x tables from the database?');
+  $confirm_question_long = t('Are you sure you want to drop the Rules 1.x tables from the database? All Rules 1.x configurations will be deleted regardless whether they have been already converted.') . ' ' . t('This action cannot be undone.');
+  return confirm_form($form, $confirm_question, 'admin/config/workflow/rules/upgrade', $confirm_question_long, t('Delete data'), t('Cancel'));
+}
+
+/**
+ * Submit handler for deleting data.
+ */
+function rules_upgrade_confirm_clear_form_submit($form, &$form_state) {
+  db_drop_table('rules_rules');
+  db_drop_table('rules_sets');
+  db_drop_table('rules_scheduler_d6');
+  drupal_set_message(t('Rules 1.x configurations have been deleted.'));
+  $form_state['redirect'] = 'admin';
+}
+
+/**
+ * Fetches a single item (rule | rule set).
+ */
+function _rules_upgrade_fetch_item($name, $table) {
+  $query = db_select($table, 'r')->fields('r')->condition('name', $name);
+  $row = $query->execute()->fetchAssoc();
+  return unserialize($row['data']);
+}
+
+/**
+ * Fetches all rules.
+ */
+function _rules_upgrade_fetch_all_rules() {
+  $static = drupal_static(__FUNCTION__);
+
+  if (!isset($static)) {
+    $query = db_select('rules_rules', 'r')->fields('r');
+    $static['rules'] = array();
+    foreach ($query->execute() as $row) {
+      $static['rules'][$row->name] = unserialize($row->data);
+    }
+  }
+  return $static['rules'];
+}
+
+/**
+ * Converts a single reaction rule.
+ */
+function rules_upgrade_convert_rule($name, $cfg_old) {
+  $config = rules_upgrade_plugin_factory($cfg_old);
+  $config->name = $name;
+
+  if ($config instanceof RulesReactionRule) {
+    rules_upgrade_convert_element($cfg_old, $config);
+  }
+  return $config;
+}
+
+/**
+ * Converts a single rule set, including all of its rules.
+ */
+function rules_upgrade_convert_rule_set($name, $cfg_old) {
+  $config = rules_plugin_factory('rule set');
+  $config->name = $name;
+  foreach (array('label', 'weight') as $key) {
+    if (isset($cfg_old[$key])) {
+      $config->$key = $cfg_old[$key];
+    }
+  }
+  if (isset($cfg_old['arguments'])) {
+    $vars = &$config->componentVariables();
+    foreach ($cfg_old['arguments'] as $var_name => $info) {
+      // Map data types as required.
+      if ($info['type'] == 'string') {
+        $info['type'] = 'text';
+      }
+      $vars[$var_name] = $info;
+    }
+  }
+
+  // Add in all rules of the set.
+  foreach (_rules_upgrade_fetch_all_rules() as $rule_name => $rule) {
+    if ($rule['#set'] == $name) {
+      drupal_set_message(' >> ' . t('Converting %plugin %name...', array('%plugin' => t('rule'), '%name' => $rule_name . ': ' . $rule['#label'])));
+      $new_rule = rules_upgrade_plugin_factory($rule);
+      rules_upgrade_convert_element($rule, $new_rule);
+      $new_rule->setParent($config);
+    }
+  }
+  return $config;
+}
+
+/**
+ * Convert a single element.
+ *
+ * @param array $element
+ *   The element to convert.
+ * @param RulesPlugin $target
+ *   The converted element to write to.
+ */
+function rules_upgrade_convert_element(array $element, RulesPlugin $target) {
+  foreach (array('active', 'label', 'weight') as $key) {
+    if (isset($element['#' . $key])) {
+      $target->$key = $element['#' . $key];
+    }
+  }
+  // Go through the parameters and take over its configuration if possible.
+  foreach ($target->pluginParameterInfo() as $name => $info) {
+    rules_upgrade_element_parameter_settings($element, $target, $name);
+  }
+  // @todo Care about php input evaluator for non-text parameters.
+
+  // Take care of variable names and labels.
+  foreach ($target->pluginProvidesVariables() as $name => $info) {
+    rules_upgrade_element_variable_settings($element, $target, $name);
+  }
+
+  if ($target instanceof RulesConditionInterface && !empty($element['#negate'])) {
+    $target->negate(TRUE);
+  }
+  if ($target instanceof RulesReactionRule) {
+    // Cut of the 'event_' prefix.
+    $target->event(substr($element['#set'], 6));
+  }
+  if ($element['#type'] == 'rule') {
+    if (!empty($element['#conditions'])) {
+      foreach (element_children($element['#conditions']) as $key) {
+        $child = rules_upgrade_plugin_factory($element['#conditions'][$key]);
+        rules_upgrade_convert_element($element['#conditions'][$key], $child);
+        $target->condition($child);
+      }
+    }
+    if (!empty($element['#actions'])) {
+      foreach (element_children($element['#actions']) as $key) {
+        $child = rules_upgrade_plugin_factory($element['#actions'][$key]);
+        rules_upgrade_convert_element($element['#actions'][$key], $child);
+        $target->action($child);
+      }
+    }
+  }
+
+  // Invoke action/condition specific hooks and a general one.
+  if (($element['#type'] == 'action' || $element['#type'] == 'condition')) {
+    if (function_exists($function = $element['#name'] . '_upgrade')) {
+      $element_name = $function($element, $target);
+    }
+    elseif (isset($element['#info']['base']) && function_exists($function = $element['#info']['base'] . '_upgrade')) {
+      $element_name = $function($element, $target);
+    }
+  }
+
+  drupal_alter('rules_element_upgrade', $element, $target);
+  // Recurse down, if necessary.
+  foreach (element_children($element) as $key) {
+    $child = rules_upgrade_plugin_factory($element[$key]);
+    rules_upgrade_convert_element($element[$key], $child);
+    $child->setParent($target);
+  }
+  if ($target instanceof RulesContainerPlugin) {
+    $target->sortChildren();
+  }
+}
+
+/**
+ * Creates the right element.
+ */
+function rules_upgrade_plugin_factory($element) {
+  if ($element['#type'] == 'rule' && !empty($element['#set']) && strpos($element['#set'], 'event_') === 0) {
+    return rules_plugin_factory('reaction rule');
+  }
+
+  switch ($element['#type']) {
+    case 'OR':
+      return rules_plugin_factory('or');
+
+    case 'AND':
+      return rules_plugin_factory('and');
+
+    default:
+      return rules_plugin_factory($element['#type']);
+
+    case 'action':
+    case 'condition':
+      if (isset($element['#name'])) {
+        // Try to come up with the right action/condition name ourself, then
+        // invoke a hook.
+        $cache = rules_get_cache();
+        $items = $cache[$element['#type'] == 'action' ? 'action_info' : 'condition_info'];
+
+        if (isset($items[$element['#name']])) {
+          $element_name = $element['#name'];
+        }
+        elseif (($name = str_replace('rules_', '', $element['#name'])) && isset($items[$name])) {
+          $element_name = $name;
+        }
+        elseif (($name = str_replace($element['#type'] . '_', '', $element['#name'])) && isset($items[$name])) {
+          $element_name = $name;
+        }
+        elseif (($name = str_replace('rules_' . $element['#type'] . '_', '', $element['#name'])) && isset($items[$name])) {
+          $element_name = $name;
+        }
+        elseif (isset($element['#info']['base']) && isset($items[$element['#info']['base']])) {
+          $element_name = $name;
+        }
+
+        // Call the upgrade callback if one has been defined.
+        if (function_exists($function = $element['#name'] . '_upgrade_map_name') || (isset($element['#info']['base']) && function_exists($function = $element['#info']['base'] . '_upgrade_map_name'))) {
+          $element_name = $function($element);
+        }
+        if (!isset($element_name)) {
+          throw new RulesIntegrityException(t("Cannot find @plugin %name. Maybe a required is missing or the module has not implemented the upgrade functionality.", array('@plugin' => $element['#type'], '%name' => $element['#name'])));
+        }
+        return rules_plugin_factory($element['#type'], $element_name);
+      }
+      break;
+  }
+}
+
+/**
+ * Converts the settings for a given parameter.
+ */
+function rules_upgrade_element_parameter_settings($element, $target, $name, $new_name = NULL) {
+  if (!isset($new_name)) {
+    $new_name = $name;
+  }
+  if (isset($element['#settings'][$name])) {
+    // In case a single token has been used, just convert it to a data
+    // selector.
+    if (is_string($element['#settings'][$name]) && preg_match("/\[(.*)\]$/", $element['#settings'][$name], $matches)) {
+      $target->settings[$new_name . ':select'] = $matches[1];
+    }
+    else {
+      $target->settings[$new_name] = $element['#settings'][$name];
+    }
+  }
+  elseif (isset($element['#settings']['#argument map'][$name])) {
+    $target->settings[$new_name . ':select'] = $element['#settings']['#argument map'][$name];
+  }
+}
+
+/**
+ * Converts the settings for a given variable.
+ */
+function rules_upgrade_element_variable_settings($element, $target, $name, $new_name = NULL) {
+  if (!isset($new_name)) {
+    $new_name = $name;
+  }
+  if (isset($element['#settings']['#argument map'][$name])) {
+    $target->settings[$new_name . ':var'] = $element['#settings']['#argument map'][$name];
+    $target->settings[$new_name . ':label'] = $element['#info']['new variables'][$target->settings[$new_name . ':var']]['label'];
+  }
+}
+
+/**
+ * Upgrade callbacks for upgrading the provided Rules 1.x integration.
+ */
+
+/**
+ * Comment.module integration.
+ */
+function rules_action_load_comment_upgrade_map_name($element) {
+  return 'entity_fetch';
+}
+
+function rules_action_load_comment_upgrade($element, $target) {
+  $target->settings['type'] = 'comment';
+  rules_upgrade_element_parameter_settings($element, $target, 'cid', 'id');
+  rules_upgrade_element_variable_settings($element, $target, 'comment_loaded', 'entity_fetched');
+}
+
+/**
+ * Node.module integration.
+ */
+function rules_condition_content_is_type_upgrade_map_name($element) {
+  return 'node_is_of_type';
+}
+
+function rules_condition_content_is_published_upgrade_map_name($element) {
+  return 'node_is_published';
+}
+
+function rules_condition_content_is_sticky_upgrade_map_name($element) {
+  return 'node_is_sticky';
+}
+
+function rules_condition_content_is_promoted_upgrade_map_name($element) {
+  return 'node_is_promoted';
+}
+
+function rules_condition_content_is_new_upgrade_map_name($element) {
+  return 'entity_is_new';
+}
+
+function rules_condition_content_is_new_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'node', 'entity');
+}
+
+function rules_action_node_set_author_upgrade_map_name($element) {
+  return 'data_set';
+}
+
+function rules_action_node_set_author_upgrade($element, $target) {
+  $target->settings['data:select'] = $element['#settings']['#argument map']['node'] . ':author';
+  $target->settings['value:select'] = $element['#settings']['#argument map']['author'];
+}
+
+function rules_action_node_load_author_upgrade_map_name($element) {
+  return 'entity_fetch';
+}
+
+function rules_action_node_load_author_upgrade($element, $target) {
+  $target->settings['type'] = 'user';
+  $target->settings['id'] = $element['#settings']['#argument map']['node'] . ':author:uid';
+}
+
+function rules_action_set_node_title_upgrade_map_name($element) {
+  return 'data_set';
+}
+
+function rules_action_set_node_title_upgrade($element, $target) {
+  $target->settings['data:select'] = $element['#settings']['#argument map']['node'] . ':title';
+  $target->settings['value'] = $element['#settings']['title'];
+}
+
+function rules_action_add_node_upgrade_map_name($element) {
+  return 'entity_create';
+}
+
+function rules_action_add_node_upgrade($element, $target) {
+  $target->settings['type'] = 'node';
+  rules_upgrade_element_parameter_settings($element, $target, 'title', 'param_title');
+  rules_upgrade_element_parameter_settings($element, $target, 'author', 'param_author');
+  rules_upgrade_element_parameter_settings($element, $target, 'type', 'param_type');
+  rules_upgrade_element_variable_settings($element, $target, 'node_added', 'entity_created');
+  if (!empty($element['#settings']['node_access'])) {
+    drupal_set_message(t('Warning: The node-access check option for the node creation action is not supported any more.'));
+  }
+}
+
+function rules_action_load_node_upgrade_map_name($element) {
+  return 'entity_fetch';
+}
+
+function rules_action_load_node_upgrade($element, $target) {
+  $target->settings['type'] = 'node';
+  rules_upgrade_element_parameter_settings($element, $target, 'nid', 'id');
+  rules_upgrade_element_parameter_settings($element, $target, 'vid', 'revision_id');
+  rules_upgrade_element_variable_settings($element, $target, 'node_loaded', 'entity_fetched');
+}
+
+function rules_action_delete_node_upgrade_map_name($element) {
+  return 'entity_delete';
+}
+
+function rules_action_delete_node_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'node', 'entity');
+}
+
+function rules_core_node_publish_action_upgrade_map_name($element) {
+  return 'node_publish';
+}
+
+function rules_core_node_unpublish_action_upgrade_map_name($element) {
+  return 'node_unpublish';
+}
+
+function rules_core_node_make_sticky_action_upgrade_map_name($element) {
+  return 'node_make_sticky_action';
+}
+
+function rules_core_node_make_unsticky_action_upgrade_map_name($element) {
+  return 'node_make_unsticky_action';
+}
+
+function rules_core_node_promote_action_upgrade_map_name($element) {
+  return 'node_promote_action';
+}
+
+function rules_core_node_unpromote_action_upgrade_map_name($element) {
+  return 'node_unpromote_action';
+}
+
+/**
+ * Path.module integration.
+ */
+function rules_condition_url_has_alias_upgrade_map_name($element) {
+  return 'path_has_alias';
+}
+
+function rules_condition_url_has_alias_upgrade($element, $target) {
+  $target->settings['source'] = $element['#settings']['src'];
+  $target->settings['alias'] = $element['#settings']['dst'];
+}
+
+function rules_condition_alias_exists_upgrade_map_name($element) {
+  return 'path_alias_exists';
+}
+
+function rules_condition_alias_exists_upgrade($element, $target) {
+  $target->settings['alias'] = $element['#settings']['dst'];
+}
+
+function rules_action_path_alias_upgrade($element, $target) {
+  $target->settings['source'] = $element['#settings']['src'];
+  $target->settings['alias'] = $element['#settings']['dst'];
+}
+
+function rules_action_node_path_alias_upgrade($element, $target) {
+  $target->settings['alias'] = $element['#settings']['dst'];
+}
+
+/**
+ * PHP.module integration.
+ */
+function rules_condition_custom_php_upgrade_map_name($element) {
+  return 'php_eval';
+}
+
+function rules_action_custom_php_upgrade_map_name($element) {
+  return 'php_eval';
+}
+
+/**
+ * General Rules integration.
+ */
+function rules_condition_text_compare_upgrade_map_name($element) {
+  // @todo Support regex.
+  return 'data_is';
+}
+
+function rules_condition_text_compare_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'text1', 'data');
+  rules_upgrade_element_parameter_settings($element, $target, 'text2', 'value');
+}
+
+function rules_condition_number_compare_upgrade_map_name($element) {
+  return 'data_is';
+}
+
+function rules_condition_number_compare_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'number1', 'data');
+  rules_upgrade_element_parameter_settings($element, $target, 'number2', 'value');
+}
+
+function rules_condition_check_boolean_upgrade_map_name($element) {
+  return 'data_is';
+}
+
+function rules_condition_check_boolean_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'boolean', 'data');
+  $target->settings['value'] = TRUE;
+}
+
+function rules_action_invoke_set_upgrade_map_name($element) {
+  return 'component_' . $element['#info']['set'];
+}
+
+function rules_action_invoke_set_upgrade($element, $target) {
+  foreach ($element['#info']['arguments'] as $name => $info) {
+    rules_upgrade_element_parameter_settings($element, $target, $name);
+  }
+}
+
+function rules_action_save_variable_upgrade_map_name($element) {
+  return isset($element['#info']['new variables']) ? 'variable_add' : 'entity_save';
+}
+
+function rules_action_save_variable_upgrade($element, $target) {
+  $type = $element['#info']['arguments']['var_name']['default value'];
+  if (isset($element['#info']['new variables'])) {
+    $target->settings['type'] = $type;
+    rules_upgrade_element_parameter_settings($element, $target, $type, 'value');
+    rules_upgrade_element_variable_settings($element, $target, $type, 'variable_added');
+  }
+  else {
+    rules_upgrade_element_parameter_settings($element, $target, $type, 'entity');
+  }
+}
+
+/**
+ * System.module integration.
+ */
+function rules_action_set_breadcrumb_upgrade_map_name($element) {
+  return 'breadcrumb_set';
+}
+
+function rules_action_mail_to_user_upgrade_map_name($element) {
+  return 'mail';
+}
+
+function rules_action_mail_to_user_upgrade($element, $target) {
+  $target->settings['to:select'] = $element['#settings']['#argument map']['user'] . ':mail';
+}
+
+function rules_action_drupal_goto_upgrade_map_name($element) {
+  return 'redirect';
+}
+
+function rules_action_drupal_goto_upgrade($element, $target) {
+  $settings = $element['#settings'];
+  $target->settings['url'] = $settings['path'];
+  $target->settings['url'] .= $settings['query'] ? '?' . $settings['query'] : '';
+  $target->settings['url'] .= $settings['fragment'] ? '#' . $settings['fragment'] : '';
+  if ($settings['immediate']) {
+    drupal_set_message(t("Warning: The 'immediate' option for the page redirect action has been dropped in Rules 2.x."));
+  }
+}
+
+function rules_action_watchdog_upgrade_map_name($element) {
+  // @todo Support action in Rules 2.x!
+  return NULL;
+}
+
+/**
+ * Taxonomy.module integration.
+ *
+ * @todo Finish.
+ */
+function rules_action_taxonomy_load_term_upgrade_map_name($element) {
+  return 'entity_fetch';
+}
+
+function rules_action_taxonomy_add_term_upgrade_map_name($element) {
+  return 'entity_create';
+}
+
+function rules_action_taxonomy_delete_term_upgrade_map_name($element) {
+  return 'entity_delete';
+}
+
+function rules_action_taxonomy_term_assign_to_content_upgrade_map_name($element) {
+  // @todo List.
+  return NULL;
+}
+
+function rules_action_taxonomy_term_remove_from_content_upgrade_map_name($element) {
+  // @todo List.
+  return NULL;
+}
+
+function rules_action_taxonomy_load_vocab_upgrade_map_name($element) {
+  return 'entity_fetch';
+}
+
+function rules_action_taxonomy_add_vocab_upgrade_map_name($element) {
+  return 'data_set';
+}
+
+/**
+ * User.module integration.
+ */
+function rules_condition_user_hasrole_upgrade_map_name($element) {
+  return 'user_has_role';
+}
+
+function rules_condition_user_hasrole_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'user', 'account');
+}
+
+function rules_condition_user_comparison_upgrade_map_name($element) {
+  return 'data_is';
+}
+
+function rules_condition_user_comparison_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'user1', 'data');
+  rules_upgrade_element_parameter_settings($element, $target, 'user2', 'value');
+}
+
+function rules_action_user_addrole_upgrade_map_name($element) {
+  return 'user_add_role';
+}
+
+function rules_action_user_addrole_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'user', 'account');
+}
+
+function rules_action_user_removerole_upgrade_map_name($element) {
+  return 'user_remove_role';
+}
+
+function rules_action_user_removerole_upgrade($element, $target) {
+  rules_upgrade_element_parameter_settings($element, $target, 'user', 'account');
+}
+
+function rules_action_load_user_upgrade_map_name($element) {
+  if (!empty($element['#settings']['username'])) {
+    drupal_set_message(t('Warning: Directly upgrading the load user by name action is not supported.'));
+  }
+  return 'entity_fetch';
+}
+
+function rules_action_load_user_upgrade($element, $target) {
+  $target->settings['type'] = 'user';
+  rules_upgrade_element_parameter_settings($element, $target, 'userid', 'id');
+  rules_upgrade_element_variable_settings($element, $target, 'user_loaded', 'entity_fetched');
+}
+
+function rules_action_user_create_upgrade_map_name($element) {
+  return 'entity_create';
+}
+
+function rules_action_user_create_upgrade($element, $target) {
+  $target->settings['type'] = 'user';
+  rules_upgrade_element_parameter_settings($element, $target, 'username', 'param_name');
+  rules_upgrade_element_parameter_settings($element, $target, 'email', 'param_mail');
+  rules_upgrade_element_variable_settings($element, $target, 'user_added', 'entity_created');
+}
+
+function rules_core_user_block_user_action_upgrade_map_name($element) {
+  return 'user_block';
+}
+
+function rules_core_user_block_user_action_upgrade($element, $target) {
+  $target->settings['account:select'] = $element['#settings']['#argument map']['user'];
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/comment.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/comment.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..2052e6f99f2484649ab18e59d2be92a5acc7fd77
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/comment.rules.inc
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Rules integration for the comment module.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_event_info().
+ */
+function rules_comment_event_info() {
+  $defaults = array(
+    'group' => t('comment'),
+    'module' => 'comment',
+    'access callback' => 'rules_comment_integration_access',
+    'class' => 'RulesCommentEventHandler',
+  );
+  return array(
+    'comment_insert' => $defaults + array(
+      'label' => t('After saving a new comment'),
+      'variables' => array(
+        'comment' => array('type' => 'comment', 'label' => t('created comment')),
+      ),
+    ),
+    'comment_update' => $defaults + array(
+      'label' => t('After updating an existing comment'),
+      'variables' => array(
+        'comment' => array(
+          'type' => 'comment',
+          'label' => t('updated comment'),
+        ),
+        'comment_unchanged' => array(
+          'type' => 'comment',
+          'label' => t('unchanged comment'),
+          'handler' => 'rules_events_entity_unchanged',
+        ),
+      ),
+    ),
+    'comment_presave' => $defaults + array(
+      'label' => t('Before saving a comment'),
+      'variables' => array(
+        'comment' => array(
+          'type' => 'comment',
+          'label' => t('saved comment'),
+          'skip save' => TRUE,
+        ),
+        'comment_unchanged' => array(
+          'type' => 'comment',
+          'label' => t('unchanged comment'),
+          'handler' => 'rules_events_entity_unchanged',
+        ),
+      ),
+    ),
+    'comment_view' => $defaults + array(
+      'label' => t('A comment is viewed'),
+      'variables' => array(
+        'comment' => array('type' => 'comment', 'label' => t('viewed comment')),
+      ),
+      'help' => t("Note that if drupal's page cache is enabled, this event won't be generated for pages served from cache."),
+    ),
+    'comment_delete' => $defaults + array(
+      'label' => t('After deleting a comment'),
+      'variables' => array(
+        'comment' => array('type' => 'comment', 'label' => t('deleted comment')),
+      ),
+    ),
+  );
+}
+
+/**
+ * Comment integration access callback.
+ */
+function rules_comment_integration_access($type, $name) {
+  if ($type == 'event' || $type == 'condition') {
+    return entity_access('view', 'comment');
+  }
+}
+
+/**
+ * Event handler support comment bundle event settings.
+ */
+class RulesCommentEventHandler extends RulesEventHandlerEntityBundle {
+
+  /**
+   * Returns the label to use for the bundle property.
+   *
+   * @return string
+   *   Returns the label to use for the bundle property.
+   */
+  protected function getBundlePropertyLabel() {
+    return t('type');
+  }
+
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/data.eval.inc b/profiles/wcm_base/modules/contrib/rules/modules/data.eval.inc
new file mode 100644
index 0000000000000000000000000000000000000000..b3b67592fdd75ea8c02873de2abae636b77a899b
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/data.eval.inc
@@ -0,0 +1,464 @@
+<?php
+
+/**
+ * @file
+ * Contains rules integration for the data module needed during evaluation.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Action: Modify data.
+ */
+function rules_action_data_set($wrapper, $value, $settings, $state, $element) {
+  if ($wrapper instanceof EntityMetadataWrapper) {
+    try {
+      // Update the value first then save changes, if possible.
+      $wrapper->set($value);
+    }
+    catch (EntityMetadataWrapperException $e) {
+      throw new RulesEvaluationException('Unable to modify data "@selector": ' . $e->getMessage(), array('@selector' => $settings['data:select']));
+    }
+    // Save changes if a property of a variable has been changed.
+    if (strpos($element->settings['data:select'], ':') !== FALSE) {
+      $info = $wrapper->info();
+      // We always have to save the changes in the parent entity. E.g. when the
+      // node author is changed, we don't want to save the author but the node.
+      $state->saveChanges(implode(':', explode(':', $settings['data:select'], -1)), $info['parent']);
+    }
+  }
+  else {
+    // A not wrapped variable (e.g. a number) is being updated. Just overwrite
+    // the variable with the new value.
+    return array('data' => $value);
+  }
+}
+
+/**
+ * Info alter callback for the data_set action.
+ */
+function rules_action_data_set_info_alter(&$element_info, $element) {
+  $element->settings += array('data:select' => NULL);
+  if ($wrapper = $element->applyDataSelector($element->settings['data:select'])) {
+    $info = $wrapper->info();
+    $element_info['parameter']['value']['type'] = $wrapper->type();
+    $element_info['parameter']['value']['options list'] = !empty($info['options list']) ? 'rules_data_selector_options_list' : FALSE;
+  }
+}
+
+/**
+ * Action: Calculate a value.
+ */
+function rules_action_data_calc($input1, $op, $input2, $settings, $state, $element) {
+  $info = $element->pluginParameterInfo();
+  // Make sure to apply date offsets intelligently.
+  if ($info['input_1']['type'] == 'date' && $info['input_2']['type'] == 'duration') {
+    $input2 = ($op == '-') ? $input2 * -1 : $input2;
+    return array('result' => (int) RulesDateOffsetProcessor::applyOffset($input1, $input2));
+  }
+
+  switch ($op) {
+    case '+':
+      $result = $input1 + $input2;
+      break;
+
+    case '-':
+      $result = $input1 - $input2;
+      break;
+
+    case '*':
+      $result = $input1 * $input2;
+      break;
+
+    case '/':
+      $result = $input1 / $input2;
+      break;
+
+    case 'min':
+      $result = min($input1, $input2);
+      break;
+
+    case 'max':
+      $result = max($input1, $input2);
+      break;
+  }
+  if (isset($result)) {
+    // Ensure results are valid integer values if necessary.
+    $variables = $element->providesVariables();
+    $var_info = reset($variables);
+    if ($var_info['type'] == 'integer') {
+      $result = (int) $result;
+    }
+    return array('result' => $result);
+  }
+}
+
+/**
+ * Info alter callback for the data_calc action.
+ */
+function rules_action_data_calc_info_alter(&$element_info, RulesPlugin $element) {
+  if ($info = $element->getArgumentInfo('input_1')) {
+    // Only allow durations as offset for date values.
+    if ($info['type'] == 'date') {
+      $element_info['parameter']['input_2']['type'] = 'duration';
+    }
+    // Specify the data type of the result.
+    $element_info['provides']['result']['type'] = $info['type'];
+
+    if ($info['type'] == 'integer' && ($info2 = $element->getArgumentInfo('input_2')) && $info2['type'] == 'decimal') {
+      $element_info['provides']['result']['type'] = 'decimal';
+    }
+    // A division with two integers results in a decimal.
+    elseif (isset($element->settings['op']) && $element->settings['op'] == '/') {
+      $element_info['provides']['result']['type'] = 'decimal';
+    }
+  }
+}
+
+/**
+ * Action: Add a list item.
+ */
+function rules_action_data_list_add($list, $item, $unique = FALSE, $pos = 'end', $settings, $state) {
+  // Optionally, only add the list item if it is not yet contained.
+  if ($unique && rules_condition_data_list_contains($list, $item, $settings, $state)) {
+    return;
+  }
+
+  switch ($pos) {
+    case 'start':
+      array_unshift($list, $item);
+      break;
+
+    default:
+      $list[] = $item;
+      break;
+  }
+  return array('list' => $list);
+}
+
+/**
+ * Info alteration callback for the "Add and Remove a list item" actions.
+ */
+function rules_data_list_info_alter(&$element_info, RulesAbstractPlugin $element) {
+  // Update the required type for the list item if it is known.
+  $element->settings += array('list:select' => NULL);
+  if ($wrapper = $element->applyDataSelector($element->settings['list:select'])) {
+    if ($type = entity_property_list_extract_type($wrapper->type())) {
+      $info = $wrapper->info();
+      $element_info['parameter']['item']['type'] = $type;
+      $element_info['parameter']['item']['options list'] = !empty($info['options list']) ? 'rules_data_selector_options_list' : FALSE;
+    }
+  }
+}
+
+/**
+ * Action: Remove a list item.
+ */
+function rules_action_data_list_remove($list, $item) {
+  foreach (array_keys($list, $item) as $key) {
+    unset($list[$key]);
+  }
+  return array('list' => $list);
+}
+
+/**
+ * Action: Add variable.
+ */
+function rules_action_variable_add($args, $element) {
+  return array('variable_added' => $args['value']);
+}
+
+/**
+ * Info alteration callback for variable add action.
+ */
+function rules_action_variable_add_info_alter(&$element_info, RulesAbstractPlugin $element) {
+  if (isset($element->settings['type']) && $type = $element->settings['type']) {
+    $cache = rules_get_cache();
+    $type_info = $cache['data_info'][$type];
+    $element_info['parameter']['value']['type'] = $type;
+    $element_info['provides']['variable_added']['type'] = $type;
+
+    // For lists, we default to an empty list so subsequent actions can add
+    // items.
+    if (entity_property_list_extract_type($type)) {
+      $element_info['parameter']['value']['default value'] = array();
+    }
+  }
+}
+
+/**
+ * Action: Convert a value.
+ */
+function rules_action_data_convert($arguments, RulesPlugin $element, $state) {
+
+  $value_info = $element->getArgumentInfo('value');
+  $from_type = $value_info['type'];
+  $target_type = $arguments['type'];
+
+  // First apply the rounding behavior if given.
+  if (isset($arguments['rounding_behavior'])) {
+    switch ($arguments['rounding_behavior']) {
+      case 'up':
+        $arguments['value'] = ceil($arguments['value']);
+        break;
+
+      case 'down':
+        $arguments['value'] = floor($arguments['value']);
+        break;
+
+      default:
+      case 'round':
+        $arguments['value'] = round($arguments['value']);
+        break;
+    }
+  }
+
+  switch ($target_type) {
+    case 'decimal':
+      $result = floatval($arguments['value']);
+      break;
+
+    case 'integer':
+      $result = intval($arguments['value']);
+      break;
+
+    case 'text':
+      $result = strval($arguments['value']);
+      break;
+
+    case 'token':
+      $result = strval($arguments['value']);
+      break;
+  }
+
+  return array('conversion_result' => $result);
+}
+
+/**
+ * Info alteration callback for variable add action.
+ */
+function rules_action_data_convert_info_alter(&$element_info, RulesAbstractPlugin $element) {
+
+  if (isset($element->settings['type']) && $type = $element->settings['type']) {
+    $element_info['provides']['conversion_result']['type'] = $type;
+
+    // Only support the rounding behavior option for integers.
+    if ($type == 'integer') {
+      $element_info['parameter']['rounding_behavior'] = array(
+        'type' => 'token',
+        'label' => t('Rounding behavior'),
+        'description' => t('The rounding behavior the conversion should use.'),
+        'options list' => 'rules_action_data_convert_rounding_behavior_options',
+        'restriction' => 'input',
+        'default value' => 'round',
+        'optional' => TRUE,
+      );
+    }
+    else {
+      unset($element_info['parameter']['rounding_behavior']);
+    }
+
+    // Configure compatible source-types:
+    switch ($type) {
+      case 'integer':
+        $sources = array('decimal', 'text', 'token', 'uri', 'date', 'duration', 'boolean');
+        break;
+
+      case 'decimal':
+        $sources = array('integer', 'text', 'token', 'uri', 'date', 'duration', 'boolean');
+        break;
+
+      case 'text':
+        $sources = array('integer', 'decimal', 'token', 'uri', 'date', 'duration', 'boolean');
+        break;
+
+      case 'token':
+        $sources = array('integer', 'decimal', 'text', 'uri', 'date', 'duration', 'boolean');
+        break;
+    }
+    $element_info['parameter']['value']['type'] = $sources;
+  }
+}
+
+/**
+ * Action: Create data.
+ */
+function rules_action_data_create($args, $element) {
+  $type = $args['type'];
+  $values = array();
+  foreach ($element->pluginParameterInfo() as $name => $info) {
+    if ($name != 'type') {
+      // Remove the parameter name prefix 'param_'.
+      $values[substr($name, 6)] = $args[$name];
+    }
+  }
+  $cache = rules_get_cache();
+  $type_info = $cache['data_info'][$type];
+  if (isset($type_info['creation callback'])) {
+    try {
+      $data = $type_info['creation callback']($values, $type);
+      return array('data_created' => $data);
+    }
+    catch (EntityMetadataWrapperException $e) {
+      throw new RulesEvaluationException('Unable to create @data": ' . $e->getMessage(), array('@data' => $type), $element);
+    }
+  }
+  else {
+    throw new RulesEvaluationException('Unable to create @data, no creation callback found.', array('@data' => $type), $element, RulesLog::ERROR);
+  }
+}
+
+/**
+ * Info alteration callback for data create action.
+ */
+function rules_action_data_create_info_alter(&$element_info, RulesAbstractPlugin $element) {
+  if (!empty($element->settings['type'])) {
+    $type = $element->settings['type'];
+    $cache = rules_get_cache();
+    $type_info = $cache['data_info'][$type];
+    if (isset($type_info['property info'])) {
+      // Add the data type's properties as parameters.
+      foreach ($type_info['property info'] as $property => $property_info) {
+        // Prefix parameter names to avoid name clashes with
+        // existing parameters.
+        $element_info['parameter']['param_' . $property] = array_intersect_key($property_info, array_flip(array('type', 'label', 'allow null')));
+        if (empty($property_info['required'])) {
+          $element_info['parameter']['param_' . $property]['optional'] = TRUE;
+          $element_info['parameter']['param_' . $property]['allow null'] = TRUE;
+        }
+      }
+    }
+    $element_info['provides']['data_created']['type'] = $type;
+  }
+}
+
+/**
+ * Creation callback for array structured data.
+ */
+function rules_action_data_create_array($values = array(), $type) {
+  // $values is an array already, so we can just pass it to the wrapper.
+  return rules_wrap_data($values, array('type' => $type));
+}
+
+/**
+ * Condition: Compare data.
+ */
+function rules_condition_data_is($data, $op, $value) {
+  switch ($op) {
+    default:
+    case '==':
+      // In case both values evaluate to FALSE, further differentiate between
+      // NULL values and values evaluating to FALSE.
+      if (!$data && !$value) {
+        return (isset($data) && isset($value)) || (!isset($data) && !isset($value));
+      }
+      return $data == $value;
+
+    case '<':
+      return $data < $value;
+
+    case '>':
+      return $data > $value;
+
+    // Note: This is deprecated by the text comparison condition and IN below.
+    case 'contains':
+      return is_string($data) && strpos($data, $value) !== FALSE || is_array($data) && in_array($value, $data);
+
+    case 'IN':
+      return is_array($value) && in_array($data, $value);
+  }
+}
+
+/**
+ * Info alteration callback for the data_is condition.
+ *
+ * If we check the bundle property of a variable, add an assertion so that later
+ * evaluated elements can make use of this information.
+ */
+function rules_condition_data_is_info_alter(&$element_info, RulesAbstractPlugin $element) {
+  $element->settings += array('data:select' => NULL, 'op' => '==');
+  if ($wrapper = $element->applyDataSelector($element->settings['data:select'])) {
+    $info = $wrapper->info();
+    $element_info['parameter']['value']['type'] = $element->settings['op'] == 'IN' ? 'list<' . $wrapper->type() . '>' : $wrapper->type();
+    $element_info['parameter']['value']['options list'] = !empty($info['options list']) ? 'rules_data_selector_options_list' : FALSE;
+  }
+}
+
+/**
+ * Condition: List contains.
+ */
+function rules_condition_data_list_contains($list, $item, $settings, $state) {
+  $wrapper = $state->currentArguments['item'];
+  if ($wrapper instanceof EntityStructureWrapper && $id = $wrapper->getIdentifier()) {
+    // Check for equal items using the identifier if there is one.
+    foreach ($state->currentArguments['list'] as $i) {
+      if ($i->getIdentifier() == $id) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+  return in_array($item, $list);
+}
+
+/**
+ * Condition: List count comparison.
+ */
+function rules_condition_data_list_count_is($list, $op = '==', $value) {
+  switch ($op) {
+    case '==':
+      return count($list) == $value;
+
+    case '<':
+      return count($list) < $value;
+
+    case '>':
+      return count($list) > $value;
+  }
+}
+
+/**
+ * Condition: Data value is empty.
+ */
+function rules_condition_data_is_empty($data) {
+  // Note that some primitive variables might not be wrapped at all.
+  if ($data instanceof EntityMetadataWrapper) {
+    try {
+      // We cannot use the dataAvailable() method from the wrapper because it
+      // is protected, so we catch possible exceptions with the value() method.
+      $value = $data->value();
+      return empty($value);
+    }
+    catch (EntityMetadataWrapperException $e) {
+      // An exception means that the wrapper is somehow broken and we treat
+      // that as empty.
+      return TRUE;
+    }
+  }
+  return empty($data);
+}
+
+/**
+ * Condition: Textual comparison.
+ */
+function rules_data_text_comparison($text, $text2, $op = 'contains') {
+  switch ($op) {
+    case 'contains':
+      return strpos($text, $text2) !== FALSE;
+
+    case 'starts':
+      return strpos($text, $text2) === 0;
+
+    case 'ends':
+      return strrpos($text, $text2) === (strlen($text) - strlen($text2));
+
+    case 'regex':
+      return (bool) preg_match('/' . str_replace('/', '\\/', $text2) . '/', $text);
+  }
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/data.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/data.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..8143e779cbc8410f5e06d326afe0f75239bf32c1
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/data.rules.inc
@@ -0,0 +1,757 @@
+<?php
+
+/**
+ * @file
+ * General data related rules integration.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_category_info() on behalf of the pseudo data module.
+ */
+function rules_data_category_info() {
+  return array(
+    'rules_data' => array(
+      'label' => t('Data'),
+      'equals group' => t('Data'),
+      'weight' => -50,
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_file_info() on behalf of the pseudo data module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_data_file_info() {
+  return array('modules/data.eval');
+}
+
+/**
+ * Implements hook_rules_action_info() on behalf of the pseudo data module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_data_action_info() {
+  $return['data_set'] = array(
+    'label' => t('Set a data value'),
+    'parameter' => array(
+      'data' => array(
+        'type' => '*',
+        'label' => t('Data'),
+        'description' => t('Specifies the data to be modified using a data selector, e.g. "node:author:name".'),
+        'restriction' => 'selector',
+        'wrapped' => TRUE,
+        'allow null' => TRUE,
+      ),
+      'value' => array(
+        'type' => '*',
+        'label' => t('Value'),
+        'description' => t('The new value to set for the specified data.'),
+        'allow null' => TRUE,
+        'optional' => TRUE,
+      ),
+    ),
+    'group' => t('Data'),
+    'base' => 'rules_action_data_set',
+  );
+  $return['data_calc'] = array(
+    'label' => t('Calculate a value'),
+    'parameter' => array(
+      'input_1' => array(
+        'type' => array('decimal', 'date'),
+        'label' => t('Input value 1'),
+        'description' => t('The first input value for the calculation.'),
+      ),
+      'op' => array(
+        'type' => 'text',
+        'label' => t('Operator'),
+        'description' => t('The calculation operator.'),
+        'options list' => 'rules_action_data_calc_operator_options',
+        'restriction' => 'input',
+        'default value' => '+',
+      ),
+      'input_2' => array(
+        'type' => 'decimal',
+        'label' => t('Input value 2'),
+        'description' => t('The second input value.'),
+      ),
+    ),
+    'group' => t('Data'),
+    'base' => 'rules_action_data_calc',
+    'provides' => array(
+      'result' => array(
+        'type' => 'unknown',
+        'label' => t('Calculation result'),
+      ),
+    ),
+  );
+  $return['list_add'] = array(
+    'label' => t('Add an item to a list'),
+    'parameter' => array(
+      'list' => array(
+        'type' => 'list',
+        'label' => t('List', array(), array('context' => 'data_types')),
+        'description' => t('The data list, to which an item is to be added.'),
+        'restriction' => 'selector',
+        'allow null' => TRUE,
+        'save' => TRUE,
+      ),
+      'item' => array(
+        'type' => 'unknown',
+        'label' => t('Item to add'),
+      ),
+      'unique' => array(
+        'type' => 'boolean',
+        'label' => t('Enforce uniqueness'),
+        'description' => t('Only add the item to the list if it is not yet contained.'),
+        'optional' => TRUE,
+        'default value' => FALSE,
+      ),
+      'pos' => array(
+        'type' => 'text',
+        'label' => t('Insert position'),
+        'optional' => TRUE,
+        'default value' => 'end',
+        'options list' => 'rules_action_data_list_add_positions',
+      ),
+    ),
+    'group' => t('Data'),
+    'base' => 'rules_action_data_list_add',
+    'callbacks' => array(
+      'info_alter' => 'rules_data_list_info_alter',
+      'form_alter' => 'rules_data_list_form_alter',
+    ),
+  );
+  $return['list_remove'] = array(
+    'label' => t('Remove an item from a list'),
+    'parameter' => array(
+      'list' => array(
+        'type' => 'list',
+        'label' => t('List', array(), array('context' => 'data_types')),
+        'description' => t('The data list for which an item is to be removed.'),
+        'restriction' => 'selector',
+        'save' => TRUE,
+      ),
+      'item' => array(
+        'type' => 'unknown',
+        'label' => t('Item to remove'),
+      ),
+    ),
+    'group' => t('Data'),
+    'base' => 'rules_action_data_list_remove',
+    'callbacks' => array(
+      'info_alter' => 'rules_data_list_info_alter',
+      'form_alter' => 'rules_data_list_form_alter',
+    ),
+  );
+  $return['variable_add'] = array(
+    'label' => t('Add a variable'),
+    'named parameter' => TRUE,
+    'parameter' => array(
+      'type' => array(
+        'type' => 'text',
+        'label' => t('Type'),
+        'options list' => 'rules_data_action_variable_add_options',
+        'description' => t('Specifies the type of the variable that should be added.'),
+        'restriction' => 'input',
+      ),
+      'value' => array(
+        'type' => 'unknown',
+        'label' => t('Value'),
+        'optional' => TRUE,
+        'description' => t('Optionally, specify the initial value of the variable.'),
+      ),
+    ),
+    'provides' => array(
+      'variable_added' => array(
+        'type' => 'unknown',
+        'label' => t('Added variable'),
+      ),
+    ),
+    'group' => t('Data'),
+    'base' => 'rules_action_variable_add',
+    'callbacks' => array(
+      'form_alter' => 'rules_action_type_form_alter',
+      'validate' => 'rules_action_create_type_validate',
+    ),
+  );
+
+  if (rules_data_action_data_create_options()) {
+    $return['data_create'] = array(
+      'label' => t('Create a data structure'),
+      'named parameter' => TRUE,
+      'parameter' => array(
+        'type' => array(
+          'type' => 'text',
+          'label' => t('Type'),
+          'options list' => 'rules_data_action_data_create_options',
+          'description' => t('Specifies the type of the data structure that should be created.'),
+          'restriction' => 'input',
+        ),
+        // Further needed parameters depend on the type.
+      ),
+      'provides' => array(
+        'data_created' => array(
+          'type' => 'unknown',
+          'label' => t('Created data'),
+        ),
+      ),
+      'group' => t('Data'),
+      'base' => 'rules_action_data_create',
+      'callbacks' => array(
+        'form_alter' => 'rules_action_type_form_alter',
+        'validate' => 'rules_action_create_type_validate',
+      ),
+    );
+  }
+  $return['data_convert'] = array(
+    'label' => t('Convert data type'),
+    'parameter' => array(
+      'type' => array(
+        'type' => 'token',
+        'label' => t('Target type'),
+        'description' => t('The data type to convert a value to.'),
+        'options list' => 'rules_action_data_convert_types_options',
+        'restriction' => 'input',
+      ),
+      'value' => array(
+        'type' => array('decimal', 'integer', 'text', 'token'),
+        'label' => t('Value to convert'),
+        'default mode' => 'selector',
+      ),
+    ),
+    'provides' => array(
+      'conversion_result' => array(
+        'type' => 'unknown',
+        'label' => t('Conversion result'),
+      ),
+    ),
+    'group' => t('Data'),
+    'base' => 'rules_action_data_convert',
+    'named parameter' => TRUE,
+    'callbacks' => array(
+      'form_alter' => 'rules_action_type_form_alter',
+    ),
+  );
+  return $return;
+}
+
+/**
+ * Data conversation action: Options list callback for the target type.
+ */
+function rules_action_data_convert_types_options(RulesPlugin $element, $param_name) {
+  return array(
+    'decimal' => t('Decimal'),
+    'integer' => t('Integer'),
+    'text' => t('Text'),
+    'token' => t('Token'),
+  );
+}
+
+/**
+ * Data conversation action: Options list callback for rounding behavior.
+ */
+function rules_action_data_convert_rounding_behavior_options(RulesPlugin $element, $param_name) {
+  return array(
+    'down' => t('Always down (9.5 -> 9)'),
+    'round' => t('Round, half up (9.5 -> 10)'),
+    'up' => t('Always up (9.5 -> 10)'),
+  );
+}
+
+/**
+ * Customize access check for data set action.
+ */
+function rules_action_data_set_access(RulesAbstractPlugin $element) {
+  if (isset($element->settings['data:select']) && $wrapper = $element->applyDataSelector($element->settings['data:select'])) {
+    return $wrapper instanceof EntityMetadataWrapper && $wrapper->access('edit');
+  }
+}
+
+/**
+ * Custom validation callback for the data set action.
+ */
+function rules_action_data_set_validate(RulesAbstractPlugin $element) {
+  $element->settings += array('data:select' => NULL);
+  $info = $element->applyDataSelector($element->settings['data:select'])->info();
+  if (strpos($element->settings['data:select'], ':') !== FALSE && empty($info['setter callback'])) {
+    throw new RulesIntegrityException(t("The selected data property doesn't support writing."), array($element, 'parameter', 'data'));
+  }
+}
+
+/**
+ * Form alter callback for the data_set action.
+ */
+function rules_action_data_set_form_alter(&$form, &$form_state, $options, RulesAbstractPlugin $element) {
+  if (!empty($options['init']) && !isset($form_state['rules_element_step'])) {
+    $form['negate']['#access'] = FALSE;
+    unset($form['parameter']['value']);
+    unset($form['parameter']['language']);
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Continue'),
+      '#limit_validation_errors' => array(array('parameter', 'data')),
+      '#submit' => array('rules_form_submit_rebuild'),
+    );
+    $form_state['rules_element_step'] = 'data_value';
+    // Clear the parameter mode for the value parameter, so its gets the proper
+    // default value based upon the type of the the selected data on rebuild.
+    unset($form_state['parameter_mode']['value']);
+  }
+  else {
+    // Change the data parameter to be not editable.
+    $form['parameter']['data']['settings']['#access'] = FALSE;
+    // @todo Improve display.
+    $form['parameter']['data']['info'] = array(
+      '#prefix' => '<p>',
+      '#markup' => t('<strong>Selected data:</strong> %selector', array('%selector' => $element->settings['data:select'])),
+      '#suffix' => '</p>',
+    );
+  }
+}
+
+/**
+ * Form alter callback for the data calculation action.
+ */
+function rules_action_data_calc_form_alter(&$form, &$form_state, $options, RulesAbstractPlugin $element) {
+
+  $form['reload'] = array(
+    '#weight' => 5,
+    '#type' => 'submit',
+    '#name' => 'reload',
+    '#value' => t('Reload form'),
+    '#limit_validation_errors' => array(array('parameter', 'input_1')),
+    '#submit' => array('rules_form_submit_rebuild'),
+    '#ajax' => rules_ui_form_default_ajax(),
+  );
+}
+
+/**
+ * Validate callback for entity create, add variable and data create actions.
+ */
+function rules_action_create_type_validate($element) {
+  if (!isset($element->settings['type'])) {
+    throw new RulesIntegrityException(t('Invalid type specified.'), array($element, 'parameter', 'type'));
+  }
+}
+
+/**
+ * Form alter callback for the list add and remove actions.
+ *
+ * Use multiple steps to configure the action to update the item configuration
+ * form once we know the data type.
+ *
+ * @see rules_data_list_info_alter()
+ */
+function rules_data_list_form_alter(&$form, &$form_state, $options, RulesAbstractPlugin $element) {
+  if (!empty($options['init']) && !isset($form_state['rules_element_step'])) {
+    unset($form['parameter']['item'], $form['parameter']['pos']);
+    $form_state['rules_element_step'] = 1;
+    $form['negate']['#access'] = FALSE;
+    $form['parameter']['unique']['#access'] = FALSE;
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Continue'),
+      '#limit_validation_errors' => array(array('parameter', 'list')),
+      '#submit' => array('rules_form_submit_rebuild'),
+    );
+  }
+  else {
+    // Change the list parameter to be not editable any more.
+    $form['parameter']['list']['settings']['#access'] = FALSE;
+    $form['parameter']['list']['info'] = array(
+      '#prefix' => '<p>',
+      '#markup' => t('<strong>Selected list:</strong> %selector', array('%selector' => $element->settings['list:select'])),
+      '#suffix' => '</p>',
+    );
+  }
+}
+
+/**
+ * Form alter callback for actions relying on the entity type or the data type.
+ */
+function rules_action_type_form_alter(&$form, &$form_state, $options, RulesAbstractPlugin $element) {
+  $first_step = empty($element->settings['type']);
+  $form['reload'] = array(
+    '#weight' => 5,
+    '#type' => 'submit',
+    '#name' => 'reload',
+    '#value' => $first_step ? t('Continue') : t('Reload form'),
+    '#limit_validation_errors' => array(array('parameter', 'type')),
+    '#submit' => array('rules_action_type_form_submit_rebuild'),
+    '#ajax' => rules_ui_form_default_ajax(),
+  );
+  // Use ajax and trigger as the reload button.
+  $form['parameter']['type']['settings']['type']['#ajax'] = $form['reload']['#ajax'] + array(
+    'event' => 'change',
+    'trigger_as' => array('name' => 'reload'),
+  );
+
+  if ($first_step) {
+    // In the first step show only the type select.
+    foreach (element_children($form['parameter']) as $key) {
+      if ($key != 'type') {
+        unset($form['parameter'][$key]);
+      }
+    }
+    unset($form['submit']);
+    unset($form['provides']);
+    // Disable #ajax for the first step as it has troubles with lazy-loaded JS.
+    // @todo Re-enable once JS lazy-loading is fixed in core.
+    unset($form['parameter']['type']['settings']['type']['#ajax']);
+    unset($form['reload']['#ajax']);
+  }
+  else {
+    // Hide the reload button in case js is enabled and it's not the first step.
+    $form['reload']['#attributes'] = array('class' => array('rules-hide-js'));
+  }
+}
+
+/**
+ * FAPI submit callback for reloading the type form for entities or data types.
+ */
+function rules_action_type_form_submit_rebuild($form, &$form_state) {
+  rules_form_submit_rebuild($form, $form_state);
+  // Clear the parameter modes for the parameters, so they get the proper
+  // default values based upon the data types on rebuild.
+  $form_state['parameter_mode'] = array();
+}
+
+/**
+ * Options list callback for possible insertion positions.
+ */
+function rules_action_data_list_add_positions() {
+  return array(
+    'end' => t('Append the item to the end.'),
+    'start' => t('Prepend the item to the front.'),
+  );
+}
+
+/**
+ * Options list callback for variable add action.
+ */
+function rules_data_action_variable_add_options() {
+  return RulesPluginUI::getOptions('data');
+}
+
+/**
+ * Options list callback for the data calculation action.
+ */
+function rules_action_data_calc_operator_options(RulesPlugin $element, $param_name) {
+  $options = array(
+    '+' => '( + )',
+    '-' => '( - )',
+    '*' => '( * )',
+    '/' => '( / )',
+    'min' => 'min',
+    'max' => 'max',
+  );
+  // Only show +/- in case a date has been selected.
+  if (($info = $element->getArgumentInfo('input_1')) && $info['type'] == 'date') {
+    unset($options['*']);
+    unset($options['/']);
+  }
+  return $options;
+}
+
+/**
+ * Options list callback for data create action.
+ */
+function rules_data_action_data_create_options() {
+  $cache = rules_get_cache();
+  $data_info = $cache['data_info'];
+  $entity_info = entity_get_info();
+  // Remove entities.
+  $data_info = array_diff_key($data_info, $entity_info);
+  $options = array();
+  foreach ($data_info as $type => $properties) {
+    if (isset($properties['creation callback'])) {
+      // Add data types with creation callback only.
+      $options[$type] = $properties['label'];
+    }
+  }
+  natcasesort($options);
+  return $options;
+}
+
+/**
+ * Implements hook_rules_condition_info() on behalf of the pseudo data module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_data_condition_info() {
+  return array(
+    'data_is' => array(
+      'label' => t('Data comparison'),
+      'parameter' => array(
+        'data' => array(
+          'type' => '*',
+          'label' => t('Data to compare'),
+          'description' => t('The data to be compared, specified by using a data selector, e.g. "node:author:name".'),
+          'allow null' => TRUE,
+        ),
+        'op' => array(
+          'type' => 'text',
+          'label' => t('Operator'),
+          'description' => t('The comparison operator.'),
+          'optional' => TRUE,
+          'default value' => '==',
+          'options list' => 'rules_condition_data_is_operator_options',
+          'restriction' => 'input',
+        ),
+        'value' => array(
+          'type' => '*',
+          'label' => t('Data value'),
+          'description' => t('The value to compare the data with.'),
+          'allow null' => TRUE,
+        ),
+      ),
+      'group' => t('Data'),
+      'base' => 'rules_condition_data_is',
+    ),
+    'data_is_empty' => array(
+      'label' => t('Data value is empty'),
+      'parameter' => array(
+        'data' => array(
+          'type' => '*',
+          'label' => t('Data to check'),
+          'description' => t('The data to be checked to be empty, specified by using a data selector, e.g. "node:author:name".'),
+          'allow null' => TRUE,
+          'wrapped' => TRUE,
+        ),
+      ),
+      'group' => t('Data'),
+      'base' => 'rules_condition_data_is_empty',
+    ),
+    'list_contains'  => array(
+      'label' => t('List contains item'),
+      'parameter' => array(
+        'list' => array(
+          'type' => 'list',
+          'label' => t('List', array(), array('context' => 'data_types')),
+          'restriction' => 'selector',
+        ),
+        'item' => array(
+          'type' => 'unknown',
+          'label' => t('Item'),
+          'description' => t('The item to check for.'),
+        ),
+      ),
+      'group' => t('Data'),
+      'base' => 'rules_condition_data_list_contains',
+      'callbacks' => array(
+        'info_alter' => 'rules_data_list_info_alter',
+        'form_alter' => 'rules_data_list_form_alter',
+      ),
+    ),
+    'list_count_is' => array(
+      'label' => t('List count comparison'),
+      'parameter' => array(
+        'list' => array(
+          'type' => 'list',
+          'label' => t('List to check'),
+          'description' => t('A multi value data element to have its count compared, specified by using a data selector, eg node:author:roles.'),
+        ),
+        'op' => array(
+          'type' => 'text',
+          'label' => t('Operator'),
+          'description' => t('The comparison operator.'),
+          'optional' => TRUE,
+          'default value' => '==',
+          'options list' => 'rules_condition_data_list_count_is_operator_options',
+          'restriction' => 'input',
+        ),
+        'value' => array(
+          'type' => 'integer',
+          'label' => t('Count'),
+          'description' => t('The count to compare the data count with.'),
+        ),
+      ),
+      'group' => t('Data'),
+      'base' => 'rules_condition_data_list_count_is',
+    ),
+    'text_matches'  => array(
+      'label' => t('Text comparison'),
+      'parameter' => array(
+        'text' => array(
+          'type' => 'text',
+          'label' => t('Text'),
+          'restriction' => 'selector',
+        ),
+        'match' => array(
+          'type' => 'text',
+          'label' => t('Matching text'),
+        ),
+        'operation' => array(
+          'type' => 'text',
+          'label' => t('Comparison operation'),
+          'options list' => 'rules_data_text_comparison_operation_list',
+          'restriction' => 'input',
+          'default value' => 'contains',
+          'optional' => TRUE,
+          'description' => t('In case the comparison operation @regex is selected, the matching pattern will be interpreted as a <a href="@regex-wikipedia">regular expression</a>.  Tip: <a href="@RegExr">RegExr: Online Regular Expression Testing Tool</a> is helpful for learning, writing, and testing Regular Expressions.', array('@regex-wikipedia' => 'http://en.wikipedia.org/wiki/Regular_expression', '@RegExr' => 'http://gskinner.com/RegExr/', '@regex' => t('regular expression'))),
+        ),
+      ),
+      'group' => t('Data'),
+      'base' => 'rules_data_text_comparison',
+    ),
+  );
+}
+
+/**
+ * Asserts the bundle of entities, if it's compared.
+ *
+ * If the bundle is compared, add the metadata assertion so other elements
+ * can make use of properties specific to the bundle.
+ */
+function rules_condition_data_is_assertions($element) {
+  // Assert the bundle of entities, if it's compared.
+  if ($wrapper = $element->applyDataSelector($element->settings['data:select'])) {
+    $info = $wrapper->info();
+    if (isset($info['parent']) && $info['parent'] instanceof EntityDrupalWrapper) {
+      $entity_info = $info['parent']->entityInfo();
+      if (isset($entity_info['entity keys']['bundle']) && $entity_info['entity keys']['bundle'] == $info['name']) {
+        // Assert that the entity is of bundle $value.
+        $value = is_array($element->settings['value']) ? $element->settings['value'] : array($element->settings['value']);
+        // Chop of the last part of the selector.
+        $parts = explode(':', $element->settings['data:select'], -1);
+        return array(implode(':', $parts) => array('bundle' => $value));
+      }
+    }
+  }
+}
+
+/**
+ * Form alter callback for the condition data_is.
+ *
+ * Use multiple steps to configure the condition as the needed type of the value
+ * depends on the selected data.
+ */
+function rules_condition_data_is_form_alter(&$form, &$form_state, $options, RulesAbstractPlugin $element) {
+  if (!empty($options['init']) && !isset($form_state['rules_element_step'])) {
+    unset($form['parameter']['op'], $form['parameter']['value']);
+    $form['negate']['#access'] = FALSE;
+    $form_state['rules_element_step'] = 'data_value';
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Continue'),
+      '#limit_validation_errors' => array(array('parameter', 'data'), array('parameter', 'op')),
+      '#submit' => array('rules_form_submit_rebuild'),
+    );
+    // Clear the parameter mode for the value parameter, so its gets the proper
+    // default value based upon the type of the the selected data on rebuild.
+    unset($form_state['parameter_mode']['value']);
+  }
+  else {
+    // Change the data parameter to be not editable.
+    $form['parameter']['data']['settings']['#access'] = FALSE;
+    // @todo Improve display.
+    $form['parameter']['data']['info'] = array(
+      '#prefix' => '<p>',
+      '#markup' => t('<strong>Selected data:</strong> %selector', array('%selector' => $element->settings['data:select'])),
+      '#suffix' => '</p>',
+    );
+
+    // Limit the operations to what makes sense for the selected data type.
+    $info = $element->pluginParameterInfo();
+    $data_info = $info['value'];
+    if ($element->settings['op'] == 'IN') {
+      $data_info['type'] = entity_property_list_extract_type($data_info['type']);
+    }
+
+    if (!RulesData::typesMatch($data_info, array('type' => array('decimal', 'date')))) {
+      $options =& $form['parameter']['op']['settings']['op']['#options'];
+      unset($options['<'], $options['>']);
+    }
+    // Remove 'contains' if it is not selected, as it is deprecated by the
+    // text comparison condition.
+    if ($element->settings['op'] != 'contains') {
+      unset($form['parameter']['op']['settings']['op']['#options']['contains']);
+    }
+
+    // Auto-refresh the form if the operation is changed, so the input form
+    // changes in case "is one of" requires a list value.
+    $form['parameter']['op']['settings']['op']['#ajax'] = rules_ui_form_default_ajax() + array(
+      'trigger_as' => array('name' => 'reload'),
+    );
+    // Provide a reload button for non-JS users.
+    $form['reload'] = array(
+      '#type' => 'submit',
+      '#value' => t('Reload form'),
+      '#limit_validation_errors' => array(array('parameter', 'data'), array('parameter', 'op')),
+      '#submit' => array('rules_form_submit_rebuild'),
+      '#ajax' => rules_ui_form_default_ajax(),
+      '#weight' => 5,
+    );
+    // Hide the reload button in case JS is enabled.
+    $form['reload']['#attributes'] = array('class' => array('rules-hide-js'));
+  }
+}
+
+/**
+ * Provides configuration help for the data_is condition.
+ */
+function rules_condition_data_is_help() {
+  return array('#markup' => t('Compare two data values of the same type with each other.'));
+}
+
+/**
+ * Options list callback for condition data_is.
+ */
+function rules_condition_data_is_operator_options() {
+  return array(
+    '==' => t('equals'),
+    'IN' => t('is one of'),
+    '<' => t('is lower than'),
+    '>' => t('is greater than'),
+    // Note: This is deprecated by the text comparison condition.
+    'contains' => t('contains'),
+  );
+}
+
+/**
+ * Options list callback for condition text_matches.
+ */
+function rules_data_text_comparison_operation_list() {
+  return array(
+    'contains' => t('contains'),
+    'starts' => t('starts with'),
+    'ends' => t('ends with'),
+    'regex' => t('regular expression'),
+  );
+}
+
+/**
+ * Returns the options list as specified by the selected property of the first parameter.
+ *
+ * @see rules_data_list_info_alter()
+ * @see rules_action_data_set_info_alter()
+ * @see rules_condition_data_is_info_alter()
+ */
+function rules_data_selector_options_list(RulesAbstractPlugin $element) {
+  $name = rules_array_key($element->pluginParameterInfo());
+  // If the selected data property has an option list, make use of it.
+  if (isset($element->settings[$name . ':select']) && $wrapper = $element->applyDataSelector($element->settings[$name . ':select'])) {
+    return $wrapper->optionsList($element instanceof RulesActionInterface ? 'edit' : 'view');
+  }
+}
+
+/**
+ * Options list callback for condition list_count_is.
+ */
+function rules_condition_data_list_count_is_operator_options() {
+  return array(
+    '==' => t('equals'),
+    '<' => t('is lower than'),
+    '>' => t('is greater than'),
+  );
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/entity.eval.inc b/profiles/wcm_base/modules/contrib/rules/modules/entity.eval.inc
new file mode 100644
index 0000000000000000000000000000000000000000..edd18a40539f485b46dfe6ef8e65c837aac1f11a
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/entity.eval.inc
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * @file
+ * Contains rules integration for entities needed during evaluation.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Action: Fetch data.
+ */
+function rules_action_entity_fetch($type, $id, $revision) {
+  $info = entity_get_info($type);
+
+  // Support the revision parameter, if applicable.
+  if (!empty($info['entity keys']['revision']) && isset($revision)) {
+    $conditions = array($info['entity keys']['revision'] => $revision);
+  }
+
+  $return = entity_load($type, array($id), isset($conditions) ? $conditions : array());
+  $entity = reset($return);
+  if (!$entity) {
+    throw new RulesEvaluationException('Unable to load @entity with id "@id"', array('@id' => $id, '@entity' => $type));
+  }
+  return array('entity_fetched' => $entity);
+}
+
+/**
+ * Info alteration callback for the entity fetch action.
+ */
+function rules_action_entity_fetch_info_alter(&$element_info, RulesAbstractPlugin $element) {
+  $element->settings += array('type' => NULL);
+  $info = entity_get_info($element->settings['type']);
+
+  // Fix the type of the identifier.
+  $element_info['parameter']['id']['type'] = isset($info['entity keys']['name']) ? 'text' : 'integer';
+
+  // Add an optional revision parameter, if supported.
+  if (!empty($info['entity keys']['revision'])) {
+    $element_info['parameter']['revision_id'] = array(
+      'type' => 'integer',
+      'label' => t('Revision identifier'),
+      'optional' => TRUE,
+    );
+  }
+  $element_info['provides']['entity_fetched']['type'] = $element->settings['type'];
+}
+
+/**
+ * Action: Query entities.
+ */
+function rules_action_entity_query($type, $property, $value, $limit) {
+  $return = entity_property_query($type, $property, $value, $limit);
+  return array('entity_fetched' => array_values($return));
+}
+
+/**
+ * Info alteration callback for the entity query action.
+ */
+function rules_action_entity_query_info_alter(&$element_info, RulesAbstractPlugin $element) {
+  $element->settings += array('type' => NULL, 'property' => NULL);
+  if ($element->settings['type']) {
+    $element_info['parameter']['property']['options list'] = 'rules_action_entity_query_property_options_list';
+
+    if ($element->settings['property']) {
+      $wrapper = rules_get_entity_metadata_wrapper_all_properties($element);
+      if (isset($wrapper->{$element->settings['property']}) && $property = $wrapper->{$element->settings['property']}) {
+        $property_type = $property->type();
+        // If the cardinality of the property > 1, i.e. of type 'list<{type}>',
+        // we will also accept a parameter of type {type}.
+        if (substr($property_type, 0, strlen('list<')) === 'list<' && substr($property_type, -strlen('>')) === '>') {
+          $property_type = array($property_type, substr($property_type, strlen('list<'), strlen($property_type) - strlen('list<>')));
+        }
+        $element_info['parameter']['value']['type'] = $property_type;
+        $element_info['parameter']['value']['options list'] = $property->optionsList() ? 'rules_action_entity_query_value_options_list' : FALSE;
+      }
+    }
+  }
+  $element_info['provides']['entity_fetched']['type'] = 'list<' . $element->settings['type'] . '>';
+}
+
+/**
+ * Action: Create entities.
+ */
+function rules_action_entity_create($args, $element) {
+  $values = array();
+  foreach ($element->pluginParameterInfo() as $name => $info) {
+    if ($name != 'type') {
+      // Remove the parameter name prefix 'param_'.
+      $values[substr($name, 6)] = $args[$name];
+    }
+  }
+  try {
+    $data = entity_property_values_create_entity($args['type'], $values);
+    return array('entity_created' => $data);
+  }
+  catch (EntityMetadataWrapperException $e) {
+    throw new RulesEvaluationException('Unable to create entity @type": ' . $e->getMessage(), array('@type' => $args['type']), $element);
+  }
+}
+
+/**
+ * Info alteration callback for the entity create action.
+ */
+function rules_action_entity_create_info_alter(&$element_info, RulesAbstractPlugin $element) {
+  if (!empty($element->settings['type']) && entity_get_info($element->settings['type'])) {
+    $wrapper = entity_metadata_wrapper($element->settings['type']);
+    // Add the data type's needed parameter for loading to the parameter info.
+    foreach ($wrapper as $name => $child) {
+      $info = $child->info();
+      if (!empty($info['required'])) {
+        $info += array('type' => 'text');
+        // Prefix parameter names to avoid name clashes
+        // with existing parameters.
+        $element_info['parameter']['param_' . $name] = array_intersect_key($info, array_flip(array('type', 'label', 'description')));
+        $element_info['parameter']['param_' . $name]['options list'] = $child->optionsList() ? 'rules_action_entity_parameter_options_list' : FALSE;
+      }
+    }
+    $element_info['provides']['entity_created']['type'] = $element->settings['type'];
+    if (($bundleKey = $wrapper->entityKey('bundle')) && isset($element->settings['param_' . $bundleKey])) {
+      $element_info['provides']['entity_created']['bundle'] = $element->settings['param_' . $bundleKey];
+    }
+  }
+}
+
+/**
+ * Action: Save entities.
+ */
+function rules_action_entity_save($wrapper, $immediate = FALSE, $settings, $state, $element) {
+  $state->saveChanges($settings['data:select'], $wrapper, $immediate);
+}
+
+/**
+ * Action: Delete entities.
+ */
+function rules_action_entity_delete($wrapper, $settings, $state, $element) {
+  try {
+    $wrapper->delete();
+  }
+  catch (EntityMetadataWrapperException $e) {
+    throw new RulesEvaluationException($e->getMessage(), array(), $element);
+  }
+}
+
+/**
+ * Condition: Entity is new.
+ */
+function rules_condition_entity_is_new($wrapper, $settings, $state, $element) {
+  return !$wrapper->getIdentifier() || !empty($wrapper->value()->is_new);
+}
+
+/**
+ * Condition: Entity has field.
+ */
+function rules_condition_entity_has_field($wrapper, $field_name, $settings, $state) {
+  return isset($wrapper->$field_name) || isset($wrapper->value()->$field_name);
+}
+
+/**
+ * Condition: Entity is of type.
+ */
+function rules_condition_entity_is_of_type($wrapper, $type) {
+  return $wrapper->type() == $type;
+}
+
+/**
+ * Condition: Entity is of type and bundle.
+ */
+function rules_condition_entity_is_of_bundle($wrapper, $type, $bundles) {
+  return $wrapper->type() == $type && in_array($wrapper->getBundle(), $bundles);
+}
+
+/**
+ * Condition: User has access to field.
+ */
+function rules_condition_entity_field_access(EntityDrupalWrapper $wrapper, $field_name, $op, $account = NULL) {
+  $field = field_info_field($field_name);
+  return !empty($field) && field_access($op, $field, $wrapper->type(), $wrapper->value(), $account = NULL);
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/entity.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/entity.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..97b3f697bd69e81679ddfce3a6a43669a64027b4
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/entity.rules.inc
@@ -0,0 +1,591 @@
+<?php
+
+/**
+ * @file
+ * General entity related rules integration.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_file_info() on behalf of the entity module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_entity_file_info() {
+  return array('modules/entity.eval');
+}
+
+/**
+ * Implements hook_rules_category_info() on behalf of the pseudo entity module.
+ */
+function rules_entity_category_info() {
+  return array(
+    'rules_entity' => array(
+      'label' => t('Entities'),
+      'equals group' => t('Entities'),
+      'weight' => -50,
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_action_info() on behalf of the entity module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_entity_action_info() {
+  if (rules_entity_action_type_options('entity_fetch')) {
+    $return['entity_fetch'] = array(
+      'label' => t('Fetch entity by id'),
+      'parameter' => array(
+        'type' => array(
+          'type' => 'text',
+          'label' => t('Entity type'),
+          'options list' => 'rules_entity_action_type_options',
+          'description' => t('Specifies the type of entity that should be fetched.'),
+          'restriction' => 'input',
+        ),
+        'id' => array('type' => 'unknown', 'label' => t('Identifier')),
+      ),
+      'provides' => array(
+        'entity_fetched' => array('type' => 'unknown', 'label' => t('Fetched entity')),
+      ),
+      'group' => t('Entities'),
+      'access callback' => 'rules_entity_action_access',
+      'base' => 'rules_action_entity_fetch',
+      'callbacks' => array(
+        'access' => 'rules_action_entity_createfetch_access',
+        'form_alter' => 'rules_action_type_form_alter',
+      ),
+    );
+    $return['entity_query'] = array(
+      'label' => t('Fetch entity by property'),
+      'parameter' => array(
+        'type' => array(
+          'type' => 'text',
+          'label' => t('Entity type'),
+          'options list' => 'rules_entity_action_type_options',
+          'description' => t('Specifies the type of the entity that should be fetched.'),
+          'restriction' => 'input',
+        ),
+        'property' => array(
+          'type' => 'text',
+          'label' => t('Property'),
+          'description' => t('The property by which the entity is to be selected.'),
+          'restriction' => 'input',
+        ),
+        'value' => array(
+          'type' => 'unknown',
+          'label' => t('Value'),
+          'description' => t('The property value of the entity to be fetched.'),
+        ),
+        'limit' => array(
+          'type' => 'integer',
+          'label' => t('Limit result count'),
+          'description' => t('Limit the maximum number of fetched entities.'),
+          'optional' => TRUE,
+          'default value' => '10',
+        ),
+      ),
+      'provides' => array(
+        'entity_fetched' => array('type' => 'list', 'label' => t('Fetched entity')),
+      ),
+      'group' => t('Entities'),
+      'access callback' => 'rules_entity_action_access',
+      'base' => 'rules_action_entity_query',
+      'callbacks' => array(
+        'form_alter' => 'rules_action_type_form_alter',
+      ),
+    );
+  }
+
+  if (rules_entity_action_type_options('entity_create')) {
+    $return['entity_create'] = array(
+      'label' => t('Create a new entity'),
+      'named parameter' => TRUE,
+      'parameter' => array(
+        'type' => array(
+          'type' => 'text',
+          'label' => t('Entity type'),
+          'options list' => 'rules_entity_action_type_options',
+          'description' => t('Specifies the type of the entity that should be created.'),
+          'restriction' => 'input',
+        ),
+        // Further needed parameter depends on the type.
+      ),
+      'provides' => array(
+        'entity_created' => array(
+          'type' => 'unknown',
+          'label' => t('Created entity'),
+          'save' => TRUE,
+        ),
+      ),
+      'group' => t('Entities'),
+      'access callback' => 'rules_entity_action_access',
+      'base' => 'rules_action_entity_create',
+      'callbacks' => array(
+        'access' => 'rules_action_entity_createfetch_access',
+        'form_alter' => 'rules_action_type_form_alter',
+        'validate' => 'rules_action_create_type_validate',
+      ),
+    );
+  }
+
+  $return['entity_save'] = array(
+    'label' => t('Save entity'),
+    'parameter' => array(
+      'data' => array(
+        'type' => 'entity',
+        'label' => t('Entity'),
+        'description' => t('Specifies the entity, which should be saved permanently.'),
+        'restriction' => 'selector',
+        'wrapped' => TRUE,
+      ),
+      'immediate' => array(
+        'type' => 'boolean',
+        'label' => t('Force saving immediately'),
+        'description' => t('Usually saving is postponed till the end of the evaluation, so that multiple saves can be fold into one. If this set, saving is forced to happen immediately.'),
+        'default value' => FALSE,
+        'optional' => TRUE,
+        'restriction' => 'input',
+      ),
+    ),
+    'group' => t('Entities'),
+    'access callback' => 'rules_entity_action_access',
+    'base' => 'rules_action_entity_save',
+    'callbacks' => array(
+      'access' => 'rules_action_entity_savedelete_access',
+    ),
+  );
+
+  $return['entity_delete'] = array(
+    'label' => t('Delete entity'),
+    'parameter' => array(
+      'data' => array(
+        'type' => 'entity',
+        'label' => t('Entity'),
+        'description' => t('Specifies the entity, which should be deleted permanently.'),
+        'restriction' => 'selector',
+        'wrapped' => TRUE,
+      ),
+    ),
+    'group' => t('Entities'),
+    'access callback' => 'rules_entity_action_access',
+    'base' => 'rules_action_entity_delete',
+    'callbacks' => array(
+      'access' => 'rules_action_entity_savedelete_access',
+    ),
+  );
+  return $return;
+}
+
+/**
+ * Custom access callback for data create and fetch action.
+ */
+function rules_action_entity_createfetch_access(RulesAbstractPlugin $element) {
+  $op = $element->getElementName() == 'entity_create' ? 'create' : 'view';
+  return entity_access($op, $element->settings['type']);
+}
+
+/**
+ * Custom access callback for the data query action.
+ */
+function rules_action_entity_query_access(RulesAbstractPlugin $element) {
+  if (!rules_action_entity_createfetch_access($element)) {
+    return FALSE;
+  }
+  $properties = entity_get_all_property_info($element->settings['type']);
+  if (isset($element->settings['property']) && isset($properties[$element->settings['property']]['access callback'])) {
+    return call_user_func($properties[$element->settings['property']]['access callback'], 'view', $element->settings['property'], $element->settings['type'], NULL, NULL);
+  }
+  return TRUE;
+}
+
+/**
+ * Options list callback for a parameter of entity_create.
+ */
+function rules_action_entity_parameter_options_list(RulesPlugin $element, $param_name) {
+  // Remove the parameter name prefix 'param_'.
+  $property_name = substr($param_name, 6);
+  $wrapper = entity_metadata_wrapper($element->settings['type']);
+  // The possible values of the "value" parameter are those of the data param.
+  return $wrapper->$property_name->optionsList();
+}
+
+/**
+ * Custom access callback for data save and delete action.
+ */
+function rules_action_entity_savedelete_access(RulesAbstractPlugin $element) {
+  if ($wrapper = $element->applyDataSelector($element->settings['data:select'])) {
+    $op = $element->getElementName() == 'entity_save' ? 'save' : 'delete';
+    return $wrapper instanceof EntityDrupalWrapper && $wrapper->entityAccess($op);
+  }
+  return FALSE;
+}
+
+/**
+ * Returns the options list for choosing a property of an entity type.
+ */
+function rules_action_entity_query_property_options_list(RulesAbstractPlugin $element) {
+  $element->settings += array('type' => NULL);
+  if ($element->settings['type']) {
+    $properties = entity_get_all_property_info($element->settings['type']);
+    return rules_extract_property($properties, 'label');
+  }
+}
+
+/**
+ * Returns the options list specified for the chosen property.
+ */
+function rules_action_entity_query_value_options_list(RulesAbstractPlugin $element) {
+  // Get the possible values for the selected property.
+  $element->settings += array('type' => NULL, 'property' => NULL);
+  if ($element->settings['type'] && $element->settings['property']) {
+    $wrapper = rules_get_entity_metadata_wrapper_all_properties($element);
+
+    if (isset($wrapper->{$element->settings['property']}) && $property = $wrapper->{$element->settings['property']}) {
+      return $property->optionsList('view');
+    }
+  }
+}
+
+/**
+ * Options list callback for data actions.
+ *
+ * @param $element
+ *   The element to return options for.
+ * @param $param
+ *   The name of the parameter to return options for.
+ */
+function rules_entity_action_type_options($element, $name = NULL) {
+  // We allow calling this function with just the element name too. That way
+  // we ease manual re-use.
+  $name = is_object($element) ? $element->getElementName() : $element;
+  return ($name == 'entity_create') ? rules_entity_type_options('create') : rules_entity_type_options();
+}
+
+/**
+ * Returns options containing entity types having the given key set in the info.
+ *
+ * Additionally, we exclude all entity types that are marked as configuration.
+ */
+function rules_entity_type_options($key = NULL) {
+  $info = entity_get_info();
+  $types = array();
+  foreach ($info as $type => $entity_info) {
+    if (empty($entity_info['configuration']) && empty($entity_info['exportable'])) {
+      if (!isset($key) || entity_type_supports($type, $key)) {
+        $types[$type] = $entity_info['label'];
+      }
+    }
+  }
+  return $types;
+}
+
+/**
+ * Options list callback for getting a list of possible entity bundles.
+ *
+ * @param $element
+ *   The element to return options for.
+ */
+function rules_entity_bundle_options(RulesAbstractPlugin $element) {
+  $bundles = array();
+  if (isset($element->settings['type'])) {
+    $entity_info = entity_get_info($element->settings['type']);
+    foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+      $bundles[$bundle_name] = $bundle_info['label'];
+    }
+  }
+  return $bundles;
+}
+
+/**
+ * Entity actions access callback.
+ *
+ * Returns TRUE if at least one type is available for configuring the action.
+ */
+function rules_entity_action_access($type, $name) {
+  if ($name == 'entity_fetch' || $name == 'entity_create' || $name == 'entity_query') {
+    $types = array_keys(rules_entity_action_type_options($name));
+    $op = $name == 'entity_create' ? 'create' : 'view';
+  }
+  elseif ($name == 'entity_save' || $name == 'entity_delete') {
+    $types = array_keys(entity_get_info());
+    $op = $name == 'entity_save' ? 'save' : 'delete';
+  }
+  foreach ($types as $key => $type) {
+    if (!entity_access($op, $type)) {
+      unset($types[$key]);
+    }
+  }
+  return !empty($types);
+}
+
+/**
+ * Implements hook_rules_condition_info() on behalf of the entity module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_entity_condition_info() {
+  return array(
+    'entity_is_new' => array(
+      'label' => t('Entity is new'),
+      'parameter' => array(
+        'entity' => array(
+          'type' => 'entity',
+          'label' => t('Entity'),
+          'description' => t('Specifies the entity for which to evaluate the condition.'),
+          'restriction' => 'selector',
+        ),
+      ),
+      'group' => t('Entities'),
+      'base' => 'rules_condition_entity_is_new',
+    ),
+    'entity_has_field' => array(
+      'label' => t('Entity has field'),
+      'parameter' => array(
+        'entity' => array(
+          'type' => 'entity',
+          'label' => t('Entity'),
+          'description' => t('Specifies the entity for which to evaluate the condition.'),
+          'restriction' => 'selector',
+        ),
+        'field' => array(
+          'type' => 'text',
+          'label' => t('Field'),
+          'description' => t('The name of the field to check for.'),
+          'options list' => 'rules_condition_entity_has_field_options',
+          'restriction' => 'input',
+        ),
+      ),
+      'group' => t('Entities'),
+      'base' => 'rules_condition_entity_has_field',
+    ),
+    'entity_is_of_type' => array(
+      'label' => t('Entity is of type'),
+      'parameter' => array(
+        'entity' => array(
+          'type' => 'entity',
+          'label' => t('Entity'),
+          'description' => t('Specifies the entity for which to evaluate the condition.'),
+        ),
+        'type' => array(
+          'type' => 'token',
+          'label' => t('Entity type'),
+          'description' => t('The entity type to check for.'),
+          'options list' => 'rules_entity_action_type_options',
+          'restriction' => 'input',
+        ),
+      ),
+      'group' => t('Entities'),
+      'base' => 'rules_condition_entity_is_of_type',
+    ),
+    'entity_is_of_bundle' => array(
+      'label' => t('Entity is of bundle'),
+      'parameter' => array(
+        'entity' => array(
+          'type' => 'entity',
+          'label' => t('Entity'),
+          'description' => t('Specifies the entity for which to evaluate the condition.'),
+        ),
+        'type' => array(
+          'type' => 'token',
+          'label' => t('Entity type'),
+          'description' => t('The type of the checked entity.'),
+          'options list' => 'rules_entity_action_type_options',
+          'restriction' => 'input',
+        ),
+        'bundle' => array(
+          'type' => 'list<text>',
+          'label' => t('Entity bundle'),
+          'description' => t('The condition is met if the entity is of one of the selected bundles.'),
+          'options list' => 'rules_entity_bundle_options',
+          'restriction' => 'input',
+        ),
+      ),
+      'group' => t('Entities'),
+      'base' => 'rules_condition_entity_is_of_bundle',
+    ),
+    'entity_field_access' => array(
+      'label' => t('User has field access'),
+      'parameter' => array(
+        'entity' => array(
+          'type' => 'entity',
+          'label' => t('Entity'),
+          'description' => t('Specifies the entity for which to evaluate the condition.'),
+          'restriction' => 'selector',
+          'wrapped' => TRUE,
+        ),
+        'field' => array(
+          'type' => 'token',
+          'label' => t('Field name'),
+          'description' => t('The name of the field to check for.'),
+          'options list' => 'rules_condition_entity_has_field_options',
+          'restriction' => 'input',
+        ),
+        'op' => array(
+          'type' => 'text',
+          'label' => t('Access operation'),
+          'options list' => 'rules_condition_entity_field_access_op_options',
+          'restriction' => 'input',
+          'optional' => TRUE,
+          'default value' => 'view',
+        ),
+        'account' => array(
+          'type' => 'user',
+          'label' => t('User account'),
+          'description' => t('Specifies the user account for which to check access. If left empty, the currently logged in user will be used.'),
+          'restriction' => 'selector',
+          'optional' => TRUE,
+          'default value' => NULL,
+        ),
+      ),
+      'group' => t('Entities'),
+      'base' => 'rules_condition_entity_field_access',
+    ),
+  );
+}
+
+/**
+ * Help callback for condition entity_is_new.
+ */
+function rules_condition_entity_is_new_help() {
+  return t('This condition determines whether the specified entity has just been created and has not yet been saved to the database.');
+}
+
+/**
+ * Returns options for choosing a field for the selected entity.
+ */
+function rules_condition_entity_has_field_options(RulesAbstractPlugin $element) {
+  // The field_info_field_map() function was introduced in Drupal 7.22. See
+  // https://www.drupal.org/node/1915646.
+  if (function_exists('field_info_field_map')) {
+    $fields = field_info_field_map();
+  }
+  else {
+    $fields = field_info_fields();
+  }
+  $field_list = drupal_map_assoc(array_keys($fields));
+  ksort($field_list);
+  return $field_list;
+}
+
+/**
+ * Returns options for choosing a field_access() operation.
+ */
+function rules_condition_entity_field_access_op_options(RulesAbstractPlugin $element) {
+  return array(
+    'view' => t('View'),
+    'edit' => t('Edit'),
+  );
+}
+
+/**
+ * Assert that the entity has the field, if there is metadata for the field.
+ */
+function rules_condition_entity_has_field_assertions($element) {
+  // Assert the field is there if the condition matches.
+  if ($wrapper = $element->applyDataSelector($element->settings['entity:select'])) {
+    $type = $wrapper->type();
+    $field_property = $element->settings['field'];
+    // Get all possible properties and check whether we have one for the field.
+    $properties = entity_get_all_property_info($type == 'entity' ? NULL : $type);
+
+    if (isset($properties[$field_property])) {
+      $assertion = array('property info' => array($field_property => $properties[$field_property]));
+      return array($element->settings['entity:select'] => $assertion);
+    }
+  }
+}
+
+/**
+ * Assert the selected entity type.
+ */
+function rules_condition_entity_is_of_type_assertions($element) {
+  if ($type = $element->settings['type']) {
+    return array('entity' => array('type' => $type));
+  }
+}
+
+/**
+ * Assert the selected entity type and bundle.
+ */
+function rules_condition_entity_is_of_bundle_assertions($element) {
+  if ($bundle = $element->settings['bundle']) {
+    $assertions = array();
+    $assertions['entity']['type'] = $element->settings['type'];
+    $assertions['entity']['bundle'] = $bundle;
+    return $assertions;
+  }
+}
+
+/**
+ * Process callback for the condition entity_is_of_bundle.
+ */
+function rules_condition_entity_is_of_bundle_process(RulesAbstractPlugin $element) {
+  // If we know the entity type, auto-populate it.
+  if (($info = $element->getArgumentInfo('entity')) && $info['type'] != 'entity') {
+    $element->settings['type'] = $info['type'];
+  }
+}
+
+/**
+ * Form alter callback for the condition entity_is_of_bundle.
+ *
+ * Use multiple steps to configure the condition as the needed bundle field list
+ * depends on the selected entity type.
+ */
+function rules_condition_entity_is_of_bundle_form_alter(&$form, &$form_state, $options, RulesAbstractPlugin $element) {
+  if (empty($element->settings['entity:select'])) {
+    $step = 1;
+  }
+  elseif (empty($element->settings['type'])) {
+    $step = 2;
+  }
+  else {
+    $step = 3;
+  }
+
+  $form['reload'] = array(
+    '#weight' => $form['submit']['#weight'] + 1,
+    '#type' => 'submit',
+    '#name' => 'reload',
+    '#value' => $step != 3 ? t('Continue') : t('Reload form'),
+    '#limit_validation_errors' => array(array('parameter', 'entity'), array('parameter', 'type')),
+    '#submit' => array('rules_form_submit_rebuild'),
+    '#ajax' => rules_ui_form_default_ajax('fade'),
+    '#attributes' => array('class' => array('rules-hide-js')),
+  );
+  // Use ajax and trigger as the reload button.
+  $form['parameter']['type']['settings']['type']['#ajax'] = $form['reload']['#ajax'] + array(
+    'event' => 'change',
+    'trigger_as' => array('name' => 'reload'),
+  );
+
+  switch ($step) {
+    case 1:
+      $form['reload']['#limit_validation_errors'] = array(array('parameter', 'entity'));
+      unset($form['parameter']['type']);
+      unset($form['reload']['#attributes']['class']);
+      // NO break.
+    case 2:
+      $form['negate']['#access'] = FALSE;
+      unset($form['parameter']['bundle']);
+      unset($form['submit']);
+      break;
+
+    case 3:
+      if (($info = $element->getArgumentInfo('entity')) && $info['type'] != 'entity') {
+        // Hide the entity type parameter if not needed.
+        unset($form['parameter']['type']);
+      }
+      break;
+  }
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/events.inc b/profiles/wcm_base/modules/contrib/rules/modules/events.inc
new file mode 100644
index 0000000000000000000000000000000000000000..01d8aedb495177a35d1fa62bed5dae6d10318634
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/events.inc
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * @file
+ * Invokes events on behalf core modules.
+ *
+ * For non-core modules, the code to invoke events should be found in the
+ * module providing rules integration.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Gets an unchanged entity that doesn't contain any recent changes.
+ *
+ * This handler assumes the name of the variable for the changed entity is the
+ * same as for the unchanged entity but without the trailing "_unchanged"; e.g.,
+ * for the "node_unchanged" variable the handler assumes there is a "node"
+ * variable.
+ */
+function rules_events_entity_unchanged($arguments, $name, $info) {
+  // Cut of the trailing _unchanged.
+  $var_name = substr($name, 0, -10);
+  $entity = $arguments[$var_name];
+  if (isset($entity->original)) {
+    return $entity->original;
+  }
+}
+
+/*
+ * Generic entity events, used for core-entities for which we provide Rules
+ * integration only. We are implementing the generic-entity hooks instead of
+ * the entity-type specific hooks to ensure we come last.
+ * See https://www.drupal.org/node/1211946 for details.
+ */
+
+/**
+ * Implements hook_entity_view().
+ */
+function rules_entity_view($entity, $type, $view_mode, $langcode) {
+  switch ($type) {
+    case 'comment':
+      rules_invoke_event('comment_view--' . $entity->node_type, $entity, $view_mode);
+      rules_invoke_event('comment_view', $entity, $view_mode);
+      break;
+
+    case 'node':
+      rules_invoke_event('node_view--' . $entity->type, $entity, $view_mode);
+      rules_invoke_event('node_view', $entity, $view_mode);
+      break;
+
+    case 'user':
+      rules_invoke_event('user_view', $entity, $view_mode);
+      break;
+  }
+}
+
+/**
+ * Implements hook_entity_presave().
+ */
+function rules_entity_presave($entity, $type) {
+  switch ($type) {
+    case 'comment':
+      rules_invoke_event('comment_presave--' . $entity->node_type, $entity);
+      rules_invoke_event('comment_presave', $entity);
+      break;
+
+    case 'node':
+      rules_invoke_event('node_presave--' . $entity->type, $entity);
+      rules_invoke_event('node_presave', $entity);
+      break;
+
+    case 'taxonomy_term':
+      rules_invoke_event('taxonomy_term_presave--' . $entity->vocabulary_machine_name, $entity);
+      rules_invoke_event('taxonomy_term_presave', $entity);
+      break;
+
+    case 'taxonomy_vocabulary':
+    case 'user':
+      rules_invoke_event($type . '_presave', $entity);
+      break;
+  }
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function rules_entity_update($entity, $type) {
+  switch ($type) {
+    case 'comment':
+      rules_invoke_event('comment_update--' . $entity->node_type, $entity);
+      rules_invoke_event('comment_update', $entity);
+      break;
+
+    case 'node':
+      rules_invoke_event('node_update--' . $entity->type, $entity);
+      rules_invoke_event('node_update', $entity);
+      break;
+
+    case 'taxonomy_term':
+      rules_invoke_event('taxonomy_term_update--' . $entity->vocabulary_machine_name, $entity);
+      rules_invoke_event('taxonomy_term_update', $entity);
+      break;
+
+    case 'taxonomy_vocabulary':
+    case 'user':
+      rules_invoke_event($type . '_update', $entity);
+      break;
+  }
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function rules_entity_insert($entity, $type) {
+  switch ($type) {
+    case 'comment':
+      rules_invoke_event('comment_insert--' . $entity->node_type, $entity);
+      rules_invoke_event('comment_insert', $entity);
+      break;
+
+    case 'node':
+      rules_invoke_event('node_insert--' . $entity->type, $entity);
+      rules_invoke_event('node_insert', $entity);
+      break;
+
+    case 'taxonomy_term':
+      rules_invoke_event('taxonomy_term_insert--' . $entity->vocabulary_machine_name, $entity);
+      rules_invoke_event('taxonomy_term_insert', $entity);
+      break;
+
+    case 'taxonomy_vocabulary':
+    case 'user':
+      rules_invoke_event($type . '_insert', $entity);
+      break;
+  }
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function rules_entity_delete($entity, $type) {
+  switch ($type) {
+    case 'comment':
+      rules_invoke_event('comment_delete--' . $entity->node_type, $entity);
+      rules_invoke_event('comment_delete', $entity);
+      break;
+
+    case 'node':
+      rules_invoke_event('node_delete--' . $entity->type, $entity);
+      rules_invoke_event('node_delete', $entity);
+      break;
+
+    case 'taxonomy_term':
+      rules_invoke_event('taxonomy_term_delete--' . $entity->vocabulary_machine_name, $entity);
+      rules_invoke_event('taxonomy_term_delete', $entity);
+      break;
+
+    case 'taxonomy_vocabulary':
+    case 'user':
+      rules_invoke_event($type . '_delete', $entity);
+      break;
+  }
+}
+
+/**
+ * Implements hook_user_login().
+ */
+function rules_user_login(&$edit, $account) {
+  rules_invoke_event('user_login', $account);
+}
+
+/**
+ * Implements hook_user_logout().
+ */
+function rules_user_logout($account) {
+  rules_invoke_event('user_logout', $account);
+}
+
+/*
+ * System events.
+ *
+ * Note that rules_init() is the main module file is used to
+ * invoke the init event.
+ */
+
+/**
+ * Implements hook_cron().
+ */
+function rules_cron() {
+  rules_invoke_event('cron');
+}
+
+/**
+ * Implements hook_watchdog().
+ */
+function rules_watchdog($log_entry) {
+  rules_invoke_event('watchdog', $log_entry);
+}
+
+/**
+ * Getter callback for the log entry message property.
+ */
+function rules_system_log_get_message($log_entry) {
+  return t($log_entry['message'], (array) $log_entry['variables']);
+}
+
+/**
+ * Gets all view modes of an entity for an entity_view event.
+ */
+function rules_get_entity_view_modes($name, $var_info) {
+  // Read the entity type from a special key out of the variable info.
+  $entity_type = $var_info['options list entity type'];
+  $info = entity_get_info($entity_type);
+  foreach ($info['view modes'] as $mode => $mode_info) {
+    $modes[$mode] = $mode_info['label'];
+  }
+  return $modes;
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/node.eval.inc b/profiles/wcm_base/modules/contrib/rules/modules/node.eval.inc
new file mode 100644
index 0000000000000000000000000000000000000000..49450dc97ad9e6090d170e38a4cfb18f3aeb6247
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/node.eval.inc
@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * @file
+ * Contains rules integration for the node module needed during evaluation.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Base class providing node condition defaults.
+ */
+abstract class RulesNodeConditionBase extends RulesConditionHandlerBase {
+
+  public static function defaults() {
+    return array(
+      'parameter' => array(
+        'node' => array('type' => 'node', 'label' => t('Content')),
+      ),
+      'category' => 'node',
+      'access callback' => 'rules_node_integration_access',
+    );
+  }
+
+}
+
+/**
+ * Condition: Check for selected content types.
+ */
+class RulesNodeConditionType extends RulesNodeConditionBase {
+
+  /**
+   * Defines the condition.
+   */
+  public static function getInfo() {
+    $info = self::defaults() + array(
+      'name' => 'node_is_of_type',
+      'label' => t('Content is of type'),
+      'help' => t('Evaluates to TRUE if the given content is of one of the selected content types.'),
+    );
+    $info['parameter']['type'] = array(
+      'type' => 'list<text>',
+      'label' => t('Content types'),
+      'options list' => 'node_type_get_names',
+      'description' => t('The content type(s) to check for.'),
+      'restriction' => 'input',
+    );
+    return $info;
+  }
+
+  /**
+   * Executes the condition.
+   */
+  public function execute($node, $types) {
+    return in_array($node->type, $types);
+  }
+
+  /**
+   * Provides the content type of a node as asserted metadata.
+   */
+  public function assertions() {
+    return array('node' => array('bundle' => $this->element->settings['type']));
+  }
+
+}
+
+/**
+ * Condition: Check if the node is published.
+ */
+class RulesNodeConditionPublished extends RulesNodeConditionBase {
+
+  /**
+   * Defines the condition.
+   */
+  public static function getInfo() {
+    return self::defaults() + array(
+      'name' => 'node_is_published',
+      'label' => t('Content is published'),
+    );
+  }
+
+  /**
+   * Executes the condition.
+   */
+  public function execute($node) {
+    return $node->status == 1;
+  }
+
+}
+
+/**
+ * Condition: Check if the node is sticky.
+ */
+class RulesNodeConditionSticky extends RulesNodeConditionBase {
+
+  /**
+   * Defines the condition.
+   */
+  public static function getInfo() {
+    return self::defaults() + array(
+      'name' => 'node_is_sticky',
+      'label' => t('Content is sticky'),
+    );
+  }
+
+  /**
+   * Executes the condition.
+   */
+  public function execute($node) {
+    return $node->sticky == 1;
+  }
+
+}
+
+/**
+ * Condition: Check if the node is promoted to the frontpage.
+ */
+class RulesNodeConditionPromoted extends RulesNodeConditionBase {
+
+  /**
+   * Defines the condition.
+   */
+  public static function getInfo() {
+    return self::defaults() + array(
+      'name' => 'node_is_promoted',
+      'label' => t('Content is promoted to frontpage'),
+    );
+  }
+
+  /**
+   * Executes the condition.
+   */
+  public function execute($node) {
+    return $node->promote == 1;
+  }
+
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/node.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/node.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..97aa00bacb9a9dd47e7b607ac449752d1ffcc23b
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/node.rules.inc
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * @file
+ * Rules integration for the node module.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_category_info() on behalf of the node module.
+ */
+function rules_node_category_info() {
+  return array(
+    'node' => array(
+      'label' => t('Node'),
+      'equals group' => t('Node'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_file_info() on behalf of the node module.
+ */
+function rules_node_file_info() {
+  return array('modules/node.eval');
+}
+
+/**
+ * Implements hook_rules_event_info() on behalf of the node module.
+ */
+function rules_node_event_info() {
+  $items = array(
+    'node_insert' => array(
+      'label' => t('After saving new content'),
+      'category' => 'node',
+      'variables' => rules_events_node_variables(t('created content')),
+      'access callback' => 'rules_node_integration_access',
+      'class' => 'RulesNodeEventHandler',
+    ),
+    'node_update' => array(
+      'label' => t('After updating existing content'),
+      'category' => 'node',
+      'variables' => rules_events_node_variables(t('updated content'), TRUE),
+      'access callback' => 'rules_node_integration_access',
+      'class' => 'RulesNodeEventHandler',
+    ),
+    'node_presave' => array(
+      'label' => t('Before saving content'),
+      'category' => 'node',
+      'variables' => rules_events_node_variables(t('saved content'), TRUE),
+      'access callback' => 'rules_node_integration_access',
+      'class' => 'RulesNodeEventHandler',
+    ),
+    'node_view' => array(
+      'label' => t('Content is viewed'),
+      'category' => 'node',
+      'help' => t("Note that if drupal's page cache is enabled, this event won't be generated for pages served from cache."),
+      'variables' => rules_events_node_variables(t('viewed content')) + array(
+        'view_mode' => array(
+          'type' => 'text',
+          'label' => t('view mode'),
+          'options list' => 'rules_get_entity_view_modes',
+          // Add the entity-type for the options list callback.
+          'options list entity type' => 'node',
+        ),
+      ),
+      'access callback' => 'rules_node_integration_access',
+      'class' => 'RulesNodeEventHandler',
+    ),
+    'node_delete' => array(
+      'label' => t('After deleting content'),
+      'category' => 'node',
+      'variables' => rules_events_node_variables(t('deleted content')),
+      'access callback' => 'rules_node_integration_access',
+      'class' => 'RulesNodeEventHandler',
+    ),
+  );
+  // Specify that on presave the node is saved anyway.
+  $items['node_presave']['variables']['node']['skip save'] = TRUE;
+  return $items;
+}
+
+/**
+ * Returns some parameter suitable for using it with a node.
+ */
+function rules_events_node_variables($node_label, $update = FALSE) {
+  $args = array(
+    'node' => array('type' => 'node', 'label' => $node_label),
+  );
+  if ($update) {
+    $args += array(
+      'node_unchanged' => array(
+        'type' => 'node',
+        'label' => t('unchanged content'),
+        'handler' => 'rules_events_entity_unchanged',
+      ),
+    );
+  }
+  return $args;
+}
+
+/**
+ * Implements hook_rules_action_info() on behalf of the node module.
+ */
+function rules_node_action_info() {
+  $defaults = array(
+    'parameter' => array(
+      'node' => array('type' => 'node', 'label' => t('Content'), 'save' => TRUE),
+    ),
+    'category' => 'node',
+    'access callback' => 'rules_node_admin_access',
+  );
+  // Add support for hand-picked core actions.
+  $core_actions = node_action_info();
+  $actions = array('node_publish_action', 'node_unpublish_action', 'node_make_sticky_action', 'node_make_unsticky_action', 'node_promote_action', 'node_unpromote_action');
+  foreach ($actions as $base) {
+    $action_name = str_replace('_action', '', $base);
+    $items[$action_name] = $defaults + array(
+      'label' => $core_actions[$base]['label'],
+      'base' => $base,
+    );
+  }
+  return $items;
+}
+
+/**
+ * Node integration access callback.
+ */
+function rules_node_integration_access($type, $name) {
+  if ($type == 'event' || $type == 'condition') {
+    return entity_access('view', 'node');
+  }
+}
+
+/**
+ * Node integration admin access callback.
+ */
+function rules_node_admin_access() {
+  return user_access('administer nodes');
+}
+
+/**
+ * Event handler support node bundle event settings.
+ */
+class RulesNodeEventHandler extends RulesEventHandlerEntityBundle {
+
+  /**
+   * Returns the label to use for the bundle property.
+   *
+   * @return string
+   */
+  protected function getBundlePropertyLabel() {
+    return t('type');
+  }
+
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/path.eval.inc b/profiles/wcm_base/modules/contrib/rules/modules/path.eval.inc
new file mode 100644
index 0000000000000000000000000000000000000000..8c6e13d0f2490b08fa47daf8ff57bfdcae26ae9e
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/path.eval.inc
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * @file
+ * Contains rules integration for the path module needed during evaluation.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Action implementation: Path alias.
+ */
+function rules_action_path_alias($source, $alias, $langcode = LANGUAGE_NONE) {
+  if (!$alias) {
+    path_delete(array('source' => $source, 'language' => $langcode));
+  }
+  elseif (!$source) {
+    path_delete(array('alias' => $alias, 'language' => $langcode));
+  }
+  // Only set the alias if the alias is not taken yet.
+  elseif (!path_load(array('alias' => $alias, 'language' => $langcode))) {
+    // Update the existing path or create a new one.
+    if ($path = path_load(array('source' => $source, 'language' => $langcode))) {
+      $path['alias'] = $alias;
+    }
+    else {
+      $path = array('source' => $source, 'alias' => $alias, 'language' => $langcode);
+    }
+    path_save($path);
+  }
+  else {
+    rules_log('The configured alias %alias already exists. Aborting.', array('%alias' => $alias));
+  }
+}
+
+/**
+ * Action Implementation: Set the URL alias for a node.
+ */
+function rules_action_node_path_alias($node, $alias) {
+  $langcode = isset($node->language) ? $node->language : LANGUAGE_NONE;
+  // Only set the alias if the alias is not taken yet.
+  if (($path = path_load(array('alias' => $alias, 'language' => $langcode))) && (empty($node->path['pid']) || $node->path['pid'] != $path['pid'])) {
+    rules_log('The configured alias %alias already exists. Aborting.', array('%alias' => $alias));
+    return FALSE;
+  }
+  $node->path['alias'] = $alias;
+}
+
+/**
+ * Action Implementation: Set the URL alias for a node.
+ */
+function rules_action_taxonomy_term_path_alias($term, $alias) {
+  // Only set the alias if the alias is not taken yet.
+  if (($path = path_load(array('alias' => $alias, 'language' => LANGUAGE_NONE))) && (empty($term->path['pid']) || $term->path['pid'] != $path['pid'])) {
+    rules_log('The configured alias %alias already exists. Aborting.', array('%alias' => $alias));
+    return FALSE;
+  }
+  $term->path['alias'] = $alias;
+}
+
+/**
+ * Condition implementation: Check if the path has an alias.
+ */
+function rules_condition_path_has_alias($source, $langcode = LANGUAGE_NONE) {
+  return (bool) drupal_lookup_path('alias', $source, $langcode);
+}
+
+/**
+ * Condition implementation: Check if the URL alias exists.
+ */
+function rules_condition_path_alias_exists($alias, $langcode = LANGUAGE_NONE) {
+  return (bool) drupal_lookup_path('source', $alias, $langcode);
+}
+
+/**
+ * Cleans the given path.
+ *
+ * A path is cleaned by replacing non ASCII characters in the path with the
+ * replacement character.
+ *
+ * Path cleaning may be customized by overriding the configuration variables:
+ * @code rules_clean_path @endcode,
+ * @code rules_path_replacement_char @endcode and
+ * @code rules_path_transliteration @endcode
+ * in the site's settings.php file.
+ */
+function rules_path_default_cleaning_method($path) {
+  $replace = variable_get('rules_path_replacement_char', '-');
+  if ($replace) {
+    // If the transliteration module is enabled, transliterate the alias first.
+    if (module_exists('transliteration') && variable_get('rules_path_transliteration', TRUE)) {
+      $path = transliteration_get($path);
+    }
+
+    $array = variable_get('rules_clean_path', array('/[^a-zA-Z0-9\-_]+/', $replace));
+    $array[2] = $path;
+    // Replace it and remove trailing and leading replacement characters.
+    $output = trim(call_user_func_array('preg_replace', $array), $replace);
+
+    if (variable_get('rules_path_lower_case', TRUE)) {
+      $output = drupal_strtolower($output);
+    }
+    return $output;
+  }
+  else {
+    return $path;
+  }
+}
+
+/**
+ * Cleans the given string so it can be used as part of a URL path.
+ */
+function rules_clean_path($path) {
+  $function = variable_get('rules_path_cleaning_callback', 'rules_path_default_cleaning_method');
+  if (!function_exists($function)) {
+    rules_log('An invalid URL path cleaning callback has been configured. Falling back to the default cleaning method.', array(), RulesLog::WARN);
+    $function = 'rules_path_default_cleaning_method';
+  }
+  return $function($path);
+}
+
+/**
+ * CTools path cleaning callback.
+ *
+ * @see rules_admin_settings()
+ */
+function rules_path_clean_ctools($path) {
+  // Make use of the CTools cleanstring implementation.
+  ctools_include('cleanstring');
+  $settings = array(
+    'separator' => variable_get('rules_path_replacement_char', '-'),
+    'transliterate' => module_exists('transliteration') && variable_get('rules_path_transliteration', TRUE),
+    'lower case' => variable_get('rules_path_lower_case', TRUE),
+  );
+  return ctools_cleanstring($path, $settings);
+}
+
+/**
+ * Pathauto path cleaning callback.
+ *
+ * @see rules_admin_settings()
+ */
+function rules_path_clean_pathauto($path) {
+  module_load_include('inc', 'pathauto');
+  return pathauto_cleanstring($path);
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/path.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/path.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..8f0eef6fd6159185857c35e5cdd74f129426102e
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/path.rules.inc
@@ -0,0 +1,173 @@
+<?php
+
+/**
+ * @file
+ * Rules integration for the path module.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_file_info() on behalf of the path module.
+ */
+function rules_path_file_info() {
+  return array('modules/path.eval');
+}
+
+/**
+ * Implements hook_rules_action_info() on behalf of the path module.
+ */
+function rules_path_action_info() {
+  return array(
+    'path_alias' => array(
+      'label' => t('Create or delete any URL alias'),
+      'group' => t('Path'),
+      'parameter' => array(
+        'source' => array(
+          'type' => 'text',
+          'label' => t('Existing system path'),
+          'description' => t('Specifies the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1+2.') . ' ' . t('Leave it empty to delete URL aliases pointing to the given path alias.'),
+          'optional' => TRUE,
+        ),
+        'alias' => array(
+          'type' => 'text',
+          'label' => t('URL alias'),
+          'description' => t('Specify an alternative path by which this data can be accessed. For example, "about" for an about page. Use a relative path and do not add a trailing slash.') . ' ' . t('Leave it empty to delete URL aliases pointing to the given system path.'),
+          'optional' => TRUE,
+          'cleaning callback' => 'rules_path_clean_replacement_values',
+        ),
+        'language' => array(
+          'type' => 'token',
+          'label' => t('Language'),
+          'description' => t('If specified, the language for which the URL alias applies.'),
+          'options list' => 'entity_metadata_language_list',
+          'optional' => TRUE,
+          'default value' => LANGUAGE_NONE,
+        ),
+      ),
+      'base' => 'rules_action_path_alias',
+      'callbacks' => array('dependencies' => 'rules_path_dependencies'),
+      'access callback' => 'rules_path_integration_access',
+    ),
+    'node_path_alias' => array(
+      'label' => t("Create or delete a content's URL alias"),
+      'group' => t('Path'),
+      'parameter' => array(
+        'node' => array(
+          'type' => 'node',
+          'label' => t('Content'),
+          'save' => TRUE,
+        ),
+        'alias' => array(
+          'type' => 'text',
+          'label' => t('URL alias'),
+          'description' => t('Specify an alternative path by which the content can be accessed. For example, "about" for an about page. Use a relative path and do not add a trailing slash.') . ' ' . t('Leave it empty to delete the URL alias.'),
+          'optional' => TRUE,
+          'cleaning callback' => 'rules_path_clean_replacement_values',
+        ),
+      ),
+      'base' => 'rules_action_node_path_alias',
+      'callbacks' => array('dependencies' => 'rules_path_dependencies'),
+      'access callback' => 'rules_path_integration_access',
+    ),
+    'taxonomy_term_path_alias' => array(
+      'label' => t("Create or delete a taxonomy term's URL alias"),
+      'group' => t('Path'),
+      'parameter' => array(
+        'node' => array(
+          'type' => 'taxonomy_term',
+          'label' => t('Taxonomy term'),
+          'save' => TRUE,
+        ),
+        'alias' => array(
+          'type' => 'text',
+          'label' => t('URL alias'),
+          'description' => t('Specify an alternative path by which the term can be accessed. For example, "content/drupal" for a Drupal term. Use a relative path and do not add a trailing slash.') . ' ' . t('Leave it empty to delete the URL alias.'),
+          'optional' => TRUE,
+          'cleaning callback' => 'rules_path_clean_replacement_values',
+        ),
+      ),
+      'base' => 'rules_action_node_path_alias',
+      'callbacks' => array('dependencies' => 'rules_path_dependencies'),
+      'access callback' => 'rules_path_integration_access',
+    ),
+  );
+}
+
+/**
+ * Callback to specify the path module as dependency.
+ */
+function rules_path_dependencies() {
+  return array('path');
+}
+
+/**
+ * Path integration access callback.
+ */
+function rules_path_integration_access($type, $name) {
+  if ($type == 'action' && $name == 'path_alias') {
+    return user_access('administer url aliases');
+  }
+  return user_access('create url aliases');
+}
+
+/**
+ * Implements hook_rules_condition_info() on behalf of the path module.
+ */
+function rules_path_condition_info() {
+  return array(
+    'path_has_alias' => array(
+      'label' => t('Path has URL alias'),
+      'group' => t('Path'),
+      'parameter' => array(
+        'source' => array(
+          'type' => 'text',
+          'label' => t('Existing system path'),
+          'description' => t('Specifies the existing path you wish to check for. For example: node/28, forum/1, taxonomy/term/1+2.'),
+          'optional' => TRUE,
+        ),
+        'language' => array(
+          'type' => 'token',
+          'label' => t('Language'),
+          'description' => t('If specified, the language for which the URL alias applies.'),
+          'options list' => 'entity_metadata_language_list',
+          'optional' => TRUE,
+          'default value' => LANGUAGE_NONE,
+        ),
+      ),
+      'base' => 'rules_condition_path_has_alias',
+      'callbacks' => array('dependencies' => 'rules_path_dependencies'),
+      'access callback' => 'rules_path_integration_access',
+    ),
+    'path_alias_exists' => array(
+      'label' => t('URL alias exists'),
+      'group' => t('Path'),
+      'parameter' => array(
+        'alias' => array(
+          'type' => 'text',
+          'label' => t('URL alias'),
+          'description' => t('Specify the URL alias to check for. For example, "about" for an about page.'),
+          'optional' => TRUE,
+          'cleaning callback' => 'rules_path_clean_replacement_values',
+        ),
+        'language' => array(
+          'type' => 'token',
+          'label' => t('Language'),
+          'description' => t('If specified, the language for which the URL alias applies.'),
+          'options list' => 'entity_metadata_language_list',
+          'optional' => TRUE,
+          'default value' => LANGUAGE_NONE,
+        ),
+      ),
+      'base' => 'rules_condition_path_alias_exists',
+      'callbacks' => array('dependencies' => 'rules_path_dependencies'),
+      'access callback' => 'rules_path_integration_access',
+    ),
+  );
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/php.eval.inc b/profiles/wcm_base/modules/contrib/rules/modules/php.eval.inc
new file mode 100644
index 0000000000000000000000000000000000000000..ac400b0855d139f07bcc7a50bd81a6e4627d312a
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/php.eval.inc
@@ -0,0 +1,184 @@
+<?php
+
+/**
+ * @file
+ * Contains rules integration for the php module needed during evaluation.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * A class implementing a rules input evaluator processing PHP.
+ */
+class RulesPHPEvaluator extends RulesDataInputEvaluator {
+
+  /**
+   * Overrides RulesDataProcessor::editAccess().
+   */
+  public function editAccess() {
+    return parent::editAccess() && (user_access('use PHP for settings') || drupal_is_cli());
+  }
+
+  /**
+   * Helper function to find variables in PHP code.
+   *
+   * @param string $text
+   *   The PHP code.
+   * @param array $var_info
+   *   Array with variable names as keys.
+   */
+  public static function getUsedVars($text, $var_info) {
+    if (strpos($text, '<?') !== FALSE) {
+      $used_vars = array();
+      foreach ($var_info as $name => $info) {
+        if (strpos($text, '$' . $name) !== FALSE) {
+          $used_vars[] = $name;
+        }
+      }
+      return $used_vars;
+    }
+  }
+
+  /**
+   * Overrides RulesDataInputEvaluator::prepare().
+   */
+  public function prepare($text, $var_info) {
+    // A returned NULL skips the evaluator.
+    $this->setting = self::getUsedVars($text, $var_info);
+  }
+
+  /**
+   * Evaluates PHP code contained in $text.
+   *
+   * This method doesn't apply $options, thus the PHP code is responsible for
+   * behaving appropriately.
+   */
+  public function evaluate($text, $options, RulesState $state) {
+    $vars['eval_options'] = $options;
+    foreach ($this->setting as $key => $var_name) {
+      $vars[$var_name] = $state->get($var_name);
+    }
+    return rules_php_eval($text, rules_unwrap_data($vars));
+  }
+
+  /**
+   * Overrides RulesDataInputEvaluator::help().
+   */
+  public static function help($var_info) {
+    module_load_include('inc', 'rules', 'rules/modules/php.rules');
+
+    $render = array(
+      '#type' => 'fieldset',
+      '#title' => t('PHP Evaluation'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+    ) + rules_php_evaluator_help($var_info);
+
+    return $render;
+  }
+
+}
+
+/**
+ * A data processor using PHP.
+ */
+class RulesPHPDataProcessor extends RulesDataProcessor {
+
+  /**
+   * Overrides RulesDataProcessor::form().
+   */
+  protected static function form($settings, $var_info) {
+    $settings += array('code' => '');
+    $form = array(
+      '#type' => 'fieldset',
+      '#title' => t('PHP evaluation'),
+      '#collapsible' => TRUE,
+      '#collapsed' => empty($settings['code']),
+      '#description' => t('Enter PHP code to process the selected argument value.'),
+    );
+    $form['code'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Code'),
+      '#description' => t('Enter PHP code without &lt;?php ?&gt; delimiters that returns the processed value. The selected value is available in the variable $value. Example: %code', array('%code' => 'return $value + 1;')),
+      '#default_value' => $settings['code'],
+      '#weight' => 5,
+    );
+    return $form;
+  }
+
+  /**
+   * Overrides RulesDataProcessor::editAccess().
+   */
+  public function editAccess() {
+    return parent::editAccess() && (user_access('use PHP for settings') || drupal_is_cli());
+  }
+
+  /**
+   * Overrides RulesDataProcessor::process().
+   */
+  public function process($value, $info, RulesState $state, RulesPlugin $element) {
+    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
+    return rules_php_eval_return($this->setting['code'], array('value' => $value));
+  }
+
+}
+
+/**
+ * Action and condition callback: Execute PHP code.
+ */
+function rules_execute_php_eval($code, $settings, $state, $element) {
+  $data = array();
+  if (!empty($settings['used_vars'])) {
+    foreach ($settings['used_vars'] as $key => $var_name) {
+      $data[$var_name] = $state->get($var_name);
+    }
+  }
+  return rules_php_eval_return($code, rules_unwrap_data($data));
+}
+
+/**
+ * Evaluates the given PHP code, with the given variables defined.
+ *
+ * @param string $code
+ *   The PHP code to run, including <?php and ?>
+ * @param array $arguments
+ *   Array containing variables to be extracted to the code.
+ *
+ * @return
+ *   The output of the php code.
+ */
+function rules_php_eval($code, $arguments = array()) {
+  extract($arguments);
+
+  ob_start();
+  print eval('?>' . $code);
+  $output = ob_get_contents();
+  ob_end_clean();
+
+  return $output;
+}
+
+/**
+ * Evaluates the given PHP code, with the given variables defined.
+ *
+ * This is like rules_php_eval(), but does return the returned data from
+ * the PHP code.
+ *
+ * @param string $code
+ *   The PHP code to run, without <?php or ?>
+ * @param array $arguments
+ *   Array containing variables to be extracted to the code.
+ *
+ * @return
+ *   The return value of the evaled code.
+ */
+function rules_php_eval_return($code, $arguments = array()) {
+  extract($arguments);
+  return eval($code);
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/php.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/php.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..d00c00ece4d90c7c2df3a2d4d7888fb742c977bc
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/php.rules.inc
@@ -0,0 +1,159 @@
+<?php
+
+/**
+ * @file
+ * Rules integration for the php module.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_file_info() on behalf of the php module.
+ */
+function rules_php_file_info() {
+  return array('modules/php.eval');
+}
+
+/**
+ * Implements hook_rules_evaluator_info() on behalf of the php module.
+ */
+function rules_php_evaluator_info() {
+  return array(
+    'php' => array(
+      'class' => 'RulesPHPEvaluator',
+      'type' => array('text', 'uri'),
+      'weight' => -10,
+      'module' => 'php',
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_data_processor_info() on behalf of the php module.
+ */
+function rules_php_data_processor_info() {
+  return array(
+    'php' => array(
+      'class' => 'RulesPHPDataProcessor',
+      'type' => array('text', 'token', 'decimal', 'integer', 'date', 'duration', 'boolean', 'uri'),
+      'weight' => 10,
+      'module' => 'php',
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_action_info() on behalf of the php module.
+ */
+function rules_php_action_info() {
+  return array(
+    'php_eval' => array(
+      'label' => t('Execute custom PHP code'),
+      'group' => t('PHP'),
+      'parameter' => array(
+        'code' => array(
+          'restriction' => 'input',
+          'type' => 'text',
+          'label' => t('PHP code'),
+          'description' => t('Enter PHP code without &lt;?php ?&gt; delimiters.'),
+        ),
+      ),
+      'base' => 'rules_execute_php_eval',
+      'access callback' => 'rules_php_integration_access',
+    ),
+  );
+}
+
+/**
+ * Alter the form for improved UX.
+ */
+function rules_execute_php_eval_form_alter(&$form, &$form_state) {
+  // Remove the PHP evaluation help to avoid confusion whether <?php tags should
+  // be used. But keep the help about available variables.
+  $form['parameter']['code']['settings']['help']['php']['#type'] = 'container';
+  $form['parameter']['code']['settings']['help']['php']['top']['#markup'] = t('The following variables are available and may be used by your PHP code:');
+}
+
+/**
+ * Process the settings to prepare code execution.
+ */
+function rules_execute_php_eval_process(RulesAbstractPlugin $element) {
+  $element->settings['used_vars'] = RulesPHPEvaluator::getUsedVars('<?' . $element->settings['code'], $element->availableVariables());
+}
+
+/**
+ * Specify the php module as dependency.
+ */
+function rules_execute_php_eval_dependencies() {
+  return array('php');
+}
+
+/**
+ * PHP integration access callback.
+ */
+function rules_php_integration_access() {
+  return user_access('use PHP for settings');
+}
+
+/**
+ * Implements hook_rules_condition_info() on behalf of the PHP module.
+ */
+function rules_php_condition_info() {
+  return array(
+    'php_eval' => array(
+      'label' => t('Execute custom PHP code'),
+      'group' => t('PHP'),
+      'parameter' => array(
+        'code' => array(
+          'restriction' => 'input',
+          'type' => 'text',
+          'label' => t('PHP code'),
+          'description' => t('Enter PHP code without &lt;?php ?&gt; delimiters that returns a boolean value; e.g. <code>@code</code>.', array('@code' => "return arg(0) == 'node';")),
+        ),
+      ),
+      'base' => 'rules_execute_php_eval',
+      'access callback' => 'rules_php_integration_access',
+    ),
+  );
+}
+
+/**
+ * Generates help for the PHP actions, conditions and input evaluator.
+ */
+function rules_php_evaluator_help($var_info, $action_help = FALSE) {
+  $render['top'] = array(
+    '#prefix' => '<p>',
+    '#suffix' => '</p>',
+    '#markup' => t('PHP code inside of &lt;?php ?&gt; delimiters will be evaluated and replaced by its output. E.g. &lt;? echo 1+1?&gt; will be replaced by 2.') . ' ' . t('Furthermore you can make use of the following variables:'),
+  );
+  $render['vars'] = array(
+    '#theme' => 'table',
+    '#header' => array(t('Variable name'), t('Type'), t('Description')),
+    '#attributes' => array('class' => array('rules-php-help')),
+  );
+
+  $cache = rules_get_cache();
+  foreach ($var_info as $name => $info) {
+    $row   = array();
+    $row[] = '$' . check_plain($name);
+    $label = isset($cache['data_info'][$info['type']]['label']) ? $cache['data_info'][$info['type']]['label'] : $info['type'];
+    $row[] = check_plain(drupal_ucfirst($label));
+    $row[] = check_plain($info['label']);
+    $render['vars']['#rows'][] = $row;
+  }
+
+  if ($action_help) {
+    $render['updated_help'] = array(
+      '#prefix' => '<p>',
+      '#suffix' => '</p>',
+      '#markup' => t("If you want to change a variable just return an array of new variable values, e.g.: !code", array('!code' => '<pre>return array("node" => $node);</pre>')),
+    );
+  }
+  return $render;
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/rules_core.eval.inc b/profiles/wcm_base/modules/contrib/rules/modules/rules_core.eval.inc
new file mode 100644
index 0000000000000000000000000000000000000000..c0e9f24602e720923de3786c4ffaf2d91b5d5005
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/rules_core.eval.inc
@@ -0,0 +1,280 @@
+<?php
+
+/**
+ * @file
+ * Contains rules core integration needed during evaluation.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Action and condition callback: Invokes a rules component.
+ *
+ * We do not use the execute() method, but handle executing ourself. That way
+ * we can utilize the existing state for saving passed variables.
+ */
+function rules_element_invoke_component($arguments, RulesPlugin $element) {
+  $info = $element->info();
+  $state = $arguments['state'];
+  $wrapped_args = $state->currentArguments;
+
+  if ($component = rules_get_cache('comp_' . $info['#config_name'])) {
+    $replacements = array('%label' => $component->label(), '@plugin' => $component->plugin());
+    // Handle recursion prevention.
+    if ($state->isBlocked($component)) {
+      return rules_log('Not evaluating @plugin %label to prevent recursion.', $replacements, RulesLog::INFO, $component);
+    }
+    $state->block($component);
+    rules_log('Evaluating @plugin %label.', $replacements, RulesLog::INFO, $component, TRUE);
+    module_invoke_all('rules_config_execute', $component);
+
+    // Manually create a new evaluation state and evaluate the component.
+    $args = array_intersect_key($wrapped_args, $component->parameterInfo());
+    $new_state = $component->setUpState($wrapped_args);
+    $return = $component->evaluate($new_state);
+
+    // Care for the right return value in case we have to provide vars.
+    if ($component instanceof RulesActionInterface && !empty($info['provides'])) {
+      $return = array();
+      foreach ($info['provides'] as $var => $var_info) {
+        $return[$var] = $new_state->get($var);
+      }
+    }
+
+    // Now merge the info about to be saved variables in the parent state.
+    $state->mergeSaveVariables($new_state, $component, $element->settings);
+    $state->unblock($component);
+
+    // Cleanup the state, what saves not mergeable variables now.
+    $new_state->cleanup();
+    rules_log('Finished evaluation of @plugin %label.', $replacements, RulesLog::INFO, $component, FALSE);
+    return $return;
+  }
+  else {
+    throw new RulesEvaluationException('Unable to get the component %name', array('%name' => $info['#config_name']), $element, RulesLog::ERROR);
+  }
+}
+
+/**
+ * A class implementing a rules input evaluator processing date input.
+ *
+ * This is needed to treat relative date inputs for strtotime() correctly.
+ * Consider for example "now".
+ */
+class RulesDateInputEvaluator extends RulesDataInputEvaluator {
+
+  const DATE_REGEX_LOOSE = '/^(\d{4})-?(\d{2})-?(\d{2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})?)?$/';
+
+  /**
+   * Overrides RulesDataInputEvaluator::prepare().
+   */
+  public function prepare($text, $var_info) {
+    if (is_numeric($text)) {
+      // Let rules skip this input evaluators in case it's already a timestamp.
+      $this->setting = NULL;
+    }
+  }
+
+  /**
+   * Overrides RulesDataInputEvaluator::evaluate().
+   */
+  public function evaluate($text, $options, RulesState $state) {
+    return self::gmstrtotime($text);
+  }
+
+  /**
+   * Convert a time string to a GMT (UTC) unix timestamp.
+   */
+  public static function gmstrtotime($date) {
+    // Pass the current timestamp in UTC to ensure the retrieved time is UTC.
+    return strtotime($date, time());
+  }
+
+  /**
+   * Determine whether the given date string specifies a fixed date.
+   */
+  public static function isFixedDateString($date) {
+    return is_string($date) && preg_match(self::DATE_REGEX_LOOSE, $date);
+  }
+
+}
+
+/**
+ * A class implementing a rules input evaluator processing URI inputs.
+ *
+ * Makes sure URIs are absolute and path aliases get applied.
+ */
+class RulesURIInputEvaluator extends RulesDataInputEvaluator {
+
+  /**
+   * Overrides RulesDataInputEvaluator::prepare().
+   */
+  public function prepare($uri, $var_info) {
+    if (!isset($this->processor) && valid_url($uri, TRUE)) {
+      // Only process if another evaluator is used or the url is not absolute.
+      $this->setting = NULL;
+    }
+  }
+
+  /**
+   * Overrides RulesDataInputEvaluator::evaluate().
+   */
+  public function evaluate($uri, $options, RulesState $state) {
+    if (!url_is_external($uri)) {
+      // Extract the path and build the URL using the url() function, so URL
+      // aliases are applied and query parameters and fragments get handled.
+      $url = drupal_parse_url($uri);
+      $url_options = array('absolute' => TRUE);
+      $url_options['query'] = $url['query'];
+      $url_options['fragment'] = $url['fragment'];
+      return url($url['path'], $url_options);
+    }
+    elseif (valid_url($uri)) {
+      return $uri;
+    }
+    throw new RulesEvaluationException('Input evaluation generated an invalid URI.', array(), NULL, RulesLog::WARN);
+  }
+
+}
+
+/**
+ * A data processor for applying date offsets.
+ */
+class RulesDateOffsetProcessor extends RulesDataProcessor {
+
+  /**
+   * Overrides RulesDataProcessor::form().
+   */
+  protected static function form($settings, $var_info) {
+    $settings += array('value' => '');
+    $form = array(
+      '#type' => 'fieldset',
+      '#title' => t('Add offset'),
+      '#collapsible' => TRUE,
+      '#collapsed' => empty($settings['value']),
+      '#description' => t('Add an offset to the selected date.'),
+    );
+    $form['value'] = array(
+      '#type' => 'rules_duration',
+      '#title' => t('Offset'),
+      '#description' => t('Note that you can also specify negative numbers.'),
+      '#default_value' => $settings['value'],
+      '#weight' => 5,
+    );
+    return $form;
+  }
+
+  /**
+   * Overrides RulesDataProcessor::process().
+   */
+  public function process($value, $info, RulesState $state, RulesPlugin $element) {
+    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
+    return RulesDateOffsetProcessor::applyOffset($value, $this->setting['value']);
+  }
+
+  /**
+   * Intelligently applies the given date offset in seconds.
+   *
+   * Intelligently apply duration values > 1 day, i.e. convert the duration
+   * to its biggest possible unit (months, days) and apply it to the date with
+   * the given unit. That's necessary as the number of days in a month
+   * differs, as well as the number of hours for a day (on DST changes).
+   */
+  public static function applyOffset($timestamp, $offset) {
+    if (abs($offset) >= 86400) {
+
+      // Get the days out of the seconds.
+      $days = intval($offset / 86400);
+      $sec = $offset % 86400;
+      // Get the months out of the number of days.
+      $months = intval($days / 30);
+      $days = $days % 30;
+
+      // Apply the offset using the DateTime::modify and convert it back to a
+      // timestamp.
+      $date = date_create("@$timestamp");
+      $date->modify("$months months $days days $sec seconds");
+      return $date->format('U');
+    }
+    else {
+      return $timestamp + $offset;
+    }
+  }
+
+}
+
+/**
+ * A data processor for applying numerical offsets.
+ */
+class RulesNumericOffsetProcessor extends RulesDataProcessor {
+
+  /**
+   * Overrides RulesDataProcessor::form().
+   */
+  protected static function form($settings, $var_info) {
+    $settings += array('value' => '');
+    $form = array(
+      '#type' => 'fieldset',
+      '#title' => t('Add offset'),
+      '#collapsible' => TRUE,
+      '#collapsed' => empty($settings['value']),
+      '#description' => t('Add an offset to the selected number. E.g. an offset of "1" adds 1 to the number before it is passed on as argument.'),
+    );
+    $form['value'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Offset'),
+      '#description' => t('Note that you can also specify negative numbers.'),
+      '#default_value' => $settings['value'],
+      '#element_validate' => array('rules_ui_element_integer_validate'),
+      '#weight' => 5,
+    );
+    return $form;
+  }
+
+  /**
+   * Overrides RulesDataProcessor::process().
+   */
+  public function process($value, $info, RulesState $state, RulesPlugin $element) {
+    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element) : $value;
+    return $value + $this->setting['value'];
+  }
+
+}
+
+/**
+ * A custom wrapper class for vocabularies.
+ *
+ * This class is capable of loading vocabularies by machine name.
+ */
+class RulesTaxonomyVocabularyWrapper extends EntityDrupalWrapper {
+
+  /**
+   * Overridden to support identifying vocabularies by machine names.
+   */
+  protected function setEntity($data) {
+    if (isset($data) && $data !== FALSE && !is_object($data) && !is_numeric($data)) {
+      // The vocabulary name has been passed.
+      parent::setEntity(taxonomy_vocabulary_machine_name_load($data));
+    }
+    else {
+      parent::setEntity($data);
+    }
+  }
+
+  /**
+   * Overridden to permit machine names as values.
+   */
+  public function validate($value) {
+    if (isset($value) && is_string($value)) {
+      return TRUE;
+    }
+    return parent::validate($value);
+  }
+
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/rules_core.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/rules_core.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..01215d568481159658384bf1d6749a9029d0ddca
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/rules_core.rules.inc
@@ -0,0 +1,341 @@
+<?php
+
+/**
+ * @file
+ * Rules integration with Drupal core.
+ *
+ * Provides data types, conditions, and actions to invoke configured components.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_category_info() on behalf of the rules_core.
+ */
+function rules_rules_core_category_info() {
+  return array(
+    'rules_components' => array(
+      'label' => t('Components'),
+      'equals group' => t('Components'),
+      'weight' => 50,
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_file_info() on behalf of the pseudo rules_core module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_rules_core_file_info() {
+  return array('modules/rules_core.eval');
+}
+
+/**
+ * Implements hook_rules_data_info() on behalf of the pseudo rules_core module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_rules_core_data_info() {
+  $return = array(
+    'text' => array(
+      'label' => t('text'),
+      'ui class' => 'RulesDataUIText',
+      'token type' => 'rules_text',
+    ),
+    'token' => array(
+      'label' => t('text token'),
+      'parent' => 'text',
+      'ui class' => 'RulesDataUITextToken',
+      'token type' => 'rules_token',
+    ),
+    // A formatted text as used by entity metadata.
+    'text_formatted' => array(
+      'label' => t('formatted text'),
+      'ui class' => 'RulesDataUITextFormatted',
+      'wrap' => TRUE,
+      'property info' => entity_property_text_formatted_info(),
+    ),
+    'decimal' => array(
+      'label' => t('decimal number'),
+      'parent' => 'text',
+      'ui class' => 'RulesDataUIDecimal',
+      'token type' => 'rules_decimal',
+    ),
+    'integer' => array(
+      'label' => t('integer'),
+      'class' => 'RulesIntegerWrapper',
+      'parent' => 'decimal',
+      'ui class' => 'RulesDataUIInteger',
+      'token type' => 'rules_integer',
+    ),
+    'date' => array(
+      'label' => t('date'),
+      'ui class' => 'RulesDataUIDate',
+      'token type' => 'rules_date',
+    ),
+    'duration' => array(
+      'label' => t('duration'),
+      'parent' => 'integer',
+      'ui class' => 'RulesDataUIDuration',
+      'token type' => 'rules_duration',
+    ),
+    'boolean' => array(
+      'label' => t('truth value'),
+      'ui class' => 'RulesDataUIBoolean',
+      'token type' => 'rules_boolean',
+    ),
+    'uri' => array(
+      'label' => t('URI'),
+      'parent' => 'text',
+      // Clean inserted tokens with "rawurlencode".
+      'cleaning callback' => 'rawurlencode',
+      'ui class' => 'RulesDataUIURI',
+      'token type' => 'rules_uri',
+    ),
+    'list' => array(
+      'label' => t('list', array(), array('context' => 'data_types')),
+      'wrap' => TRUE,
+      'group' => t('List', array(), array('context' => 'data_types')),
+    ),
+    'list<text>' => array(
+      'label' => t('list of text'),
+      'ui class' => 'RulesDataUIListText',
+      'wrap' => TRUE,
+      'group' => t('List', array(), array('context' => 'data_types')),
+    ),
+    'list<integer>' => array(
+      'label' => t('list of integer'),
+      'ui class' => 'RulesDataUIListInteger',
+      'wrap' => TRUE,
+      'group' => t('List', array(), array('context' => 'data_types')),
+    ),
+    'list<token>' => array(
+      'label' => t('list of text tokens'),
+      'ui class' => 'RulesDataUIListToken',
+      'wrap' => TRUE,
+      'group' => t('List', array(), array('context' => 'data_types')),
+    ),
+    'entity' => array(
+      'label' => t('any entity'),
+      'group' => t('Entity'),
+      'is wrapped' => TRUE,
+    ),
+    'ip_address' => array(
+      'label' => t('IP Address'),
+      'parent' => 'text',
+      'ui class' => 'RulesDataUIIPAddress',
+      'token type' => 'rules_text',
+    ),
+  );
+  foreach (entity_get_info() as $type => $info) {
+    if (!empty($info['label'])) {
+      $return[$type] = array(
+        'label' => strtolower($info['label'][0]) . substr($info['label'], 1),
+        'parent' => 'entity',
+        'wrap' => TRUE,
+        'group' => t('Entity'),
+        'ui class' => empty($info['exportable']) ? 'RulesDataUIEntity' : 'RulesDataUIEntityExportable',
+      );
+      // If this entity type serves as bundle for another one, provide an
+      // options list for selecting a bundle entity.
+      if (!empty($info['bundle of'])) {
+        $return[$type]['ui class'] = 'RulesDataUIBundleEntity';
+      }
+    }
+  }
+
+  if (module_exists('taxonomy')) {
+    // For exportability identify vocabularies by name.
+    $return['taxonomy_vocabulary']['wrapper class'] = 'RulesTaxonomyVocabularyWrapper';
+    $return['taxonomy_vocabulary']['ui class'] = 'RulesDataUITaxonomyVocabulary';
+  }
+
+  return $return;
+}
+
+/**
+ * Implements hook_rules_data_info_alter() on behalf of the pseudo rules_core module.
+ *
+ * Makes sure there is a list<type> data type for each type registered.
+ *
+ * @see rules_rules_data_info_alter()
+ */
+function rules_rules_core_data_info_alter(&$data_info) {
+  foreach ($data_info as $type => $info) {
+    if (!entity_property_list_extract_type($type)) {
+      $list_type = "list<$type>";
+      if (!isset($data_info[$list_type])) {
+        $data_info[$list_type] = array(
+          'label' => t('list of @type_label items', array('@type_label' => $info['label'])),
+          'wrap' => TRUE,
+          'group' => t('List', array(), array('context' => 'data_types')),
+        );
+        if (isset($info['parent']) && $info['parent'] == 'entity') {
+          $data_info[$list_type]['ui class'] = 'RulesDataUIListEntity';
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_rules_evaluator_info() on behalf of the pseudo rules_core
+ * module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_rules_core_evaluator_info() {
+  return array(
+    // Process strtotime() inputs to timestamps.
+    'date' => array(
+      'class' => 'RulesDateInputEvaluator',
+      'type' => 'date',
+      'weight' => -10,
+    ),
+    // Post-process any input value to absolute URIs.
+    'uri' => array(
+      'class' => 'RulesURIInputEvaluator',
+      'type' => 'uri',
+      'weight' => 50,
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_data_processor_info() on behalf of the pseudo
+ * rules_core module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_rules_core_data_processor_info() {
+  return array(
+    'date_offset' => array(
+      'class' => 'RulesDateOffsetProcessor',
+      'type' => 'date',
+      'weight' => -2,
+    ),
+    'num_offset' => array(
+      'class' => 'RulesNumericOffsetProcessor',
+      'type' => array('integer', 'decimal'),
+      'weight' => -2,
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_condition_info() on behalf of the pseudo rules_core
+ * module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_rules_core_condition_info() {
+  $defaults = array(
+    'group' => t('Components'),
+    'base' => 'rules_element_invoke_component',
+    'named parameter' => TRUE,
+    'access callback' => 'rules_element_invoke_component_access_callback',
+  );
+  $items = array();
+  foreach (rules_get_components(FALSE, 'condition') as $name => $config) {
+    $items['component_' . $name] = $defaults + array(
+      'label' => $config->plugin() . ': ' . drupal_ucfirst($config->label()),
+      'parameter' => $config->parameterInfo(),
+    );
+    $items['component_' . $name]['#config_name'] = $name;
+  }
+  return $items;
+}
+
+/**
+ * Implements hook_rules_action_info() on behalf of the pseudo rules_core
+ * module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_rules_core_action_info() {
+  $defaults = array(
+    'group' => t('Components'),
+    'base' => 'rules_element_invoke_component',
+    'named parameter' => TRUE,
+    'access callback' => 'rules_element_invoke_component_access_callback',
+  );
+  $items = array();
+  foreach (rules_get_components(FALSE, 'action') as $name => $config) {
+    $items['component_' . $name] = $defaults + array(
+      'label' => $config->plugin() . ': ' . drupal_ucfirst($config->label()),
+      'parameter' => $config->parameterInfo(),
+      'provides' => $config->providesVariables(),
+    );
+    $items['component_' . $name]['#config_name'] = $name;
+  }
+  return $items;
+}
+
+/**
+ * Implements RulesPluginUIInterface::operations() for the action.
+ */
+function rules_element_invoke_component_operations(RulesPlugin $element) {
+  $defaults = $element->extender('RulesPluginUI')->operations();
+  $info = $element->info();
+
+  // Add an operation for editing the component.
+  $defaults['#links']['component'] = array(
+    'title' => t('edit component'),
+    'href' => RulesPluginUI::path($info['#config_name']),
+  );
+  return $defaults;
+}
+
+/**
+ * Validate callback to make sure the invoked component exists and is not dirty.
+ *
+ * @see rules_scheduler_action_schedule_validate()
+ */
+function rules_element_invoke_component_validate(RulesPlugin $element) {
+  $info = $element->info();
+  $component = rules_config_load($info['#config_name']);
+  // Check if a component exists.
+  if (!$component) {
+    throw new RulesIntegrityException(t('The component %config does not exist.', array('%config' => $info['#config_name'])), $element);
+  }
+  // Check if a component is marked as dirty.
+  rules_config_update_dirty_flag($component);
+  if (!empty($component->dirty)) {
+    throw new RulesIntegrityException(t('The utilized component %config fails the integrity check.', array('%config' => $info['#config_name'])), $element);
+  }
+}
+
+/**
+ * Implements the features export callback of the RulesPluginFeaturesIntegrationInterface.
+ */
+function rules_element_invoke_component_features_export(&$export, &$pipe, $module_name = '', $element) {
+  // Add the used component to the pipe.
+  $info = $element->info();
+  $pipe['rules_config'][] = $info['#config_name'];
+}
+
+/**
+ * Access callback for the invoke component condition/action.
+ */
+function rules_element_invoke_component_access_callback($type, $name) {
+  // Cut of the leading 'component_' from the action name.
+  $component = rules_config_load(substr($name, 10));
+
+  if (!$component) {
+    // Missing component.
+    return FALSE;
+  }
+  // If access is not exposed for this component, default to component access.
+  if (empty($component->access_exposed)) {
+    return $component->access();
+  }
+  // Apply the permissions.
+  return user_access('bypass rules access') || user_access("use Rules component $component->name");
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/system.eval.inc b/profiles/wcm_base/modules/contrib/rules/modules/system.eval.inc
new file mode 100644
index 0000000000000000000000000000000000000000..d784db80731113bd9d90d3ac7ec211f621e77111
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/system.eval.inc
@@ -0,0 +1,290 @@
+<?php
+
+/**
+ * @file
+ * Contains rules integration for the system module needed during evaluation.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Action: Show a drupal message.
+ */
+function rules_action_drupal_message($message, $status, $repeat) {
+  drupal_set_message(filter_xss_admin($message), $status, $repeat);
+}
+
+/**
+ * Action: Page redirect.
+ *
+ * @see rules_page_build()
+ * @see rules_drupal_goto_alter()
+ */
+function rules_action_drupal_goto($url, $force = FALSE, $destination = FALSE) {
+  // Don't let administrators lock them out from Rules administration pages.
+  if (isset($_GET['q']) && strpos($_GET['q'], 'admin/config/workflow/rules') === 0) {
+    rules_log('Skipped page redirect on Rules administration page.', array(), RulesLog::WARN);
+    return;
+  }
+  // Do not redirect during batch processing.
+  if (($batch = batch_get()) && isset($batch['current_set'])) {
+    rules_log('Skipped page redirect during batch processing.');
+    return;
+  }
+
+  // Keep the current destination parameter if there is one set.
+  if ($destination) {
+    $url .= strpos($url, '?') === FALSE ? '?' : '&';
+    $url .= drupal_http_build_query(drupal_get_destination());
+  }
+  // If force is enabled, remove any destination parameter.
+  if ($force && isset($_GET['destination'])) {
+    unset($_GET['destination']);
+  }
+  // We don't invoke drupal_goto() right now, as this would end the current
+  // page execution unpredictably for modules. So we'll take over drupal_goto()
+  // calls from somewhere else via hook_drupal_goto_alter() and make sure
+  // a drupal_goto() is invoked before the page is output with
+  // rules_page_build().
+  $GLOBALS['_rules_action_drupal_goto_do'] = array($url, $force);
+}
+
+/**
+ * Action: Set breadcrumb.
+ */
+function rules_action_breadcrumb_set(array $titles, array $paths) {
+  $trail = array(l(t('Home'), ''));
+  foreach ($titles as $i => $title) {
+    // Skip empty titles.
+    if ($title = trim($title)) {
+      // Output plaintext instead of a link if there is a title
+      // without a path.
+      $path = trim($paths[$i]);
+      if (!empty($paths[$i]) && $paths[$i] != '<none>') {
+        $trail[] = l($title, trim($paths[$i]));
+      }
+      else {
+        $trail[] = check_plain($title);
+      }
+    }
+  }
+  drupal_set_breadcrumb($trail);
+}
+
+/**
+ * Action Implementation: Send mail.
+ */
+function rules_action_mail($to, $subject, $message, $from = NULL, $langcode, $settings, RulesState $state, RulesPlugin $element) {
+  $to = str_replace(array("\r", "\n"), '', $to);
+  $from = !empty($from) ? str_replace(array("\r", "\n"), '', $from) : NULL;
+  $params = array(
+    'subject' => $subject,
+    'message' => $message,
+    'langcode' => $langcode,
+  );
+  // Set a unique key for this mail.
+  $name = isset($element->root()->name) ? $element->root()->name : 'unnamed';
+  $key = 'rules_action_mail_' . $name . '_' . $element->elementId();
+  $languages = language_list();
+  $language = isset($languages[$langcode]) ? $languages[$langcode] : language_default();
+
+  $message = drupal_mail('rules', $key, $to, $language, $params, $from);
+  if ($message['result']) {
+    watchdog('rules', 'Successfully sent email to %recipient', array('%recipient' => $to));
+  }
+}
+
+/**
+ * Action: Send mail to all users of a specific role group(s).
+ */
+function rules_action_mail_to_users_of_role($roles, $subject, $message, $from = NULL, $settings, RulesState $state, RulesPlugin $element) {
+  $from = !empty($from) ? str_replace(array("\r", "\n"), '', $from) : NULL;
+
+  // All authenticated users, which is everybody.
+  if (in_array(DRUPAL_AUTHENTICATED_RID, $roles)) {
+    $result = db_query('SELECT mail FROM {users} WHERE uid > 0');
+  }
+  else {
+    // Avoid sending emails to members of two or more target role groups.
+    $result = db_query('SELECT DISTINCT u.mail FROM {users} u INNER JOIN {users_roles} r ON u.uid = r.uid WHERE r.rid IN (:rids)', array(':rids' => $roles));
+  }
+
+  // Now, actually send the mails.
+  $params = array(
+    'subject' => $subject,
+    'message' => $message,
+  );
+  // Set a unique key for this mail.
+  $name = isset($element->root()->name) ? $element->root()->name : 'unnamed';
+  $key = 'rules_action_mail_to_users_of_role_' . $name . '_' . $element->elementId();  $languages = language_list();
+
+  $message = array('result' => TRUE);
+  foreach ($result as $row) {
+    $message = drupal_mail('rules', $key, $row->mail, language_default(), $params, $from);
+    // If $message['result'] is FALSE, then it's likely that email sending is
+    // failing at the moment, and we should just abort sending any more. If
+    // however, $message['result'] is NULL, then it's likely that a module has
+    // aborted sending this particular email to this particular user, and we
+    // should just keep on sending emails to the other users.
+    // For more information on the result value, see drupal_mail().
+    if ($message['result'] === FALSE) {
+      break;
+    }
+  }
+  if ($message['result'] !== FALSE) {
+    $role_names = array_intersect_key(user_roles(TRUE), array_flip($roles));
+    watchdog('rules', 'Successfully sent email to the role(s) %roles.', array('%roles' => implode(', ', $role_names)));
+  }
+}
+
+/**
+ * Implements hook_mail().
+ *
+ * Sets the message subject and body as configured.
+ */
+function rules_mail($key, &$message, $params) {
+
+  $message['subject'] .= str_replace(array("\r", "\n"), '', $params['subject']);
+  $message['body'][] = $params['message'];
+}
+
+/**
+ * Action: Block an IP address.
+ */
+function rules_action_block_ip($ip_address = NULL) {
+  if (empty($ip_address)) {
+    $ip_address = ip_address();
+  }
+  db_insert('blocked_ips')->fields(array('ip' => $ip_address))->execute();
+  watchdog('rules', 'Banned IP address %ip', array('%ip' => $ip_address));
+}
+
+/**
+ * A class implementing a rules input evaluator processing tokens.
+ */
+class RulesTokenEvaluator extends RulesDataInputEvaluator {
+
+  /**
+   * Overrides RulesDataInputEvaluator::prepare().
+   */
+  public function prepare($text, $var_info) {
+    $text = is_array($text) ? implode('', $text) : $text;
+    // Skip this evaluator if there are no tokens.
+    $this->setting = token_scan($text) ? TRUE : NULL;
+  }
+
+  /**
+   * Evaluate tokens.
+   *
+   * We replace the tokens on our own as we cannot use token_replace(), because
+   * token usually assumes that $data['node'] is a of type node, which doesn't
+   * hold in general in our case.
+   * So we properly map variable names to variable data types and then run the
+   * replacement ourself.
+   */
+  public function evaluate($text, $options, RulesState $state) {
+    $var_info = $state->varInfo();
+    $options += array('sanitize' => FALSE);
+
+    $replacements = array();
+    $data = array();
+    // We also support replacing tokens in a list of textual values.
+    $whole_text = is_array($text) ? implode('', $text) : $text;
+    foreach (token_scan($whole_text) as $var_name => $tokens) {
+      $var_name = str_replace('-', '_', $var_name);
+      if (isset($var_info[$var_name]) && ($token_type = _rules_system_token_map_type($var_info[$var_name]['type']))) {
+        // We have to key $data with the type token uses for the variable.
+        $data = rules_unwrap_data(array($token_type => $state->get($var_name)), array($token_type => $var_info[$var_name]));
+        $replacements += token_generate($token_type, $tokens, $data, $options);
+      }
+      else {
+        $replacements += token_generate($var_name, $tokens, array(), $options);
+      }
+      // Remove tokens if no replacement value is found. As token_replace() does
+      // if 'clear' is set.
+      $replacements += array_fill_keys($tokens, '');
+    }
+
+    // Optionally clean the list of replacement values.
+    if (!empty($options['callback']) && function_exists($options['callback'])) {
+      $function = $options['callback'];
+      $function($replacements, $data, $options);
+    }
+
+    // Actually apply the replacements.
+    $tokens = array_keys($replacements);
+    $values = array_values($replacements);
+    if (is_array($text)) {
+      foreach ($text as $i => $text_item) {
+        $text[$i] = str_replace($tokens, $values, $text_item);
+      }
+      return $text;
+    }
+    return str_replace($tokens, $values, $text);
+  }
+
+  /**
+   * Create documentation about the available replacement patterns.
+   *
+   * @param array $var_info
+   *   Array with the available variables.
+   *
+   * @return array
+   *   Renderable array with the replacement pattern documentation.
+   */
+  public static function help($var_info) {
+    $render = array(
+      '#type' => 'fieldset',
+      '#title' => t('Replacement patterns'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#description' => t('Note that token replacements containing chained objects – such as [node:author:uid] – are not listed here, but are still available. The <em>data selection</em> input mode may help you find more complex replacement patterns. See <a href="@url">the online documentation</a> for more information about complex replacement patterns.',
+        array('@url' => rules_external_help('chained-tokens'))),
+    );
+    $token_info = token_info();
+    foreach ($var_info as $name => $info) {
+      $token_types[$name] = _rules_system_token_map_type($info['type']);
+    }
+
+    foreach ($token_types as $name => $token_type) {
+      if (isset($token_info['types'][$token_type])) {
+        $render[$name] = array(
+          '#theme' => 'table',
+          '#header' => array(t('Token'), t('Label'), t('Description')),
+          '#prefix' => '<h3>' . t('Replacement patterns for %label', array('%label' => $var_info[$name]['label'])) . '</h3>',
+        );
+        foreach ($token_info['tokens'][$token_type] as $token => $info) {
+          $token = '[' . str_replace('_', '-', $name) . ':' . $token . ']';
+          $render[$name]['#rows'][$token] = array(
+            check_plain($token),
+            check_plain($info['name']),
+            check_plain($info['description']),
+          );
+        }
+      }
+    }
+    return $render;
+  }
+
+}
+
+/**
+ * Looks for a token type mapping. Defaults to passing through the type.
+ */
+function _rules_system_token_map_type($type) {
+  $entity_info = entity_get_info();
+  if (isset($entity_info[$type]['token type'])) {
+    return $entity_info[$type]['token type'];
+  }
+  $cache = rules_get_cache();
+  if (isset($cache['data_info'][$type]['token type'])) {
+    return $cache['data_info'][$type]['token type'];
+  }
+  return $type;
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/system.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/system.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..3f41dd4e39ae5c22e4ad1e289c634e694349a085
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/system.rules.inc
@@ -0,0 +1,307 @@
+<?php
+
+/**
+ * @file
+ * Rules integration for the system module.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_file_info() on behalf of the system module.
+ */
+function rules_system_file_info() {
+  return array('modules/system.eval');
+}
+
+/**
+ * Implements hook_rules_event_info() on behalf of the system module.
+ */
+function rules_system_event_info() {
+  return array(
+    'init' => array(
+      'label' => t('Drupal is initializing'),
+      'group' => t('System'),
+      'help' => t("Be aware that some actions might initialize the theme system. After that, it's impossible for any module to change the used theme."),
+      'access callback' => 'rules_system_integration_access',
+    ),
+    'cron' => array(
+      'label' => t('Cron maintenance tasks are performed'),
+      'group' => t('System'),
+      'access callback' => 'rules_system_integration_access',
+    ),
+    'watchdog' => array(
+      'label' => t('System log entry is created'),
+      'variables' => array(
+        'log_entry' => array(
+          'type' => 'log_entry',
+          'label' => t('Log entry'),
+        ),
+      ),
+      'group' => t('System'),
+      'access callback' => 'rules_system_integration_access',
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_data_info() on behalf of the system module.
+ *
+ * @see rules_core_modules()
+ */
+function rules_system_data_info() {
+  return array(
+    'log_entry' => array(
+      'label' => t('watchdog log entry'),
+      'wrap' => TRUE,
+      'property info' => _rules_system_watchdog_log_entry_info(),
+    ),
+  );
+}
+
+/**
+ * Defines property info for watchdog log entries.
+ *
+ * Used by the log entry data type to provide a useful metadata wrapper.
+ */
+function _rules_system_watchdog_log_entry_info() {
+  return array(
+    'type' => array(
+      'type' => 'text',
+      'label' => t('The category to which this message belongs'),
+    ),
+    'message' => array(
+      'type' => 'text',
+      'label' => ('Log message'),
+      'getter callback' => 'rules_system_log_get_message',
+      'sanitized' => TRUE,
+    ),
+    'severity' => array(
+      'type' => 'integer',
+      'label' => t('Severity'),
+      'options list' => 'watchdog_severity_levels',
+    ),
+    'request_uri' => array(
+      'type' => 'uri',
+      'label' => t('Request uri'),
+    ),
+    'link' => array(
+      'type' => 'text',
+      'label' => t('An associated, HTML formatted link'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_action_info() on behalf of the system module.
+ */
+function rules_system_action_info() {
+  return array(
+    'drupal_message' => array(
+      'label' => t('Show a message on the site'),
+      'group' => t('System'),
+      'parameter' => array(
+        'message' => array(
+          'type' => 'text',
+          'label' => t('Message'),
+          'sanitize' => TRUE,
+          'translatable' => TRUE,
+        ),
+        'type' => array(
+          'type' => 'token',
+          'label' => t('Message type'),
+          'options list' => 'rules_action_drupal_message_types',
+          'default value' => 'status',
+          'optional' => TRUE,
+        ),
+        'repeat' => array(
+          'type' => 'boolean',
+          'label' => t('Repeat message'),
+          'description' => t("If disabled and the message has been already shown, then the message won't be repeated."),
+          'default value' => TRUE,
+          'optional' => TRUE,
+          'restriction' => 'input',
+        ),
+      ),
+      'base' => 'rules_action_drupal_message',
+      'access callback' => 'rules_system_integration_access',
+    ),
+    'redirect' => array(
+      'label' => t('Page redirect'),
+      'group' => t('System'),
+      'parameter' => array(
+        'url' => array(
+          'type' => 'uri',
+          'label' => t('URL'),
+          'description' => t('A Drupal path, path alias, or external URL to redirect to. Enter (optional) queries after "?" and (optional) anchor after "#".'),
+        ),
+        'force' => array(
+          'type' => 'boolean',
+          'label' => t('Force redirect'),
+          'restriction' => 'input',
+          'description' => t("Force the redirect even if another destination parameter is present. Per default Drupal would redirect to the path given as destination parameter, in case it is set. Usually the destination parameter is set by appending it to the URL, e.g. !example_url", array('!example_url' => 'http://example.com/user/login?destination=node/2')),
+          'optional' => TRUE,
+          'default value' => TRUE,
+        ),
+        'destination' => array(
+          'type' => 'boolean',
+          'label' => t('Append destination parameter'),
+          'restriction' => 'input',
+          'description' => t('Whether to append a destination parameter to the URL, so another redirect issued later on would lead back to the origin page.'),
+          'optional' => TRUE,
+          'default value' => FALSE,
+        ),
+      ),
+      'base' => 'rules_action_drupal_goto',
+      'access callback' => 'rules_system_integration_access',
+    ),
+    'breadcrumb_set' => array(
+      'label' => t('Set breadcrumb'),
+      'group' => t('System'),
+      'parameter' => array(
+        'titles' => array(
+          'type' => 'list<text>',
+          'label' => t('Titles'),
+          'description' => t('A list of titles for the breadcrumb links.'),
+          'translatable' => TRUE,
+        ),
+        'paths' => array(
+          'type' => 'list<text>',
+          'label' => t('Paths'),
+          'description' => t('A list of Drupal paths for the breadcrumb links, matching the order of the titles.'),
+        ),
+      ),
+      'base' => 'rules_action_breadcrumb_set',
+      'access callback' => 'rules_system_integration_access',
+    ),
+    'mail' => array(
+      'label' => t('Send mail'),
+      'group' => t('System'),
+      'parameter' => array(
+        'to' => array(
+          'type' => 'text',
+          'label' => t('To'),
+          'description' => t('The e-mail address or addresses where the message will be sent to. The formatting of this string must comply with RFC 2822.'),
+        ),
+        'subject' => array(
+          'type' => 'text',
+          'label' => t('Subject'),
+          'description' => t("The mail's subject."),
+          'translatable' => TRUE,
+        ),
+        'message' => array(
+          'type' => 'text',
+          'label' => t('Message'),
+          'description' => t("The mail's message body."),
+          'translatable' => TRUE,
+        ),
+        'from' => array(
+          'type' => 'text',
+          'label' => t('From'),
+          'description' => t("The mail's from address. Leave it empty to use the site-wide configured address."),
+          'optional' => TRUE,
+        ),
+        'language' => array(
+          'type' => 'token',
+          'label' => t('Language'),
+          'description' => t('If specified, the language used for getting the mail message and subject.'),
+          'options list' => 'entity_metadata_language_list',
+          'optional' => TRUE,
+          'default value' => LANGUAGE_NONE,
+          'default mode' => 'selector',
+        ),
+      ),
+      'base' => 'rules_action_mail',
+      'access callback' => 'rules_system_integration_access',
+    ),
+    'mail_to_users_of_role' => array(
+      'label' => t('Send mail to all users of a role'),
+      'group' => t('System'),
+      'parameter' => array(
+        'roles' => array(
+          'type' => 'list<integer>',
+          'label' => t('Roles'),
+          'options list' => 'entity_metadata_user_roles',
+          'description' => t('Select the roles whose users should receive the mail.'),
+        ),
+        'subject' => array(
+          'type' => 'text',
+          'label' => t('Subject'),
+          'description' => t("The mail's subject."),
+        ),
+        'message' => array(
+          'type' => 'text',
+          'label' => t('Message'),
+          'description' => t("The mail's message body."),
+        ),
+        'from' => array(
+          'type' => 'text',
+          'label' => t('From'),
+          'description' => t("The mail's from address. Leave it empty to use the site-wide configured address."),
+          'optional' => TRUE,
+        ),
+      ),
+      'base' => 'rules_action_mail_to_users_of_role',
+      'access callback' => 'rules_system_integration_access',
+    ),
+    'block_ip' => array(
+      'label' => t('Block IP address'),
+      'group' => t('System'),
+      'parameter' => array(
+        'ip_address' => array(
+          'type' => 'ip_address',
+          'label' => t('IP address'),
+          'description' => t('If not provided, the IP address of the current user will be used.'),
+          'optional' => TRUE,
+          'default value' => NULL,
+        ),
+      ),
+      'base' => 'rules_action_block_ip',
+      'access callback' => 'rules_system_integration_access',
+    ),
+  );
+}
+
+/**
+ * Help callback for the "Send mail to users of a role" action.
+ */
+function rules_action_mail_to_users_of_role_help() {
+  return t('WARNING: This may cause problems if there are too many users of these roles on your site, as your server may not be able to handle all the mail requests all at once.');
+}
+
+/**
+ * System integration access callback.
+ */
+function rules_system_integration_access($type, $name) {
+  return user_access('administer site configuration');
+}
+
+/**
+ * Options list callback defining drupal_message types.
+ */
+function rules_action_drupal_message_types() {
+  return array(
+    'status' => t('Status'),
+    'warning' => t('Warning'),
+    'error' => t('Error'),
+  );
+}
+
+/**
+ * Implements hook_rules_evaluator_info() on behalf of the system module.
+ */
+function rules_system_evaluator_info() {
+  return array(
+    'token' => array(
+      'class' => 'RulesTokenEvaluator',
+      'type' => array('text', 'uri', 'list<text>', 'list<uri>'),
+      'weight' => 0,
+    ),
+  );
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/taxonomy.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/taxonomy.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..69c3c3eca991e972053e08cc4eb0488583a9b244
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/taxonomy.rules.inc
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * @file
+ * Rules integration for the taxonomy_term module.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_event_info().
+ */
+function rules_taxonomy_event_info() {
+  $defaults_term = array(
+    'group' => t('Taxonomy'),
+    'access callback' => 'rules_taxonomy_term_integration_access',
+    'module' => 'taxonomy',
+    'class' => 'RulesTaxonomyEventHandler',
+  );
+  $defaults_vocab = array(
+    'group' => t('Taxonomy'),
+    'access callback' => 'rules_taxonomy_vocabulary_integration_access',
+    'module' => 'taxonomy',
+  );
+  return array(
+    'taxonomy_term_insert' => $defaults_term + array(
+      'label' => t('After saving a new term'),
+      'variables' => array(
+        'term' => array('type' => 'taxonomy_term', 'label' => t('created term')),
+      ),
+    ),
+    'taxonomy_term_update' => $defaults_term + array(
+      'label' => t('After updating an existing term'),
+      'variables' => array(
+        'term' => array('type' => 'taxonomy_term', 'label' => t('updated term')),
+        'term_unchanged' => array(
+          'type' => 'taxonomy_term',
+          'label' => t('unchanged term'),
+          'handler' => 'rules_events_entity_unchanged',
+        ),
+      ),
+    ),
+    'taxonomy_term_presave' => $defaults_term + array(
+      'label' => t('Before saving a taxonomy term'),
+      'variables' => array(
+        'term' => array(
+          'type' => 'taxonomy_term',
+          'label' => t('saved term'),
+          'skip save' => TRUE,
+        ),
+        'term_unchanged' => array(
+          'type' => 'taxonomy_term',
+          'label' => t('unchanged term'),
+          'handler' => 'rules_events_entity_unchanged',
+        ),
+      ),
+    ),
+    'taxonomy_term_delete' => $defaults_term + array(
+      'label' => t('After deleting a term'),
+      'variables' => array(
+        'term' => array('type' => 'taxonomy_term', 'label' => t('deleted term')),
+      ),
+    ),
+    'taxonomy_vocabulary_insert' => $defaults_vocab + array(
+      'label' => t('After saving a new vocabulary'),
+      'variables' => array(
+        'vocabulary' => array('type' => 'taxonomy_vocabulary', 'label' => t('created vocabulary')),
+      ),
+    ),
+    'taxonomy_vocabulary_update' => $defaults_vocab + array(
+      'label' => t('After updating an existing vocabulary'),
+      'variables' => array(
+        'vocabulary' => array(
+          'type' => 'taxonomy_vocabulary',
+          'label' => t('updated vocabulary'),
+        ),
+        'vocabulary_unchanged' => array(
+          'type' => 'taxonomy_vocabulary',
+          'label' => t('unchanged vocabulary'),
+          'handler' => 'rules_events_entity_unchanged',
+        ),
+      ),
+    ),
+    'taxonomy_vocabulary_presave' => $defaults_vocab + array(
+      'label' => t('Before saving a vocabulary'),
+      'variables' => array(
+        'vocabulary' => array(
+          'type' => 'taxonomy_vocabulary',
+          'label' => t('saved vocabulary'),
+          'skip save' => TRUE,
+        ),
+        'vocabulary_unchanged' => array(
+          'type' => 'taxonomy_vocabulary',
+          'label' => t('unchanged vocabulary'),
+          'handler' => 'rules_events_entity_unchanged',
+        ),
+      ),
+    ),
+    'taxonomy_vocabulary_delete' => $defaults_vocab + array(
+      'label' => t('After deleting a vocabulary'),
+      'variables' => array(
+        'vocabulary' => array(
+          'type' => 'taxonomy_vocabulary',
+          'label' => t('deleted vocabulary'),
+        ),
+      ),
+    ),
+  );
+}
+
+/**
+ * Taxonomy term integration access callback.
+ */
+function rules_taxonomy_term_integration_access($type, $name) {
+  if ($type == 'event' || $type == 'condition') {
+    return entity_access('view', 'taxonomy_term');
+  }
+}
+
+/**
+ * Taxonomy vocabulary integration access callback.
+ */
+function rules_taxonomy_vocabulary_integration_access($type, $name) {
+  if ($type == 'event' || $type == 'condition') {
+    return entity_access('view', 'taxonomy_vocabulary');
+  }
+}
+
+/**
+ * Event handler support taxonomy bundle event settings.
+ */
+class RulesTaxonomyEventHandler extends RulesEventHandlerEntityBundle {
+
+  /**
+   * Returns the label to use for the bundle property.
+   *
+   * @return string
+   *   The label to use for the bundle property.
+   */
+  protected function getBundlePropertyLabel() {
+    return t('vocabulary');
+  }
+
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/user.eval.inc b/profiles/wcm_base/modules/contrib/rules/modules/user.eval.inc
new file mode 100644
index 0000000000000000000000000000000000000000..395c454c03a2fec21200a14b409d12d052d94de1
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/user.eval.inc
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * @file
+ * Contains rules integration for the user module needed during evaluation.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Condition user: condition to check whether user has particular roles.
+ */
+function rules_condition_user_has_role($account, $roles, $operation = 'AND') {
+  switch ($operation) {
+    case 'OR':
+      foreach ($roles as $rid) {
+        if (isset($account->roles[$rid])) {
+          return TRUE;
+        }
+      }
+      return FALSE;
+
+    case 'AND':
+      foreach ($roles as $rid) {
+        if (!isset($account->roles[$rid])) {
+          return FALSE;
+        }
+      }
+      return TRUE;
+  }
+}
+
+/**
+ * Condition: User is blocked.
+ */
+function rules_condition_user_is_blocked($account) {
+  return $account->status == 0;
+}
+
+/**
+ * Action: Adds roles to a particular user.
+ */
+function rules_action_user_add_role($account, $roles) {
+  if ($account->uid || !empty($account->is_new)) {
+    // Get role list (minus the anonymous).
+    $role_list = user_roles(TRUE);
+
+    foreach ($roles as $rid) {
+      $account->roles[$rid] = $role_list[$rid];
+    }
+    if (!empty($account->is_new) && $account->uid) {
+      // user_save() inserts roles after invoking hook_user_insert() anyway, so
+      // we skip saving to avoid errors due saving them twice.
+      return FALSE;
+    }
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Action: Remove roles from a given user.
+ */
+function rules_action_user_remove_role($account, $roles) {
+  if ($account->uid || !empty($account->is_new)) {
+    foreach ($roles as $rid) {
+      // If the user has this role, remove it.
+      if (isset($account->roles[$rid])) {
+        unset($account->roles[$rid]);
+      }
+    }
+    if (!empty($account->is_new) && $account->uid) {
+      // user_save() inserts roles after invoking hook_user_insert() anyway, so
+      // we skip saving to avoid errors due saving them twice.
+      return FALSE;
+    }
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Action: Block a user.
+ */
+function rules_action_user_block($account) {
+  $account->status = 0;
+  drupal_session_destroy_uid($account->uid);
+}
+
+/**
+ * Action: Unblock a user.
+ */
+function rules_action_user_unblock($account) {
+  $account->status = 1;
+}
+
+/**
+ * Action: Send a user account e-mail.
+ */
+function rules_action_user_send_account_email($account, $email_type) {
+  // If we received an authenticated user account...
+  if (!empty($account->uid)) {
+    module_load_include('inc', 'rules', 'modules/user.rules');
+    $types = rules_user_account_email_options_list();
+
+    // Attempt to send the account e-mail.
+    // This code is adapted from _user_mail_notify().
+    $params = array('account' => $account);
+    $language = user_preferred_language($account);
+    $mail = drupal_mail('user', $email_type, $account->mail, $language, $params);
+    if ($email_type == 'register_pending_approval') {
+      // If a user registered requiring admin approval, notify the admin, too.
+      // We use the site default language for this.
+      drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params);
+    }
+
+    $result = empty($mail) ? NULL : $mail['result'];
+
+    // Log the success or failure.
+    if ($result) {
+      watchdog('rules', '%type e-mail sent to %recipient.', array('%type' => $types[$email_type], '%recipient' => $account->mail));
+    }
+    else {
+      watchdog('rules', 'Failed to send %type e-mail to %recipient.', array('%type' => $types[$email_type], '%recipient' => $account->mail));
+    }
+  }
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/modules/user.rules.inc b/profiles/wcm_base/modules/contrib/rules/modules/user.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..9feb1ce6fb17513ea8f6e9dc75e9598e3a8d6329
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/modules/user.rules.inc
@@ -0,0 +1,277 @@
+<?php
+
+/**
+ * @file
+ * Rules integration for the user module.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_file_info() on behalf of the user module.
+ */
+function rules_user_file_info() {
+  return array('modules/user.eval');
+}
+
+/**
+ * Implements hook_rules_event_info().
+ */
+function rules_user_event_info() {
+  return array(
+    'user_insert' => array(
+      'label' => t('After saving a new user account'),
+      'group' => t('User'),
+      'variables' => array(
+        'account' => array('type' => 'user', 'label' => t('registered user')),
+      ),
+      'access callback' => 'rules_user_integration_access',
+    ),
+    'user_update' => array(
+      'label' => t('After updating an existing user account'),
+      'group' => t('User'),
+      'variables' => array(
+        'account' => array('type' => 'user', 'label' => t('updated user')),
+        'account_unchanged' => array('type' => 'user', 'label' => t('unchanged user'), 'handler' => 'rules_events_entity_unchanged'),
+      ),
+      'access callback' => 'rules_user_integration_access',
+    ),
+    'user_presave' => array(
+      'label' => t('Before saving a user account'),
+      'group' => t('User'),
+      'variables' => array(
+        'account' => array('type' => 'user', 'label' => t('saved user'), 'skip save' => TRUE),
+        'account_unchanged' => array('type' => 'user', 'label' => t('unchanged user'), 'handler' => 'rules_events_entity_unchanged'),
+      ),
+      'access callback' => 'rules_user_integration_access',
+    ),
+    'user_view' => array(
+      'label' => t('User account page is viewed'),
+      'group' => t('User'),
+      'variables' => array(
+        'account' => array('type' => 'user', 'label' => t('viewed user')),
+        'view_mode' => array(
+          'type' => 'text',
+          'label' => t('view mode'),
+          'options list' => 'rules_get_entity_view_modes',
+          // Add the entity-type for the options list callback.
+          'options list entity type' => 'user',
+        ),
+      ),
+      'access callback' => 'rules_user_integration_access',
+      'help' => t("Note that if drupal's page cache is enabled, this event won't be generated for pages served from cache."),
+    ),
+    'user_delete' => array(
+      'label' => t('After a user account has been deleted'),
+      'group' => t('User'),
+      'variables' => array(
+        'account' => array('type' => 'user', 'label' => t('deleted user')),
+      ),
+      'access callback' => 'rules_user_integration_access',
+    ),
+    'user_login' => array(
+      'label' => t('User has logged in'),
+      'group' => t('User'),
+      'variables' => array(
+        'account' => array('type' => 'user', 'label' => t('logged in user')),
+      ),
+      'access callback' => 'rules_user_integration_access',
+    ),
+    'user_logout' => array(
+      'label' => t('User has logged out'),
+      'group' => t('User'),
+      'variables' => array(
+        'account' => array('type' => 'user', 'label' => t('logged out user')),
+      ),
+      'access callback' => 'rules_user_integration_access',
+    ),
+  );
+}
+
+/**
+ * Options list for user cancel methods.
+ *
+ * @todo Use for providing a user_cancel action.
+ */
+function rules_user_cancel_methods() {
+  module_load_include('inc', 'user', 'user.pages');
+  foreach (user_cancel_methods() as $method => $form) {
+    $methods[$method] = $form['#title'];
+  }
+  return $methods;
+}
+
+/**
+ * User integration access callback.
+ */
+function rules_user_integration_access($type, $name) {
+  if ($type == 'event' || $type == 'condition') {
+    return entity_access('view', 'user');
+  }
+  // Else return admin access.
+  return user_access('administer users');
+}
+
+/**
+ * Implements hook_rules_condition_info() on behalf of the user module.
+ */
+function rules_user_condition_info() {
+  return array(
+    'user_has_role' => array(
+      'label' => t('User has role(s)'),
+      'parameter' => array(
+        'account' => array('type' => 'user', 'label' => t('User')),
+        'roles' => array(
+          'type' => 'list<integer>',
+          'label' => t('Roles'),
+          'options list' => 'rules_user_roles_options_list',
+        ),
+        'operation' => array(
+          'type' => 'text',
+          'label' => t('Match roles'),
+          'options list' => 'rules_user_condition_operations',
+          'restriction' => 'input',
+          'optional' => TRUE,
+          'default value' => 'AND',
+          'description' => t('If matching against all selected roles, the user must have <em>all</em> the roles selected.'),
+        ),
+      ),
+      'group' => t('User'),
+      'access callback' => 'rules_user_integration_access',
+      'base' => 'rules_condition_user_has_role',
+    ),
+    'user_is_blocked' => array(
+      'label' => t('User is blocked'),
+      'parameter' => array(
+        'account' => array('type' => 'user', 'label' => t('User')),
+      ),
+      'group' => t('User'),
+      'access callback' => 'rules_user_integration_access',
+      'base' => 'rules_condition_user_is_blocked',
+    ),
+  );
+}
+
+/**
+ * User has role condition help callback.
+ */
+function rules_condition_user_has_role_help() {
+  return t('Whether the user has the selected role(s).');
+}
+
+/**
+ * Options list callback for the operation parameter of condition user has role.
+ */
+function rules_user_condition_operations() {
+  return array(
+    'AND' => t('all'),
+    'OR' => t('any'),
+  );
+}
+
+/**
+ * Implements hook_rules_action_info() on behalf of the user module.
+ */
+function rules_user_action_info() {
+  $defaults = array(
+    'parameter' => array(
+      'account' => array(
+        'type' => 'user',
+        'label' => t('User'),
+        'description' => t('The user whose roles should be changed.'),
+        'save' => TRUE,
+      ),
+      'roles' => array(
+        'type' => 'list<integer>',
+        'label' => t('Roles'),
+        'options list' => 'rules_user_roles_options_list',
+      ),
+    ),
+    'group' => t('User'),
+    'access callback' => 'rules_user_role_change_access',
+  );
+  $items['user_add_role'] = $defaults + array(
+    'label' => t('Add user role'),
+    'base' => 'rules_action_user_add_role',
+  );
+  $items['user_remove_role'] = $defaults + array(
+    'label' => t('Remove user role'),
+    'base' => 'rules_action_user_remove_role',
+  );
+  $defaults = array(
+    'parameter' => array(
+      'account' => array(
+        'type' => 'user',
+        'label' => t('User'),
+        'save' => TRUE,
+      ),
+    ),
+    'group' => t('User'),
+    'access callback' => 'rules_user_integration_access',
+  );
+  $items['user_block'] = $defaults + array(
+    'label' => t('Block a user'),
+    'base' => 'rules_action_user_block',
+  );
+  $items['user_unblock'] = $defaults + array(
+    'label' => t('Unblock a user'),
+    'base' => 'rules_action_user_unblock',
+  );
+  $items['user_send_account_email'] = array(
+    'label' => t('Send account e-mail'),
+    'parameter' => array(
+      'account' => array(
+        'type' => 'user',
+        'label' => t('Account'),
+      ),
+      'email_type' => array(
+        'type' => 'text',
+        'label' => t('E-mail type'),
+        'description' => t("Select the e-mail based on your site's account settings to send to the user."),
+        'options list' => 'rules_user_account_email_options_list',
+      ),
+    ),
+    'group' => t('User'),
+    'base' => 'rules_action_user_send_account_email',
+    'access callback' => 'rules_user_integration_access',
+  );
+  return $items;
+}
+
+/**
+ * User integration role actions access callback.
+ */
+function rules_user_role_change_access() {
+  return entity_metadata_user_roles() && user_access('administer permissions');
+}
+
+/**
+ * Options list callback for user roles.
+ */
+function rules_user_roles_options_list($element) {
+  return entity_metadata_user_roles('roles', array(), $element instanceof RulesConditionInterface ? 'view' : 'edit');
+}
+
+/**
+ * Options list callback for user account e-mail types.
+ *
+ * @see _user_mail_notify()
+ */
+function rules_user_account_email_options_list() {
+  return array(
+    'register_admin_created' => t('Welcome (new user created by administrator)'),
+    'register_no_approval_required' => t('Welcome (no approval required)'),
+    'register_pending_approval' => t('Welcome (awaiting approval)'),
+    'password_reset' => t('Password recovery'),
+    'status_activated' => t('Account activation'),
+    'status_blocked' => t('Account blocked'),
+    'cancel_confirm' => t('Account cancellation confirmation'),
+    'status_canceled' => t('Account canceled'),
+  );
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/rules.api.php b/profiles/wcm_base/modules/contrib/rules/rules.api.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8af7bd4a6c2d291d402ef6d9672976520257c41
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules.api.php
@@ -0,0 +1,1103 @@
+<?php
+
+/**
+ * @file
+ * Documentation for hooks provided by the Rules API.
+ *
+ * This file contains no working PHP code; it exists to provide additional
+ * documentation for doxygen as well as to document hooks in the standard
+ * Drupal manner.
+ */
+
+/**
+ * @defgroup rules Rules module integrations.
+ *
+ * Module integrations with the rules module.
+ *
+ * The Rules developer documentation describes how modules can integrate with
+ * rules: https://www.drupal.org/node/298486.
+ */
+
+/**
+ * @defgroup rules_hooks Rules' hooks
+ * @{
+ * Hooks that can be implemented by other modules in order to extend rules.
+ */
+
+/**
+ * Define rules compatible actions.
+ *
+ * This hook is required in order to add a new rules action. It should be
+ * placed into the file MODULENAME.rules.inc, which gets automatically included
+ * when the hook is invoked.
+ *
+ * However, as an alternative to implementing this hook, class based plugin
+ * handlers may be provided by implementing RulesActionHandlerInterface. See
+ * the interface for details.
+ *
+ * @return array
+ *   An array of information about the module's provided rules actions.
+ *   The array contains a sub-array for each action, with the action name as
+ *   the key. Actions names may only contain lowercase alpha-numeric characters
+ *   and underscores and should be prefixed with the providing module name.
+ *   Possible attributes for each sub-array are:
+ *   - label: The label of the action. Start capitalized. Required.
+ *   - group: A group for this element, used for grouping the actions in the
+ *     interface. Should start with a capital letter and be translated.
+ *     Required.
+ *   - parameter: (optional) An array describing all parameter of the action
+ *     with the parameter's name as key. Each parameter has to be
+ *     described by a sub-array with possible attributes as described
+ *     afterwards, whereas the name of a parameter needs to be a lowercase,
+ *     valid PHP variable name.
+ *   - provides: (optional) An array describing the variables the action
+ *     provides to the evaluation state with the variable name as key. Each
+ *     variable has to be described by a sub-array with possible attributes as
+ *     described afterwards, whereas the name of a parameter needs to be a
+ *     lowercase, valid PHP variable name.
+ *   - 'named parameter': (optional) If set to TRUE, the arguments will be
+ *     passed as a single array with the parameter names as keys. This emulates
+ *     named parameters in PHP and is in particular useful if the number of
+ *     parameters can vary. Defaults to FALSE.
+ *   - base: (optional) The base for action implementation callbacks to use
+ *     instead of the action's name. Defaults to the action name.
+ *   - callbacks: (optional) An array which allows to set specific function
+ *     callbacks for the action. The default for each callback is the actions
+ *     base appended by '_' and the callback name.
+ *   - 'access callback': (optional) A callback which has to return whether the
+ *     currently logged in user is allowed to configure this action. See
+ *     rules_node_integration_access() for an example callback.
+ *   Each 'parameter' array may contain the following properties:
+ *   - label: The label of the parameter. Start capitalized. Required.
+ *   - type: The rules data type of the parameter, which is to be passed to the
+ *     action. All types declared in hook_rules_data_info() may be specified, as
+ *     well as an array of possible types. Also lists and lists of a given type
+ *     can be specified by using the notating list<integer> as introduced by
+ *     the entity metadata module, see hook_entity_property_info(). The special
+ *     keyword '*' can be used when all types should be allowed. Required.
+ *   - bundles: (optional) An array of bundle names. When the specified type is
+ *     set to a single entity type, this may be used to restrict the allowed
+ *     bundles.
+ *   - description: (optional) If necessary, a further description of the
+ *     parameter.
+ *   - options list: (optional) A callback that returns an array of possible
+ *     values for this parameter. The callback has to return an array as used
+ *     by hook_options_list(). For an example implementation see
+ *     rules_data_action_type_options().
+ *   - save: (optional) If this is set to TRUE, the parameter will be saved by
+ *     rules when the rules evaluation ends. This is only supported for savable
+ *     data types. If the action returns FALSE, saving is skipped.
+ *   - optional: (optional) May be set to TRUE, when the parameter isn't
+ *     required.
+ *   - 'default value': (optional) The value to pass to the action, in case the
+ *     parameter is optional and there is no specified value.
+ *   - 'allow null': (optional) Usually Rules will not pass any NULL values as
+ *     argument, but abort the evaluation if a NULL value is present. If set to
+ *     TRUE, Rules will not abort and pass the NULL value through. Defaults to
+ *     FALSE.
+ *   - restriction: (optional) Restrict how the argument for this parameter may
+ *     be provided. Supported values are 'selector' and 'input'.
+ *   - default mode: (optional) Customize the default mode for providing the
+ *     argument value for a parameter. Supported values are 'selector' and
+ *     'input'. The default depends on the required data type.
+ *   - sanitize: (optional) Allows parameters of type 'text' to demand an
+ *     already sanitized argument. If enabled, any user specified value won't be
+ *     sanitized itself, but replacements applied by input evaluators are as
+ *     well as values retrieved from selected data sources.
+ *   - translatable: (optional) If set to TRUE, the provided argument value
+ *     of the parameter is translatable via i18n String translation. This is
+ *     applicable for textual parameters only, i.e. parameters of type 'text',
+ *     'token', 'list<text>' and 'list<token>'. Defaults to FALSE.
+ *   - ui class: (optional) Allows overriding the UI class, which is used to
+ *     generate the configuration UI of a parameter. Defaults to the UI class of
+ *     the specified data type.
+ *   - cleaning callback: (optional) A callback that input evaluators may use
+ *     to clean inserted replacements; e.g. this is used by the token evaluator.
+ *   - wrapped: (optional) Set this to TRUE in case the data should be passed
+ *     wrapped. This only applies to wrapped data types, e.g. entities.
+ *   Each 'provides' array may contain the following properties:
+ *   - label: The label of the variable. Start capitalized. Required.
+ *   - type: The rules data type of the variable. All types declared in
+ *     hook_rules_data_info() may be specified. Types may be parametrized e.g.
+ *     the types node<page> or list<integer> are valid.
+ *   - save: (optional) If this is set to TRUE, the provided variable is saved
+ *     by rules when the rules evaluation ends. Only possible for savable data
+ *     types. Defaults to FALSE.
+ *   The module has to provide an implementation for each action, being a
+ *   function named as specified in the 'base' key or for the execution callback.
+ *   All other possible callbacks are optional.
+ *   Supported action callbacks by rules are defined and documented in the
+ *   RulesPluginImplInterface. However any module may extend the action plugin
+ *   based upon a defined interface using hook_rules_plugin_info(). All methods
+ *   defined in those interfaces can be overridden by the action implementation.
+ *   The callback implementations for those interfaces may reside in any file
+ *   specified in hook_rules_file_info().
+ *
+ * @see hook_rules_file_info()
+ * @see rules_action_execution_callback()
+ * @see hook_rules_plugin_info()
+ * @see RulesPluginImplInterface
+ */
+function hook_rules_action_info() {
+  return array(
+    'mail_user' => array(
+      'label' => t('Send a mail to a user'),
+      'parameter' => array(
+        'user' => array('type' => 'user', 'label' => t('Recipient')),
+      ),
+      'group' => t('System'),
+      'base' => 'rules_action_mail_user',
+      'callbacks' => array(
+        'validate' => 'rules_action_custom_validation',
+        'help' => 'rules_mail_help',
+      ),
+    ),
+  );
+}
+
+/**
+ * Define categories for Rules items, e.g. actions, conditions or events.
+ *
+ * Categories are similar to the previously used 'group' key in e.g.
+ * hook_rules_action_info(), but have a machine name and some more optional
+ * keys like a weight, or an icon.
+ *
+ * For best compatibility, modules may keep using the 'group' key for referring
+ * to categories. However, if a 'group' key and a 'category' is given the group
+ * will be treated as grouping in the given category (e.g. group "paypal" in
+ * category "commerce payment").
+ *
+ * @return array
+ *   An array of information about the module's provided categories.
+ *   The array contains a sub-array for each category, with the category name as
+ *   the key. Names may only contain lowercase alpha-numeric characters
+ *   and underscores and should be prefixed with the providing module name.
+ *   Possible attributes for each sub-array are:
+ *   - label: The label of the category. Start capitalized. Required.
+ *   - weight: (optional) A weight for sorting the category. Defaults to 0.
+ *   - equals group: (optional) For BC, categories may be defined that equal
+ *     a previously used 'group'.
+ *   - icon: (optional) The file path of an icon to use, relative to the module
+ *     or specified icon path. The icon should be a transparent SVG containing
+ *     no colors (only #fff). See https://www.drupal.org/node/2090265 for
+ *     instructions on how to create a suitable icon.
+ *     Note that the icon is currently not used by Rules, however other UIs
+ *     building upon Rules (like fluxkraft) do, and future releases of Rules
+ *     might do as well. Consequently, the definition of an icon is optional.
+ *     However, if both an icon font and icon is given, the icon is preferred.
+ *   - icon path: (optional) The base path for the icon. Defaults to the
+ *     providing module's directory.
+ *   - icon font class: (optional) An icon font class referring to a suitable
+ *     icon. Icon font class names should map to the ones as defined by Font
+ *     Awesome, while themes might want to choose to provide another icon font.
+ *     See http://fortawesome.github.io/Font-Awesome/cheatsheet/.
+ *   - icon background color: (optional) The color used as icon background.
+ *     Should have a high contrast to white. Defaults to #ddd.
+ */
+function hook_rules_category_info() {
+  return array(
+    'rules_data' => array(
+      'label' => t('Data'),
+      'equals group' => t('Data'),
+      'weight' => -50,
+    ),
+    'fluxtwitter' => array(
+      'label' => t('Twitter'),
+      'icon font class' => 'icon-twitter',
+      'icon font background color' => '#30a9fd',
+    ),
+  );
+}
+
+/**
+ * Specify files containing rules integration code.
+ *
+ * All files specified in that hook will be included when rules looks for
+ * existing callbacks for any plugin. Rules remembers which callback is found in
+ * which file and automatically includes the right file before it is executing
+ * a plugin method callback. The file yourmodule.rules.inc is added by default
+ * and need not be specified here.
+ * This allows you to add new include files only containing functions serving as
+ * plugin method callbacks in any file without having to care about file
+ * inclusion.
+ *
+ * @return array
+ *   An array of file names without the file ending which defaults to '.inc'.
+ */
+function hook_rules_file_info() {
+  return array('yourmodule.rules-eval');
+}
+
+/**
+ * Specifies directories for class-based plugin handler discovery.
+ *
+ * Implementing this hook is not a requirement, it is just one option to load
+ * the files containing the classes during discovery - see
+ * rules_discover_plugins().
+ *
+ * @return string|array
+ *   A directory relative to the module directory, which holds the files
+ *   containing rules plugin handlers, or multiple directories keyed by the
+ *   module the directory is contained in.
+ *   All files in those directories having a 'php' or 'inc' file extension will
+ *   be loaded during discovery. Optionally, wildcards ('*') may be used to
+ *   match multiple directories.
+ *
+ * @see rules_discover_plugins()
+ */
+function hook_rules_directory() {
+  return 'lib/Drupal/fluxtwitter/Rules/*';
+}
+
+/**
+ * The execution callback for an action.
+ *
+ * It should be placed in any file included by your module or in a file
+ * specified using hook_rules_file_info().
+ *
+ * @param
+ *   The callback gets arguments passed as described as parameter in
+ *   hook_rules_action_info() as well as an array containing the action's
+ *   configuration settings.
+ *
+ * @return array
+ *   The action may return an array containing parameter or provided variables
+ *   with their names as key. This is used update the value of a parameter or to
+ *   provide the value for a provided variable.
+ *   Apart from that any parameters which have the key 'save' set to TRUE will
+ *   be remembered to be saved by rules unless the action returns FALSE.
+ *   Conditions have to return a boolean value in any case.
+ *
+ * @see hook_rules_action_info()
+ * @see hook_rules_file_info()
+ */
+function rules_action_execution_callback($node, $title, $settings) {
+  $node->title = $title;
+  return array('node' => $node);
+}
+
+/**
+ * Define rules conditions.
+ *
+ * This hook is required in order to add a new rules condition. It should be
+ * placed into the file MODULENAME.rules.inc, which gets automatically included
+ * when the hook is invoked.
+ *
+ * However, as an alternative to implementing this hook, class based plugin
+ * handlers may be provided by implementing RulesConditionHandlerInterface. See
+ * the interface for details.
+ *
+ * Adding conditions works exactly the same way as adding actions, with the
+ * exception that conditions can't provide variables and cannot save parameters.
+ * Thus the 'provides' attribute is not supported. Furthermore the condition
+ * implementation callback has to return a boolean value.
+ *
+ * @see hook_rules_action_info()
+ */
+function hook_rules_condition_info() {
+  return array(
+    'rules_condition_text_compare' => array(
+      'label' => t('Textual comparison'),
+      'parameter' => array(
+        'text1' => array('label' => t('Text 1'), 'type' => 'text'),
+        'text2' => array('label' => t('Text 2'), 'type' => 'text'),
+      ),
+      'group' => t('Rules'),
+    ),
+  );
+}
+
+/**
+ * Define rules events.
+ *
+ * This hook is required in order to add a new rules event. It should be
+ * placed into the file MODULENAME.rules.inc, which gets automatically included
+ * when the hook is invoked.
+ * The module has to invoke the event when it occurs using rules_invoke_event().
+ * This function call has to happen outside of MODULENAME.rules.inc,
+ * usually it's invoked directly from the providing module but wrapped by a
+ * module_exists('rules') check.
+ *
+ * However, as an alternative to implementing this hook, class based event
+ * handlers may be provided by implementing RulesEventHandlerInterface. See
+ * the interface for details.
+ *
+ * @return array
+ *   An array of information about the module's provided rules events. The array
+ *   contains a sub-array for each event, with the event name as the key. The
+ *   name may only contain lower case alpha-numeric characters and underscores
+ *   and should be prefixed with the providing module name. Possible attributes
+ *   for each sub-array are:
+ *   - label: The label of the event. Start capitalized. Required.
+ *   - group: A group for this element, used for grouping the events in the
+ *     interface. Should start with a capital letter and be translated.
+ *     Required.
+ *   - class: (optional) An event handler class implementing the
+ *     RulesEventHandlerInterface. If none is specified the
+ *     RulesEventDefaultHandler class will be used. While the default event
+ *     handler has no settings, custom event handlers may be implemented to
+ *     to make an event configurable. See RulesEventHandlerInterface.
+ *   - access callback: (optional) An callback, which has to return whether the
+ *     currently logged in user is allowed to configure rules for this event.
+ *     Access should be only granted, if the user at least may access all the
+ *     variables provided by the event.
+ *   - help: (optional) A help text for rules reaction on this event.
+ *   - variables: (optional) An array describing all variables that are
+ *     available for elements reacting on this event. Each variable has to be
+ *     described by a sub-array with the possible attributes:
+ *     - label: The label of the variable. Start capitalized. Required.
+ *     - type: The rules data type of the variable. All types declared in
+ *       hook_rules_data_info() or supported by hook_entity_property_info() may
+ *       be specified.
+ *     - bundle: (optional) If the type is an entity type, the bundle of the
+ *       entity.
+ *     - description: (optional) A description for the variable.
+ *     - 'options list': (optional) A callback that returns an array of possible
+ *       values for this variable as specified for entity properties at
+ *       hook_entity_property_info().
+ *     - 'skip save': (optional) If the variable is saved after the event has
+ *       occurred anyway, set this to TRUE. So rules won't save the variable a
+ *       second time. Defaults to FALSE.
+ *     - handler: (optional) A handler to load the actual variable value. This
+ *       is useful for lazy loading variables. The handler gets all so far
+ *       available variables passed in the order as defined. Also see
+ *       https://www.drupal.org/node/884554.
+ *       Note that for lazy-loading entities just the entity id may be passed
+ *       as variable value, so a handler is not necessary in that case.
+ *
+ * @see rules_invoke_event()
+ */
+function hook_rules_event_info() {
+  $items = array(
+    'node_insert' => array(
+      'label' => t('After saving new content'),
+      'group' => t('Node'),
+      'variables' => rules_events_node_variables(t('created content')),
+    ),
+    'node_update' => array(
+      'label' => t('After updating existing content'),
+      'group' => t('Node'),
+      'variables' => rules_events_node_variables(t('updated content'), TRUE),
+    ),
+    'node_presave' => array(
+      'label' => t('Content is going to be saved'),
+      'group' => t('Node'),
+      'variables' => rules_events_node_variables(t('saved content'), TRUE),
+    ),
+    'node_view' => array(
+      'label' => t('Content is going to be viewed'),
+      'group' => t('Node'),
+      'variables' => rules_events_node_variables(t('viewed content')) + array(
+        'view_mode' => array('type' => 'text', 'label' => t('view mode')),
+      ),
+    ),
+    'node_delete' => array(
+      'label' => t('After deleting content'),
+      'group' => t('Node'),
+      'variables' => rules_events_node_variables(t('deleted content')),
+    ),
+  );
+  // Specify that on presave the node is saved anyway.
+  $items['node_presave']['variables']['node']['skip save'] = TRUE;
+  return $items;
+}
+
+/**
+ * Define rules data types.
+ *
+ * This hook is required in order to add a new rules data type. It should be
+ * placed into the file MODULENAME.rules.inc, which gets automatically included
+ * when the hook is invoked.
+ * Rules builds upon the entity metadata module, thus to improve the support of
+ * your data in rules, make it an entity if possible and provide metadata about
+ * its properties and CRUD functions by integrating with the entity metadata
+ * module.
+ * For a list of data types defined by rules see rules_rules_core_data_info().
+ *
+ * @return array
+ *   An array of information about the module's provided data types. The array
+ *   contains a sub-array for each data type, with the data type name as the
+ *   key. The name may only contain lower case alpha-numeric characters and
+ *   underscores and should be prefixed with the providing module name. Possible
+ *   attributes for each sub-array are:
+ *   - label: The label of the data type. Start uncapitalized. Required.
+ *   - parent: (optional) A parent type may be set to specify a sub-type
+ *     relationship, which will be only used for checking compatible types. E.g.
+ *     the 'entity' data type is parent of the 'node' data type, thus a node may
+ *     be also used for any action needing an 'entity' parameter. Can be set to
+ *     any known rules data type.
+ *   - ui class: (optional) Specify a class that is used to generate the
+ *     configuration UI to configure parameters of this type. The given class
+ *     must extend RulesDataUI and may implement the
+ *     RulesDataDirectInputFormInterface in order to allow the direct data input
+ *     configuration mode. For supporting selecting values from options lists,
+ *     the UI class may implement RulesDataInputOptionsListInterface also.
+ *     Defaults to RulesDataUI.
+ *   - wrap: (optional) If set to TRUE, the data is wrapped internally using
+ *     wrappers provided by the entity API module. This is required for entities
+ *     and data structures to support selecting a property via the data selector
+ *     and for intelligent saving.
+ *   - is wrapped: (optional) In case the data wrapper is already wrapped when
+ *     passed to Rules and Rules should not unwrap it when passing the data as
+ *     argument, e.g. to an action, set this to TRUE. The default FALSE is fine
+ *     in most cases.
+ *   - wrapper class: (optional) Allows the specification of a custom wrapper
+ *     class, which has to inherit from 'EntityMetadataWrapper'. If given Rules
+ *     makes use of the class for wrapping the data of the given type. However
+ *     note that if data is already wrapped when it is passed to Rules, the
+ *     existing wrappers will be kept.
+ *     For modules implementing identifiable data types being non-entities the
+ *     class RulesIdentifiableDataWrapper is provided, which can be used as base
+ *     for a custom wrapper class. See RulesIdentifiableDataWrapper for details.
+ *   - property info: (optional) May be used for non-entity data structures to
+ *     provide info about the data properties, such that data selectors via an
+ *     entity metadata wrapper are supported. Specify an array as expected by
+ *     the $info parameter of entity_metadata_wrapper().
+ *   - creation callback: (optional) If 'property info' is given, an optional
+ *     callback that makes use of the property info to create a new instance of
+ *     this data type. Entities should use hook_entity_info() to specify the
+ *     'creation callback' instead, as utilized by the entity API module. See
+ *     rules_action_data_create_array() for an example callback.
+ *   - property defaults: (optional) May be used for non-entity data structures
+ *     to to provide property info defaults for the data properties. Specify an
+ *     array as expected by entity_metadata_wrapper().
+ *   - group: (optional) A group for this element, used for grouping the data
+ *     types in the interface. Should start with a capital letter and be
+ *     translated.
+ *   - token type: (optional) The type name as used by the token module.
+ *     Defaults to the type name as used by rules. Use FALSE to let token ignore
+ *     this type.
+ *   - cleaning callback: (optional) A callback that input evaluators may use
+ *     to clean inserted replacements; e.g. this is used by the token evaluator.
+ *
+ * @see entity_metadata_wrapper()
+ * @see hook_rules_data_info_alter()
+ * @see rules_rules_core_data_info()
+ */
+function hook_rules_data_info() {
+  return array(
+    'node' => array(
+      'label' => t('content'),
+      'parent' => 'entity',
+      'group' => t('Node'),
+    ),
+    // Formatted text as used by in hook_entity_property_info() for text fields.
+    'text_formatted' => array(
+      'label' => t('formatted text'),
+      'ui class' => 'RulesDataUITextFormatted',
+      'wrap' => TRUE,
+      'property info' => entity_property_text_formatted_info(),
+    ),
+  );
+}
+
+/**
+ * Defines rules plugins.
+ *
+ * A rules configuration may consist of elements being instances of any rules
+ * plugin. This hook can be used to define new or to extend rules plugins.
+ *
+ * @return array
+ *   An array of information about the module's provided rules plugins. The
+ *   array contains a sub-array for each plugin, with the plugin name as the
+ *   key. The name may only contain lower case alpha-numeric characters,
+ *   underscores and spaces and should be prefixed with the providing module
+ *   name. Possible attributes for
+ *   each sub-array are:
+ *   - label: A label for the plugin. Start capitalized. Required only for
+ *     components (see below).
+ *   - class: The implementation class. Has to extend the RulesPlugin class.
+ *   - embeddable: A container class in which elements of those plugin may be
+ *     embedded via the UI or FALSE to disallow embedding it via the UI. This
+ *     has no implications on the API level though. Common classes that are
+ *     used here are RulesConditionContainer and RulesActionContainer.
+ *   - component: If set to TRUE, the rules admin UI will list elements of those
+ *     plugin in the components UI and allows the creation of new components
+ *     based upon this plugin. Optional.
+ *   - extenders: This allows one to specify faces extenders, which may be used
+ *     to dynamically implement interfaces. Optional. All extenders specified
+ *     here are setup automatically by rules once the object is created. To
+ *     specify set this to an array, where the keys are the implemented
+ *     interfaces pointing to another array with the keys:
+ *     - class: The class of the extender, implementing the FacesExtender
+ *       and the specified interface. Either 'class' or 'methods' has to exist.
+ *     - methods: An array of callbacks that implement the methods of the
+ *       interface where the method names are the keys and the callback names
+ *       the values. There has to be a callback for each defined method.
+ *     - file: An optional array describing the file to include when a method
+ *       of the interface is invoked. The array entries known are 'type',
+ *       'module', and 'name' matching the parameters of module_load_include().
+ *       Only 'module' is required as 'type' defaults to 'inc' and 'name' to
+ *       NULL.
+ *   - overrides: An optional array, which may be used to specify callbacks to
+ *     override specific methods. For that the following keys are supported:
+ *     - methods: As in the extenders array, but you may specify as many methods
+ *       here as you like.
+ *     - file: Optionally an array specifying a file to include for a method.
+ *       For each method appearing in methods a file may be specified by using
+ *       the method name as key and another array as value, which describes the
+ *       file to include - looking like the file array supported by 'extenders'.
+ *   - import keys: (optional) Embeddable plugins may specify an array of import
+ *     keys, which the plugin make use for exporting. Defaults to the upper
+ *     case plugin name, thus the key 'OR' in an export triggers the creation
+ *     of the 'or' plugin. Note that only uppercase values are allowed, as
+ *     lower case values are treated as action or condition exports.
+ *
+ * @see RulesPlugin
+ * @see hook_rules_plugin_info_alter()
+ */
+function hook_rules_plugin_info() {
+  return array(
+    'or' => array(
+      'label' => t('Condition set (OR)'),
+      'class' => 'RulesOr',
+      'embeddable' => 'RulesConditionContainer',
+      'component' => TRUE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionContainerUI',
+        ),
+      ),
+    ),
+    'rule' => array(
+      'class' => 'Rule',
+      'embeddable' => 'RulesRuleSet',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesRuleUI',
+        ),
+      ),
+      'import keys' => array('DO', 'IF'),
+    ),
+  );
+}
+
+/**
+ * Declare provided rules input evaluators.
+ *
+ * The hook implementation should be placed into the file MODULENAME.rules.inc,
+ * which gets automatically included when the hook is invoked.
+ * For implementing an input evaluator a class has to be provided which
+ * extends the abstract RulesDataInputEvaluator class. Therefore the abstract
+ * methods prepare() and evaluate() have to be implemented, as well as access()
+ * and help() could be overridden in order to control access permissions or to
+ * provide some usage help.
+ *
+ * @return array
+ *   An array of information about the module's provided input evaluators. The
+ *   array contains a sub-array for each evaluator, with the evaluator name as
+ *   the key. The name may only contain lower case alpha-numeric characters and
+ *   underscores and should be prefixed with the providing module name. Possible
+ *   attributes for each sub-array are:
+ *   - class: The implementation class, which has to extend the
+ *     RulesDataInputEvaluator class. Required.
+ *   - weight: A weight for controlling the evaluation order of multiple
+ *     evaluators. Required.
+ *   - type: Optionally, the data types for which the input evaluator should be
+ *     used. Defaults to 'text'. Multiple data types may be specified using an
+ *     array.
+ *
+ * @see RulesDataInputEvaluator
+ * @see hook_rules_evaluator_info_alter()
+ */
+function hook_rules_evaluator_info() {
+  return array(
+    'token' => array(
+      'class' => 'RulesTokenEvaluator',
+      'type' => array('text', 'uri'),
+      'weight' => 0,
+    ),
+  );
+}
+
+/**
+ * Declare provided rules data processors.
+ *
+ * The hook implementation should be placed into the file MODULENAME.rules.inc,
+ * which gets automatically included when the hook is invoked.
+ * For implementing a data processors a class has to be provided which
+ * extends the abstract RulesDataProcessor class. Therefore the abstract
+ * method process() has to be implemented, but also the methods form() and
+ * access() could be overridden in order to provide a configuration form or
+ * to control access permissions.
+ *
+ * @return array
+ *   An array of information about the module's provided data processors. The
+ *   array contains a sub-array for each processor, with the processor name as
+ *   the key. The name may only contain lower case alpha-numeric characters and
+ *   underscores and should be prefixed with the providing module name, whereas
+ *   'select' is reserved as well.
+ *   Possible attributes for each sub-array are:
+ *   - class: The implementation class, which has to extend the
+ *     RulesDataProcessor class. Required.
+ *   - weight: A weight for controlling the processing order of multiple data
+ *     processors. Required.
+ *   - type: Optionally, the data types for which the data processor should be
+ *     used. Defaults to 'text'. Multiple data types may be specified using an
+ *     array.
+ *
+ * @see RulesDataProcessor
+ * @see hook_rules_data_processor_info_alter()
+ */
+function hook_rules_data_processor_info() {
+  return array(
+    'date_offset' => array(
+      'class' => 'RulesDateOffsetProcessor',
+      'type' => 'date',
+      'weight' => -2,
+    ),
+  );
+}
+
+/**
+ * Alter rules compatible actions.
+ *
+ * The implementation should be placed into the file MODULENAME.rules.inc, which
+ * gets automatically included when the hook is invoked.
+ *
+ * @param $actions
+ *   The items of all modules as returned from hook_rules_action_info().
+ *
+ * @see hook_rules_action_info()
+ */
+function hook_rules_action_info_alter(&$actions) {
+  // The rules action is more powerful, so hide the core action.
+  unset($actions['rules_core_node_assign_owner_action']);
+  // We prefer handling saving by rules - not by the user.
+  unset($actions['rules_core_node_save_action']);
+}
+
+/**
+ * Alter rules conditions.
+ *
+ * The implementation should be placed into the file MODULENAME.rules.inc, which
+ * gets automatically included when the hook is invoked.
+ *
+ * @param $conditions
+ *   The items of all modules as returned from hook_rules_condition_info().
+ *
+ * @see hook_rules_condition_info()
+ */
+function hook_rules_condition_info_alter(&$conditions) {
+  // Change conditions.
+}
+
+/**
+ * Alter rules events.
+ *
+ * The implementation should be placed into the file MODULENAME.rules.inc, which
+ * gets automatically included when the hook is invoked.
+ *
+ * @param $events
+ *   The items of all modules as returned from hook_rules_event_info().
+ *
+ * @see hook_rules_event_info()
+ */
+function hook_rules_event_info_alter(&$events) {
+  // Change events.
+}
+
+/**
+ * Alter rules data types.
+ *
+ * The implementation should be placed into the file MODULENAME.rules.inc, which
+ * gets automatically included when the hook is invoked.
+ *
+ * @param $data_info
+ *   The items of all modules as returned from hook_rules_data_info().
+ *
+ * @see hook_rules_data_info()
+ */
+function hook_rules_data_info_alter(&$data_info) {
+  // Change data types.
+}
+
+/**
+ * Alter rules plugin info.
+ *
+ * The implementation should be placed into the file MODULENAME.rules.inc, which
+ * gets automatically included when the hook is invoked.
+ *
+ * @param $plugin_info
+ *   The items of all modules as returned from hook_rules_plugin_info().
+ *
+ * @see hook_rules_plugin_info()
+ */
+function hook_rules_plugin_info_alter(&$plugin_info) {
+  // Change plugin info.
+}
+
+/**
+ * Alter rules input evaluator info.
+ *
+ * The implementation should be placed into the file MODULENAME.rules.inc, which
+ * gets automatically included when the hook is invoked.
+ *
+ * @param $evaluator_info
+ *   The items of all modules as returned from hook_rules_evaluator_info().
+ *
+ * @see hook_rules_evaluator_info()
+ */
+function hook_rules_evaluator_info_alter(&$evaluator_info) {
+  // Change evaluator info.
+}
+
+/**
+ * Alter rules data_processor info.
+ *
+ * The implementation should be placed into the file MODULENAME.rules.inc, which
+ * gets automatically included when the hook is invoked.
+ *
+ * @param $processor_info
+ *   The items of all modules as returned from hook_rules_data_processor_info().
+ *
+ * @see hook_rules_data_processor_info()
+ */
+function hook_rules_data_processor_info_alter(&$processor_info) {
+  // Change processor info.
+}
+
+/**
+ * Act on rules configuration being loaded from the database.
+ *
+ * This hook is invoked during rules configuration loading, which is handled
+ * by entity_load(), via classes RulesEntityController and EntityCRUDController.
+ *
+ * @param array $configs
+ *   An array of rules configurations being loaded, keyed by id.
+ */
+function hook_rules_config_load($configs) {
+  $result = db_query('SELECT id, foo FROM {mytable} WHERE id IN(:ids)', array(':ids' => array_keys($configs)));
+  foreach ($result as $record) {
+    $configs[$record->id]->foo = $record->foo;
+  }
+}
+
+/**
+ * Respond to creation of a new rules configuration.
+ *
+ * This hook is invoked after the rules configuration is inserted into the
+ * the database.
+ *
+ * @param RulesPlugin $config
+ *   The rules configuration that is being created.
+ */
+function hook_rules_config_insert($config) {
+  db_insert('mytable')
+    ->fields(array(
+      'nid' => $config->id,
+      'plugin' => $config->plugin,
+    ))
+    ->execute();
+}
+
+/**
+ * Act on a rules configuration being inserted or updated.
+ *
+ * This hook is invoked before the rules configuration is saved to the
+ * database.
+ *
+ * @param RulesPlugin $config
+ *   The rules configuration that is being inserted or updated.
+ */
+function hook_rules_config_presave($config) {
+  if ($config->id && $config->owner == 'your_module') {
+    // Add custom condition.
+    $config->conditon(/* Your condition */);
+  }
+}
+
+/**
+ * Respond to updates to a rules configuration.
+ *
+ * This hook is invoked after the configuration has been updated in the
+ * database.
+ *
+ * @param RulesPlugin $config
+ *   The rules configuration that is being updated.
+ */
+function hook_rules_config_update($config) {
+  db_update('mytable')
+    ->fields(array('plugin' => $config->plugin))
+    ->condition('id', $config->id)
+    ->execute();
+}
+
+/**
+ * Respond to rules configuration deletion.
+ *
+ * This hook is invoked after the configuration has been removed from the
+ * database.
+ *
+ * @param RulesPlugin $config
+ *   The rules configuration that is being deleted.
+ */
+function hook_rules_config_delete($config) {
+  db_delete('mytable')
+    ->condition('id', $config->id)
+    ->execute();
+}
+
+/**
+ * Respond to rules configuration execution.
+ *
+ * This hook is invoked right before the rules configuration is executed.
+ *
+ * @param RulesPlugin $config
+ *   The rules configuration that is being executed.
+ */
+function hook_rules_config_execute($config) {
+
+}
+
+/**
+ * Define default rules configurations.
+ *
+ * This hook is invoked when rules configurations are loaded. The implementation
+ * should be placed into the file MODULENAME.rules_defaults.inc, which gets
+ * automatically included when the hook is invoked.
+ *
+ * @return array
+ *   An array of rules configurations with the configuration names as keys.
+ *
+ * @see hook_default_rules_configuration_alter()
+ * @see hook_rules_config_defaults_rebuild()
+ */
+function hook_default_rules_configuration() {
+  $rule = rules_reaction_rule();
+  $rule->label = 'example default rule';
+  // Add rules tags.
+  $rule->tags = array('Admin', 'Tag2');
+  $rule->active = FALSE;
+  $rule->event('node_update')
+       ->condition(rules_condition('data_is', array('data:select' => 'node:status', 'value' => TRUE))->negate())
+       ->condition('data_is', array('data:select' => 'node:type', 'value' => 'page'))
+       ->action('drupal_message', array('message' => 'A node has been updated.'));
+
+  $configs['rules_test_default_1'] = $rule;
+
+  return $configs;
+}
+
+/**
+ * Alter default rules configurations.
+ *
+ * The implementation should be placed into the file
+ * MODULENAME.rules_defaults.inc, which gets automatically included when the
+ * hook is invoked.
+ *
+ * @param $configs
+ *   The default configurations of all modules as returned from
+ *   hook_default_rules_configuration().
+ *
+ * @see hook_default_rules_configuration()
+ */
+function hook_default_rules_configuration_alter(&$configs) {
+  // Add custom condition.
+  $configs['foo']->condition('bar');
+}
+
+/**
+ * Act after rebuilding default configurations.
+ *
+ * This hook is invoked by the entity module after default rules configurations
+ * have been rebuilt; i.e. defaults have been saved to the database.
+ *
+ * @param array $rules_configs
+ *   The array of default rules configurations which have been inserted or
+ *   updated, keyed by name.
+ * @param array $originals
+ *   An array of original rules configurations keyed by name; i.e. the rules
+ *   configurations before the current defaults have been applied. For inserted
+ *   rules configurations no original is available.
+ *
+ * @see hook_default_rules_configuration()
+ * @see entity_defaults_rebuild()
+ */
+function hook_rules_config_defaults_rebuild($rules_configs, $originals) {
+  // Once all defaults have been rebuilt, update all i18n strings at once. That
+  // way we build the rules cache once the rebuild is complete and avoid
+  // rebuilding caches for each updated rule.
+  foreach ($rules_configs as $name => $rule_config) {
+    if (empty($originals[$name])) {
+      rules_i18n_rules_config_insert($rule_config);
+    }
+    else {
+      rules_i18n_rules_config_update($rule_config, $originals[$name]);
+    }
+  }
+}
+
+/**
+ * Alter rules components before execution.
+ *
+ * This hooks allows altering rules components before they are cached for later
+ * re-use. Use this hook only for altering the component in order to prepare
+ * re-use through rules_invoke_component() or the provided condition/action.
+ * Note that this hook is only invoked for any components cached for execution,
+ * but not for components that are programmatically created and executed on the
+ * fly (without saving them).
+ *
+ * @param $plugin
+ *   The name of the component plugin.
+ * @param $component
+ *   The component that is to be cached.
+ *
+ * @see rules_invoke_component()
+ */
+function hook_rules_component_alter($plugin, RulesPlugin $component) {
+
+}
+
+/**
+ * Alters event sets.
+ *
+ * This hooks allows altering rules event sets, which contain all rules that are
+ * triggered upon a specific event. Rules internally caches all rules associated
+ * to an event in an event set, which is cached for fast evaluation. This hook
+ * is invoked just before any event set is cached, thus it allows altering of
+ * the to be executed rules without the changes to appear in the UI, e.g. to add
+ * a further condition to some rules.
+ *
+ * @param $event_name
+ *   The name of the event.
+ * @param $event_set
+ *   The event set that is to be cached.
+ *
+ * @see rules_invoke_event()
+ */
+function hook_rules_event_set_alter($event_name, RulesEventSet $event_set) {
+
+}
+
+/**
+ * D6 to D7 upgrade procedure hook for mapping action or condition names.
+ *
+ * If for a module the action or condition name changed since Drupal 6, this
+ * "hook" can be implemented in order to map to the new name of the action or
+ * condition.
+ *
+ * This is no real hook, but a callback that is invoked for each Drupal 6
+ * action or condition that is to be upgraded to Drupal 7. E.g. the function
+ * name called for the action "rules_action_set_node_title" would be
+ * "rules_action_set_node_title_upgrade_map_name".
+ *
+ * @param $element
+ *   The element array of a configured condition or action which is to be
+ *   upgraded.
+ *
+ * @return string
+ *   The name of the action or condition which should be used.
+ */
+function hook_rules_action_base_upgrade_map_name($element) {
+  return 'data_set';
+}
+
+/**
+ * D6 to D7 upgrade process hook for mapping action or condition configuration.
+ *
+ * During upgrading Drupal 6 rule configurations to Drupal 7 Rules is taking
+ * care of upgrading the configuration of all known parameters, which only works
+ * if the parameter name has not changed.
+ * If something changed, this callback can be used to properly apply the
+ * configuration of the Drupal 6 action ($element) to the Drupal 7 version
+ * ($target).
+ *
+ * This is no real hook, but a callback that is invoked for each Drupal 6
+ * action or condition that is to be upgraded to Drupal 7. E.g. the function
+ * name called for the action "rules_action_set_node_title" would be
+ * "rules_action_set_node_title_upgrade".
+ *
+ * @param $element
+ *   The element array of a configured condition or action which is to be
+ *   upgraded.
+ * @param $target
+ *   The Drupal 7 version of the configured element.
+ *
+ * @see hook_rules_element_upgrade_alter()
+ */
+function hook_rules_action_base_upgrade($element, RulesPlugin $target) {
+  $target->settings['data:select'] = $element['#settings']['#argument map']['node'] . ':title';
+  $target->settings['value'] = $element['#settings']['title'];
+}
+
+/**
+ * D6 to D7 upgrade process hook for mapping action or condition configuration.
+ *
+ * A alter hook that is called after the action/condition specific callback for
+ * each element of a configuration that is upgraded.
+ *
+ * @param $element
+ *   The element array of a configured condition or action which is to be
+ *   upgraded.
+ * @param $target
+ *   The Drupal 7 version of the configured element.
+ *
+ * @see hook_rules_action_base_upgrade()
+ */
+function hook_rules_element_upgrade_alter($element, $target) {
+
+}
+
+/**
+ * Allows modules to alter or to extend the provided Rules UI.
+ *
+ * Use this hook over the regular hook_menu_alter() as the Rules UI is re-used
+ * and embedded by modules. See rules_ui().
+ *
+ * @param $items
+ *   The menu items to alter.
+ * @param $base_path
+ *   The base path of the Rules UI.
+ * @param $base_count
+ *   The count of the directories contained in the base path.
+ */
+function hook_rules_ui_menu_alter(&$items, $base_path, $base_count) {
+  $items[$base_path . '/manage/%rules_config/schedule'] = array(
+    'title callback' => 'rules_get_title',
+    'title arguments' => array('Schedule !plugin "!label"', $base_count + 1),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_scheduler_schedule_form', $base_count + 1, $base_path),
+    'access callback' => 'rules_config_access',
+    'access arguments' => array('update', $base_count + 1),
+    'file' => 'rules_scheduler.admin.inc',
+    'file path' => drupal_get_path('module', 'rules_scheduler'),
+  );
+}
+
+/**
+ * Control access to Rules configurations.
+ *
+ * Modules may implement this hook if they want to have a say in whether or not
+ * a given user has access to perform a given operation on a Rules
+ * configuration.
+ *
+ * @param string $op
+ *   The operation being performed. One of 'view', 'create', 'update' or
+ *   'delete'.
+ * @param $rules_config
+ *   (optional) A Rules configuration to check access for. If nothing is given,
+ *   access for all Rules configurations is determined.
+ * @param $account
+ *   (optional) The user to check for. If no account is passed, access is
+ *   determined for the current user.
+ *
+ * @return bool|null
+ *   Return TRUE to grant access, FALSE to explicitly deny access. Return NULL
+ *   or nothing to not affect the operation.
+ *   Access is granted as soon as a module grants access and no one denies
+ *   access. Thus if no module explicitly grants access, access will be denied.
+ *
+ * @see rules_config_access()
+ */
+function hook_rules_config_access($op, $rules_config = NULL, $account = NULL) {
+  // Instead of returning FALSE return nothing, so others still can grant
+  // access.
+  if (isset($rules_config) && $rules_config->owner == 'mymodule' && user_access('my modules permission')) {
+    return TRUE;
+  }
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/rules.drush.inc b/profiles/wcm_base/modules/contrib/rules/rules.drush.inc
new file mode 100644
index 0000000000000000000000000000000000000000..9d4c18dcc6c5af298c9fbf5bfdb07e1450443ead
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules.drush.inc
@@ -0,0 +1,252 @@
+<?php
+
+/**
+ * @file
+ * Rules module drush integration.
+ */
+
+/**
+ * Implements hook_drush_command().
+ */
+function rules_drush_command() {
+  $items = array();
+
+  $items['rules-list'] = array(
+    'description' => 'List all the active and inactive rules for your site.',
+    'drupal dependencies' => array('rules'),
+    'aliases' => array('rules'),
+    'outputformat' => array(
+      'default' => 'table',
+      'pipe-format' => 'list',
+      'field-labels' => array(
+        'rule' => dt('Rule'),
+        'label' => dt('Label'),
+        'event' => dt('Event'),
+        'active' => dt('Active'),
+        'status' => dt('Status'),
+      ),
+      'output-data-type' => 'format-table',
+    ),
+  );
+  $items['rules-enable'] = array(
+    'description' => 'Enable a rule on your site.',
+    'arguments' => array(
+      'rule' => 'Rule name to enable.',
+    ),
+    'drupal dependencies' => array('rules'),
+    'aliases' => array('re'),
+  );
+  $items['rules-disable'] = array(
+    'description' => 'Disable a rule on your site.',
+    'arguments' => array(
+      'rule' => 'Rule name to export.',
+    ),
+    'drupal dependencies' => array('rules'),
+    'aliases' => array('rd'),
+  );
+  $items['rules-revert'] = array(
+    'description' => 'Revert a rule to its original state on your site.',
+    'arguments' => array(
+      'rule' => 'Rule name to revert.',
+    ),
+    'drupal dependencies' => array('rules'),
+  );
+  $items['rules-delete'] = array(
+    'description' => 'Delete a rule on your site.',
+    'arguments' => array(
+      'rule' => 'Rules name to delete.',
+    ),
+    'drupal dependencies' => array('rules'),
+  );
+  $items['rules-export'] = array(
+    'description' => 'Export a rule.',
+    'arguments' => array(
+      'rule' => 'Rules name to export.',
+    ),
+    'drupal dependencies' => array('rules'),
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_drush_help().
+ */
+function rules_drush_help($section) {
+  switch ($section) {
+    case 'drush:rules-list':
+      return dt('List all the rules on your site.');
+
+    case 'drush:rules-enable':
+      return dt('Enable/activate a rule on your site.');
+
+    case 'drush:rules-disable':
+      return dt('Disable/deactivate a rule on your site.');
+
+    case 'drush:rules-revert':
+      return dt('Revert a module-provided rule to its original state on your site.');
+
+    case 'drush:rules-delete':
+      return dt('Delete a rule on your site.');
+
+    case 'drush:rules-export':
+      return dt('Export a rule.');
+  }
+}
+
+/**
+ * Get a list of all rules.
+ */
+function drush_rules_list() {
+  $rules = rules_config_load_multiple(FALSE);
+  $rows = array();
+  foreach ($rules as $rule) {
+    if (!empty($rule->name) && !empty($rule->label)) {
+      $events = array();
+      $event_info = rules_fetch_data('event_info');
+      if ($rule instanceof RulesTriggerableInterface) {
+        foreach ($rule->events() as $event_name) {
+          $event_info += array(
+            $event_name => array(
+              'label' => dt('Unknown event "!event_name"', array('!event_name' => $event_name)),
+            ),
+          );
+          $events[] = check_plain($event_info[$event_name]['label']);
+        }
+      }
+      $rows[$rule->name] = array(
+        'rule' => $rule->name,
+        'label' => $rule->label,
+        'event' => implode(', ', $events),
+        'active' => $rule->active ? dt('Enabled') : dt('Disabled'),
+        'status' => $rule->status ? theme('entity_status', array('status' => $rule->status, 'html' => FALSE)) : '',
+      );
+    }
+  }
+  if (version_compare(DRUSH_VERSION, '6.0', '<')) {
+    drush_print_table($rows, TRUE);
+  }
+  return $rows;
+}
+
+/**
+ * Enable a rule on the site.
+ */
+function drush_rules_enable() {
+  $args = func_get_args();
+  $rule_name = (!empty($args) && is_array($args)) ? array_shift($args) : '';
+  if (empty($rule_name)) {
+    return drush_set_error('', 'No rule name given.');
+  }
+
+  $rule = rules_config_load($rule_name);
+  if (empty($rule)) {
+    return drush_set_error('', dt('Could not load rule named "!rule-name".', array('!rule-name' => $rule_name)));
+  }
+
+  if (empty($rule->active)) {
+    $rule->active = TRUE;
+    $rule->save();
+    drush_log(dt('The rule "!name" has been enabled.', array('!name' => $rule_name)), 'success');
+  }
+  else {
+    drush_log(dt('The rule "!name" is already enabled.', array('!name' => $rule_name)), 'warning');
+  }
+}
+
+/**
+ * Disable a rule on the site.
+ */
+function drush_rules_disable() {
+  $args = func_get_args();
+  $rule_name = (!empty($args) && is_array($args)) ? array_shift($args) : '';
+  if (empty($rule_name)) {
+    return drush_set_error('', 'No rule name given.');
+  }
+
+  $rule = rules_config_load($rule_name);
+  if (empty($rule)) {
+    return drush_set_error('', dt('Could not load rule named "!rule-name".', array('!rule-name' => $rule_name)));
+  }
+
+  if (!empty($rule->active)) {
+    $rule->active = FALSE;
+    $rule->save();
+    drush_log(dt('The rule "!name" has been disabled.', array('!name' => $rule_name)), 'success');
+  }
+  else {
+    drush_log(dt('The rule "!name" is already disabled.', array('!name' => $rule_name)), 'warning');
+  }
+}
+
+/**
+ * Reverts a rule on the site.
+ */
+function drush_rules_revert() {
+  $args = func_get_args();
+  $rule_name = (!empty($args) && is_array($args)) ? array_shift($args) : '';
+  if (empty($rule_name)) {
+    return drush_set_error('', 'No rule name given.');
+  }
+
+  $rule = rules_config_load($rule_name);
+  if (empty($rule)) {
+    return drush_set_error('', dt('Could not load rule named "!rule-name".', array('!rule-name' => $rule_name)));
+  }
+
+  if (($rule->status & ENTITY_OVERRIDDEN) == ENTITY_OVERRIDDEN) {
+    if (drush_confirm(dt('Are you sure you want to revert the rule named "!rule-name"? This action cannot be undone.', array('!rule-name' => $rule_name)))) {
+      $rule->delete();
+      drush_log(dt('The rule "!name" has been reverted to its default state.', array('!name' => $rule_name)), 'success');
+    }
+    else {
+      drush_user_abort();
+    }
+  }
+  else {
+    drush_log(dt('The rule "!name" has not been overridden and can\'t be reverted.', array('!name' => $rule_name)), 'warning');
+  }
+}
+
+/**
+ * Deletes a rule on the site.
+ */
+function drush_rules_delete() {
+  $args = func_get_args();
+  $rule_name = (!empty($args) && is_array($args)) ? array_shift($args) : '';
+  if (empty($rule_name)) {
+    return drush_set_error('', 'No rule name given.');
+  }
+
+  $rule = rules_config_load($rule_name);
+  if (empty($rule)) {
+    return drush_set_error('', dt('Could not load rule named "!rule-name".', array('!rule-name' => $rule_name)));
+  }
+
+  if (drush_confirm(dt('Are you sure you want to delete the rule named "!rule-name"? This action cannot be undone.', array('!rule-name' => $rule_name)))) {
+    $rule->delete();
+    drush_log(dt('The rule "!name" has been deleted.', array('!name' => $rule_name)), 'success');
+  }
+  else {
+    drush_user_abort();
+  }
+}
+
+/**
+ * Exports a single rule.
+ */
+function drush_rules_export() {
+  $args = func_get_args();
+  $rule_name = (!empty($args) && is_array($args)) ? array_shift($args) : '';
+  if (empty($rule_name)) {
+    return drush_set_error('', dt('No rule name given.'));
+  }
+
+  $rule = rules_config_load($rule_name);
+  if (empty($rule)) {
+    return drush_set_error('', dt('Could not load rule named "!rule-name".', array('!rule-name' => $rule_name)));
+  }
+
+  drush_print($rule->export());
+  drush_log(dt('The rule "!name" has been exported.', array('!name' => $rule_name)), 'success');
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules.features.inc b/profiles/wcm_base/modules/contrib/rules/rules.features.inc
new file mode 100644
index 0000000000000000000000000000000000000000..e3224b569331461c4ec7757929236796db950135
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules.features.inc
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * @file
+ * Provides Features integration for the Rules module.
+ *
+ * This code is based upon the features integration provided by the Entity API.
+ */
+
+/**
+ * Controller handling the features integration.
+ */
+class RulesFeaturesController extends EntityDefaultFeaturesController {
+
+  /**
+   * Defines the result for hook_features_api().
+   */
+  public function api() {
+    $info = parent::api();
+    $info['rules_config']['default_file'] = FEATURES_DEFAULTS_CUSTOM;
+    $info['rules_config']['default_filename'] = 'rules_defaults';
+    return $info;
+  }
+
+  /**
+   * Generates the result for hook_features_export().
+   *
+   * Overridden to add in rules-specific stuff.
+   */
+  public function export($data, &$export, $module_name = '') {
+    $pipe = parent::export($data, $export, $module_name);
+    foreach (entity_load_multiple_by_name($this->type, $data) as $name => $rules_config) {
+      // Add in the dependencies.
+      $export['dependencies'] += drupal_map_assoc($rules_config->dependencies());
+      // Add in plugin / element specific additions.
+      $iterator = new RecursiveIteratorIterator($rules_config, RecursiveIteratorIterator::SELF_FIRST);
+      foreach ($iterator as $element) {
+        if ($element->facesAs('RulesPluginFeaturesIntegrationInterface')) {
+          // Directly use __call() so we can pass $export by reference.
+          $element->__call('features_export', array(&$export, &$pipe, $module_name));
+        }
+      }
+    }
+    return $pipe;
+  }
+
+}
+
+/**
+ * Default extension callback used as default for the abstract plugin class.
+ *
+ * Actions and conditions may override this with an implementation which
+ * actually does something.
+ *
+ * @see RulesPluginFeaturesIntegrationInterface
+ */
+function rules_features_abstract_default_features_export(&$export, &$pipe, $module_name = '', $element) {
+  // Do nothing.
+}
+
+/**
+ * Interface to give features access to the faces extensions mechanism.
+ *
+ * Interface that allows rules plugins or actions/conditions to customize the
+ * features export by implementing the interface using the faces extensions
+ * mechanism.
+ *
+ * @see hook_rules_plugin_info()
+ * @see hook_rules_action_info()
+ */
+interface RulesPluginFeaturesIntegrationInterface {
+
+  /**
+   * Allows customizing the features export for a given rule element.
+   */
+  public function features_export(&$export, &$pipe, $module_name = '');
+
+}
+
+/**
+ * Interface for backwards compatibility with older versions of Rules.
+ *
+ * Mis-spelled interface provided so that contributed modules which were
+ * implementing the wrong spelling (corrected in Rules 7.x-2.12) will not stop
+ * working now that the interface is spelled correctly.
+ *
+ * @todo Remove this when we can be sure that no contributed modules are
+ * still using the wrong spelling.
+ */
+interface RulesPluginFeaturesIntegrationInterace extends RulesPluginFeaturesIntegrationInterface {
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules.info b/profiles/wcm_base/modules/contrib/rules/rules.info
new file mode 100644
index 0000000000000000000000000000000000000000..123805da6f4acb13371cb66f4f002a3c7caa2a25
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules.info
@@ -0,0 +1,35 @@
+name = Rules
+description = React on events and conditionally evaluate actions.
+package = Rules
+core = 7.x
+files[] = rules.features.inc
+files[] = includes/faces.inc
+files[] = includes/rules.core.inc
+files[] = includes/rules.event.inc
+files[] = includes/rules.processor.inc
+files[] = includes/rules.plugins.inc
+files[] = includes/rules.state.inc
+files[] = modules/comment.rules.inc
+files[] = modules/node.eval.inc
+files[] = modules/node.rules.inc
+files[] = modules/php.eval.inc
+files[] = modules/rules_core.eval.inc
+files[] = modules/system.eval.inc
+files[] = modules/taxonomy.rules.inc
+files[] = ui/ui.controller.inc
+files[] = ui/ui.core.inc
+files[] = ui/ui.data.inc
+files[] = ui/ui.plugins.inc
+
+; Test cases
+files[] = tests/rules.test
+files[] = tests/rules_test.rules.inc
+
+dependencies[] = entity_token
+dependencies[] = entity
+
+; Information added by Drupal.org packaging script on 2019-01-24
+version = "7.x-2.12"
+core = "7.x"
+project = "rules"
+datestamp = "1548305586"
diff --git a/profiles/wcm_base/modules/contrib/rules/rules.install b/profiles/wcm_base/modules/contrib/rules/rules.install
new file mode 100644
index 0000000000000000000000000000000000000000..b852bae1b0aea64483a782aef23bae9789ee80b5
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules.install
@@ -0,0 +1,563 @@
+<?php
+
+/**
+ * @file
+ * Rules - Installation file.
+ */
+
+/**
+ * Implements hook_enable().
+ */
+function rules_enable() {
+  // Enable evaluation of Rules right after enabling the module.
+  rules_event_invocation_enabled(TRUE);
+}
+
+/**
+ * Implements hook_install().
+ */
+function rules_install() {
+  module_load_include('inc', 'rules', 'modules/events');
+  // Set the modules' weight to 20, see
+  // https://www.drupal.org/node/445084#comment-1533280 for the reasoning.
+  db_query("UPDATE {system} SET weight = 20 WHERE name = 'rules'");
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function rules_uninstall() {
+  variable_del('rules_debug');
+  variable_del('rules_debug_log');
+  variable_del('rules_log_errors');
+  variable_del('rules_log_level');
+
+  variable_del('rules_clean_path');
+  variable_del('rules_path_cleaning_callback');
+  variable_del('rules_path_lower_case');
+  variable_del('rules_path_replacement_char');
+  variable_del('rules_path_transliteration');
+
+  // Delete all the debug region variables and then clear the variables cache.
+  db_delete('variable')
+    ->condition('name', 'rules_debug_region_%', 'LIKE')
+    ->execute();
+  cache_clear_all('variables', 'cache_bootstrap');
+}
+
+/**
+ * Implements hook_schema().
+ */
+function rules_schema() {
+  $schema['rules_config'] = array(
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'The internal identifier for any configuration.',
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => '64',
+        'not null' => TRUE,
+        'description' => 'The name of the configuration.',
+      ),
+      'label' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+        'description' => 'The label of the configuration.',
+        'default' => 'unlabeled',
+      ),
+      'plugin' => array(
+        'type' => 'varchar',
+        'length' => 127,
+        'not null' => TRUE,
+        'description' => 'The name of the plugin of this configuration.',
+      ),
+      'active' => array(
+        'description' => 'Boolean indicating whether the configuration is active. Usage depends on how the using module makes use of it.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'weight' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+        'description' => 'Weight of the configuration. Usage depends on how the using module makes use of it.',
+      ),
+      'status' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        // Set the default to ENTITY_CUSTOM without using the constant as it is
+        // not safe to use it at this point.
+        'default' => 0x01,
+        'size' => 'tiny',
+        'description' => 'The exportable status of the entity.',
+      ),
+      'dirty' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+        'description' => 'Dirty configurations fail the integrity check, e.g. due to missing dependencies.',
+      ),
+      'module' => array(
+        'description' => 'The name of the providing module if the entity has been defined in code.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+      ),
+      'owner' => array(
+        'description' => 'The name of the module via which the rule has been configured.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => 'rules',
+      ),
+      'access_exposed' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+        'description' => 'Whether to use a permission to control access for using components.',
+      ),
+      'data' => array(
+        'type' => 'blob',
+        'size' => 'big',
+        'not null' => FALSE,
+        'serialize' => TRUE,
+        'description' => 'Everything else, serialized.',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'name' => array('name'),
+    ),
+    'indexes' => array(
+      'plugin' => array('plugin', 'active'),
+    ),
+  );
+  $schema['rules_trigger'] = array(
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier of the configuration.',
+      ),
+      'event' => array(
+        'type' => 'varchar',
+        'length' => '127',
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The name of the event on which the configuration should be triggered.',
+      ),
+    ),
+    'primary key' => array('id', 'event'),
+    'foreign keys' => array(
+      'table' => 'rules_config',
+      'columns' => array('id' => 'id'),
+    ),
+  );
+  $schema['rules_tags'] = array(
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier of the configuration.',
+      ),
+      'tag' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+        'description' => 'The tag string associated with this configuration',
+      ),
+    ),
+    'primary key' => array('id', 'tag'),
+    'foreign keys' => array(
+      'table' => 'rules_config',
+      'columns' => array('id' => 'id'),
+    ),
+  );
+  $schema['rules_dependencies'] = array(
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier of the configuration.',
+      ),
+      'module' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+        'description' => 'The name of the module that is required for the configuration.',
+      ),
+    ),
+    'primary key' => array('id', 'module'),
+    'indexes' => array(
+      'module' => array('module'),
+    ),
+    'foreign keys' => array(
+      'table' => 'rules_config',
+      'columns' => array('id' => 'id'),
+    ),
+  );
+  $schema['cache_rules'] = drupal_get_schema_unprocessed('system', 'cache');
+  $schema['cache_rules']['description'] = 'Cache table for the rules engine to store configured items.';
+  return $schema;
+}
+
+/**
+ * Upgrade from Rules 6.x-1.x to 7.x.
+ */
+function rules_update_7200() {
+  // Create the new db tables first.
+  $schema['rules_config'] = array(
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'The internal identifier for any configuration.',
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+        'description' => 'The name of the configuration.',
+      ),
+      'label' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => TRUE,
+        'description' => 'The label of the configuration.',
+        'default' => 'unlabeled',
+      ),
+      'plugin' => array(
+        'type' => 'varchar',
+        'length' => 127,
+        'not null' => TRUE,
+        'description' => 'The name of the plugin of this configuration.',
+      ),
+      'active' => array(
+        'description' => 'Boolean indicating whether the configuration is active. Usage depends on how the using module makes use of it.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'weight' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+        'description' => 'Weight of the configuration. Usage depends on how the using module makes use of it.',
+      ),
+      'status' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        // Set the default to ENTITY_CUSTOM without using the constant as it is
+        // not safe to use it at this point.
+        'default' => 0x01,
+        'size' => 'tiny',
+        'description' => 'The exportable status of the entity.',
+      ),
+      'module' => array(
+        'description' => 'The name of the providing module if the entity has been defined in code.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+      ),
+      'data' => array(
+        'type' => 'blob',
+        'size' => 'big',
+        'not null' => FALSE,
+        'serialize' => TRUE,
+        'description' => 'Everything else, serialized.',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'name' => array('name'),
+    ),
+  );
+  $schema['rules_trigger'] = array(
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier of the configuration.',
+      ),
+      'event' => array(
+        'type' => 'varchar',
+        'length' => '127',
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The name of the event on which the configuration should be triggered.',
+      ),
+    ),
+    'primary key' => array('id', 'event'),
+    'foreign keys' => array(
+      'table' => 'rules_config',
+      'columns' => array('id' => 'id'),
+    ),
+  );
+  db_create_table('rules_config', $schema['rules_config']);
+  db_create_table('rules_trigger', $schema['rules_trigger']);
+  // The cache table already exists, but changed. So re-create it.
+  db_drop_table('cache_rules');
+  $schema['cache_rules'] = drupal_get_schema_unprocessed('system', 'cache');
+  $schema['cache_rules']['description'] = 'Cache table for the rules engine to store configured items.';
+  db_create_table('cache_rules', $schema['cache_rules']);
+  // Remove deprecated variables.
+  variable_del('rules_inactive_sets');
+  variable_del('rules_show_fixed');
+  variable_del('rules_hide_token_message');
+  variable_del('rules_counter');
+
+  return t('The database tables for Rules 2.x have been created. The old tables from Rules 1.x are still available and contain your rules, which are not updated yet.');
+}
+
+/**
+ * Add in the exportable entity db columns as required by the entity API.
+ */
+function rules_update_7201() {
+  // Previously this was update 7200, so check whether we need to run it really.
+  // The update has been moved as 7200 needs to be the 6.x-7.x upgrade.
+  if (!db_field_exists('rules_config', 'status')) {
+    db_add_field('rules_config', 'status', array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => ENTITY_CUSTOM,
+      'size' => 'tiny',
+      'description' => 'The exportable status of the entity.',
+    ));
+    // The module column did already exist before.
+  }
+}
+
+/**
+ * Add an index for the rules configuration plugin column.
+ */
+function rules_update_7202() {
+  db_add_index('rules_config', 'plugin', array('plugin'));
+}
+
+/**
+ * Fix the length of the rules_config.name column.
+ */
+function rules_update_7203() {
+  db_drop_unique_key('rules_config', 'name');
+  $keys = array(
+    'unique keys' => array(
+      'name' => array('name'),
+    ),
+  );
+  db_change_field('rules_config', 'name', 'name', array(
+    'type' => 'varchar',
+    'length' => '64',
+    'not null' => TRUE,
+    'description' => 'The name of the configuration.',
+  ), $keys);
+}
+
+/**
+ * Add a table for rules-config tags.
+ */
+function rules_update_7204() {
+  if (!db_table_exists('rules_tags')) {
+    $schema['rules_tags'] = array(
+      'fields' => array(
+        'id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'The primary identifier of the configuration.',
+        ),
+        'tag' => array(
+          'type' => 'varchar',
+          'length' => '255',
+          'not null' => TRUE,
+          'description' => 'The tag string associated with this configuration',
+        ),
+      ),
+      'primary key' => array('id', 'tag'),
+      'foreign keys' => array(
+        'table' => 'rules_config',
+        'columns' => array('id' => 'id'),
+      ),
+    );
+    db_create_table('rules_tags', $schema['rules_tags']);
+  }
+}
+
+/**
+ * Add the rules_dependencies table and the rules_config.dirty column.
+ */
+function rules_update_7205() {
+  if (!db_table_exists('rules_dependencies')) {
+    $schema['rules_dependencies'] = array(
+      'fields' => array(
+        'id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'The primary identifier of the configuration.',
+        ),
+        'module' => array(
+          'type' => 'varchar',
+          'length' => '255',
+          'not null' => TRUE,
+          'description' => 'The name of the module that is required for the configuration.',
+        ),
+      ),
+      'primary key' => array('id', 'module'),
+      'indexes' => array(
+        'module' => array('module'),
+      ),
+      'foreign keys' => array(
+        'table' => 'rules_config',
+        'columns' => array('id' => 'id'),
+      ),
+    );
+    db_create_table('rules_dependencies', $schema['rules_dependencies']);
+  }
+  if (!db_field_exists('rules_config', 'dirty')) {
+    db_add_field('rules_config', 'dirty', array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 0,
+      'size' => 'tiny',
+    ));
+  }
+}
+
+/**
+ * Flush all caches.
+ */
+function rules_update_7206() {
+  // The update system is going to flush all caches anyway, so nothing to do.
+}
+
+/**
+ * Flush all caches.
+ */
+function rules_update_7207() {
+  // The update system is going to flush all caches anyway, so nothing to do.
+}
+
+/**
+ * Flush all caches to update the data_is_empty condition info.
+ */
+function rules_update_7208() {
+  // The update system is going to flush all caches anyway, so nothing to do.
+}
+
+/**
+ * Creates a flag that enables a permission for using components.
+ */
+function rules_update_7209() {
+  // Create a access exposed flag column.
+  db_add_field('rules_config', 'access_exposed', array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'size' => 'tiny',
+    'description' => 'Whether to use a permission to control access for using components.',
+  ));
+}
+
+/**
+ * Deletes the unused rules_empty_sets variable.
+ */
+function rules_update_7210() {
+  variable_del('rules_empty_sets');
+}
+
+/**
+ * Creates the "owner" column.
+ */
+function rules_update_7211() {
+  // Create a owner column.
+  if (!db_field_exists('rules_config', 'owner')) {
+    db_add_field('rules_config', 'owner', array(
+      'description' => 'The name of the module via which the rule has been configured.',
+      'type' => 'varchar',
+      'length' => 255,
+      'not null' => TRUE,
+      'default' => 'rules',
+    ));
+  }
+}
+
+/**
+ * Make sure registry gets rebuilt to avoid upgrade troubles.
+ */
+function rules_update_7212() {
+  // Make sure module information gets refreshed and registry is rebuilt.
+  drupal_static_reset('system_rebuild_module_data');
+  registry_rebuild();
+}
+
+/**
+ * Recover the "owner" property for broken configurations.
+ */
+function rules_update_7213() {
+  $rows = db_select('rules_config', 'c')
+    ->fields('c')
+    ->condition('status', ENTITY_OVERRIDDEN)
+    ->condition('owner', 'rules', '<>')
+    ->execute()
+    ->fetchAllAssoc('id');
+
+  foreach ($rows as $id => $row) {
+    if ($row->module == $row->owner) {
+      db_update('rules_config')
+        ->condition('id', $id)
+        ->fields(array('owner' => 'rules'))
+        ->execute();
+    }
+  }
+}
+
+/**
+ * Switch out the rules_event_whitelist variable for a cache equivalent.
+ */
+function rules_update_7214() {
+  // Enable Rules if currently disabled so that this update won't fail.
+  $disable_rules = FALSE;
+  if (!module_exists('rules')) {
+    module_enable(array('rules'));
+    $disable_rules = TRUE;
+  }
+  // Set new event_whitelist cache cid.
+  rules_set_cache('rules_event_whitelist', variable_get('rules_event_whitelist', array()));
+  // Delete old conf variable.
+  variable_del('rules_event_whitelist');
+  // Avoid any missing class errors.
+  registry_rebuild();
+  // Clear and rebuild Rules caches.
+  // See: rules_admin_settings_cache_rebuild_submit.
+  rules_clear_cache();
+  rules_get_cache();
+  _rules_rebuild_component_cache();
+  RulesEventSet::rebuildEventCache();
+  // Disable Rules again if it was disabled before this update started.
+  if ($disable_rules) {
+    module_disable(array('rules'));
+  }
+}
+
+/**
+ * Add an index for retrieving active config of a certain plugin.
+ */
+function rules_update_7215() {
+  if (db_index_exists('rules_config', 'plugin')) {
+    db_drop_index('rules_config', 'plugin');
+  }
+  db_add_index('rules_config', 'plugin', array('plugin', 'active'));
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules.module b/profiles/wcm_base/modules/contrib/rules/rules.module
new file mode 100644
index 0000000000000000000000000000000000000000..e713cd9598e9191fdb7d5950467e1ef7c59958fb
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules.module
@@ -0,0 +1,1756 @@
+<?php
+
+/**
+ * @file
+ * Rules engine module.
+ */
+
+// The class autoloader may fail for classes added in 7.x-2.4 (Issue 2090511).
+if (!drupal_autoload_class('RulesEventHandlerEntityBundle')) {
+  require_once dirname(__FILE__) . '/includes/rules.event.inc';
+}
+
+// Include our hook implementations early, as they can be called even before
+// hook_init().
+require_once dirname(__FILE__) . '/modules/events.inc';
+
+/**
+ * Implements hook_module_implements_alter().
+ */
+function rules_module_implements_alter(&$implementations, $hook) {
+  // Ensures the invocation of hook_menu_get_item_alter() triggers
+  // rules_menu_get_item_alter() first so the rules invocation is ready for all
+  // sub-sequent hook implementations.
+  if ($hook == 'menu_get_item_alter' && array_key_exists('rules', $implementations)) {
+    $group = $implementations['rules'];
+    unset($implementations['rules']);
+    $implementations = array_merge(array('rules' => $group), $implementations);
+  }
+}
+
+/**
+ * Implements hook_menu_get_item_alter().
+ */
+function rules_menu_get_item_alter() {
+  // Make sure that event invocation is enabled before menu items are loaded.
+  // But make sure later calls to menu_get_item() won't automatically re-enabled
+  // the rules invocation.
+  // Example: modules that implement hook_entity_ENTITY_TYPE_load() might want
+  // to invoke Rules events in that load hook, which is also invoked for menu
+  // item loading. Since this can happen even before hook_init() we need to make
+  // sure that firing Rules events is enabled at that point. A typical use case
+  // for this is Drupal Commerce with commerce_cart_commerce_order_load().
+  if (!drupal_static('rules_init', FALSE)) {
+    rules_event_invocation_enabled(TRUE);
+  }
+}
+
+/**
+ * Implements hook_init().
+ */
+function rules_init() {
+  // See rules_menu_get_item_alter().
+  $rules_init = &drupal_static(__FUNCTION__, FALSE);
+  $rules_init = TRUE;
+  // Enable event invocation once hook_init() was invoked for Rules.
+  rules_event_invocation_enabled(TRUE);
+  rules_invoke_event('init');
+}
+
+/**
+ * Returns an instance of the rules UI controller.
+ *
+ * This function is for convenience, to ease re-using the Rules UI.
+ * See the rules_admin.module for example usage.
+ *
+ * @return RulesUIController
+ */
+function rules_ui() {
+  $static = drupal_static(__FUNCTION__);
+  if (!isset($static)) {
+    $static = new RulesUIController();
+  }
+  return $static;
+}
+
+/**
+ * Returns a new rules action.
+ *
+ * @param $name
+ *   The action's name.
+ * @param array $settings
+ *   The action's settings array.
+ *
+ * @return RulesAction
+ */
+function rules_action($name, $settings = array()) {
+  return rules_plugin_factory('action', $name, $settings);
+}
+
+/**
+ * Returns a new rules condition.
+ *
+ * @param $name
+ *   The condition's name.
+ * @param array $settings
+ *   The condition's settings array.
+ *
+ * @return RulesCondition
+ */
+function rules_condition($name, $settings = array()) {
+  return rules_plugin_factory('condition', $name, $settings);
+}
+
+/**
+ * Creates a new rule.
+ *
+ * @param array $variables
+ *   The array of variables to setup in the evaluation state, making them
+ *   available for the configuration elements. Values for the variables need to
+ *   be passed as argument when the rule is executed. Only Rule instances with
+ *   no variables can be embedded in other configurations, e.g. rule sets.
+ *   The array has to be keyed by variable name and contain a sub-array for each
+ *   variable that has the same structure as the arrays used for describing
+ *   parameters of an action, see hook_rules_action_info(). However, in addition
+ *   to that the following keys are supported:
+ *    - parameter: (optional) If set to FALSE, no parameter for the variable
+ *      is created - thus no argument needs to be passed to the rule for the
+ *      variable upon execution. As a consequence no value will be set
+ *      initially, but the "Set data value" action may be used to do so. This is
+ *      in particular useful for defining variables which can be provided to the
+ *      caller (see $provides argument) but need not be passed in as parameter.
+ * @param array $provides
+ *   The names of variables which should be provided to the caller. Only
+ *   variables contained in $variables may be specified.
+ *
+ * @return Rule
+ */
+function rule($variables = NULL, $provides = array()) {
+  return rules_plugin_factory('rule', $variables, $provides);
+}
+
+/**
+ * Creates a new reaction rule.
+ *
+ * @return RulesReactionRule
+ */
+function rules_reaction_rule() {
+  return rules_plugin_factory('reaction rule');
+}
+
+/**
+ * Creates a logical OR condition container.
+ *
+ * @param array $variables
+ *   An optional array as for rule().
+ *
+ * @return RulesOr
+ */
+function rules_or($variables = NULL) {
+  return rules_plugin_factory('or', $variables);
+}
+
+/**
+ * Creates a logical AND condition container.
+ *
+ * @param array $variables
+ *   An optional array as for rule().
+ *
+ * @return RulesAnd
+ */
+function rules_and($variables = NULL) {
+  return rules_plugin_factory('and', $variables);
+}
+
+/**
+ * Creates a loop.
+ *
+ * @param array $settings
+ *   The loop settings, containing
+ *     'list:select': The data selector for the list to loop over.
+ *     'item:var': Optionally a name for the list item variable.
+ *     'item:label': Optionally a label for the list item variable.
+ * @param array $variables
+ *   An optional array as for rule().
+ *
+ * @return RulesLoop
+ */
+function rules_loop($settings = array(), $variables = NULL) {
+  return rules_plugin_factory('loop', $settings, $variables);
+}
+
+/**
+ * Creates a rule set.
+ *
+ * @param array $variables
+ *   An array as for rule().
+ * @param array $provides
+ *   The names of variables which should be provided to the caller. See rule().
+ *
+ * @return RulesRuleSet
+ */
+function rules_rule_set($variables = array(), $provides = array()) {
+  return rules_plugin_factory('rule set', $variables, $provides);
+}
+
+/**
+ * Creates an action set.
+ *
+ * @param array $variables
+ *   An array as for rule().
+ * @param array $provides
+ *   The names of variables which should be provided to the caller. See rule().
+ *
+ * @return RulesActionSet
+ */
+function rules_action_set($variables = array(), $provides = array()) {
+  return rules_plugin_factory('action set', $variables, $provides);
+}
+
+/**
+ * Log a message to the rules logger.
+ *
+ * @param $msg
+ *   The message to log.
+ * @param array $args
+ *   An array of placeholder arguments as used by t().
+ * @param $priority
+ *   A priority as defined by the RulesLog class.
+ * @param RulesPlugin $element
+ *   (optional) The RulesElement causing the log entry.
+ * @param bool $scope
+ *   (optional) This may be used to denote the beginning (TRUE) or the end
+ *   (FALSE) of a new execution scope.
+ */
+function rules_log($msg, $args = array(), $priority = RulesLog::INFO, RulesPlugin $element = NULL, $scope = NULL) {
+  static $logger, $settings;
+
+  // Statically cache the variable settings as this is called very often.
+  if (!isset($settings)) {
+    $settings['rules_log_errors'] = variable_get('rules_log_errors', RulesLog::WARN);
+    $settings['rules_debug_log'] = variable_get('rules_debug_log', FALSE);
+    $settings['rules_debug'] = variable_get('rules_debug', FALSE);
+  }
+
+  if ($priority >= $settings['rules_log_errors']) {
+    $link = NULL;
+    if (isset($element) && isset($element->root()->name)) {
+      $link = l(t('edit configuration'), RulesPluginUI::path($element->root()->name, 'edit', $element));
+    }
+    // Disabled rules invocation to avoid an endless loop when using
+    // watchdog - which would trigger a rules event.
+    rules_event_invocation_enabled(FALSE);
+    watchdog('rules', $msg, $args, $priority == RulesLog::WARN ? WATCHDOG_WARNING : WATCHDOG_ERROR, $link);
+    rules_event_invocation_enabled(TRUE);
+  }
+  // Do nothing in case debugging is totally disabled.
+  if (!$settings['rules_debug_log'] && !$settings['rules_debug']) {
+    return;
+  }
+  if (!isset($logger)) {
+    $logger = RulesLog::logger();
+  }
+  $path = isset($element) && isset($element->root()->name) ? RulesPluginUI::path($element->root()->name, 'edit', $element) : NULL;
+  $logger->log($msg, $args, $priority, $scope, $path);
+}
+
+/**
+ * Fetches module definitions for the given hook name.
+ *
+ * Used for collecting events, rules, actions and condition from other modules.
+ *
+ * @param $hook
+ *   The hook of the definitions to get from invoking hook_rules_{$hook}.
+ */
+function rules_fetch_data($hook) {
+  $data = &drupal_static(__FUNCTION__, array());
+  static $discover = array(
+    'action_info' => 'RulesActionHandlerInterface',
+    'condition_info' => 'RulesConditionHandlerInterface',
+    'event_info' => 'RulesEventHandlerInterface',
+  );
+
+  if (!isset($data[$hook])) {
+    $data[$hook] = array();
+    foreach (module_implements('rules_' . $hook) as $module) {
+      $result = call_user_func($module . '_rules_' . $hook);
+      if (isset($result) && is_array($result)) {
+        foreach ($result as $name => $item) {
+          $item += array('module' => $module);
+          $data[$hook][$name] = $item;
+        }
+      }
+    }
+    // Support class discovery.
+    if (isset($discover[$hook])) {
+      $data[$hook] += rules_discover_plugins($discover[$hook]);
+    }
+    drupal_alter('rules_' . $hook, $data[$hook]);
+  }
+  return $data[$hook];
+}
+
+/**
+ * Discover plugin implementations.
+ *
+ * Class based plugin handlers must be loaded when rules caches are rebuilt,
+ * such that they get discovered properly. You have the following options:
+ *  - Put it into a regular module file (discouraged)
+ *  - Put it into your module.rules.inc file
+ *  - Put it in any file and declare it using hook_rules_file_info()
+ *  - Put it in any file and declare it using hook_rules_directory()
+ *
+ * In addition to that, the class must be loadable via regular class
+ * auto-loading, thus put the file holding the class in your info file or use
+ * another class-loader.
+ *
+ * @param string $class
+ *   The class or interface the plugins must implement. For a plugin to be
+ *   discovered it must have a static getInfo() method also.
+ *
+ * @return array
+ *   An info-hook style array containing info about discovered plugins.
+ *
+ * @see RulesActionHandlerInterface
+ * @see RulesConditionHandlerInterface
+ * @see RulesEventHandlerInterface
+ */
+function rules_discover_plugins($class) {
+  // Make sure all files possibly holding plugins are included.
+  RulesAbstractPlugin::includeFiles();
+
+  $items = array();
+  foreach (get_declared_classes() as $plugin_class) {
+    if (is_subclass_of($plugin_class, $class) && method_exists($plugin_class, 'getInfo')) {
+      $info = call_user_func(array($plugin_class, 'getInfo'));
+      $info['class'] = $plugin_class;
+      $info['module'] = _rules_discover_module($plugin_class);
+      $items[$info['name']] = $info;
+    }
+  }
+  return $items;
+}
+
+/**
+ * Determines the module providing the given class.
+ *
+ * @param string $class
+ *   The name of the class or interface plugins to discover.
+ *
+ * @return string|false
+ *   The path of the class, relative to the Drupal installation root,
+ *   or FALSE if not discovered.
+ */
+function _rules_discover_module($class) {
+  $paths = &drupal_static(__FUNCTION__);
+
+  if (!isset($paths)) {
+    // Build up a map of modules keyed by their directory.
+    foreach (system_list('module_enabled') as $name => $module_info) {
+      $paths[dirname($module_info->filename)] = $name;
+    }
+  }
+
+  // Retrieve the class file and convert its absolute path to a regular Drupal
+  // path relative to the installation root.
+  $reflection = new ReflectionClass($class);
+  $path = str_replace(realpath(DRUPAL_ROOT) . DIRECTORY_SEPARATOR, '', realpath(dirname($reflection->getFileName())));
+  $path = DIRECTORY_SEPARATOR != '/' ? str_replace(DIRECTORY_SEPARATOR, '/', $path) : $path;
+
+  // Go up the path until we match a module.
+  $parts = explode('/', $path);
+  while (!isset($paths[$path]) && array_pop($parts)) {
+    $path = dirname($path);
+  }
+  return isset($paths[$path]) ? $paths[$path] : FALSE;
+}
+
+/**
+ * Gets a rules cache entry.
+ */
+function &rules_get_cache($cid = 'data') {
+  // Make use of the fast, advanced drupal static pattern.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['cache'] = &drupal_static(__FUNCTION__, array());
+  }
+  $cache = &$drupal_static_fast['cache'];
+
+  if (!isset($cache[$cid])) {
+    // The main 'data' cache includes translated strings, so each language is
+    // cached separately.
+    $cid_suffix = $cid == 'data' ? ':' . $GLOBALS['language']->language : '';
+
+    if ($get = cache_get($cid . $cid_suffix, 'cache_rules')) {
+      $cache[$cid] = $get->data;
+    }
+    else {
+      // Prevent stampeding by ensuring the cache is rebuilt just once at the
+      // same time.
+      while (!lock_acquire(__FUNCTION__ . $cid . $cid_suffix, 60)) {
+        // Now wait until the lock is released.
+        lock_wait(__FUNCTION__ . $cid . $cid_suffix, 30);
+        // If the lock is released it's likely the cache was rebuild. Thus check
+        // again if we can fetch it from the persistent cache.
+        if ($get = cache_get($cid . $cid_suffix, 'cache_rules')) {
+          $cache[$cid] = $get->data;
+          return $cache[$cid];
+        }
+      }
+      if ($cid === 'data') {
+        // There is no 'data' cache so we need to rebuild it. Make sure
+        // subsequent cache gets of the main 'data' cache during rebuild get
+        // the interim cache by passing in the reference of the static cache
+        // variable.
+        _rules_rebuild_cache($cache['data']);
+      }
+      elseif (strpos($cid, 'comp_') === 0) {
+        $cache[$cid] = FALSE;
+        _rules_rebuild_component_cache();
+      }
+      elseif (strpos($cid, 'event_') === 0 || $cid == 'rules_event_whitelist') {
+        $cache[$cid] = FALSE;
+        RulesEventSet::rebuildEventCache();
+      }
+      else {
+        $cache[$cid] = FALSE;
+      }
+      // Ensure a set lock is released.
+      lock_release(__FUNCTION__ . $cid . $cid_suffix);
+    }
+  }
+  return $cache[$cid];
+}
+
+/**
+ * Rebuilds the rules cache.
+ *
+ * This rebuilds the rules 'data' cache and invokes rebuildCache() methods on
+ * all plugin classes, which allows plugins to add their own data to the cache.
+ * The cache is rebuilt in the order the plugins are defined.
+ *
+ * Note that building the action/condition info cache triggers loading of all
+ * components, thus depends on entity-loading and so syncing entities in code
+ * to the database.
+ *
+ * @see rules_rules_plugin_info()
+ * @see entity_defaults_rebuild()
+ */
+function _rules_rebuild_cache(&$cache) {
+  foreach (array('data_info', 'plugin_info') as $hook) {
+    $cache[$hook] = rules_fetch_data($hook);
+  }
+  foreach ($cache['plugin_info'] as $name => &$info) {
+    // Let the items add something to the cache.
+    $item = new $info['class']();
+    $item->rebuildCache($info, $cache);
+  }
+  $cid_suffix = ':' . $GLOBALS['language']->language;
+  cache_set('data' . $cid_suffix, $cache, 'cache_rules');
+}
+
+/**
+ * Cache components to allow efficient usage via rules_invoke_component().
+ *
+ * @see rules_invoke_component()
+ * @see rules_get_cache()
+ */
+function _rules_rebuild_component_cache() {
+  $components = rules_get_components();
+
+  foreach ($components as $id => $component) {
+    // If a component is marked as dirty, check if this still applies.
+    if ($component->dirty) {
+      rules_config_update_dirty_flag($component);
+    }
+    if (!$component->dirty) {
+      // Clone the component to avoid modules getting the to be cached
+      // version from the static loading cache.
+      $component = clone $component;
+      $component->optimize();
+      // Allow modules to alter the cached component.
+      drupal_alter('rules_component', $component->plugin, $component);
+      rules_set_cache('comp_' . $component->name, $component);
+    }
+  }
+}
+
+/**
+ * Sets a rules cache item.
+ *
+ * In addition to calling cache_set(), this function makes sure the cache item
+ * is immediately available via rules_get_cache() by keeping all cache items
+ * in memory. That way we can guarantee rules_get_cache() is able to retrieve
+ * any cache item, even if all cache gets fail.
+ *
+ * @see rules_get_cache()
+ */
+function rules_set_cache($cid, $data) {
+  $cache = &drupal_static('rules_get_cache', array());
+  $cache[$cid] = $data;
+  cache_set($cid, $data, 'cache_rules');
+}
+
+/**
+ * Implements hook_flush_caches().
+ */
+function rules_flush_caches() {
+  return array('cache_rules');
+}
+
+/**
+ * Clears the rule set cache.
+ */
+function rules_clear_cache() {
+  cache_clear_all('*', 'cache_rules', TRUE);
+  drupal_static_reset('rules_get_cache');
+  drupal_static_reset('rules_fetch_data');
+  drupal_static_reset('rules_config_update_dirty_flag');
+  entity_get_controller('rules_config')->resetCache();
+}
+
+/**
+ * Imports the given export and returns the imported configuration.
+ *
+ * @param string $export
+ *   A serialized string in JSON format as produced by the RulesPlugin::export()
+ *   method, or the PHP export as usual PHP array.
+ * @param string $error_msg
+ *
+ * @return RulesPlugin
+ */
+function rules_import($export, &$error_msg = '') {
+  return entity_get_controller('rules_config')->import($export, $error_msg);
+}
+
+/**
+ * Wraps the given data.
+ *
+ * @param $data
+ *   If available, the actual data, else NULL.
+ * @param $info
+ *   An array of info about this data.
+ * @param bool $force
+ *   Usually data is only wrapped if really needed. If set to TRUE, wrapping the
+ *   data is forced, so primitive data types are also wrapped.
+ *
+ * @return EntityMetadataWrapper
+ *   An EntityMetadataWrapper or the unwrapped data.
+ *
+ * @see hook_rules_data_info()
+ */
+function &rules_wrap_data($data = NULL, $info, $force = FALSE) {
+  // If the data is already wrapped, use the existing wrapper.
+  if ($data instanceof EntityMetadataWrapper) {
+    return $data;
+  }
+  $cache = rules_get_cache();
+  // Define the keys to be passed through to the metadata wrapper.
+  $wrapper_keys = array_flip(array('property info', 'property defaults'));
+  if (isset($cache['data_info'][$info['type']])) {
+    $info += array_intersect_key($cache['data_info'][$info['type']], $wrapper_keys);
+  }
+  // If a list is given, also add in the info of the item type.
+  $list_item_type = entity_property_list_extract_type($info['type']);
+  if ($list_item_type && isset($cache['data_info'][$list_item_type])) {
+    $info += array_intersect_key($cache['data_info'][$list_item_type], $wrapper_keys);
+  }
+  // By default we do not wrap the data, except for completely unknown types.
+  if (!empty($cache['data_info'][$info['type']]['wrap']) || $list_item_type || $force || empty($cache['data_info'][$info['type']])) {
+    unset($info['handler']);
+    // Allow data types to define custom wrapper classes.
+    if (!empty($cache['data_info'][$info['type']]['wrapper class'])) {
+      $class = $cache['data_info'][$info['type']]['wrapper class'];
+      $wrapper = new $class($info['type'], $data, $info);
+    }
+    else {
+      $wrapper = entity_metadata_wrapper($info['type'], $data, $info);
+    }
+    return $wrapper;
+  }
+  return $data;
+}
+
+/**
+ * Unwraps the given data, if it's wrapped.
+ *
+ * @param array $data
+ *   An array of wrapped data.
+ * @param array $info
+ *   Optionally an array of info about how to unwrap the data. Keyed as $data.
+ *
+ * @return array
+ *   An array containing unwrapped or passed through data.
+ */
+function rules_unwrap_data(array $data, $info = array()) {
+  $cache = rules_get_cache();
+  foreach ($data as $key => $entry) {
+    // If it's a wrapper, unwrap unless specified otherwise.
+    if ($entry instanceof EntityMetadataWrapper) {
+      if (!isset($info[$key]['allow null'])) {
+        $info[$key]['allow null'] = FALSE;
+      }
+      if (!isset($info[$key]['wrapped'])) {
+        // By default, do not unwrap special data types that are always wrapped.
+        $info[$key]['wrapped'] = (isset($info[$key]['type']) && is_string($info[$key]['type']) && !empty($cache['data_info'][$info[$key]['type']]['is wrapped']));
+      }
+      // Activate the decode option by default if 'sanitize' is not enabled, so
+      // any text is either sanitized or decoded.
+      // @see EntityMetadataWrapper::value()
+      $options = $info[$key] + array('decode' => empty($info[$key]['sanitize']));
+
+      try {
+        if (!($info[$key]['allow null'] && $info[$key]['wrapped'])) {
+          $value = $entry->value($options);
+
+          if (!$info[$key]['wrapped']) {
+            $data[$key] = $value;
+          }
+          if (!$info[$key]['allow null'] && !isset($value)) {
+            throw new RulesEvaluationException('The variable or parameter %name is empty.', array('%name' => $key));
+          }
+        }
+      }
+      catch (EntityMetadataWrapperException $e) {
+        throw new RulesEvaluationException('Unable to get the data value for the variable or parameter %name. Error: !error', array('%name' => $key, '!error' => $e->getMessage()));
+      }
+    }
+  }
+  return $data;
+}
+
+/**
+ * Gets event info for a given event.
+ *
+ * @param string $event_name
+ *   A (configured) event name.
+ *
+ * @return array
+ *   An array of event info. If the event is unknown, a suiting info array is
+ *   generated and returned
+ */
+function rules_get_event_info($event_name) {
+  $base_event_name = rules_get_event_base_name($event_name);
+  $events = rules_fetch_data('event_info');
+  if (isset($events[$base_event_name])) {
+    return $events[$base_event_name] + array('name' => $base_event_name);
+  }
+  return array(
+    'label' => t('Unknown event "!event_name"', array('!event_name' => $base_event_name)),
+    'name' => $base_event_name,
+  );
+}
+
+/**
+ * Returns the base name of a configured event name.
+ *
+ * For a configured event name like node_view--article the base event name
+ * node_view is returned.
+ *
+ * @param string $event_name
+ *   A (configured) event name.
+ *
+ * @return string
+ *   The event base name.
+ */
+function rules_get_event_base_name($event_name) {
+  // Cut off any suffix from a configured event name.
+  if (strpos($event_name, '--') !== FALSE) {
+    $parts = explode('--', $event_name, 2);
+    return $parts[0];
+  }
+  return $event_name;
+}
+
+/**
+ * Returns the rule event handler for the given event.
+ *
+ * Events having no settings are handled via the class RulesEventSettingsNone.
+ *
+ * @param string $event_name
+ *   The event name (base or configured).
+ * @param array $settings
+ *   (optional) An array of event settings to set on the handler.
+ *
+ * @return RulesEventHandlerInterface
+ *   The event handler.
+ */
+function rules_get_event_handler($event_name, array $settings = NULL) {
+  $event_name = rules_get_event_base_name($event_name);
+  $event_info = rules_get_event_info($event_name);
+  $class = !empty($event_info['class']) ? $event_info['class'] : 'RulesEventDefaultHandler';
+  $handler = new $class($event_name, $event_info);
+  return isset($settings) ? $handler->setSettings($settings) : $handler;
+}
+
+/**
+ * Creates a new instance of a the given rules plugin.
+ *
+ * @return RulesPlugin
+ */
+function rules_plugin_factory($plugin_name, $arg1 = NULL, $arg2 = NULL) {
+  $cache = rules_get_cache();
+  if (isset($cache['plugin_info'][$plugin_name]['class'])) {
+    return new $cache['plugin_info'][$plugin_name]['class']($arg1, $arg2);
+  }
+}
+
+/**
+ * Implements hook_rules_plugin_info().
+ *
+ * Note that the cache is rebuilt in the order of the plugins. Therefore the
+ * condition and action plugins must be at the top, so that any components
+ * re-building their cache can create configurations including properly setup-ed
+ * actions and conditions.
+ */
+function rules_rules_plugin_info() {
+  return array(
+    'condition' => array(
+      'class' => 'RulesCondition',
+      'embeddable' => 'RulesConditionContainer',
+      'extenders' => array(
+        'RulesPluginImplInterface' => array(
+          'class' => 'RulesAbstractPluginDefaults',
+        ),
+        'RulesPluginFeaturesIntegrationInterface' => array(
+          'methods' => array(
+            'features_export' => 'rules_features_abstract_default_features_export',
+          ),
+        ),
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesAbstractPluginUI',
+        ),
+      ),
+    ),
+    'action' => array(
+      'class' => 'RulesAction',
+      'embeddable' => 'RulesActionContainer',
+      'extenders' => array(
+        'RulesPluginImplInterface' => array(
+          'class' => 'RulesAbstractPluginDefaults',
+        ),
+        'RulesPluginFeaturesIntegrationInterface' => array(
+          'methods' => array(
+            'features_export' => 'rules_features_abstract_default_features_export',
+          ),
+        ),
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesAbstractPluginUI',
+        ),
+      ),
+    ),
+    'or' => array(
+      'label' => t('Condition set (OR)'),
+      'class' => 'RulesOr',
+      'embeddable' => 'RulesConditionContainer',
+      'component' => TRUE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionContainerUI',
+        ),
+      ),
+    ),
+    'and' => array(
+      'label' => t('Condition set (AND)'),
+      'class' => 'RulesAnd',
+      'embeddable' => 'RulesConditionContainer',
+      'component' => TRUE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionContainerUI',
+        ),
+      ),
+    ),
+    'action set' => array(
+      'label' => t('Action set'),
+      'class' => 'RulesActionSet',
+      'embeddable' => FALSE,
+      'component' => TRUE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesActionContainerUI',
+        ),
+      ),
+    ),
+    'rule' => array(
+      'label' => t('Rule'),
+      'class' => 'Rule',
+      'embeddable' => 'RulesRuleSet',
+      'component' => TRUE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesRuleUI',
+        ),
+      ),
+    ),
+    'loop' => array(
+      'class' => 'RulesLoop',
+      'embeddable' => 'RulesActionContainer',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesLoopUI',
+        ),
+      ),
+    ),
+    'reaction rule' => array(
+      'class' => 'RulesReactionRule',
+      'embeddable' => FALSE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesReactionRuleUI',
+        ),
+      ),
+    ),
+    'event set' => array(
+      'class' => 'RulesEventSet',
+      'embeddable' => FALSE,
+    ),
+    'rule set' => array(
+      'label' => t('Rule set'),
+      'class' => 'RulesRuleSet',
+      'component' => TRUE,
+      // Rule sets don't get embedded - we use a separate action to execute.
+      'embeddable' => FALSE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesRuleSetUI',
+        ),
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function rules_entity_info() {
+  return array(
+    'rules_config' => array(
+      'label' => t('Rules configuration'),
+      'controller class' => 'RulesEntityController',
+      'base table' => 'rules_config',
+      'fieldable' => TRUE,
+      'entity keys' => array(
+        'id' => 'id',
+        'name' => 'name',
+        'label' => 'label',
+      ),
+      'module' => 'rules',
+      'static cache' => TRUE,
+      'bundles' => array(),
+      'configuration' => TRUE,
+      'exportable' => TRUE,
+      'export' => array(
+        'default hook' => 'default_rules_configuration',
+      ),
+      'access callback' => 'rules_config_access',
+      'features controller class' => 'RulesFeaturesController',
+    ),
+  );
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function rules_hook_info() {
+  foreach (array('plugin_info', 'rules_directory', 'data_info', 'condition_info', 'action_info', 'event_info', 'file_info', 'evaluator_info', 'data_processor_info') as $hook) {
+    $hooks['rules_' . $hook] = array(
+      'group' => 'rules',
+    );
+    $hooks['rules_' . $hook . '_alter'] = array(
+      'group' => 'rules',
+    );
+  }
+  $hooks['default_rules_configuration'] = array(
+    'group' => 'rules_defaults',
+  );
+  $hooks['default_rules_configuration_alter'] = array(
+    'group' => 'rules_defaults',
+  );
+  return $hooks;
+}
+
+/**
+ * Load rule configurations from the database.
+ *
+ * This function should be used whenever you need to load more than one entity
+ * from the database. The entities are loaded into memory and will not require
+ * database access if loaded again during the same page request.
+ *
+ * @see hook_entity_info()
+ * @see RulesEntityController
+ *
+ * @param array|false $names
+ *   An array of rules configuration names or FALSE to load all.
+ * @param array $conditions
+ *   An array of conditions in the form 'field' => $value.
+ *
+ * @return array
+ *   An array of rule configurations indexed by their ids.
+ */
+function rules_config_load_multiple($names = array(), $conditions = array()) {
+  return entity_load_multiple_by_name('rules_config', $names, $conditions);
+}
+
+/**
+ * Loads a single rule configuration from the database.
+ *
+ * @see rules_config_load_multiple()
+ *
+ * @return RulesPlugin
+ */
+function rules_config_load($name) {
+  return entity_load_single('rules_config', $name);
+}
+
+/**
+ * Returns an array of configured components.
+ *
+ * For actually executing a component use rules_invoke_component(), as this
+ * retrieves the component from cache instead.
+ *
+ * @param $label
+ *   Whether to return only the label or the whole component object.
+ * @param $type
+ *   Optionally filter for 'action' or 'condition' components.
+ * @param array $conditions
+ *   An array of additional conditions as required by rules_config_load().
+ *
+ * @return array
+ *   An array keyed by component name containing either the label or the config.
+ */
+function rules_get_components($label = FALSE, $type = NULL, $conditions = array()) {
+  $cache = rules_get_cache();
+  $plugins = array_keys(rules_filter_array($cache['plugin_info'], 'component', TRUE));
+  $conditions = $conditions + array('plugin' => $plugins);
+  $faces = array(
+    'action' => 'RulesActionInterface',
+    'condition' => 'RulesConditionInterface',
+  );
+  $items = array();
+  foreach (rules_config_load_multiple(FALSE, $conditions) as $name => $config) {
+    if (!isset($type) || $config instanceof $faces[$type]) {
+      $items[$name] = $label ? $config->label() : $config;
+    }
+  }
+  return $items;
+}
+
+/**
+ * Delete rule configurations from database.
+ *
+ * @param array $ids
+ *   An array of entity IDs.
+ */
+function rules_config_delete(array $ids) {
+  return entity_get_controller('rules_config')->delete($ids);
+}
+
+/**
+ * Ensures the configuration's 'dirty' flag is up to date by running an integrity check.
+ *
+ * @param bool $update
+ *   (optional) Whether the dirty flag is also updated in the database if
+ *   necessary. Defaults to TRUE.
+ */
+function rules_config_update_dirty_flag($rules_config, $update = TRUE) {
+  // Keep a log of already check configurations to avoid repetitive checks on
+  // often used components.
+  // @see rules_element_invoke_component_validate()
+  $checked = &drupal_static(__FUNCTION__, array());
+  if (!empty($checked[$rules_config->name])) {
+    return;
+  }
+  $checked[$rules_config->name] = TRUE;
+
+  $was_dirty = !empty($rules_config->dirty);
+  try {
+    // First set the rule to dirty, so any repetitive checks give green light
+    // for this configuration.
+    $rules_config->dirty = FALSE;
+    $rules_config->integrityCheck();
+    if ($was_dirty) {
+      $variables = array('%label' => $rules_config->label(), '%name' => $rules_config->name, '@plugin' => $rules_config->plugin());
+      watchdog('rules', 'The @plugin %label (%name) was marked dirty, but passes the integrity check now and is active again.', $variables, WATCHDOG_INFO);
+    }
+  }
+  catch (RulesIntegrityException $e) {
+    $rules_config->dirty = TRUE;
+    if (!$was_dirty) {
+      $variables = array('%label' => $rules_config->label(), '%name' => $rules_config->name, '!message' => $e->getMessage(), '@plugin' => $rules_config->plugin());
+      watchdog('rules', 'The @plugin %label (%name) fails the integrity check and cannot be executed. Error: !message', $variables, WATCHDOG_ERROR);
+    }
+  }
+  // Save the updated dirty flag to the database.
+  if ($was_dirty != $rules_config->dirty) {
+    db_update('rules_config')
+      ->fields(array('dirty' => (int) $rules_config->dirty))
+      ->condition('id', $rules_config->id)
+      ->execute();
+  }
+}
+
+/**
+ * Invokes a hook and the associated rules event.
+ *
+ * Calling this function does the same as calling module_invoke_all() and
+ * rules_invoke_event() separately, however merges both functions into one in
+ * order to ease usage and to work efficiently.
+ *
+ * @param $hook
+ *   The name of the hook / event to invoke.
+ * @param ...
+ *   Arguments to pass to the hook / event.
+ *
+ * @return array
+ *   An array of return values of the hook implementations. If modules return
+ *   arrays from their implementations, those are merged into one array.
+ */
+function rules_invoke_all() {
+  // Copied code from module_invoke_all().
+  $args = func_get_args();
+  $hook = $args[0];
+  unset($args[0]);
+  $return = array();
+  foreach (module_implements($hook) as $module) {
+    $function = $module . '_' . $hook;
+    if (function_exists($function)) {
+      $result = call_user_func_array($function, $args);
+      if (isset($result) && is_array($result)) {
+        $return = array_merge_recursive($return, $result);
+      }
+      elseif (isset($result)) {
+        $return[] = $result;
+      }
+    }
+  }
+  // Invoke the event.
+  rules_invoke_event_by_args($hook, $args);
+
+  return $return;
+}
+
+/**
+ * Invokes configured rules for the given event.
+ *
+ * @param $event_name
+ *   The event's name.
+ * @param ...
+ *   Pass parameters for the variables provided by this event, as defined in
+ *   hook_rules_event_info(). Example given:
+ *   @code
+ *     rules_invoke_event('node_view', $node, $view_mode);
+ *   @endcode
+ *
+ * @see rules_invoke_event_by_args()
+ */
+function rules_invoke_event() {
+  $args = func_get_args();
+  $event_name = $args[0];
+  unset($args[0]);
+  // We maintain a whitelist of configured events to reduces the number of cache
+  // reads. If the whitelist is not in the cache we proceed and it is rebuilt.
+  if (rules_event_invocation_enabled()) {
+    $whitelist = rules_get_cache('rules_event_whitelist');
+    if ((($whitelist === FALSE) || isset($whitelist[$event_name])) && $event = rules_get_cache('event_' . $event_name)) {
+      $event->executeByArgs($args);
+    }
+  }
+}
+
+/**
+ * Invokes configured rules for the given event.
+ *
+ * @param $event_name
+ *   The event's name.
+ * @param array $args
+ *   An array of parameters for the variables provided by the event, as defined
+ *   in hook_rules_event_info(). Either pass an array keyed by the variable
+ *   names or a numerically indexed array, in which case the ordering of the
+ *   passed parameters has to match the order of the specified variables.
+ *   Example given:
+ *   @code
+ *     rules_invoke_event_by_args('node_view', array('node' => $node, 'view_mode' => $view_mode));
+ *   @endcode
+ *
+ * @see rules_invoke_event()
+ */
+function rules_invoke_event_by_args($event_name, $args = array()) {
+  // We maintain a whitelist of configured events to reduces the number of cache
+  // reads. If the whitelist is empty we proceed and it is rebuilt.
+  if (rules_event_invocation_enabled()) {
+    $whitelist = rules_get_cache('rules_event_whitelist');
+    if ((empty($whitelist) || isset($whitelist[$event_name])) && $event = rules_get_cache('event_' . $event_name)) {
+      $event->executeByArgs($args);
+    }
+  }
+}
+
+/**
+ * Invokes a rule component, e.g. a rule set.
+ *
+ * @param $component_name
+ *   The component's name.
+ * @param $args
+ *   Pass further parameters as required for the invoked component.
+ *
+ * @return array
+ *   An array of variables as provided by the component, or FALSE in case the
+ *   component could not be executed.
+ */
+function rules_invoke_component() {
+  $args = func_get_args();
+  $name = array_shift($args);
+  if ($component = rules_get_cache('comp_' . $name)) {
+    return $component->executeByArgs($args);
+  }
+  return FALSE;
+}
+
+/**
+ * Filters the given array of arrays.
+ *
+ * This filter operates by keeping only entries which have $key set to the
+ * value of $value.
+ *
+ * @param array $array
+ *   The array of arrays to filter.
+ * @param $key
+ *   The key used for the comparison.
+ * @param $value
+ *   The value to compare the array's entry to.
+ *
+ * @return array
+ *   The filtered array.
+ */
+function rules_filter_array($array, $key, $value) {
+  $return = array();
+  foreach ($array as $i => $entry) {
+    $entry += array($key => NULL);
+    if ($entry[$key] == $value) {
+      $return[$i] = $entry;
+    }
+  }
+  return $return;
+}
+
+/**
+ * Merges the $update array into $array.
+ *
+ * Makes sure no values of $array not appearing in $update are lost.
+ *
+ * @return array
+ *   The updated array.
+ */
+function rules_update_array(array $array, array $update) {
+  foreach ($update as $key => $data) {
+    if (isset($array[$key]) && is_array($array[$key]) && is_array($data)) {
+      $array[$key] = rules_update_array($array[$key], $data);
+    }
+    else {
+      $array[$key] = $data;
+    }
+  }
+  return $array;
+}
+
+/**
+ * Extracts the property with the given name.
+ *
+ * @param array $arrays
+ *   An array of arrays from which a property is to be extracted.
+ * @param $key
+ *   The name of the property to extract.
+ *
+ * @return array
+ *   An array of extracted properties, keyed as in $arrays.
+ */
+function rules_extract_property($arrays, $key) {
+  $data = array();
+  foreach ($arrays as $name => $item) {
+    $data[$name] = $item[$key];
+  }
+  return $data;
+}
+
+/**
+ * Returns the first key of the array.
+ */
+function rules_array_key($array) {
+  reset($array);
+  return key($array);
+}
+
+/**
+ * Clean replacements so they are URL friendly.
+ *
+ * Can be used as 'cleaning callback' for action or condition parameters.
+ *
+ * @param $replacements
+ *   An array of token replacements that need to be "cleaned" for use in the URL.
+ * @param array $data
+ *   An array of objects used to generate the replacements.
+ * @param array $options
+ *   An array of options used to generate the replacements.
+ *
+ * @see rules_path_action_info()
+ */
+function rules_path_clean_replacement_values(&$replacements, $data = array(), $options = array()) {
+  // Include path.eval.inc which contains path cleaning functions.
+  module_load_include('inc', 'rules', 'modules/path.eval');
+  foreach ($replacements as $token => $value) {
+    $replacements[$token] = rules_clean_path($value);
+  }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function rules_theme() {
+  return array(
+    'rules_elements' => array(
+      'render element' => 'element',
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_content_group' => array(
+      'render element' => 'element',
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_parameter_configuration' => array(
+      'render element' => 'element',
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_variable_view' => array(
+      'render element' => 'element',
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_data_selector_help' => array(
+      'variables' => array('parameter' => NULL, 'variables' => NULL),
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_ui_variable_form' => array(
+      'render element' => 'element',
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_log' => array(
+      'render element' => 'element',
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_autocomplete' => array(
+      'render element' => 'element',
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_debug_element' => array(
+      'render element' => 'element',
+      'file' => 'ui/ui.theme.inc',
+    ),
+    'rules_settings_help' => array(
+      'variables' => array('text' => '', 'heading' => ''),
+      'file' => 'ui/ui.theme.inc',
+    ),
+  );
+}
+
+/**
+ * Implements hook_permission().
+ */
+function rules_permission() {
+  $perms = array(
+    'administer rules' => array(
+      'title' => t('Administer rule configurations'),
+      'description' => t('Administer rule configurations including events, conditions and actions for which the user has sufficient access permissions.'),
+    ),
+    'bypass rules access' => array(
+      'title' => t('Bypass Rules access control'),
+      'description' => t('Control all configurations regardless of permission restrictions of events, conditions or actions.'),
+      'restrict access' => TRUE,
+    ),
+    'access rules debug' => array(
+      'title' => t('Access the Rules debug log'),
+    ),
+  );
+
+  // Fetch all components to generate the access keys.
+  $conditions['plugin'] = array_keys(rules_filter_array(rules_fetch_data('plugin_info'), 'component', TRUE));
+  $conditions['access_exposed'] = 1;
+  $components = entity_load('rules_config', FALSE, $conditions);
+  $perms += rules_permissions_by_component($components);
+
+  return $perms;
+}
+
+/**
+ * Helper function to get all the permissions for components that have access exposed.
+ */
+function rules_permissions_by_component(array $components = array()) {
+  $perms = array();
+  foreach ($components as $component) {
+    $perms += array(
+      "use Rules component $component->name" => array(
+        'title' => t('Use Rules component %component', array('%component' => $component->label())),
+        'description' => t('Controls access for using the component %component via the provided action or condition. <a href="@component-edit-url">Edit this component.</a>', array('%component' => $component->label(), '@component-edit-url' => url(RulesPluginUI::path($component->name)))),
+      ),
+    );
+  }
+  return $perms;
+}
+
+/**
+ * Menu callback for loading rules configuration elements.
+ *
+ * @see RulesUIController::config_menu()
+ */
+function rules_element_load($element_id, $config_name) {
+  $config = rules_config_load($config_name);
+  return $config->elementMap()->lookup($element_id);
+}
+
+/**
+ * Menu callback for getting the title as configured.
+ *
+ * @see RulesUIController::config_menu()
+ */
+function rules_get_title($text, $element) {
+  if ($element instanceof RulesPlugin) {
+    $cache = rules_get_cache();
+    $plugin = $element->plugin();
+    $plugin = isset($cache['plugin_info'][$plugin]['label']) ? $cache['plugin_info'][$plugin]['label'] : $plugin;
+    $plugin = drupal_strtolower(drupal_substr($plugin, 0, 1)) . drupal_substr($plugin, 1);
+    return t($text, array('!label' => $element->label(), '!plugin' => $plugin));
+  }
+  // As fallback treat $element as simple string.
+  return t($text, array('!plugin' => $element));
+}
+
+/**
+ * Menu callback for getting the title for the add element page.
+ *
+ * Uses a work-a-round for accessing the plugin name.
+ *
+ * @see RulesUIController::config_menu()
+ */
+function rules_menu_add_element_title($array) {
+  $plugin_name = arg($array[0]);
+  $cache = rules_get_cache();
+  if (isset($cache['plugin_info'][$plugin_name]['class'])) {
+    $info = $cache['plugin_info'][$plugin_name] + array('label' => $plugin_name);
+    $label = drupal_strtolower(drupal_substr($info['label'], 0, 1)) . drupal_substr($info['label'], 1);
+    return t('Add a new !plugin', array('!plugin' => $label));
+  }
+}
+
+/**
+ * Returns the current region for the debug log.
+ */
+function rules_debug_log_region() {
+  // If there is no setting for the current theme use the default theme setting.
+  global $theme_key;
+  $theme_default = variable_get('theme_default', 'bartik');
+  return variable_get('rules_debug_region_' . $theme_key, variable_get('rules_debug_region_' . $theme_default, 'help'));
+}
+
+/**
+ * Implements hook_page_build() to add the rules debug log to the page bottom.
+ */
+function rules_page_build(&$page) {
+  // Invoke a the page redirect, in case the action has been executed.
+  // @see rules_action_drupal_goto()
+  if (isset($GLOBALS['_rules_action_drupal_goto_do'])) {
+    list($url, $force) = $GLOBALS['_rules_action_drupal_goto_do'];
+    drupal_goto($url);
+  }
+
+  if (isset($_SESSION['rules_debug'])) {
+    $region = rules_debug_log_region();
+    foreach ($_SESSION['rules_debug'] as $log) {
+      $page[$region]['rules_debug'][] = array(
+        '#markup' => $log,
+      );
+      $page[$region]['rules_debug']['#theme_wrappers'] = array('rules_log');
+    }
+    unset($_SESSION['rules_debug']);
+  }
+
+  if (rules_show_debug_output()) {
+    $region = rules_debug_log_region();
+    $page[$region]['rules_debug']['#pre_render'] = array('rules_debug_log_pre_render');
+  }
+}
+
+/**
+ * Pre-render callback for the debug log, which renders and then clears it.
+ */
+function rules_debug_log_pre_render($elements) {
+  $logger = RulesLog::logger();
+  if ($log = $logger->render()) {
+    $logger = RulesLog::logger();
+    $logger->clear();
+    $elements[] = array('#markup' => $log);
+    $elements['#theme_wrappers'] = array('rules_log');
+    // Log the rules log to the system log if enabled.
+    if (variable_get('rules_debug_log', FALSE)) {
+      watchdog('rules', 'Rules debug information: !log', array('!log' => $log), WATCHDOG_NOTICE);
+    }
+  }
+  return $elements;
+}
+
+/**
+ * Implements hook_drupal_goto_alter().
+ *
+ * @see rules_action_drupal_goto()
+ */
+function rules_drupal_goto_alter(&$path, &$options, &$http_response_code) {
+  // Invoke a the page redirect, in case the action has been executed.
+  if (isset($GLOBALS['_rules_action_drupal_goto_do'])) {
+    list($url, $force) = $GLOBALS['_rules_action_drupal_goto_do'];
+
+    if ($force || !isset($_GET['destination'])) {
+      $url = drupal_parse_url($url);
+      $path = $url['path'];
+      $options['query'] = $url['query'];
+      $options['fragment'] = $url['fragment'];
+      $http_response_code = 302;
+    }
+  }
+}
+
+/**
+ * Returns whether the debug log should be shown.
+ */
+function rules_show_debug_output() {
+  // For performance avoid unnecessary auto-loading of the RulesLog class.
+  if (!class_exists('RulesLog', FALSE)) {
+    return FALSE;
+  }
+  if (variable_get('rules_debug', 0) == RulesLog::INFO && user_access('access rules debug')) {
+    return TRUE;
+  }
+  return variable_get('rules_debug', 0) == RulesLog::WARN && user_access('access rules debug') && RulesLog::logger()->hasErrors();
+}
+
+/**
+ * Implements hook_exit().
+ */
+function rules_exit() {
+  // Bail out if this is cached request and modules are not loaded.
+  if (!module_exists('rules') || !module_exists('user')) {
+    return;
+  }
+  if (rules_show_debug_output()) {
+    if ($log = RulesLog::logger()->render()) {
+      // Keep the log in the session so we can show it on the next page.
+      $_SESSION['rules_debug'][] = $log;
+    }
+  }
+  // Log the rules log to the system log if enabled.
+  if (variable_get('rules_debug_log', FALSE) && $log = RulesLog::logger()->render()) {
+    watchdog('rules', 'Rules debug information: !log', array('!log' => $log), WATCHDOG_NOTICE);
+  }
+}
+
+/**
+ * Implements hook_element_info().
+ */
+function rules_element_info() {
+  // A duration form element for rules. Needs ui.forms.inc included.
+  $types['rules_duration'] = array(
+    '#input' => TRUE,
+    '#tree' => TRUE,
+    '#default_value' => 0,
+    '#value_callback' => 'rules_ui_element_duration_value',
+    '#process' => array('rules_ui_element_duration_process', 'ajax_process_form'),
+    '#after_build' => array('rules_ui_element_duration_after_build'),
+    '#pre_render' => array('form_pre_render_conditional_form_element'),
+  );
+  $types['rules_data_selection'] = array(
+    '#input' => TRUE,
+    '#pre_render' => array('form_pre_render_conditional_form_element'),
+    '#process' => array('rules_data_selection_process', 'ajax_process_form'),
+    '#theme' => 'rules_autocomplete',
+  );
+  return $types;
+}
+
+/**
+ * Implements hook_modules_enabled().
+ */
+function rules_modules_enabled($modules) {
+  // Re-enable Rules configurations that are dirty, because they require one of
+  // the enabled the modules.
+  $query = db_select('rules_dependencies', 'rd');
+  $query->join('rules_config', 'rc', 'rd.id = rc.id');
+  $query->fields('rd', array('id'))
+        ->condition('rd.module', $modules, 'IN')
+        ->condition('rc.dirty', 1);
+  $ids = $query->execute()->fetchCol();
+
+  // If there are some configurations that might work again, re-check all dirty
+  // configurations as others might work again too, e.g. consider a rule that is
+  // dirty because it requires a dirty component.
+  if ($ids) {
+    $rules_configs = rules_config_load_multiple(FALSE, array('dirty' => 1));
+    foreach ($rules_configs as $rules_config) {
+      try {
+        $rules_config->integrityCheck();
+        // If no exceptions were thrown we can set the configuration back to OK.
+        db_update('rules_config')
+          ->fields(array('dirty' => 0))
+          ->condition('id', $rules_config->id)
+          ->execute();
+        if ($rules_config->active) {
+          drupal_set_message(t('All dependencies for the Rules configuration %config are met again, so it has been re-activated.', array('%config' => $rules_config->label())));
+        }
+      }
+      catch (RulesIntegrityException $e) {
+        // The rule is still dirty, so do nothing.
+      }
+    }
+  }
+  rules_clear_cache();
+}
+
+/**
+ * Implements hook_modules_disabled().
+ */
+function rules_modules_disabled($modules) {
+  // Disable Rules configurations that depend on one of the disabled modules.
+  $query = db_select('rules_dependencies', 'rd');
+  $query->join('rules_config', 'rc', 'rd.id = rc.id');
+  $query->fields('rd', array('id'))
+        ->distinct()
+        ->condition('rd.module', $modules, 'IN')
+        ->condition('rc.dirty', 0);
+  $ids = $query->execute()->fetchCol();
+
+  if (!empty($ids)) {
+    db_update('rules_config')
+      ->fields(array('dirty' => 1))
+      ->condition('id', $ids, 'IN')
+      ->execute();
+    // Tell the user about enabled rules that have been marked as dirty.
+    $count = db_select('rules_config', 'r')
+      ->fields('r')
+      ->condition('id', $ids, 'IN')
+      ->condition('active', 1)
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+    if ($count > 0) {
+      $message = format_plural($count,
+        '1 Rules configuration requires some of the disabled modules to function and cannot be executed any more.',
+        '@count Rules configurations require some of the disabled modules to function and cannot be executed any more.'
+      );
+      drupal_set_message($message, 'warning');
+    }
+  }
+  rules_clear_cache();
+}
+
+/**
+ * Access callback for dealing with Rules configurations.
+ *
+ * @see entity_access()
+ */
+function rules_config_access($op, $rules_config = NULL, $account = NULL) {
+  if (user_access('bypass rules access', $account)) {
+    return TRUE;
+  }
+  // Allow modules to grant / deny access.
+  $access = module_invoke_all('rules_config_access', $op, $rules_config, $account);
+
+  // Only grant access if at least one module granted access and no one denied
+  // access.
+  if (in_array(FALSE, $access, TRUE)) {
+    return FALSE;
+  }
+  elseif (in_array(TRUE, $access, TRUE)) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Implements hook_rules_config_access().
+ */
+function rules_rules_config_access($op, $rules_config = NULL, $account = NULL) {
+  // Instead of returning FALSE return nothing, so others still can grant
+  // access.
+  if (!isset($rules_config) || (isset($account) && $account->uid != $GLOBALS['user']->uid)) {
+    return;
+  }
+  if (user_access('administer rules', $account) && ($op == 'view' || $rules_config->access())) {
+    return TRUE;
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function rules_menu() {
+  $items['admin/config/workflow/rules/upgrade'] = array(
+    'title' => 'Upgrade',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_upgrade_form'),
+    'access arguments' => array('administer rules'),
+    'file' => 'includes/rules.upgrade.inc',
+    'file path' => drupal_get_path('module', 'rules'),
+    'type' => MENU_CALLBACK,
+  );
+  $items['admin/config/workflow/rules/upgrade/clear'] = array(
+    'title' => 'Clear',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_upgrade_confirm_clear_form'),
+    'access arguments' => array('administer rules'),
+    'file' => 'includes/rules.upgrade.inc',
+    'file path' => drupal_get_path('module', 'rules'),
+    'type' => MENU_CALLBACK,
+  );
+  $items['admin/config/workflow/rules/autocomplete_tags'] = array(
+    'title' => 'Rules tags autocomplete',
+    'page callback' => 'rules_autocomplete_tags',
+    'page arguments' => array(5),
+    'access arguments' => array('administer rules'),
+    'file' => 'ui/ui.forms.inc',
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Helper function to keep track of external documentation pages for Rules.
+ *
+ * @param string $topic
+ *   The topic key for used for identifying help pages.
+ *
+ * @return string|array|false
+ *   Either a URL for the given page, or the full list of external help pages.
+ */
+function rules_external_help($topic = NULL) {
+  $help = array(
+    'rules' =>                'https://www.drupal.org/node/298480',
+    'terminology' =>          'https://www.drupal.org/node/1299990',
+    'condition-components' => 'https://www.drupal.org/node/1300034',
+    'data-selection' =>       'https://www.drupal.org/node/1300042',
+    'chained-tokens' =>       'https://www.drupal.org/node/1300042',
+    'loops' =>                'https://www.drupal.org/node/1300058',
+    'components' =>           'https://www.drupal.org/node/1300024',
+    'component-types' =>      'https://www.drupal.org/node/1300024',
+    'variables' =>            'https://www.drupal.org/node/1300024',
+    'scheduler' =>            'https://www.drupal.org/node/1300068',
+    'coding' =>               'https://www.drupal.org/node/878720',
+  );
+
+  if (isset($topic)) {
+    return isset($help[$topic]) ? $help[$topic] : FALSE;
+  }
+  return $help;
+}
+
+/**
+ * Implements hook_help().
+ */
+function rules_help($path, $arg) {
+  // Only enable the help if the admin module is active.
+  if ($path == 'admin/help#rules' && module_exists('rules_admin')) {
+
+    $output['header'] = array(
+      '#markup' => t('Rules documentation is kept online. Please use the links below for more information about Rules. Feel free to contribute to improving the online documentation!'),
+    );
+    // Build a list of essential Rules help pages, formatted as a bullet list.
+    $link_list['rules'] = l(t('Rules introduction'), rules_external_help('rules'));
+    $link_list['terminology'] = l(t('Rules terminology'), rules_external_help('terminology'));
+    $link_list['scheduler'] = l(t('Rules Scheduler'), rules_external_help('scheduler'));
+    $link_list['coding'] = l(t('Coding for Rules'), rules_external_help('coding'));
+
+    $output['topic-list'] = array(
+      '#markup' => theme('item_list', array('items' => $link_list)),
+    );
+    return render($output);
+  }
+}
+
+/**
+ * Implements hook_token_info().
+ */
+function rules_token_info() {
+  $cache = rules_get_cache();
+  $data_info = $cache['data_info'];
+
+  $types = array('text', 'integer', 'uri', 'token', 'decimal', 'date', 'duration');
+
+  foreach ($types as $type) {
+    $token_type = $data_info[$type]['token type'];
+
+    $token_info['types'][$token_type] = array(
+      'name' => $data_info[$type]['label'],
+      'description' => t('Tokens related to %label Rules variables.', array('%label' => $data_info[$type]['label'])),
+      'needs-data' => $token_type,
+    );
+    $token_info['tokens'][$token_type]['value'] = array(
+      'name' => t("Value"),
+      'description' => t('The value of the variable.'),
+    );
+  }
+  return $token_info;
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function rules_tokens($type, $tokens, $data, $options = array()) {
+  // Handle replacements of primitive variable types.
+  if (substr($type, 0, 6) == 'rules_' && !empty($data[$type])) {
+    // Leverage entity tokens token processor by passing on as struct.
+    $info['property info']['value'] = array(
+      'type' => substr($type, 6),
+      'label' => '',
+    );
+    // Entity tokens uses metadata wrappers as values for 'struct' types.
+    $wrapper = entity_metadata_wrapper('struct', array('value' => $data[$type]), $info);
+    return entity_token_tokens('struct', $tokens, array('struct' => $wrapper), $options);
+  }
+}
+
+/**
+ * Helper function that retrieves a metadata wrapper with all properties.
+ *
+ * Note that without this helper, bundle-specific properties aren't added.
+ */
+function rules_get_entity_metadata_wrapper_all_properties(RulesAbstractPlugin $element) {
+  return entity_metadata_wrapper($element->settings['type'], NULL, array(
+    'property info alter' => 'rules_entity_metadata_wrapper_all_properties_callback',
+  ));
+}
+
+/**
+ * Callback that returns a metadata wrapper with all properties.
+ */
+function rules_entity_metadata_wrapper_all_properties_callback(EntityMetadataWrapper $wrapper, $property_info) {
+  $info = $wrapper->info();
+  $properties = entity_get_all_property_info($info['type']);
+  $property_info['properties'] += $properties;
+  return $property_info;
+}
+
+/**
+ * Helper to enable or disable the invocation of rules events.
+ *
+ * Rules invocation is disabled by default, such that Rules does not operate
+ * when Drupal is not fully bootstrapped. It gets enabled in rules_init() and
+ * rules_enable().
+ *
+ * @param bool|null $enable
+ *   NULL to leave the setting as is and TRUE / FALSE to change the behaviour.
+ *
+ * @return bool
+ *   Whether the rules invocation is enabled or disabled.
+ */
+function rules_event_invocation_enabled($enable = NULL) {
+  static $invocation_enabled = FALSE;
+  if (isset($enable)) {
+    $invocation_enabled = (bool) $enable;
+  }
+  // Disable invocation if configured or if site runs in maintenance mode.
+  return $invocation_enabled && !defined('MAINTENANCE_MODE');
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules.rules.inc b/profiles/wcm_base/modules/contrib/rules/rules.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..b828c1d31935b0c7b02c92541acd13a3c6ef0af4
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules.rules.inc
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Includes any rules integration provided by the module.
+ */
+
+/**
+ * Load all module includes as soon as this file gets included, which is done
+ * automatically by module_implements().
+ */
+foreach (rules_core_modules() as $module) {
+  module_load_include('inc', 'rules', "modules/$module.rules");
+}
+
+/**
+ * Defines a list of core module on whose behalf we provide module integration.
+ *
+ * We also add a pseudo 'data' module, which will be used for providing generic
+ * rules data integration, 'entity' for entity-related integration and 'rules'
+ * for providing some general stuff.
+ */
+function rules_core_modules() {
+  // Make use of the fast, advanced drupal static pattern.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast = &drupal_static(__FUNCTION__);
+  }
+  $modules = &$drupal_static_fast;
+
+  if (!isset($modules)) {
+    $modules = array('data', 'entity', 'node', 'system', 'user', 'rules_core');
+    foreach (array('comment', 'taxonomy', 'php', 'path') as $module) {
+      if (module_exists($module)) {
+        $modules[] = $module;
+      }
+    }
+  }
+  return $modules;
+}
+
+/**
+ * Returns all items for a hook applying the right module defaults.
+ */
+function _rules_rules_collect_items($hook) {
+  $items = array();
+  foreach (rules_core_modules() as $module) {
+    if (function_exists($function = "rules_{$module}_{$hook}")) {
+      $items += (array) $function();
+    }
+  }
+  return $items;
+}
+
+/**
+ * Implements hook_rules_file_info().
+ */
+function rules_rules_file_info() {
+  // Make use of the fast, advanced drupal static pattern.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast = &drupal_static(__FUNCTION__);
+  }
+  $items = &$drupal_static_fast;
+  if (!isset($items)) {
+    $items = array();
+    foreach (rules_core_modules() as $module) {
+      if (function_exists($function = "rules_{$module}_file_info")) {
+        $items = array_merge($items, (array) $function());
+        // Automatically add "$module.rules.inc" for each module.
+        $items[] = 'modules/' . $module . '.rules';
+      }
+    }
+  }
+  return $items;
+}
+
+/**
+ * Implements hook_rules_category_info().
+ */
+function rules_rules_category_info() {
+  return _rules_rules_collect_items('category_info');
+}
+
+/**
+ * Implements hook_rules_action_info().
+ */
+function rules_rules_action_info() {
+  return _rules_rules_collect_items('action_info');
+}
+
+/**
+ * Implements hook_rules_condition_info().
+ */
+function rules_rules_condition_info() {
+  return _rules_rules_collect_items('condition_info');
+}
+
+/**
+ * Implements hook_rules_event_info().
+ */
+function rules_rules_event_info() {
+  return _rules_rules_collect_items('event_info');
+}
+
+/**
+ * Implements hook_rules_data_info().
+ */
+function rules_rules_data_info() {
+  return _rules_rules_collect_items('data_info');
+}
+
+/**
+ * Implements hook_rules_data_info_alter().
+ */
+function rules_rules_data_info_alter(&$items) {
+  // For now just invoke the rules core implementation manually.
+  rules_rules_core_data_info_alter($items);
+}
+
+/**
+ * Implements hook_rules_evaluator_info().
+ */
+function rules_rules_evaluator_info() {
+  return _rules_rules_collect_items('evaluator_info');
+}
+
+/**
+ * Implements hook_rules_data_processor_info().
+ */
+function rules_rules_data_processor_info() {
+  return _rules_rules_collect_items('data_processor_info');
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.inc b/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.inc
new file mode 100644
index 0000000000000000000000000000000000000000..fec08e826fb58646f6ce3891b531466ac504d319
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.inc
@@ -0,0 +1,434 @@
+<?php
+
+/**
+ * @file
+ * Implements rule management and configuration screens.
+ */
+
+/**
+ * Reaction rules overview.
+ */
+function rules_admin_reaction_overview($form, &$form_state, $base_path) {
+  RulesPluginUI::formDefaults($form, $form_state);
+
+  $conditions = array('plugin' => 'reaction rule', 'active' => TRUE);
+  $collapsed = TRUE;
+  if (empty($_GET['tag'])) {
+    $tag = 0;
+  }
+  else {
+    $tag = $_GET['tag'];
+    $conditions['tags'] = array($tag);
+    $collapsed = FALSE;
+  }
+  if (empty($_GET['event'])) {
+    $event = 0;
+  }
+  else {
+    $event = $_GET['event'];
+    // Filter using a wildcard suffix so configured event names with suffixes
+    // are found also.
+    $conditions['event'] = $event . '%';
+    $collapsed = FALSE;
+  }
+  $form['help'] = array(
+    '#markup' => t('Reaction rules, listed below, react on selected events on the site. Each reaction rule may fire any number of <em>actions</em>, and may have any number of <em>conditions</em> that must be met for the actions to be executed. You can also set up <a href="@url1">components</a> – stand-alone sets of Rules configuration that can be used in Rules and other parts of your site. See <a href="@url2">the online documentation</a> for an introduction on how to use Rules.',
+      array('@url1' => url('admin/config/workflow/rules/components'),
+            '@url2' => rules_external_help('rules'))),
+  );
+
+  $form['filter'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Filter'),
+    '#collapsible' => TRUE,
+  );
+  $form['filter']['#id'] = 'rules-filter-form';
+  $form['filter']['#attached']['css'][] = drupal_get_path('module', 'rules') . '/ui/rules.ui.css';
+  $form['filter']['event'] = array(
+    '#type' => 'select',
+    '#title' => t('Filter by event'),
+    '#options' => array(0 => t('<All>')) + RulesPluginUI::getOptions('event'),
+    '#default_value' => $event,
+  );
+  $form['filter']['tag'] = array(
+    '#type' => 'select',
+    '#title' => t('Filter by tag'),
+    '#options' => array(0 => t('<All>')) + RulesPluginUI::getTags(),
+    '#default_value' => $tag,
+  );
+  $form['filter']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Filter'),
+    '#name' => '', // prevent from showing up in $_GET.
+  );
+
+  $options = array('show plugin' => FALSE, 'base path' => $base_path);
+  $form['active'] = rules_ui()->overviewTable($conditions, $options);
+  $form['active']['#caption'] = t('Active rules');
+  $form['active']['#empty'] = t('There are no active rules. <a href="!url">Add new rule</a>.', array('!url' => url('admin/config/workflow/rules/reaction/add')));
+
+  $conditions['active'] = FALSE;
+  $form['inactive'] = rules_ui()->overviewTable($conditions, $options);
+  $form['inactive']['#caption'] = t('Inactive rules');
+  $form['inactive']['#empty'] = t('There are no inactive rules.');
+
+  $form['filter']['#collapsed'] = $collapsed;
+  $form['#submit'][] = 'rules_form_submit_rebuild';
+  $form['#method'] = 'get';
+  return $form;
+}
+
+/**
+ * Components overview.
+ */
+function rules_admin_components_overview($form, &$form_state, $base_path) {
+  RulesPluginUI::formDefaults($form, $form_state);
+
+  $collapsed = TRUE;
+  if (empty($_GET['tag'])) {
+    $tag = 0;
+  }
+  else {
+    $tag = $_GET['tag'];
+    $conditions['tags'] = array($tag);
+    $collapsed = FALSE;
+  }
+  if (empty($_GET['plugin'])) {
+    // Get the plugin name usable as component.
+    $conditions['plugin'] = array_keys(rules_filter_array(rules_fetch_data('plugin_info'), 'component', TRUE));
+    $plugin = 0;
+  }
+  else {
+    $plugin = $_GET['plugin'];
+    $conditions['plugin'] = $plugin;
+    $collapsed = FALSE;
+  }
+  $form['help'] = array(
+    '#markup' => t('Components are stand-alone sets of Rules configuration that can be used by Rules and other modules on your site. Components are for example useful if you want to use the same conditions, actions or rules in multiple places, or call them from your custom module. You may also export each component separately. See <a href="@url">the online documentation</a> for more information about how to use components.',
+      array('@url' => rules_external_help('components'))),
+  );
+  $form['filter'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Filter'),
+    '#collapsible' => TRUE,
+  );
+  $form['filter']['#id'] = 'rules-filter-form';
+  $form['filter']['#attached']['css'][] = drupal_get_path('module', 'rules') . '/ui/rules.ui.css';
+  $form['filter']['plugin'] = array(
+    '#type' => 'select',
+    '#title' => t('Filter by plugin'),
+    '#options' => array(0 => t('<All>')) + rules_admin_component_options(),
+    '#default_value' => $plugin,
+  );
+  $form['filter']['tag'] = array(
+    '#type' => 'select',
+    '#title' => t('Filter by tag'),
+    '#options' => array(0 => '<All>') + RulesPluginUI::getTags(),
+    '#default_value' => $tag,
+  );
+  $form['filter']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Filter'),
+    '#name' => '', // prevent from showing up in $_GET.
+  );
+
+  $form['table'] = RulesPluginUI::overviewTable($conditions, array('hide status op' => TRUE));
+  $form['table']['#empty'] = t('There are no rule components.');
+
+  $form['filter']['#collapsed'] = $collapsed;
+  $form['#submit'][] = 'rules_form_submit_rebuild';
+  $form['#method'] = 'get';
+  return $form;
+}
+
+/**
+ * Rules settings form.
+ */
+function rules_admin_settings($form, &$form_state) {
+
+  if (module_exists('path')) {
+    // Present a list of available path cleaning callbacks.
+    // @see rules_clean_path()
+    $options = array(
+      'rules_path_default_cleaning_method' => t('Rules (built in)'),
+    );
+    if (module_exists('ctools')) {
+      $options['rules_path_clean_ctools'] = t('CTools');
+    }
+    if (module_exists('pathauto')) {
+      $options['rules_path_clean_pathauto'] = t('Pathauto');
+      $pathauto_help = t("Note that Pathauto's URL path cleaning method can be configured at <a href='!url'>admin/config/search/path/settings</a>.", array('!url' => url('admin/config/search/path/settings')));
+    }
+    else {
+      $pathauto_help = t('Install the <a href="https://www.drupal.org/project/pathauto">Pathauto module</a> in order to get a configurable URL path cleaning method.');
+    }
+
+    $form['path']['rules_path_cleaning_callback'] = array(
+      '#type' => 'select',
+      '#title' => t('URL path cleaning method'),
+      '#description' => t('Choose the path cleaning method to be applied when generating URL path aliases.') . ' ' . $pathauto_help,
+      '#default_value' => variable_get('rules_path_cleaning_callback', 'rules_path_default_cleaning_method'),
+      '#options' => $options,
+    );
+  }
+
+  $form['rules_log_errors'] = array(
+    '#type' => 'radios',
+    '#title' => t('Logging of Rules evaluation errors'),
+    '#options' => array(
+      RulesLog::WARN => t('Log all warnings and errors'),
+      RulesLog::ERROR => t('Log errors only'),
+    ),
+    '#default_value' => variable_get('rules_log_errors', RulesLog::WARN),
+    '#description' => t('Evaluations errors are logged to the system log.'),
+  );
+
+  $form['debug'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Debugging'),
+  );
+  $form['debug']['rules_debug_log'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Log debug information to the system log'),
+    '#default_value' => variable_get('rules_debug_log', FALSE),
+  );
+  $form['debug']['rules_debug'] = array(
+    '#type' => 'radios',
+    '#title' => t('Show debug information'),
+    '#default_value' => variable_get('rules_debug', 0),
+    '#options' => array(
+      0 => t('Never'),
+      RulesLog::WARN => t('In case of errors'),
+      RulesLog::INFO => t('Always'),
+    ),
+    '#description' => t('Debug information is only shown when rules are evaluated and is visible for users having the permission <a href="!url">%link</a>.', array('%link' => t('Access the Rules debug log'), '!url' => url('admin/people/permissions', array('fragment' => 'module-rules')))),
+  );
+
+  $form['debug']['regions'] = array(
+    '#type' => 'container',
+    '#states' => array(
+      // Hide the regions settings when the debug log is disabled.
+      'invisible' => array(
+        'input[name="rules_debug"]' => array('value' => 0),
+      ),
+    ),
+  );
+
+  $theme_default = variable_get('theme_default', 'bartik');
+  $admin_theme = variable_get('admin_theme', 'seven');
+
+  $form['debug']['regions']['rules_debug_region_' . $theme_default] = array(
+    '#type' => 'select',
+    '#title' => t('Default theme region'),
+    '#description' => t("The region, where the debug log should be displayed on the default theme %theme. For other themes, Rules will try to display the log on the same region, or hide it in case it doesn't exist.", array('%theme' => $theme_default)),
+    '#options' => system_region_list($theme_default, REGIONS_VISIBLE),
+    '#default_value' => variable_get('rules_debug_region_' . $theme_default, 'help'),
+  );
+
+  $form['debug']['regions']['rules_debug_region_' . $admin_theme] = array(
+    '#type' => 'select',
+    '#title' => t('Admin theme region'),
+    '#description' => t('The region, where the debug log should be displayed on the admin theme %theme.', array('%theme' => $admin_theme)),
+    '#options' => system_region_list($admin_theme, REGIONS_VISIBLE),
+    '#default_value' => variable_get('rules_debug_region_' . $admin_theme, 'help'),
+  );
+  if (db_table_exists('rules_rules')) {
+    drupal_set_message(t('There are left over rule configurations from a previous Rules 1.x installation. Proceed to the <a href="!url">upgrade page</a> to convert them and consult the README.txt for more details.', array('!url' => url('admin/config/workflow/rules/upgrade'))), 'warning');
+  }
+
+  return system_settings_form($form);
+}
+
+/**
+ * Advanced settings form.
+ */
+function rules_admin_settings_advanced($form, &$form_state) {
+
+  $form['integrity'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Integrity'),
+    '#description' => t('Rules checks the integrity of your configurations to discover and exclude broken configurations from evaluation.'),
+  );
+  $form['integrity']['start_integrity_check'] = array(
+    '#type' => 'submit',
+    '#value' => t('Recheck integrity'),
+    '#submit' => array('rules_admin_settings_integrity_check_submit'),
+  );
+  $form['cache'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Cache'),
+    '#description' => t('Rules caches information about available actions, conditions and data types. Additionally all components and reaction rules are cached for efficient evaluation.'),
+  );
+  $form['cache']['rebuild_rules_cache'] = array(
+    '#type' => 'submit',
+    '#value' => t("Rebuild Rules' cache"),
+    '#weight' => 2,
+    '#submit' => array('rules_admin_settings_cache_rebuild_submit'),
+  );
+  return $form;
+}
+
+/**
+ * Form submit callback to check the integrity of all configurations.
+ */
+function rules_admin_settings_integrity_check_submit($form, &$form_state) {
+  $start = microtime(TRUE);
+  $count = 0;
+  $rules_configs = rules_config_load_multiple(FALSE);
+  foreach ($rules_configs as $rules_config) {
+    rules_config_update_dirty_flag($rules_config, TRUE, TRUE);
+    if ($rules_config->dirty) {
+      $count++;
+      $variables = array('%label' => $rules_config->label(), '%name' => $rules_config->name, '@plugin' => $rules_config->plugin(), '!uri' => url(RulesPluginUI::path($rules_config->name)));
+      drupal_set_message(t('The @plugin <a href="!uri">%label (%name)</a> fails the integrity check and cannot be executed.', $variables), 'error');
+    }
+
+  }
+  drupal_set_message(t('Integrity of %count configurations checked in %duration seconds. %count_failed broken configurations found.', array(
+    '%count' => count($rules_configs),
+    '%count_failed' => $count,
+    '%duration' => round(microtime(TRUE) - $start, 2),
+  )));
+}
+
+/**
+ * Form submit callback: Rebuild the Rules' cache.
+ */
+function rules_admin_settings_cache_rebuild_submit($form, &$form_state) {
+  $start = microtime(TRUE);
+  rules_clear_cache();
+  // Manually trigger cache rebuilding of all caches.
+  rules_get_cache();
+  _rules_rebuild_component_cache();
+  RulesEventSet::rebuildEventCache();
+  drupal_set_message(t('Rules cache rebuilt in %duration seconds.', array(
+    '%duration' => round(microtime(TRUE) - $start, 2),
+  )));
+}
+
+/**
+ * Add reaction rules form.
+ */
+function rules_admin_add_reaction_rule($form, &$form_state, $base_path) {
+  RulesPluginUI::formDefaults($form, $form_state);
+
+  $rules_config = isset($form_state['rules_config']) ? $form_state['rules_config'] : rules_reaction_rule();
+  $rules_config->form($form, $form_state, array('show settings' => TRUE, 'button' => TRUE));
+
+  $form['settings']['#collapsible'] = FALSE;
+  $form['settings']['#type'] = 'container';
+  $form['settings']['label']['#default_value'] = '';
+
+  // Hide the rule elements stuff for now.
+  foreach (array('elements', 'conditions', 'add', 'events') as $key) {
+    $form[$key]['#access'] = FALSE;
+  }
+  foreach (array('active', 'weight') as $key) {
+    $form['settings'][$key]['#access'] = FALSE;
+  }
+  // Incorporate the form to add the first event.
+  $form['settings'] += rules_ui_add_event(array(), $form_state, $rules_config, $base_path);
+  $form['settings']['event']['#tree'] = FALSE;
+  $form['settings']['event_settings']['#tree'] = FALSE;
+  unset($form['settings']['help']);
+
+  unset($form['settings']['submit']);
+  $form['submit']['#value'] = t('Save');
+
+  $form_state += array('rules_config' => $rules_config);
+  $form['#validate'][] = 'rules_ui_add_reaction_rule_validate';
+  $form['#validate'][] = 'rules_ui_edit_element_validate';
+  $form['#submit'][] = 'rules_ui_add_reaction_rule_submit';
+  return $form;
+}
+
+/**
+ * Form validation callback.
+ */
+function rules_ui_add_reaction_rule_validate(&$form, &$form_state) {
+  rules_ui_add_event_validate($form['settings'], $form_state);
+}
+
+/**
+ * Form submit callback.
+ */
+function rules_ui_add_reaction_rule_submit(&$form, &$form_state) {
+  rules_ui_add_event_apply($form['settings'], $form_state);
+  rules_ui_edit_element_submit($form, $form_state);
+}
+
+/**
+ * Add component form.
+ */
+function rules_admin_add_component($form, &$form_state, $base_path) {
+  RulesPluginUI::$basePath = $base_path;
+  RulesPluginUI::formDefaults($form, $form_state);
+
+  $form['plugin_name'] = array(
+    '#type' => 'select',
+    '#title' => t('Component plugin'),
+    '#options' => rules_admin_component_options(),
+    '#description' => t('Choose which kind of component to create. Each component type is described in <a href="@url">the online documentation</a>.',
+      array('@url' => rules_external_help('component-types'))),
+    '#weight' => -2,
+    '#default_value' => isset($form_state['values']['plugin_name']) ? $form_state['values']['plugin_name'] : '',
+  );
+
+  if (!isset($form_state['rules_config'])) {
+    $form['continue'] = array(
+      '#type' => 'submit',
+      '#name' => 'continue',
+      '#submit' => array('rules_admin_add_component_create_submit'),
+      '#value' => t('Continue'),
+    );
+  }
+  else {
+    $form['plugin_name']['#disabled'] = TRUE;
+    $form_state['rules_config']->form($form, $form_state, array('show settings' => TRUE, 'button' => TRUE, 'init' => TRUE));
+    $form['settings']['#collapsible'] = FALSE;
+    $form['settings']['#type'] = 'container';
+    $form['settings']['label']['#default_value'] = '';
+    $form['settings']['#weight'] = -1;
+
+    // Hide the rule elements stuff for now.
+    foreach (array('elements', 'negate') as $key) {
+      $form[$key]['#access'] = FALSE;
+    }
+    foreach (array('active', 'weight') as $key) {
+      $form['settings'][$key]['#access'] = FALSE;
+    }
+  }
+  return $form;
+}
+
+function rules_admin_component_options() {
+  $cache = rules_get_cache();
+  return rules_extract_property(rules_filter_array($cache['plugin_info'], 'component', TRUE), 'label');
+}
+
+/**
+ * Submit callback that creates the new component object initially.
+ */
+function rules_admin_add_component_create_submit($form, &$form_state) {
+  $form_state['rules_config'] = rules_plugin_factory($form_state['values']['plugin_name']);
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Validation callback for adding a component.
+ */
+function rules_admin_add_component_validate($form, &$form_state) {
+  if (isset($form_state['rules_config'])) {
+    $form_state['rules_config']->form_validate($form, $form_state);
+  }
+}
+
+/**
+ * Final submit callback for adding a component.
+ */
+function rules_admin_add_component_submit($form, &$form_state) {
+  $rules_config = $form_state['rules_config'];
+  $rules_config->form_submit($form, $form_state);
+  drupal_set_message(t('Your changes have been saved.'));
+  $form_state['redirect'] = RulesPluginUI::path($rules_config->name);
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.info b/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.info
new file mode 100644
index 0000000000000000000000000000000000000000..6bce23964a0775077d0d27c81a5630295360afea
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.info
@@ -0,0 +1,15 @@
+name = Rules UI
+description = Administrative interface for managing rules.
+package = Rules
+core = 7.x
+dependencies[] = rules
+configure = admin/config/workflow/rules
+
+; Test cases
+files[] = tests/rules_admin.test
+
+; Information added by Drupal.org packaging script on 2019-01-24
+version = "7.x-2.12"
+core = "7.x"
+project = "rules"
+datestamp = "1548305586"
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.module b/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.module
new file mode 100644
index 0000000000000000000000000000000000000000..f5a38e75868c5e129b371b27ae65beefa1bcace9
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_admin/rules_admin.module
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * @file
+ * Rules Admin User Interface.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function rules_admin_menu() {
+  // Reaction rules UI menu entries.
+  $reaction_path = 'admin/config/workflow/rules/reaction';
+  $items = rules_ui()->config_menu($reaction_path);
+
+  $items[$reaction_path] = array(
+    'title' => 'Rules',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -1,
+  );
+  $items[$reaction_path . '/add'] = array(
+    'title' => 'Add new rule',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_admin_add_reaction_rule', $reaction_path),
+    'access arguments' => array('administer rules'),
+    'type' => MENU_LOCAL_ACTION,
+    'file' => 'rules_admin.inc',
+    'weight' => 0,
+  );
+  $items[$reaction_path . '/import'] = array(
+    'title' => 'Import rule',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_ui_import_form', $reaction_path),
+    'access arguments' => array('administer rules'),
+    'file' => 'ui/ui.forms.inc',
+    'file path' => drupal_get_path('module', 'rules'),
+    'type' => MENU_LOCAL_ACTION,
+  );
+
+  // Components UI menu entries.
+  $component_path = 'admin/config/workflow/rules/components';
+  $items += rules_ui()->config_menu($component_path);
+  $items[$component_path] = array(
+    'title' => 'Components',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_admin_components_overview', $component_path),
+    'access arguments' => array('administer rules'),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'rules_admin.inc',
+    'weight' => 0,
+  );
+  $items[$component_path . '/add'] = array(
+    'title' => 'Add new component',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_admin_add_component', $component_path),
+    'access arguments' => array('administer rules'),
+    'type' => MENU_LOCAL_ACTION,
+    'file' => 'rules_admin.inc',
+    'weight' => 0,
+  );
+  $items[$component_path . '/import'] = array(
+    'title' => 'Import component',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_ui_import_form', $component_path),
+    'access arguments' => array('administer rules'),
+    'file' => 'ui/ui.forms.inc',
+    'file path' => drupal_get_path('module', 'rules'),
+    'type' => MENU_LOCAL_ACTION,
+  );
+
+  // Some general rules admin menu items.
+  $items['admin/config/workflow/rules'] = array(
+    'title' => 'Rules',
+    'position' => 'right',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_admin_reaction_overview', $reaction_path),
+    'description' => 'Manage reaction rules and rule components.',
+    'access arguments' => array('administer rules'),
+    'file' => 'rules_admin.inc',
+  );
+  $items['admin/config/workflow/rules/settings'] = array(
+    'title' => 'Settings',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_admin_settings'),
+    'access arguments' => array('administer rules'),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'rules_admin.inc',
+    'weight' => 1,
+  );
+  $items['admin/config/workflow/rules/settings/basic'] = array(
+    'title' => 'Basic',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/config/workflow/rules/settings/advanced'] = array(
+    'title' => 'Advanced',
+    'type' => MENU_LOCAL_TASK,
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_admin_settings_advanced'),
+    'access arguments' => array('administer rules'),
+    'file' => 'rules_admin.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_form_alter().
+ *
+ * Since the overview forms are GET forms, we don't want them to send a wide
+ * variety of information. We need to use hook_form_alter() because the
+ * properties are added after form creation.
+ */
+function rules_admin_form_alter(&$form, &$form_state, $form_id) {
+  if ($form_id == 'rules_admin_reaction_overview' || $form_id == 'rules_admin_components_overview') {
+    $form['form_build_id']['#access'] = FALSE;
+    $form['form_token']['#access'] = FALSE;
+    $form['form_id']['#access'] = FALSE;
+  }
+}
+
+/**
+ * Implements hook_system_info_alter().
+ *
+ * Adds configuration links for Rules and Rules Scheduler in the modules list.
+ * (This is done by the Rules UI module, without which there would be no
+ * configuration pages to visit.)
+ */
+function rules_admin_system_info_alter(&$info, $file, $type) {
+  if ($file->name == 'rules') {
+    $info['configure'] = 'admin/config/workflow/rules';
+  }
+  if ($file->name == 'rules_scheduler') {
+    $info['configure'] = 'admin/config/workflow/rules/schedule';
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_admin/tests/rules_admin.test b/profiles/wcm_base/modules/contrib/rules/rules_admin/tests/rules_admin.test
new file mode 100644
index 0000000000000000000000000000000000000000..fce8075e3bb3af43e87aca1d1f285209ca1da03c
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_admin/tests/rules_admin.test
@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * @file
+ * Rules UI tests.
+ */
+
+/**
+ * Tests for creating rules through the UI.
+ */
+class RulesUiTestCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules UI Tests ',
+      'description' => 'Tests Rules UI.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules', 'rules_admin', 'rules_test');
+    RulesLog::logger()->clear();
+    variable_set('rules_debug_log', TRUE);
+  }
+
+  /**
+   * Tests that NOT condition labels are not HTML-encoded in the UI.
+   *
+   * @see https://www.drupal.org/project/rules/issues/1945006
+   */
+  public function testConditionLabel() {
+    // Create a simple user account with permission to create a rule.
+    $user = $this->drupalCreateUser(array('access administration pages', 'administer rules'));
+    $this->drupalLogin($user);
+
+    // First we need an event.
+    $this->drupalGet('admin/config/workflow/rules/reaction/add');
+    $edit = array(
+      'settings[label]' => 'Test node event',
+      'settings[name]' => 'test_node_event',
+      'event' => 'node_insert',
+    );
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertText('Editing reaction rule', 'Rule edit page is shown.');
+
+    // Now add a condition with a special character in the label.
+    $this->clickLink('Add condition');
+    $this->assertText('Add a new condition', 'Condition edit page is shown.');
+    $edit = array(
+      'element_name' => 'rules_test_condition_apostrophe',
+    );
+    $this->drupalPost(NULL, $edit, 'Continue');
+
+    // Negate the condition, as this is how it gets improperly HTML encoded.
+    $edit = array(
+      'negate' => TRUE,
+    );
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertNoRaw("&amp;#039;", 'Apostrophe is not HTML-encoded.');
+  }
+
+}
+
+/**
+ * UI test cases for the minimal profile.
+ *
+ * The minimal profile is useful for testing because it has fewer dependencies
+ * so the tests run faster. Also, removing the profile-specific configuration
+ * reveals assumptions in the code. For example, the minimal profile doesn't
+ * define any content types, so when Rules expects to have content types to
+ * operate on that assumpation may cause errors.
+ */
+class RulesMinimalProfileTestCase extends DrupalWebTestCase {
+
+  protected $profile = 'minimal';
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules UI Minimal Profile Tests ',
+      'description' => 'Tests UI support for minimal profile.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules', 'rules_admin');
+    RulesLog::logger()->clear();
+    variable_set('rules_debug_log', TRUE);
+  }
+
+  /**
+   * Tests node event UI without content types.
+   *
+   * @see https://www.drupal.org/project/rules/issues/2267341
+   */
+  public function testNodeEventUi() {
+    // Create a simple user account with permission to create a rule.
+    $user = $this->drupalCreateUser(array('access administration pages', 'administer rules'));
+    $this->drupalLogin($user);
+
+    $this->drupalGet('admin/config/workflow/rules/reaction/add');
+    $edit = array(
+      'settings[label]' => 'Test node event',
+      'settings[name]' => 'test_node_event',
+      'event' => 'node_insert',
+    );
+    $this->drupalPostAJAX(NULL, $edit, 'event');
+    $this->assertText('Restrict by type', 'Restrict by type selection is visible.');
+    $this->drupalPost(NULL, $edit, 'Save');
+    $this->assertText('Editing reaction rule', 'Rule edit page is shown.');
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.i18n.inc b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.i18n.inc
new file mode 100644
index 0000000000000000000000000000000000000000..da49f39bab7731c8b7486ce5efc44e60dc26fd1a
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.i18n.inc
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * @file
+ * Internationalization integration based upon the entity API i18n stuff.
+ */
+
+/**
+ * Rules i18n integration controller.
+ */
+class RulesI18nStringController extends EntityDefaultI18nStringController {
+
+  /**
+   * Overridden to customize i18n object info.
+   *
+   * @see EntityDefaultI18nStringController::hook_object_info()
+   */
+  public function hook_object_info() {
+    $info = parent::hook_object_info();
+    $info['rules_config']['class'] = 'RulesI18nStringObjectWrapper';
+    return $info;
+  }
+
+  /**
+   * Overridden to customize the used menu wildcard.
+   */
+  protected function menuWildcard() {
+    return '%rules_config';
+  }
+
+  /**
+   * Provide the menu base path. We can provide only one though.
+   */
+  protected function menuBasePath() {
+    return 'admin/config/workflow/rules/reaction';
+  }
+
+}
+
+/**
+ * Custom I18n String object wrapper, which register custom properties per config.
+ */
+class RulesI18nStringObjectWrapper extends i18n_string_object_wrapper {
+
+  /**
+   * Get translatable properties.
+   */
+  protected function build_properties() {
+    $strings = parent::build_properties();
+    $properties = array();
+
+    // Also add in the configuration label, as the i18n String UI requires
+    // a String to be available always.
+    $properties['label'] = array(
+      'title' => t('Configuration name'),
+      'string' => $this->object->label,
+    );
+
+    $this->buildElementProperties($this->object, $properties);
+
+    // Add in translations for all elements.
+    foreach ($this->object->elements() as $element) {
+      $this->buildElementProperties($element, $properties);
+    }
+    $strings[$this->get_textgroup()]['rules_config'][$this->object->name] = $properties;
+    return $strings;
+  }
+
+  /**
+   * Adds in translatable properties of the given element.
+   */
+  protected function buildElementProperties($element, &$properties) {
+
+    foreach ($element->pluginParameterInfo() as $name => $info) {
+      // Add in all directly provided input variables.
+      if (!empty($info['translatable']) && isset($element->settings[$name])) {
+        // If its an array of textual values, translate each value on its own.
+        if (is_array($element->settings[$name])) {
+          foreach ($element->settings[$name] as $i => $value) {
+            $properties[$element->elementId() . ':' . $name . ':' . $i] = array(
+              'title' => t('@plugin "@label" (id @id), @parameter, Value @delta', array('@plugin' => drupal_ucfirst($element->plugin()), '@label' => $element->label(), '@id' => $element->elementId(), '@parameter' => $info['label'], '@delta' => $i + 1)),
+              'string' => $value,
+            );
+          }
+        }
+        else {
+          $properties[$element->elementId() . ':' . $name] = array(
+            'title' => t('@plugin "@label" (id @id), @parameter', array('@plugin' => drupal_ucfirst($element->plugin()), '@label' => $element->label(), '@id' => $element->elementId(), '@parameter' => $info['label'])),
+            'string' => $element->settings[$name],
+          );
+        }
+      }
+    }
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.info b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.info
new file mode 100644
index 0000000000000000000000000000000000000000..835d413f8115384570113511a40495d9eff2443f
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.info
@@ -0,0 +1,15 @@
+name = Rules translation
+description = Allows translating rules.
+dependencies[] = rules
+dependencies[] = i18n_string
+package = Multilingual - Internationalization
+core = 7.x
+files[] = rules_i18n.i18n.inc
+files[] = rules_i18n.rules.inc
+files[] = rules_i18n.test
+
+; Information added by Drupal.org packaging script on 2019-01-24
+version = "7.x-2.12"
+core = "7.x"
+project = "rules"
+datestamp = "1548305586"
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.install b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.install
new file mode 100644
index 0000000000000000000000000000000000000000..7debfd9844f3558bef47e587b882327f30bc367b
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.install
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * Install file for Rules i18n.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function rules_i18n_install() {
+  global $language;
+
+  $langcode = $language->language;
+  drupal_static_reset('i18n_object_info');
+  drupal_static_reset('entity_get_info');
+  drupal_static_reset('entity_i18n_controller');
+  cache_clear_all("entity_info:$langcode", 'cache');
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.module b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.module
new file mode 100644
index 0000000000000000000000000000000000000000..e7cea79d8383f9cd45c8781a5d7136d7ce5977a0
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.module
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * @file
+ * Rules i18n integration.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function rules_i18n_rules_ui_menu_alter(&$items, $base_path, $base_count) {
+
+  $items[$base_path . '/manage/%rules_config/edit'] = array(
+    'title' => 'Edit',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -100,
+  );
+
+  // For reaction-rules i18n generates the menu items, for the others we provide
+  // further i18n menu items for all other base paths.
+
+  if ($base_path != 'admin/config/workflow/rules/reaction') {
+
+    $items[$base_path . '/manage/%rules_config/translate'] = array(
+      'title' => 'Translate',
+      'page callback' => 'i18n_page_translate_localize',
+      'page arguments' => array('rules_config', $base_count + 1),
+      'access callback' => 'i18n_object_translate_access',
+      'access arguments' => array('rules_config', $base_count + 1),
+      'type' => MENU_LOCAL_TASK,
+      'file' => 'i18n.pages.inc',
+      'file path' => drupal_get_path('module', 'i18n'),
+      'weight' => 10,
+    );
+
+    $items[$base_path . '/manage/%rules_config/translate/%i18n_language'] = array(
+      'title' => 'Translate',
+      'page callback' => 'i18n_page_translate_localize',
+      'page arguments' => array('rules_config', $base_count + 1, $base_count + 3),
+      'access callback' => 'i18n_object_translate_access',
+      'access arguments' => array('rules_config', $base_count),
+      'type' => MENU_CALLBACK,
+      'file' => 'i18n.pages.inc',
+      'file path' => drupal_get_path('module', 'i18n'),
+      'weight' => 10,
+    );
+  }
+}
+
+/**
+ * Implements hook_entity_info_alter().
+ */
+function rules_i18n_entity_info_alter(&$info) {
+  // Enable i18n support via the entity API.
+  $info['rules_config']['i18n controller class'] = 'RulesI18nStringController';
+}
+
+/**
+ * Implements hook_rules_config_insert().
+ */
+function rules_i18n_rules_config_insert($rules_config) {
+  // Do nothing when rebuilding defaults to avoid multiple cache rebuilds.
+  // @see rules_i18n_rules_config_defaults_rebuild()
+  if (!empty($rules_config->is_rebuild)) {
+    return;
+  }
+
+  i18n_string_object_update('rules_config', $rules_config);
+}
+
+/**
+ * Implements hook_rules_config_update().
+ */
+function rules_i18n_rules_config_update($rules_config, $original = NULL) {
+  // Do nothing when rebuilding defaults to avoid multiple cache rebuilds.
+  // @see rules_i18n_rules_config_defaults_rebuild()
+  if (!empty($rules_config->is_rebuild)) {
+    return;
+  }
+  $original = $original ? $original : $rules_config->original;
+
+  // Account for name changes.
+  if ($original->name != $rules_config->name) {
+    i18n_string_update_context("rules:rules_config:{$original->name}:*", "rules:rules_config:{$rules_config->name}:*");
+  }
+
+  // We need to remove the strings of any disappeared properties, i.e. strings
+  // from translatable parameters of deleted actions.
+
+  // i18n_object() uses a static cache per config, so bypass it to wrap the
+  // original entity.
+  $object_key = i18n_object_key('rules_config', $original);
+  $old_i18n_object = new RulesI18nStringObjectWrapper('rules_config', $object_key, $original);
+  $old_strings = $old_i18n_object->get_strings(array('empty' => TRUE));
+
+  // Note: For the strings to have updated values, the updated entity needs to
+  // be handled last due to i18n's cache.
+  $strings = i18n_object('rules_config', $rules_config)->get_strings(array('empty' => TRUE));
+
+  foreach (array_diff_key($old_strings, $strings) as $name => $string) {
+    $string->remove(array('empty' => TRUE));
+  }
+  // Now update the remaining strings.
+  foreach ($strings as $string) {
+    $string->update(array('empty' => TRUE, 'update' => TRUE));
+  }
+}
+
+/**
+ * Implements hook_rules_config_delete().
+ */
+function rules_i18n_rules_config_delete($rules_config) {
+  // Only react on real delete, not revert.
+  if (!$rules_config->hasStatus(ENTITY_IN_CODE)) {
+    i18n_string_object_remove('rules_config', $rules_config);
+  }
+}
+
+/**
+ * Implements hook_rules_config_defaults_rebuild().
+ */
+function rules_i18n_rules_config_defaults_rebuild($rules_configs, $originals) {
+  // Once all defaults have been rebuilt, update all i18n strings at once. That
+  // way we build the rules cache once the rebuild is complete and avoid
+  // rebuilding caches for each updated rule.
+  foreach ($rules_configs as $name => $rule_config) {
+    if (empty($originals[$name])) {
+      rules_i18n_rules_config_insert($rule_config);
+    }
+    else {
+      rules_i18n_rules_config_update($rule_config, $originals[$name]);
+    }
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.rules.inc b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..b4c4f9cebe1359be375470f98b397b497f94787a
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.rules.inc
@@ -0,0 +1,203 @@
+<?php
+
+/**
+ * @file
+ * Internationalization rules integration.
+ */
+
+/**
+ * Implements hook_rules_action_info().
+ */
+function rules_i18n_rules_action_info() {
+  $items['rules_i18n_t'] = array(
+    'label' => t('Translate a text'),
+    'group' => t('Translation'),
+    'parameter' => array(
+      'text' => array(
+        'type' => 'text',
+        'label' => t('Text'),
+        'description' => t('The text to translate.'),
+        'translatable' => TRUE,
+      ),
+      'language' => array(
+        'type' => 'token',
+        'label' => t('Language'),
+        'description' => t('The language to translate the text into.'),
+        'options list' => 'entity_metadata_language_list',
+        'default mode' => 'select',
+      ),
+    ),
+    'provides' => array(
+      'text' => array('type' => 'text', 'label' => t('The translated text')),
+    ),
+    'base' => 'rules_i18n_action_t',
+    'access callback' => 'rules_i18n_rules_integration_access',
+  );
+  $items['rules_i18n_select'] = array(
+    'label' => t('Select a translated value'),
+    'group' => t('Translation'),
+    'parameter' => array(
+      'data' => array(
+        'type' => '*',
+        'label' => t('Data'),
+        'description' => t('Select a translated value, e.g. a translatable field. If the selected data is not translatable, the language neutral value will be selected.'),
+        'translatable' => TRUE,
+        'restrict' => 'select',
+      ),
+      'language' => array(
+        'type' => 'token',
+        'label' => t('Language'),
+        'description' => t('The language to translate the value into.'),
+        'options list' => 'entity_metadata_language_list',
+      ),
+    ),
+    'provides' => array(
+      'data_translated' => array('type' => '*', 'label' => t('The translated value')),
+    ),
+    'base' => 'rules_i18n_action_select',
+    'access callback' => 'rules_i18n_rules_integration_access',
+  );
+  return $items;
+}
+
+/**
+ * Access callback for the rules i18n integration.
+ */
+function rules_i18n_rules_integration_access() {
+  return user_access('translate interface');
+}
+
+/**
+ * Action callback: Translate a text.
+ */
+function rules_i18n_action_t($text) {
+  // Nothing to do, as our input evaluator has already translated it.
+  // @see RulesI18nStringEvaluator
+  return array('text' => $text);
+}
+
+/**
+ * Implements the form_alter callback for the "Translate a text" action to set a default selector.
+ */
+function rules_i18n_action_t_form_alter(&$form, &$form_state, $options, $element) {
+  if (isset($form['parameter']['language']['settings']['language:select']) && empty($element->settings['language:select'])) {
+    $form['parameter']['language']['settings']['language:select']['#default_value'] = 'site:current-page:language';
+  }
+}
+
+/**
+ * Action callback: Select a translated value.
+ */
+function rules_i18n_action_select($data) {
+  // Nothing to do, as Rules applies the language to the data selector for us.
+  return array('data_translated' => $data);
+}
+
+/**
+ * Action "Select a translated value" info_alter callback.
+ */
+function rules_i18n_action_select_info_alter(&$element_info, $element) {
+  $element->settings += array('data:select' => NULL);
+  if ($wrapper = $element->applyDataSelector($element->settings['data:select'])) {
+    $info = $wrapper->info();
+    // Pass through the data type of the selected data.
+    $element_info['provides']['data_translated']['type'] = $wrapper->type();
+  }
+}
+
+/**
+ * Implements hook_rules_evaluator_info().
+ */
+function rules_i18n_rules_evaluator_info() {
+  return array(
+    'i18n' => array(
+      'class' => 'RulesI18nStringEvaluator',
+      'type' => array('text', 'list<text>', 'token', 'list<token>'),
+      // Be sure to translate after doing PHP evaluation.
+      'weight' => -8,
+    ),
+  );
+}
+
+/**
+ * A class implementing a rules input evaluator processing tokens.
+ */
+class RulesI18nStringEvaluator extends RulesDataInputEvaluator {
+
+  public static function access() {
+    return user_access('translate admin strings');
+  }
+
+  /**
+   * Overrides RulesDataInputEvaluator::prepare().
+   */
+  public function prepare($text, $var_info, $param_info = NULL) {
+    if (!empty($param_info['translatable'])) {
+      $this->setting = TRUE;
+    }
+    else {
+      // Else, skip this evaluator.
+      $this->setting = NULL;
+    }
+  }
+
+  /**
+   * Prepare the i18n-context string.
+   *
+   * We have to use process() here instead of evaluate() because we need more
+   * context than evaluate() provides.
+   */
+  public function process($value, $info, RulesState $state, RulesPlugin $element, $options = NULL) {
+    $options = isset($options) ? $options : $this->getEvaluatorOptions($info, $state, $element);
+    $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element, $options) : $value;
+    if (isset($element->root()->name)) {
+      $config_name = $element->root()->name;
+      $id = $element->elementId();
+      $name = $info['#name'];
+      $options['i18n context'] = "rules:rules_config:$config_name:$id:$name";
+      return $this->evaluate($value, $options, $state);
+    }
+    return $value;
+  }
+
+  /**
+   * Translate the value.
+   *
+   * If the element provides a language parameter, we are using this target
+   * language provided via $options['language']. Sanitizing is handled by Rules,
+   * so disable that for i18n.
+   */
+  public function evaluate($value, $options, RulesState $state) {
+    $langcode = isset($options['language']) ? $options['language']->language : NULL;
+    if (is_array($value)) {
+      foreach ($value as $key => $text) {
+        $value[$key] = i18n_string($options['i18n context'] . ':' . $key, $text, array('langcode' => $langcode, 'sanitize' => FALSE));
+      }
+    }
+    else {
+      $value = i18n_string($options['i18n context'], $value, array('langcode' => $langcode, 'sanitize' => FALSE));
+    }
+    return $value;
+  }
+
+  /**
+   * Overrides RulesDataInputEvaluator::help().
+   */
+  public static function help($var_info, $param_info = array()) {
+    if (!empty($param_info['translatable'])) {
+      if (!empty($param_info['custom translation language'])) {
+        $text = t('Translations can be provided at the %translate tab. The argument value is translated to the configured language.', array('%translate' => t('Translate')));
+      }
+      else {
+        $text = t('Translations can be provided at the %translate tab. The argument value is translated to the current interface language.', array('%translate' => t('Translate')));
+      }
+      $render = array(
+        '#theme' => 'rules_settings_help',
+        '#text' => $text,
+        '#heading' => t('Translation'),
+      );
+      return $render;
+    }
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.test b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.test
new file mode 100644
index 0000000000000000000000000000000000000000..615d3dda794ada1b572e6da5ba3ecab6dfc2c0a9
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_i18n/rules_i18n.test
@@ -0,0 +1,190 @@
+<?php
+
+/**
+ * @file
+ * Rules i18n tests.
+ */
+
+/**
+ * Test the i18n integration.
+ */
+class RulesI18nTestCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules I18n',
+      'description' => 'Tests translating Rules configs.',
+      'group' => 'Rules',
+      'dependencies' => array('i18n_string'),
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules_i18n');
+    $this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes', 'administer languages', 'administer content types', 'administer blocks', 'access administration pages'));
+    $this->drupalLogin($this->admin_user);
+    $this->addLanguage('de');
+  }
+
+  /**
+   * Copied from i18n module (class Drupali18nTestCase).
+   *
+   * We cannot extend from Drupali18nTestCase as else the test-bot would die.
+   */
+  public function addLanguage($language_code) {
+    // Check to make sure that language has not already been installed.
+    $this->drupalGet('admin/config/regional/language');
+
+    if (strpos($this->drupalGetContent(), 'enabled[' . $language_code . ']') === FALSE) {
+      // Doesn't have language installed so add it.
+      $edit = array();
+      $edit['langcode'] = $language_code;
+      $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+      // Make sure we are not using a stale list.
+      drupal_static_reset('language_list');
+      $languages = language_list('language');
+      $this->assertTrue(array_key_exists($language_code, $languages), t('Language was installed successfully.'));
+
+      if (array_key_exists($language_code, $languages)) {
+        $this->assertRaw(t('The language %language has been created and can now be used. More information is available on the <a href="@locale-help">help screen</a>.', array('%language' => $languages[$language_code]->name, '@locale-help' => url('admin/help/locale'))), t('Language has been created.'));
+      }
+    }
+    elseif ($this->xpath('//input[@type="checkbox" and @name=:name and @checked="checked"]', array(':name' => 'enabled[' . $language_code . ']'))) {
+      // It's installed and enabled. No need to do anything.
+      $this->assertTrue(TRUE, 'Language [' . $language_code . '] already installed and enabled.');
+    }
+    else {
+      // It's installed but not enabled. Enable it.
+      $this->assertTrue(TRUE, 'Language [' . $language_code . '] already installed.');
+      $this->drupalPost(NULL, array('enabled[' . $language_code . ']' => TRUE), t('Save configuration'));
+      $this->assertRaw(t('Configuration saved.'), t('Language successfully enabled.'));
+    }
+  }
+
+  /**
+   * Tests translating rules configs.
+   */
+  public function testRulesConfigTranslation() {
+    // Create a rule and translate it.
+    $rule = rule();
+    $rule->label = 'label-en';
+    $rule->action('drupal_message', array('message' => 'English message for [site:current-user].'));
+    $rule->save();
+
+    $actions = $rule->actions();
+    $id = $actions[0]->elementId();
+
+    // Add a translation.
+    i18n_string_textgroup('rules')->update_translation("rules_config:{$rule->name}:label", 'de', 'label-de');
+    i18n_string_textgroup('rules')->update_translation("rules_config:{$rule->name}:$id:message", 'de', 'German message für [site:current-user].');
+
+    // Execute the Rule and make sure the translated message has been output.
+    // To do so, set the global language to German.
+    $languages = language_list();
+    $GLOBALS['language'] = $languages['de'];
+
+    // Clear messages and execute the rule.
+    i18n_string_textgroup('rules')->cache_reset();
+    drupal_get_messages();
+    $rule->execute();
+
+    $messages = drupal_get_messages();
+    $this->assertEqual($messages['status'][0], 'German message für ' . $GLOBALS['user']->name . '.', 'Translated message has been output.');
+
+    // Test re-naming the rule.
+    $rule->name = 'rules_i18n_name_2';
+    $rule->save();
+    $translation = entity_i18n_string("rules:rules_config:{$rule->name}:label", $rule->label, 'de');
+    $this->assertEqual($translation, 'label-de', 'Translation survives a name change.');
+
+    // Test updating and make sure the translation stays.
+    $rule->label = 'Label new';
+    $rule->save();
+    $translation = entity_i18n_string("rules:rules_config:{$rule->name}:label", $rule->label, 'de');
+    $this->assertEqual($translation, 'label-de', 'Translation survives an update.');
+
+    // Test deleting the action and make sure the string is deleted too.
+    $actions[0]->delete();
+    $rule->save();
+    $translation = entity_i18n_string("rules_config:{$rule->name}:$id:message", 'English message for [site:current-user].', 'de');
+    $this->assertEqual($translation, 'English message for [site:current-user].', 'Translation of deleted action has been deleted.');
+
+    // Now delete the whole config and make sure all translations are deleted.
+    $rule->delete();
+    $translation = entity_i18n_string("rules_config:{$rule->name}:label", 'label-en', 'de');
+    $this->assertEqual($translation, 'label-en', 'Translation of deleted config has been deleted.');
+  }
+
+  /**
+   * Tests the "Translate a text" action.
+   */
+  public function testI18nActionT() {
+    $set = rules_action_set(array());
+    $set->action('rules_i18n_t', array(
+      'text' => 'untranslated',
+      'language' => 'de',
+    ));
+    $set->action('drupal_message', array('message:select' => 'text'));
+    $set->save('rules_i18n_test');
+
+    // Add a translation.
+    $actions = $set->getIterator();
+    $id = $actions[0]->elementId();
+    i18n_string_textgroup('rules')->update_translation("rules_config:{$set->name}:$id:text", 'de', 'text-de');
+
+    // Clear messages and execute it.
+    drupal_get_messages();
+    $set->execute();
+    $messages = drupal_get_messages();
+    $this->assertEqual($messages['status'][0], 'text-de', 'Text has been successfully translated.');
+
+    // Enable the PHP module and make sure PHP in translations is not evaluated.
+    module_enable(array('php'));
+    i18n_string_textgroup('rules')->update_translation("rules_config:{$set->name}:$id:text", 'de', 'text <?php echo "eval";?>');
+
+    // Clear messages and execute it.
+    drupal_get_messages();
+    $set->execute();
+    $messages = drupal_get_messages();
+    $this->assertEqual($messages['status'][0], check_plain('text <?php echo "eval";?>'), 'PHP in translated text is not executed.');
+  }
+
+  /**
+   * Tests the "Select a translated value" action.
+   */
+  public function testI18nActionSelect() {
+    // Make the body field and the node type 'page' translatable.
+    $field = field_info_field('body');
+    $field['translatable'] = TRUE;
+    field_update_field($field);
+    variable_set('language_content_type_page', 1);
+
+    $set = rules_action_set(array('node' => array('type' => 'node')));
+    $set->action('rules_i18n_select', array(
+      'data:select' => 'node:body:value',
+      'language' => 'de',
+      'data_translated:var' => 'body',
+    ));
+    $set->action('drupal_message', array('message:select' => 'body'));
+    $set->save();
+
+    $body['en'][0] = array('value' => 'English body.');
+    $body['de'][0] = array('value' => 'German body.');
+    $node = $this->drupalCreateNode(array('language' => 'en', 'body' => $body));
+
+    // Clear messages and execute it.
+    drupal_get_messages();
+    $set->execute($node);
+
+    $messages = drupal_get_messages();
+    $this->assertEqual($messages['status'][0], "German body.\n", 'Translated text has been selected.');
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.handler.inc b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.handler.inc
new file mode 100644
index 0000000000000000000000000000000000000000..60341576383d868c8bb866b3c3d5afc091b953f0
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.handler.inc
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Views integration for the rules scheduler module.
+ */
+
+/**
+ * Default scheduled task handler.
+ */
+class RulesSchedulerDefaultTaskHandler implements RulesSchedulerTaskHandlerInterface {
+
+  /**
+   * The task array.
+   *
+   * @var array
+   */
+  protected $task;
+
+  /**
+   * Constructs a repetitive task handler object.
+   */
+  public function __construct(array $task) {
+    $this->task = $task;
+  }
+
+  /**
+   * Implements RulesSchedulerTaskHandlerInterface::runTask().
+   */
+  public function runTask() {
+    if ($component = rules_get_cache('comp_' . $this->task['config'])) {
+      $replacements = array('%label' => $component->label(), '%plugin' => $component->plugin());
+      $replacements['%identifier'] = $this->task['identifier'] ? $this->task['identifier'] : t('without identifier');
+      rules_log('Scheduled evaluation of %plugin %label, task %identifier.', $replacements, RulesLog::INFO, $component, TRUE);
+      $state = unserialize($this->task['data']);
+      $state->restoreBlocks();
+      // Block the config to prevent any future recursion.
+      $state->block($component);
+      // Finally evaluate the component with the given state.
+      $component->evaluate($state);
+      $state->unblock($component);
+      rules_log('Finished evaluation of %plugin %label, task %identifier.', $replacements, RulesLog::INFO, $component, FALSE);
+      $state->cleanUp();
+    }
+  }
+
+  /**
+   * Implements RulesSchedulerTaskHandlerInterface::afterTaskQueued().
+   */
+  public function afterTaskQueued() {
+    // Delete the task from the task list.
+    db_delete('rules_scheduler')
+      ->condition('tid', $this->task['tid'])
+      ->execute();
+  }
+
+  /**
+   * Implements RulesSchedulerTaskHandlerInterface::getTask().
+   */
+  public function getTask() {
+    return $this->task;
+  }
+
+}
+
+/**
+ * Interface for scheduled task handlers.
+ *
+ * Task handlers control the behavior of a task when it's queued or executed.
+ * Unless specified otherwise, the RulesSchedulerDefaultTaskHandler task handler
+ * is used.
+ *
+ * @see rules_scheduler_run_task()
+ * @see rules_scheduler_cron()
+ * @see RulesSchedulerDefaultTaskHandler
+ */
+interface RulesSchedulerTaskHandlerInterface {
+
+  /**
+   * Processes a queue item.
+   *
+   * @throws RulesEvaluationException
+   *   If there are any problems executing the task.
+   *
+   * @see rules_scheduler_run_task()
+   */
+  public function runTask();
+
+  /**
+   * Processes a task after it has been queued.
+   *
+   * @see rules_scheduler_cron()
+   */
+  public function afterTaskQueued();
+
+  /**
+   * Returns the task associated with the task handler.
+   *
+   * @return array
+   *   The task (queue item) array.
+   */
+  public function getTask();
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.views.inc b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.views.inc
new file mode 100644
index 0000000000000000000000000000000000000000..2820d45073ae0f5294907438c2c769b458d001d2
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.views.inc
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Views integration for the rules scheduler module.
+ */
+
+/**
+ * Implements hook_views_data().
+ *
+ * Specifies the list of future scheduled tasks displayed on the schedule page.
+ */
+function rules_scheduler_views_data() {
+  $table = array(
+    'rules_scheduler' => array(
+      'table' => array(
+        'group' => 'Rules scheduler',
+        'base' => array(
+          'field' => 'tid',
+          'title' => t('Scheduled Rules components'),
+          'help' => t("Scheduled Rules components that are executed based on time and cron"),
+          'weight' => -10,
+        ),
+      ),
+      'tid' => array(
+        'title' => t('Tid'),
+        'help' => t('The internal ID of the scheduled component'),
+        'field' => array(
+          'click sortable' => TRUE,
+        ),
+        'filter' => array(
+          'handler' => 'views_handler_filter_numeric',
+        ),
+        'sort' => array(
+          'handler' => 'views_handler_sort',
+        ),
+      ),
+      'config' => array(
+        'title' => t('Component name'),
+        'help' => t('The name of the component'),
+        'field' => array(
+          'click sortable' => TRUE,
+        ),
+        'filter' => array(
+          'handler' => 'rules_scheduler_views_filter',
+        ),
+        'argument' => array(
+          'handler' => 'views_handler_argument_string',
+        ),
+        'sort' => array(
+          'handler' => 'views_handler_sort',
+        ),
+      ),
+      'date' => array(
+        'title' => t('Scheduled date'),
+        'help' => t('Scheduled date and time stamp'),
+        'field' => array(
+          'handler' => 'views_handler_field_date',
+          'click sortable' => TRUE,
+        ),
+        'filter' => array(
+          'handler' => 'views_handler_filter',
+        ),
+        'sort' => array(
+          'handler' => 'views_handler_sort',
+        ),
+      ),
+      'identifier' => array(
+        'title' => t('User provided identifier'),
+        'help' => t('ID to recognize this specific scheduled task'),
+        'field' => array(
+          'click sortable' => TRUE,
+        ),
+        'filter' => array(
+          'handler' => 'views_handler_filter_string',
+        ),
+        'sort' => array(
+          'handler' => 'views_handler_sort',
+        ),
+      ),
+    ),
+  );
+  return $table;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.views_default.inc b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.views_default.inc
new file mode 100644
index 0000000000000000000000000000000000000000..708dd631a2bb770b567f06bc3537797c0f7b6f5b
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler.views_default.inc
@@ -0,0 +1,180 @@
+<?php
+
+/**
+ * @file
+ * Views integration for the rules scheduler module.
+ */
+
+/**
+ * Implements hook_views_default_views().
+ */
+function rules_scheduler_views_default_views() {
+  $view = new view();
+  $view->name = 'rules_scheduler';
+  $view->description = 'Scheduled Rules components';
+  $view->tag = '';
+  $view->base_table = 'rules_scheduler';
+  $view->api_version = '3.0-alpha1';
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+
+  /* Display: Defaults */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->display->display_options['access']['type'] = 'perm';
+  $handler->display->display_options['access']['perm'] = 'administer rules';
+  $handler->display->display_options['cache']['type'] = 'none';
+  $handler->display->display_options['query']['type'] = 'views_query';
+  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['pager']['type'] = 'full';
+  $handler->display->display_options['pager']['options']['items_per_page'] = '30';
+  $handler->display->display_options['pager']['options']['offset'] = '0';
+  $handler->display->display_options['pager']['options']['id'] = '0';
+  $handler->display->display_options['style_plugin'] = 'table';
+  $handler->display->display_options['style_options']['columns'] = array(
+    'tid' => 'tid',
+    'config' => 'config',
+    'date' => 'date',
+    'identifier' => 'identifier',
+    'nothing' => 'nothing',
+  );
+  $handler->display->display_options['style_options']['default'] = 'date';
+  $handler->display->display_options['style_options']['info'] = array(
+    'tid' => array(
+      'sortable' => 0,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+    ),
+    'config' => array(
+      'sortable' => 1,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+    ),
+    'date' => array(
+      'sortable' => 1,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+    ),
+    'identifier' => array(
+      'sortable' => 1,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+    ),
+    'nothing' => array(
+      'align' => '',
+      'separator' => '',
+    ),
+  );
+  $handler->display->display_options['style_options']['override'] = 1;
+  $handler->display->display_options['style_options']['sticky'] = 0;
+  /* Empty text: Global: Text area */
+  $handler->display->display_options['empty']['area']['id'] = 'area';
+  $handler->display->display_options['empty']['area']['table'] = 'views';
+  $handler->display->display_options['empty']['area']['field'] = 'area';
+  $handler->display->display_options['empty']['area']['empty'] = FALSE;
+  $handler->display->display_options['empty']['area']['content'] = 'No tasks have been scheduled.';
+  $handler->display->display_options['empty']['area']['format'] = 'plain_text';
+  /* Field: Rules scheduler: Tid */
+  $handler->display->display_options['fields']['tid']['id'] = 'tid';
+  $handler->display->display_options['fields']['tid']['table'] = 'rules_scheduler';
+  $handler->display->display_options['fields']['tid']['field'] = 'tid';
+  /* Field: Rules scheduler: Component name */
+  $handler->display->display_options['fields']['config']['id'] = 'config';
+  $handler->display->display_options['fields']['config']['table'] = 'rules_scheduler';
+  $handler->display->display_options['fields']['config']['field'] = 'config';
+  $handler->display->display_options['fields']['config']['alter']['alter_text'] = 0;
+  $handler->display->display_options['fields']['config']['alter']['make_link'] = 1;
+  $handler->display->display_options['fields']['config']['alter']['path'] = 'admin/config/workflow/rules/components/manage/[config]';
+  $handler->display->display_options['fields']['config']['alter']['absolute'] = 0;
+  $handler->display->display_options['fields']['config']['alter']['trim'] = 0;
+  $handler->display->display_options['fields']['config']['alter']['word_boundary'] = 1;
+  $handler->display->display_options['fields']['config']['alter']['ellipsis'] = 1;
+  $handler->display->display_options['fields']['config']['alter']['strip_tags'] = 0;
+  $handler->display->display_options['fields']['config']['alter']['html'] = 0;
+  $handler->display->display_options['fields']['config']['element_label_colon'] = 1;
+  $handler->display->display_options['fields']['config']['element_default_classes'] = 1;
+  $handler->display->display_options['fields']['config']['hide_empty'] = 0;
+  $handler->display->display_options['fields']['config']['empty_zero'] = 0;
+  /* Field: Rules scheduler: Scheduled date */
+  $handler->display->display_options['fields']['date']['id'] = 'date';
+  $handler->display->display_options['fields']['date']['table'] = 'rules_scheduler';
+  $handler->display->display_options['fields']['date']['field'] = 'date';
+  /* Field: Rules scheduler: User provided identifier */
+  $handler->display->display_options['fields']['identifier']['id'] = 'identifier';
+  $handler->display->display_options['fields']['identifier']['table'] = 'rules_scheduler';
+  $handler->display->display_options['fields']['identifier']['field'] = 'identifier';
+  /* Field: Global: Custom text */
+  $handler->display->display_options['fields']['nothing']['id'] = 'nothing';
+  $handler->display->display_options['fields']['nothing']['table'] = 'views';
+  $handler->display->display_options['fields']['nothing']['field'] = 'nothing';
+  $handler->display->display_options['fields']['nothing']['label'] = 'Operations';
+  $handler->display->display_options['fields']['nothing']['alter']['text'] = 'delete';
+  $handler->display->display_options['fields']['nothing']['alter']['make_link'] = 1;
+  $handler->display->display_options['fields']['nothing']['alter']['path'] = 'admin/config/workflow/rules/schedule/[tid]/delete';
+  $handler->display->display_options['fields']['nothing']['alter']['absolute'] = 0;
+  $handler->display->display_options['fields']['nothing']['alter']['alt'] = 'Delete this scheduled task';
+  $handler->display->display_options['fields']['nothing']['alter']['trim'] = 0;
+  $handler->display->display_options['fields']['nothing']['alter']['word_boundary'] = 1;
+  $handler->display->display_options['fields']['nothing']['alter']['ellipsis'] = 1;
+  $handler->display->display_options['fields']['nothing']['alter']['strip_tags'] = 0;
+  $handler->display->display_options['fields']['nothing']['alter']['html'] = 0;
+  $handler->display->display_options['fields']['nothing']['element_label_colon'] = 1;
+  $handler->display->display_options['fields']['nothing']['element_default_classes'] = 1;
+  $handler->display->display_options['fields']['nothing']['hide_empty'] = 0;
+  $handler->display->display_options['fields']['nothing']['empty_zero'] = 0;
+  /* Sort criterion: Rules scheduler: Scheduled date */
+  $handler->display->display_options['sorts']['date']['id'] = 'date';
+  $handler->display->display_options['sorts']['date']['table'] = 'rules_scheduler';
+  $handler->display->display_options['sorts']['date']['field'] = 'date';
+  /* Argument: Rules scheduler: Component name */
+  $handler->display->display_options['arguments']['config']['id'] = 'config';
+  $handler->display->display_options['arguments']['config']['table'] = 'rules_scheduler';
+  $handler->display->display_options['arguments']['config']['field'] = 'config';
+  $handler->display->display_options['arguments']['config']['style_plugin'] = 'default_summary';
+  $handler->display->display_options['arguments']['config']['wildcard'] = '0';
+  $handler->display->display_options['arguments']['config']['default_argument_type'] = 'fixed';
+  $handler->display->display_options['arguments']['config']['glossary'] = 0;
+  $handler->display->display_options['arguments']['config']['limit'] = '0';
+  $handler->display->display_options['arguments']['config']['transform_dash'] = 0;
+  /* Filter: Rules scheduler: Component name */
+  $handler->display->display_options['filters']['config']['id'] = 'config';
+  $handler->display->display_options['filters']['config']['table'] = 'rules_scheduler';
+  $handler->display->display_options['filters']['config']['field'] = 'config';
+  $handler->display->display_options['filters']['config']['exposed'] = TRUE;
+  $handler->display->display_options['filters']['config']['expose']['operator'] = 'config_op';
+  $handler->display->display_options['filters']['config']['expose']['label'] = 'Component filter';
+  $handler->display->display_options['filters']['config']['expose']['identifier'] = 'config';
+  $handler->display->display_options['filters']['config']['expose']['remember'] = 1;
+  $handler->display->display_options['filters']['config']['expose']['use_operator'] = 0;
+  $handler->display->display_options['filters']['config']['expose']['reduce'] = 0;
+  $translatables['rules_scheduler'] = array(
+    t('Defaults'),
+    t('more'),
+    t('Apply'),
+    t('Reset'),
+    t('Sort By'),
+    t('Asc'),
+    t('Desc'),
+    t('Items per page'),
+    t('- All -'),
+    t('Offset'),
+    t('No tasks have been scheduled.'),
+    t('Tid'),
+    t('Component name'),
+    t('admin/config/workflow/rules/components/manage/[config]'),
+    t('Scheduled date'),
+    t('User provided identifier'),
+    t('Operations'),
+    t('delete'),
+    t('admin/config/workflow/rules/schedule/[tid]/delete'),
+    t('Delete this scheduled task'),
+    t('All'),
+    t('Component filter'),
+  );
+
+  $views = array();
+  $views[$view->name] = $view;
+  return $views;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler_views_filter.inc b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler_views_filter.inc
new file mode 100644
index 0000000000000000000000000000000000000000..5994d8326573ded3b7ded37f24b8b8eda264301d
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/includes/rules_scheduler_views_filter.inc
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * An extended subclass for component filtering.
+ */
+
+class rules_scheduler_views_filter extends views_handler_filter_in_operator {
+
+  public function get_value_options() {
+    if (!isset($this->value_options)) {
+      $this->value_title = t('Component');
+      $result = db_select('rules_scheduler', 'r')
+        ->fields('r', array('config'))
+        ->distinct()
+        ->execute();
+      $config_names = array();
+      foreach ($result as $record) {
+        $config_names[$record->config] = $record->config;
+      }
+      $this->value_options = $config_names;
+    }
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.admin.inc b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.admin.inc
new file mode 100644
index 0000000000000000000000000000000000000000..dfc36188f77a99484f2c5b6217177ac9b90f735f
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.admin.inc
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * @file
+ * Admin forms for scheduling.
+ */
+
+/**
+ * Schedule page with a view for the scheduled tasks.
+ */
+function rules_scheduler_schedule_page() {
+  // Display view for all scheduled tasks.
+  if (module_exists('views')) {
+    // We cannot use views_embed_view() here as we need to set the path for the
+    // component filter form.
+    $view = views_get_view('rules_scheduler');
+    $view->override_path = RULES_SCHEDULER_PATH;
+    $task_list = $view->preview();
+  }
+  else {
+    $task_list = t('To display scheduled tasks you have to install the <a href="https://www.drupal.org/project/views">Views</a> module.');
+  }
+  $page['task_view'] = array(
+    '#markup' => $task_list,
+  );
+  $form = drupal_get_form('rules_scheduler_form');
+  $page['delete'] = array(
+    '#markup' => drupal_render($form),
+  );
+  return $page;
+}
+
+/**
+ * Form for deletion of tasks by component.
+ */
+function rules_scheduler_form($form, &$form_state) {
+  $result = db_select('rules_scheduler', 'r')
+    ->fields('r', array('config'))
+    ->distinct()
+    ->execute();
+  $config_options = array_intersect_key(rules_get_components(TRUE), $result->fetchAllAssoc('config'));
+
+  // Fieldset for canceling by component name.
+  $form['delete_by_config'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Delete tasks by component name'),
+    '#disabled' => empty($config_options),
+  );
+  $form['delete_by_config']['config'] = array(
+    '#title' => t('Component'),
+    '#type' => 'select',
+    '#options' => $config_options,
+    '#description' => t('Select the component for which to delete all scheduled tasks.'),
+    '#required' => TRUE,
+  );
+  $form['delete_by_config']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Delete tasks'),
+    '#submit' => array('rules_scheduler_form_delete_by_config_submit'),
+  );
+  return $form;
+}
+
+/**
+ * Submit handler for deleting future scheduled tasks.
+ */
+function rules_scheduler_form_delete_by_config_submit($form, &$form_state) {
+  $config = rules_config_load($form_state['values']['config']);
+  rules_action('schedule_delete')->execute($config->name);
+  drupal_set_message(t('All scheduled tasks associated with %config have been deleted.', array('%config' => $config->label())));
+  $form_state['redirect'] = RULES_SCHEDULER_PATH;
+}
+
+/**
+ * Confirmation form for deleting single tasks.
+ */
+function rules_scheduler_delete_task($form, &$form_state, $task) {
+  $form_state['task'] = $task;
+  $config = rules_config_load($task['config']);
+  $path['path'] = isset($_GET['destination']) ? $_GET['destination'] : RULES_SCHEDULER_PATH;
+
+  $title = t('Are you sure you want to delete the scheduled task %id?', array('%id' => $task['tid']));
+  if (!empty($task['identifier'])) {
+    $msg = t('This task with the custom identifier %id executes component %label on %date. The action cannot be undone.', array(
+      '%label' => $config->label(),
+      '%id' => $task['identifier'],
+      '%date' => format_date($task['date']),
+    ));
+  }
+  else {
+    $msg = t('This task executes component %label and will be executed on %date. The action cannot be undone.', array(
+      '%label' => $config->label(),
+      '%date' => format_date($task['date']),
+    ));
+  }
+  return confirm_form($form, $title, $path, $msg, t('Delete'), t('Cancel'));
+}
+
+/**
+ * Submit handler for deleting single tasks.
+ */
+function rules_scheduler_delete_task_submit($form, &$form_state) {
+  rules_scheduler_task_delete($form_state['task']['tid']);
+  drupal_set_message(t('Task %tid has been deleted.', array('%tid' => $form_state['task']['tid'])));
+  $form_state['redirect'] = RULES_SCHEDULER_PATH;
+}
+
+/**
+ * Configuration form to manually schedule a rules component.
+ */
+function rules_scheduler_schedule_form($form, &$form_state, $rules_config, $base_path) {
+  // Only components can be scheduled.
+  if (!($rules_config instanceof RulesTriggerableInterface)) {
+    RulesPluginUI::$basePath = $base_path;
+    $form_state['component'] = $rules_config->name;
+    $action = rules_action('schedule', array('component' => $rules_config->name));
+    $action->form($form, $form_state);
+    // The component should be fixed, so hide the parameter for it.
+    $form['parameter']['component']['#access'] = FALSE;
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Schedule'),
+    );
+    $form['#validate'] = array('rules_ui_form_rules_config_validate');
+    return $form;
+  }
+  drupal_not_found();
+  exit;
+}
+
+/**
+ * Submit callback to execute the scheduling action.
+ */
+function rules_scheduler_schedule_form_submit($form, &$form_state) {
+  $action = $form_state['rules_element'];
+  $action->execute();
+  drupal_set_message(t('Component %label has been scheduled.', array('%label' => rules_config_load($form_state['component'])->label())));
+  $form_state['redirect'] = RULES_SCHEDULER_PATH;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.drush.inc b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.drush.inc
new file mode 100644
index 0000000000000000000000000000000000000000..a6ed7956214f45ff2481b416dcaccc990a0f0a66
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.drush.inc
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Rules Scheduler Drush integration.
+ */
+
+/**
+ * Implements hook_drush_command().
+ */
+function rules_scheduler_drush_command() {
+  $items = array();
+
+  $items['rules-scheduler-tasks'] = array(
+    'description' => 'Check for scheduled tasks to be added to the queue.',
+    'options' => array(
+      'claim' => 'Optionally claim tasks from the queue to work on. Any value set will override the default time spent on this queue.',
+    ),
+    'drupal dependencies' => array('rules', 'rules_scheduler'),
+    'aliases' => array('rusch'),
+    'examples' => array(
+      'drush rusch' => 'Add scheduled tasks to the queue.',
+      'drush rusch --claim' => 'Add scheduled tasks to the queue and claim items for the default amount of time.',
+      'drush rusch --claim=30' => 'Add scheduled tasks to the queue and claim items for 30 seconds.',
+    ),
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_drush_help().
+ */
+function rules_scheduler_drush_help($section) {
+  switch ($section) {
+    case 'drush:rules-scheduler-tasks':
+      return dt('Checks for scheduled tasks to be added the queue. Can optionally claim tasks from the queue to work on.');
+  }
+}
+
+/**
+ * Command callback for processing the rules_scheduler_tasks queue.
+ *
+ * @see rules_scheduler_cron_queue_info()
+ * @see rules_scheduler_cron()
+ */
+function drush_rules_scheduler_tasks() {
+  if (rules_scheduler_queue_tasks()) {
+    // hook_exit() is not invoked for drush runs, so register it as shutdown
+    // callback for logging the rules log to the watchdog.
+    drupal_register_shutdown_function('rules_exit');
+    // Clear the log before running tasks via the queue to avoid logging
+    // unrelated logs from previous operations.
+    RulesLog::logger()->clear();
+    drush_log(dt('Added scheduled tasks to the queue.'), 'success');
+  }
+
+  $claim = drush_get_option('claim', FALSE);
+  if ($claim) {
+    // Fetch the queue information and let other modules alter it.
+    $queue_name = 'rules_scheduler_tasks';
+    $info = module_invoke('rules_scheduler', 'cron_queue_info');
+    drupal_alter('cron_queue_info', $info);
+
+    $function = $info[$queue_name]['worker callback'];
+    // The drush option can override the default process time.
+    $time = is_numeric($claim) ? (int) $claim : $info[$queue_name]['time'];
+    $end = time() + $time;
+    // Claim items and process the queue.
+    $queue = DrupalQueue::get($queue_name);
+    $claimed = 0;
+    while (time() < $end && ($item = $queue->claimItem())) {
+      $function($item->data);
+      $queue->deleteItem($item);
+      $claimed++;
+    }
+    if ($claimed) {
+      drush_log(dt('Claimed and worked on !claimed scheduled tasks for up to !time seconds.', array('!claimed' => $claimed, '!time' => $time)), 'success');
+    }
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.info b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.info
new file mode 100644
index 0000000000000000000000000000000000000000..44731c2987fe11b081223ca2ffe99ef0b45894fe
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.info
@@ -0,0 +1,19 @@
+name = Rules Scheduler
+description = Schedule the execution of Rules components using actions.
+dependencies[] = rules
+package = Rules
+core = 7.x
+files[] = includes/rules_scheduler.handler.inc
+
+; Views handlers
+files[] = includes/rules_scheduler_views_filter.inc
+
+; Test cases
+files[] = tests/rules_scheduler.test
+files[] = tests/rules_scheduler_test.inc
+
+; Information added by Drupal.org packaging script on 2019-01-24
+version = "7.x-2.12"
+core = "7.x"
+project = "rules"
+datestamp = "1548305586"
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.install b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.install
new file mode 100644
index 0000000000000000000000000000000000000000..0aa814324a53f5cb403340e0eb944e1ff297735e
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.install
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * @file
+ * Rules Scheduler - Installation file.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function rules_scheduler_schema() {
+  $schema['rules_scheduler'] = array(
+    'description' => 'Stores scheduled tasks.',
+    'fields' => array(
+      'tid' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => "The scheduled task's id.",
+      ),
+      'config' => array(
+        'type' => 'varchar',
+        'length' => '64',
+        'default' => '',
+        'not null' => TRUE,
+        'description' => "The scheduled configuration's name.",
+      ),
+      'date' => array(
+        'description' => 'The Unix timestamp of when the task is to be scheduled.',
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'data' => array(
+        'type' => 'blob',
+        'size' => 'big',
+        'not null' => FALSE,
+        'serialize' => TRUE,
+        'description' => 'The whole, serialized evaluation data.',
+      ),
+      'identifier' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'default' => '',
+        'not null' => FALSE,
+        'description' => 'The user defined string identifying this task.',
+      ),
+      'handler' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'not null' => FALSE,
+        'description' => 'The fully-qualified class name of the queue item handler.',
+      ),
+    ),
+    'primary key' => array('tid'),
+    'indexes' => array(
+      'date' => array('date'),
+    ),
+    'unique key' => array(
+      'id' => array('config', 'identifier'),
+    ),
+  );
+  return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function rules_scheduler_install() {
+  // Create the queue to hold scheduled tasks.
+  $queue = DrupalQueue::get('rules_scheduler_tasks', TRUE);
+  $queue->createQueue();
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function rules_scheduler_uninstall() {
+  // Clean up after ourselves by deleting the queue and all items in it.
+  $queue = DrupalQueue::get('rules_scheduler_tasks');
+  $queue->deleteQueue();
+}
+
+/**
+ * Upgrade from Rules scheduler 6.x-1.x to 7.x.
+ */
+function rules_scheduler_update_7200() {
+  // Rename the old table so we can keep its content and start over with a
+  // fresh one.
+  db_rename_table('rules_scheduler', 'rules_scheduler_d6');
+  // Create the d7 table.
+  $schema['rules_scheduler'] = array(
+    'description' => 'Stores scheduled tasks.',
+    'fields' => array(
+      'tid' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => "The scheduled task's id.",
+      ),
+      'config' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'default' => '',
+        'not null' => TRUE,
+        'description' => "The scheduled configuration's name.",
+      ),
+      'date' => array(
+        'description' => 'The Unix timestamp of when the task is to be scheduled.',
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'data' => array(
+        'type' => 'text',
+        'not null' => FALSE,
+        'serialize' => TRUE,
+        'description' => 'The whole, serialized evaluation data.',
+      ),
+      'identifier' => array(
+        'type' => 'varchar',
+        'length' => '255',
+        'default' => '',
+        'not null' => FALSE,
+        'description' => 'The user defined string identifying this task.',
+      ),
+    ),
+    'primary key' => array('tid'),
+    'indexes' => array('date' => array('date')),
+  );
+  db_create_table('rules_scheduler', $schema['rules_scheduler']);
+}
+
+/**
+ * Fix the length of the rules_scheduler.name column.
+ */
+function rules_scheduler_update_7202() {
+  // Note that update 7201 (add the 'id' unique key') has been removed as it is
+  // incorporated by 7202. For anyone that has already run the previous update
+  // 7201, we have to first drop the unique key.
+  db_drop_unique_key('rules_scheduler', 'id');
+  db_change_field('rules_scheduler', 'config', 'config', array(
+    'type' => 'varchar',
+    'length' => '64',
+    'default' => '',
+    'not null' => TRUE,
+    'description' => "The scheduled configuration's name.",
+  ));
+  db_add_unique_key('rules_scheduler', 'id', array('config', 'identifier'));
+}
+
+/**
+ * Add a database column for specifying a queue item handler.
+ */
+function rules_scheduler_update_7203() {
+  db_add_field('rules_scheduler', 'handler', array(
+    'type' => 'varchar',
+    'length' => '255',
+    'not null' => FALSE,
+    'description' => 'The fully-qualified class name of the queue item handler.',
+  ));
+}
+
+/**
+ * Rename rules_scheduler.state into rules_scheduler.data.
+ */
+function rules_scheduler_update_7204() {
+  if (db_field_exists('rules_scheduler', 'state')) {
+    db_change_field('rules_scheduler', 'state', 'data', array(
+      'type' => 'text',
+      'not null' => FALSE,
+      'serialize' => TRUE,
+      'description' => 'The whole, serialized evaluation data.',
+    ));
+  }
+}
+
+/**
+ * Use blob:big for rules_scheduler.data for compatibility with PostgreSQL.
+ */
+function rules_scheduler_update_7205() {
+  if (db_field_exists('rules_scheduler', 'data')) {
+    db_change_field('rules_scheduler', 'data', 'data', array(
+      'type' => 'blob',
+      'size' => 'big',
+      'not null' => FALSE,
+      'serialize' => TRUE,
+      'description' => 'The whole, serialized evaluation data.',
+    ));
+  }
+}
+
+/**
+ * Rules upgrade callback for mapping the action name.
+ */
+function rules_scheduler_action_upgrade_map_name($element) {
+  return 'schedule';
+}
+
+/**
+ * Rules upgrade callback.
+ */
+function rules_scheduler_action_upgrade($element, $target) {
+  $target->settings['component'] = $element['#info']['set'];
+  $target->settings['date'] = $element['#settings']['task_date'];
+  $target->settings['identifier'] = $element['#settings']['task_identifier'];
+
+  unset($element['#info']['arguments']['task_date'], $element['#info']['arguments']['task_identifier']);
+  foreach ($element['#info']['arguments'] as $name => $info) {
+    rules_upgrade_element_parameter_settings($element, $target, $name, $info, 'param_' . $name);
+  }
+}
+
+/**
+ * Rules upgrade callback for mapping the action name.
+ */
+function rules_action_delete_scheduled_set_upgrade_map_name($element) {
+  return 'schedule_delete';
+}
+
+/**
+ * Rules upgrade callback.
+ */
+function rules_action_delete_scheduled_set_upgrade($element, $target) {
+  $target->settings['component'] = $element['#settings']['ruleset'];
+  $target->settings['task'] = $element['#settings']['task_identifier'];
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.module b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.module
new file mode 100644
index 0000000000000000000000000000000000000000..a4c3e4b9c50334f120e0c7639ccd1e1537a12dd3
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.module
@@ -0,0 +1,220 @@
+<?php
+
+/**
+ * @file
+ * Rules scheduler module.
+ */
+
+define('RULES_SCHEDULER_PATH', 'admin/config/workflow/rules/schedule');
+
+/**
+ * Implements hook_cron().
+ */
+function rules_scheduler_cron() {
+  if (rules_scheduler_queue_tasks()) {
+    // hook_exit() is not invoked for cron runs, so register it as shutdown
+    // callback for logging the rules log to the watchdog.
+    drupal_register_shutdown_function('rules_exit');
+    // Clear the log before running tasks via the queue to avoid logging
+    // unrelated logs from previous cron-operations.
+    RulesLog::logger()->clear();
+  }
+}
+
+/**
+ * Implements hook_cron_queue_info().
+ */
+function rules_scheduler_cron_queue_info() {
+  $queues['rules_scheduler_tasks'] = array(
+    'worker callback' => 'rules_scheduler_run_task',
+    'time' => 15,
+  );
+  return $queues;
+}
+
+/**
+ * Queue worker callback for running a single task.
+ *
+ * @param array $task
+ *   The task to process.
+ */
+function rules_scheduler_run_task(array $task) {
+  try {
+    // BC support for tasks that have been already queued, before update
+    // rules_scheduler_update_7204() ran.
+    if (isset($task['state'])) {
+      $task['data'] = $task['state'];
+    }
+    rules_scheduler_task_handler($task)->runTask();
+  }
+  catch (RulesEvaluationException $e) {
+    rules_log($e->msg, $e->args, $e->severity);
+    rules_log('Unable to execute task with identifier %id scheduled on date %date.', array('%id' => $task['identifier'], '%date' => format_date($task['date'])), RulesLog::ERROR);
+  }
+}
+
+/**
+ * Returns the task handler for a given task.
+ *
+ * @param array $task
+ *   A task (queue item) array.
+ *
+ * @throws RulesEvaluationException
+ *   If the task handler class is missing.
+ *
+ * @return RulesSchedulerTaskHandlerInterface
+ *   The task handler.
+ */
+function rules_scheduler_task_handler(array $task) {
+  $class = !empty($task['handler']) ? $task['handler'] : 'RulesSchedulerDefaultTaskHandler';
+  if (!class_exists($class)) {
+    throw new RulesEvaluationException('Missing task handler implementation %class.', array('%class' => $class), NULL, RulesLog::ERROR);
+  }
+  return new $class($task);
+}
+
+/**
+ * Implements hook_rules_ui_menu_alter().
+ *
+ * Adds a menu item for the 'schedule' operation.
+ */
+function rules_scheduler_rules_ui_menu_alter(&$items, $base_path, $base_count) {
+  $items[$base_path . '/manage/%rules_config/schedule'] = array(
+    'title callback' => 'rules_get_title',
+    'title arguments' => array('Schedule !plugin "!label"', $base_count + 1),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_scheduler_schedule_form', $base_count + 1, $base_path),
+    'access callback' => 'rules_config_access',
+    'access arguments' => array('update', $base_count + 1),
+    'file' => 'rules_scheduler.admin.inc',
+    'file path' => drupal_get_path('module', 'rules_scheduler'),
+  );
+}
+
+/**
+ * Implements hook_menu().
+ */
+function rules_scheduler_menu() {
+  $items = array();
+  $items[RULES_SCHEDULER_PATH] = array(
+    'title' => 'Schedule',
+    'type' => MENU_LOCAL_TASK,
+    'page callback' => 'rules_scheduler_schedule_page',
+    'access arguments' => array('administer rules'),
+    'file' => 'rules_scheduler.admin.inc',
+  );
+  $items[RULES_SCHEDULER_PATH . '/%rules_scheduler_task/delete'] = array(
+    'title' => 'Delete a scheduled task',
+    'type' => MENU_CALLBACK,
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('rules_scheduler_delete_task', 5),
+    'access arguments' => array('administer rules'),
+    'file' => 'rules_scheduler.admin.inc',
+  );
+  return $items;
+}
+
+/**
+ * Loads a task by a given task ID.
+ *
+ * @param int $tid
+ *   The task ID.
+ */
+function rules_scheduler_task_load($tid) {
+  $result = db_select('rules_scheduler', 'r')
+    ->fields('r')
+    ->condition('tid', (int) $tid)
+    ->execute();
+  return $result->fetchAssoc();
+}
+
+/**
+ * Deletes a task by a given task ID.
+ *
+ * @param int $tid
+ *   The task ID.
+ */
+function rules_scheduler_task_delete($tid) {
+  db_delete('rules_scheduler')
+    ->condition('tid', $tid)
+    ->execute();
+}
+
+/**
+ * Schedule a task to be executed later on.
+ *
+ * @param array $task
+ *   An array representing the task with the following keys:
+ *   - config: The machine readable name of the to-be-scheduled component.
+ *   - date: Timestamp when the component should be executed.
+ *   - state: (deprecated) Rules evaluation state to use for scheduling.
+ *   - data: Any additional data to store with the task.
+ *   - handler: The name of the task handler class.
+ *   - identifier: User provided string to identify the task per scheduled
+ *   configuration.
+ */
+function rules_scheduler_schedule_task($task) {
+  // Map the deprecated 'state' property into 'data'.
+  if (isset($task['state'])) {
+    $task['data'] = $task['state'];
+    unset($task['state']);
+  }
+  if (!empty($task['identifier'])) {
+    // If there is a task with the same identifier and component, we replace it.
+    db_delete('rules_scheduler')
+      ->condition('config', $task['config'])
+      ->condition('identifier', $task['identifier'])
+      ->execute();
+  }
+  drupal_write_record('rules_scheduler', $task);
+}
+
+/**
+ * Queue tasks that are ready for execution.
+ *
+ * @return bool
+ *   TRUE if any queue items where created, otherwise FALSE.
+ */
+function rules_scheduler_queue_tasks() {
+  $items_created = FALSE;
+  // Limit adding tasks to 1000 per cron run.
+  $result = db_select('rules_scheduler', 'r', array('fetch' => PDO::FETCH_ASSOC))
+    ->fields('r')
+    ->condition('date', time(), '<=')
+    ->orderBy('date')
+    ->range(0, 1000)
+    ->execute();
+
+  $queue = DrupalQueue::get('rules_scheduler_tasks');
+  foreach ($result as $task) {
+    // Add the task to the queue and remove the entry afterwards.
+    if ($queue->createItem($task)) {
+      $items_created = TRUE;
+      rules_scheduler_task_handler($task)->afterTaskQueued();
+    }
+  }
+  return $items_created;
+}
+
+/**
+ * Implements hook_rules_config_delete().
+ */
+function rules_scheduler_rules_config_delete($rules_config) {
+  // Only react on real delete, not revert.
+  if (!$rules_config->hasStatus(ENTITY_IN_CODE)) {
+    // Delete all tasks scheduled for this config.
+    db_delete('rules_scheduler')
+      ->condition('config', $rules_config->name)
+      ->execute();
+  }
+}
+
+/**
+ * Implements hook_views_api().
+ */
+function rules_scheduler_views_api() {
+  return array(
+    'api' => '3.0-alpha1',
+    'path' => drupal_get_path('module', 'rules_scheduler') . '/includes',
+  );
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.rules.inc b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..4f31f70a857bfada377595acc210bf7920687e0d
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/rules_scheduler.rules.inc
@@ -0,0 +1,217 @@
+<?php
+
+/**
+ * @file
+ * Rules integration for the rules scheduler module.
+ *
+ * @addtogroup rules
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_rules_action_info().
+ */
+function rules_scheduler_rules_action_info() {
+  $items['schedule'] = array(
+    'label' => t('Schedule component evaluation'),
+    'group' => t('Rules scheduler'),
+    'base' => 'rules_scheduler_action_schedule',
+    'named parameter' => TRUE,
+    'parameter' => array(
+      'component' => array(
+        'type' => 'text',
+        'label' => t('Component'),
+        'options list' => 'rules_scheduler_component_options_list',
+        'restriction' => 'input',
+        'description' => 'Select the component to schedule. Only components containing actions are available – no condition sets.',
+      ),
+      'date' => array(
+        'type' => 'date',
+        'label' => t('Scheduled evaluation date'),
+      ),
+      'identifier' => array(
+        'type' => 'text',
+        'label' => t('Identifier'),
+        'description' => t('A string used for identifying this task. Any existing tasks for this component with the same identifier will be replaced.'),
+        'optional' => TRUE,
+      ),
+      // Further needed parameter by the component are added during processing.
+    ),
+  );
+  // Add action to delete scheduled tasks.
+  $items['schedule_delete'] = array(
+    'label' => t('Delete scheduled tasks'),
+    'group' => t('Rules scheduler'),
+    'base' => 'rules_scheduler_action_delete',
+    'parameter' => array(
+      'component' => array(
+        'type' => 'text',
+        'label' => t('Component'),
+        'options list' => 'rules_scheduler_component_options_list',
+        'description' => t('The component for which scheduled tasks will be deleted.'),
+        'optional' => TRUE,
+      ),
+      'task' => array(
+        'type' => 'text',
+        'label' => t('Task identifier'),
+        'description' => t('All tasks that are annotated with the given identifier will be deleted.'),
+        'optional' => TRUE,
+      ),
+    ),
+  );
+  return $items;
+}
+
+/**
+ * Options list callback returning a list of action components.
+ */
+function rules_scheduler_component_options_list() {
+  return rules_get_components(TRUE, 'action');
+}
+
+/**
+ * Base action implementation for scheduling components.
+ */
+function rules_scheduler_action_schedule($args, $element) {
+  $state = $args['state'];
+  if ($component = rules_get_cache('comp_' . $args['component'])) {
+    // Manually create a new evaluation state for scheduling the evaluation.
+    $new_state = new RulesState();
+
+    // Register all parameters as variables.
+    foreach ($element->pluginParameterInfo() as $name => $info) {
+      if (strpos($name, 'param_') === 0) {
+        // Remove the parameter name prefix 'param_'.
+        $var_name = substr($name, 6);
+        $new_state->addVariable($var_name, $state->currentArguments[$name], $info);
+      }
+    }
+    rules_scheduler_schedule_task(array(
+      'date' => $args['date'],
+      'config' => $args['component'],
+      'data' => $new_state,
+      'identifier' => $args['identifier'],
+    ));
+  }
+  else {
+    throw new RulesEvaluationException('Unable to get the component %name', array('%name' => $args['component']), $element, RulesLog::ERROR);
+  }
+}
+
+/**
+ * Info alteration callback for the schedule action.
+ */
+function rules_scheduler_action_schedule_info_alter(&$element_info, RulesPlugin $element) {
+  if (isset($element->settings['component'])) {
+    // If run during a cache rebuild the cache might not be instantiated yet,
+    // so fail back to loading the component from database.
+    if (($component = rules_get_cache('comp_' . $element->settings['component'])) || $component = rules_config_load($element->settings['component'])) {
+      // Add in the needed parameters.
+      foreach ($component->parameterInfo() as $name => $info) {
+        $element_info['parameter']['param_' . $name] = $info;
+      }
+    }
+  }
+}
+
+/**
+ * Validate callback for the schedule action.
+ *
+ * Makes sure the component exists and is not dirty.
+ *
+ * @see rules_element_invoke_component_validate()
+ */
+function rules_scheduler_action_schedule_validate(RulesPlugin $element) {
+  $info = $element->info();
+  $component = rules_config_load($element->settings['component']);
+  if (!$component) {
+    throw new RulesIntegrityException(t('The component %config does not exist.', array('%config' => $element->settings['component'])), $element);
+  }
+  // Check if the component is marked as dirty.
+  rules_config_update_dirty_flag($component);
+  if (!empty($component->dirty)) {
+    throw new RulesIntegrityException(t('The utilized component %config fails the integrity check.', array('%config' => $element->settings['component'])), $element);
+  }
+}
+
+/**
+ * Help for the schedule action.
+ */
+function rules_scheduler_action_schedule_help() {
+  return t("Note that component evaluation is triggered by <em>cron</em> – make sure cron is configured correctly by checking your site's !status. The scheduling time accuracy depends on your configured cron interval. See <a href='@url'>the online documentation</a> for more information on how to schedule evaluation of components.",
+    array('!status' => l(t('Status report'), 'admin/reports/status'),
+          '@url' => rules_external_help('scheduler')));
+}
+
+/**
+ * Form alter callback for the schedule action.
+ */
+function rules_scheduler_action_schedule_form_alter(&$form, &$form_state, $options, RulesAbstractPlugin $element) {
+  $first_step = empty($element->settings['component']);
+  $form['reload'] = array(
+    '#weight' => 5,
+    '#type' => 'submit',
+    '#name' => 'reload',
+    '#value' => $first_step ? t('Continue') : t('Reload form'),
+    '#limit_validation_errors' => array(array('parameter', 'component')),
+    '#submit' => array('rules_action_type_form_submit_rebuild'),
+    '#ajax' => rules_ui_form_default_ajax(),
+  );
+  // Use ajax and trigger as the reload button.
+  $form['parameter']['component']['settings']['type']['#ajax'] = $form['reload']['#ajax'] + array(
+    'event' => 'change',
+    'trigger_as' => array('name' => 'reload'),
+  );
+
+  if ($first_step) {
+    // In the first step show only the component select.
+    foreach (element_children($form['parameter']) as $key) {
+      if ($key != 'component') {
+        unset($form['parameter'][$key]);
+      }
+    }
+    unset($form['submit']);
+    unset($form['provides']);
+  }
+  else {
+    // Hide the reload button in case js is enabled and it's not the first step.
+    $form['reload']['#attributes'] = array('class' => array('rules-hide-js'));
+  }
+}
+
+/**
+ * Action: Delete scheduled tasks.
+ */
+function rules_scheduler_action_delete($component_name = NULL, $task_identifier = NULL) {
+  $query = db_delete('rules_scheduler');
+  if (!empty($component_name)) {
+    $query->condition('config', $component_name);
+  }
+  if (!empty($task_identifier)) {
+    $query->condition('identifier', $task_identifier);
+  }
+  $query->execute();
+}
+
+/**
+ * Cancels scheduled task action validation callback.
+ */
+function rules_scheduler_action_delete_validate($element) {
+  if (empty($element->settings['task']) && empty($element->settings['task:select']) &&
+      empty($element->settings['component']) && empty($element->settings['component:select'])) {
+
+    throw new RulesIntegrityException(t('You have to specify at least either a component or a task identifier.'), $element);
+  }
+}
+
+/**
+ * Help for the cancel action.
+ */
+function rules_scheduler_action_delete_help() {
+  return t('This action allows you to delete scheduled tasks that are waiting for future execution.') . ' ' . t('They can be addressed by an identifier or by the component name, whereas if both are specified only tasks fulfilling both requirements will be deleted.');
+}
+
+/**
+ * @}
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler.test b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler.test
new file mode 100644
index 0000000000000000000000000000000000000000..4e19e80c1b00298b09d472d5481b00b774333bea
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler.test
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * @file
+ * Rules Scheduler tests.
+ */
+
+/**
+ * Test cases for the Rules Scheduler module.
+ */
+class RulesSchedulerTestCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules Scheduler tests',
+      'description' => 'Test scheduling components.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules_scheduler', 'rules_scheduler_test');
+    RulesLog::logger()->clear();
+    variable_set('rules_debug_log', TRUE);
+  }
+
+  /**
+   * Tests scheduling components from the action.
+   *
+   * Note that this also makes sure Rules properly handles timezones, else this
+   * test could fail due to a wrong 'now' timestamp.
+   */
+  public function testComponentSchedule() {
+    $set = rules_rule_set(array(
+      'node1' => array('type' => 'node', 'label' => 'node'),
+    ));
+    $set->rule(rule()->condition('node_is_published', array('node:select' => 'node1'))
+                     ->action('node_unpublish', array('node:select' => 'node1'))
+               );
+    $set->integrityCheck()->save('rules_test_set_2');
+
+    // Use different names for the variables to ensure they are properly mapped.
+    $rule = rule(array(
+      'node2' => array('type' => 'node', 'label' => 'node'),
+    ));
+    $rule->action('schedule', array(
+      'component' => 'rules_test_set_2',
+      'identifier' => 'node_[node2:nid]',
+      'date' => 'now',
+      'param_node1:select' => 'node2',
+    ));
+
+    $node = $this->drupalCreateNode(array('title' => 'The title.', 'status' => 1));
+    $rule->execute($node);
+
+    // Run cron to let the rules scheduler do its work.
+    $this->cronRun();
+
+    $node = node_load($node->nid, NULL, TRUE);
+    $this->assertFalse($node->status, 'The component has been properly scheduled.');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Makes sure recursion prevention is working fine for scheduled rule sets.
+   */
+  public function testRecursionPrevention() {
+    $set = rules_rule_set(array(
+      'node1' => array('type' => 'node', 'label' => 'node'),
+    ));
+    $set->rule(rule()->condition('node_is_published', array('node:select' => 'node1'))
+                     ->action('node_unpublish', array('node:select' => 'node1'))
+               );
+    $set->integrityCheck()->save('rules_test_set_2');
+
+    // Add an reaction rule that is triggered upon a node save. The scheduled
+    // component changes the node, thus it would be scheduled again and run in
+    // an endless loop.
+    $rule = rules_reaction_rule();
+    $rule->event('node_insert');
+    $rule->event('node_update');
+    $rule->action('schedule', array(
+      'component' => 'rules_test_set_2',
+      'identifier' => 'test_recursion_prevention',
+      'date' => 'now',
+      'param_node1:select' => 'node',
+    ));
+    $rule->save();
+
+    // Create a node, what triggers the rule.
+    $node = $this->drupalCreateNode(array('title' => 'The title.', 'status' => 1));
+    // Run cron to let the rules scheduler do its work.
+    $this->cronRun();
+
+    $node = node_load($node->nid, NULL, TRUE);
+    $this->assertFalse($node->status, 'The component has been properly scheduled.');
+
+    // Create a simple user account with permission to see the dblog.
+    $user = $this->drupalCreateUser(array('access site reports'));
+    $this->drupalLogin($user);
+
+    // View the database log.
+    $this->drupalGet('admin/reports/dblog');
+
+    // Can't use
+    // $this->clickLink('Rules debug information: " Scheduled evaluation...')
+    // because xpath doesn't allow : or " in the string.
+    // So instead, use our own xpath to figure out the href of the second link
+    // on the page (the first link is the most recent log entry, which is the
+    // log entry for the user login, above.)
+
+    // All links.
+    $links = $this->xpath('//a[contains(@href, :href)]', array(':href' => 'admin/reports/event/'));
+    // Strip off /?q= from href.
+    $href = explode('=', $links[1]['href']);
+    // Click the link for the RulesLog entry.
+    $this->drupalGet($href[1]);
+    $this->assertRaw(RulesTestCase::t('Not evaluating reaction rule %unlabeled to prevent recursion.', array('unlabeled' => $rule->name)), "Scheduled recursion prevented.");
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests that custom task handlers are properly invoked.
+   */
+  public function testCustomTaskHandler() {
+    // Set up a scheduled task that will simply write a variable when executed.
+    $variable = 'rules_schedule_task_handler_variable';
+    rules_scheduler_schedule_task(array(
+      'date' => REQUEST_TIME,
+      'identifier' => '',
+      'config' => '',
+      'data' => array('variable' => $variable),
+      'handler' => 'RulesTestTaskHandler',
+    ));
+
+    // Run cron to let the rules scheduler do its work.
+    $this->cronRun();
+
+    // The task handler should have set the variable to TRUE now.
+    $this->assertTrue(variable_get($variable));
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.inc b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.inc
new file mode 100644
index 0000000000000000000000000000000000000000..39b378e4cfecfdff3e8410e33bc9e2931489a93c
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.inc
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Include file for Rules Scheduler tests.
+ */
+
+/**
+ * Test task handler class.
+ */
+class RulesTestTaskHandler extends RulesSchedulerDefaultTaskHandler {
+
+  /**
+   * Overrides RulesSchedulerDefaultTaskHandler::runTask().
+   */
+  public function runTask() {
+    $task = $this->getTask();
+    $data = unserialize($task['data']);
+
+    // Set the variable defined in the test to TRUE.
+    variable_set($data['variable'], TRUE);
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.info b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.info
new file mode 100644
index 0000000000000000000000000000000000000000..b5b2bc31f19bc7fc1206eb7bd48250ab76a4c901
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.info
@@ -0,0 +1,12 @@
+name = "Rules Scheduler Tests"
+description = "Support module for the Rules Scheduler tests."
+package = Testing
+core = 7.x
+files[] = rules_scheduler_test.inc
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2019-01-24
+version = "7.x-2.12"
+core = "7.x"
+project = "rules"
+datestamp = "1548305586"
diff --git a/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.module b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.module
new file mode 100644
index 0000000000000000000000000000000000000000..11e48755742e37f199f93135c3fe42956c5d803b
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/rules_scheduler/tests/rules_scheduler_test.module
@@ -0,0 +1,6 @@
+<?php
+
+/**
+ * @file
+ * Rules Scheduler test module.
+ */
diff --git a/profiles/wcm_base/modules/contrib/rules/tests/rules.test b/profiles/wcm_base/modules/contrib/rules/tests/rules.test
new file mode 100644
index 0000000000000000000000000000000000000000..fe0a1d93249969f64a9195b065f79fa069f8a561
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/tests/rules.test
@@ -0,0 +1,2215 @@
+<?php
+
+/**
+ * @file
+ * Rules tests.
+ */
+
+/**
+ * Rules test cases.
+ */
+class RulesTestCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules Engine tests',
+      'description' => 'Test using the rules API to create and evaluate rules.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules', 'rules_test');
+    RulesLog::logger()->clear();
+    variable_set('rules_debug_log', TRUE);
+  }
+
+  /**
+   * Calculates the output of t() given an array of placeholders to replace.
+   */
+  public static function t($text, $strings) {
+    $placeholders = array();
+    foreach ($strings as $key => $string) {
+      $key = !is_numeric($key) ? $key : $string;
+      $placeholders['%' . $key] = drupal_placeholder($string);
+    }
+    return strtr($text, $placeholders);
+  }
+
+  /**
+   * Helper function to create a test Rule.
+   */
+  protected function createTestRule() {
+    $rule = rule();
+    $rule->condition('rules_test_condition_true')
+         ->condition('rules_test_condition_true')
+         ->condition(rules_or()
+           ->condition(rules_condition('rules_test_condition_true')->negate())
+           ->condition('rules_test_condition_false')
+           ->condition(rules_and()
+             ->condition('rules_test_condition_false')
+             ->condition('rules_test_condition_true')
+             ->negate()
+           )
+         );
+    $rule->action('rules_test_action');
+    return $rule;
+  }
+
+  /**
+   * Tests creating a rule and iterating over the rule elements.
+   */
+  public function testRuleCreation() {
+    $rule = $this->createTestRule();
+    $rule->integrityCheck();
+    $rule->execute();
+    $log = RulesLog::logger()->get();
+    $last = array_pop($log);
+    $last = array_pop($log);
+    $last = array_pop($log);
+    $this->assertEqual($last[0], 'action called', 'Action called');
+    RulesLog::logger()->checkLog();
+
+    // Make sure condition and action iterators are working.
+    $it = new RecursiveIteratorIterator($rule->conditions(), RecursiveIteratorIterator::SELF_FIRST);
+    $this->assertEqual(iterator_count($it), 8, 'Iterated over all conditions and condition containers');
+    $it = new RecursiveIteratorIterator($rule->conditions());
+    $this->assertEqual(iterator_count($it), 6, 'Iterated over all conditions');
+    $this->assertEqual(iterator_count($rule->actions()), 1, 'Iterated over all actions');
+    $this->assertEqual(iterator_count($rule->elements()), 10, 'Iterated over all rule elements.');
+
+    // Test getting dependencies and the integrity check.
+    $rule->integrityCheck();
+    $this->assertTrue($rule->dependencies() === array('rules_test'), 'Dependencies correctly returned.');
+  }
+
+  /**
+   * Tests handling dependencies.
+   */
+  public function testDependencies() {
+    $action = rules_action('rules_node_publish_action');
+    $this->assertEqual($action->dependencies(), array('rules_test'), 'Providing module is returned as dependency.');
+
+    $container = new RulesTestContainer();
+    $this->assertEqual($container->dependencies(), array('rules_test'), 'Providing module for container plugin is returned as dependency.');
+
+    // Test handling unmet dependencies.
+    $rule = rules_config_load('rules_export_test');
+    $this->assertTrue(in_array('comment', $rule->dependencies) && !$rule->dirty, 'Dependencies have been imported.');
+
+    // Remove the required comment module and make sure the rule is dirty then.
+    module_disable(array('comment'));
+    rules_clear_cache();
+    $rule = rules_config_load('rules_export_test');
+    $this->assertTrue($rule->dirty, 'Rule has been marked as dirty');
+
+    // Now try re-enabling.
+    module_enable(array('comment'));
+    rules_clear_cache();
+    $rule = rules_config_load('rules_export_test');
+    $this->assertTrue(!$rule->dirty, 'Rule has been marked as not dirty again.');
+
+    // Test it with components.
+    module_enable(array('path'));
+    $action_set = rules_action_set(array('node' => array('type' => 'node')));
+    $action_set->action('node_path_alias');
+    $action_set->save('rules_test_alias');
+
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->action('component_rules_test_alias');
+    $rule->integrityCheck();
+    $rule->save('rules_test_rule');
+
+    $rule = rules_config_load('rules_test_rule');
+    $component = rules_config_load('rules_test_alias');
+    $this->assertTrue(in_array('path', $component->dependencies) && !$rule->dirty && !$component->dirty, 'Component has path module dependency.');
+
+    // Now disable path module and make sure both configs are marked as dirty.
+    module_disable(array('path'));
+    rules_clear_cache();
+    $rule = rules_config_load('rules_test_rule');
+    $component = rules_config_load('rules_test_alias');
+
+    $this->assertTrue($component->dirty, 'Component has been marked as dirty');
+    $node = $this->drupalCreateNode();
+    $result = rules_invoke_component('rules_test_alias', $node);
+    $this->assertTrue($result === FALSE, 'Unable to execute a dirty component.');
+
+    // When the rule is evaluated, the broken component is detected and the
+    // rule should be marked as dirty too.
+    $rule->execute($node);
+    $this->assertTrue($rule->dirty, 'Rule has been marked as dirty');
+
+    module_enable(array('path'));
+    rules_clear_cache();
+
+    // Trigger rebuilding the cache, so configs are checked again.
+    rules_get_cache();
+
+    $rule = rules_config_load('rules_test_rule');
+    $component = rules_config_load('rules_test_alias');
+    $this->assertTrue(!$component->dirty, 'Component has been marked as not dirty again.');
+    $this->assertTrue(!$rule->dirty, 'Rule has been marked as not dirty again.');
+  }
+
+  /**
+   * Tests setting up an action, serializing, and executing it.
+   */
+  public function testActionSetup() {
+    $action = rules_action('rules_node_publish_action');
+
+    $s = serialize($action);
+    $action2 = unserialize($s);
+    $node = (object) array('status' => 0, 'type' => 'page');
+    $node->title = 'test';
+
+    $action2->execute($node);
+    $this->assertEqual($node->status, 1, 'Action executed correctly');
+
+    $this->assertTrue(in_array('node', array_keys($action2->parameterInfo())), 'Parameter info returned.');
+
+    $node->status = 0;
+    $action2->integrityCheck();
+    $action2->executeByArgs(array('node' => $node));
+    $this->assertEqual($node->status, 1, 'Action executed correctly');
+
+    // Test calling an extended + overridden method.
+    $this->assertEqual($action2->help(), 'custom', 'Using custom help callback.');
+
+    // Inspect the cache
+    //$this->pass(serialize(rules_get_cache()));
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests executing with wrong arguments.
+   */
+  public function testActionExecutionFails() {
+    $action = rules_action('rules_node_publish_action');
+    try {
+      $action->execute();
+      $this->fail("Execution hasn't created an exception.");
+    }
+    catch (RulesEvaluationException $e) {
+      $this->pass("RulesEvaluationException was thrown: " . $e);
+    }
+  }
+
+  /**
+   * Tests setting up a rule and mapping variables.
+   */
+  public function testVariableMapping() {
+    $rule = rule(array(
+      'node' => array('type' => 'node'),
+      'node_unchanged' => array('type' => 'node'),
+    ));
+    $rule->condition(rules_condition('rules_condition_content_is_published')->negate())
+         ->condition('rules_condition_content_is_type', array('type' => array('page', 'story')))
+         ->action('rules_node_publish_action', array('node:select' => 'node_unchanged'));
+
+    $node1 = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
+    $node2 = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
+    $rule->integrityCheck();
+    $rule->execute($node1, $node2);
+    $this->assertEqual($node2->status, 1, 'Action executed correctly on node2.');
+    $this->assertEqual($node1->status, 0, 'Action not executed on node1.');
+
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests making use of class based actions.
+   */
+  public function testClassBasedActions() {
+    $cache = rules_get_cache();
+    $this->assertTrue(!empty($cache['action_info']['rules_test_class_action']), 'Action has been discovered.');
+    $action = rules_action('rules_test_class_action');
+
+    $parameters = $action->parameterInfo();
+    $this->assertTrue($parameters['node'], 'Action parameter needs a value.');
+
+    $node = $this->drupalCreateNode();
+    $action->execute($node);
+    $log = RulesLog::logger()->get();
+    $last = array_pop($log);
+    $last = array_pop($log);
+    $this->assertEqual($last[0], 'Action called with node ' . $node->nid, 'Action called');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests CRUD functionality.
+   */
+  public function testRulesCRUD() {
+    $rule = $this->createTestRule();
+    $rule->integrityCheck()->save('test');
+
+    $this->assertEqual(TRUE, $rule->active, 'Rule is active.');
+    $this->assertEqual(0, $rule->weight, 'Rule weight is zero.');
+
+    $results = entity_load('rules_config', array('test'));
+    $rule2 = array_pop($results);
+    $this->assertEqual($rule->id, $rule2->id, 'Rule created and loaded');
+    $this->assertEqual(get_class($rule2), get_class($rule), 'Class properly instantiated.');
+    $rule2->execute();
+    // Update.
+    $rule2->save();
+
+    // Make sure all rule elements are still here.
+    $it = new RecursiveIteratorIterator($rule2->conditions(), RecursiveIteratorIterator::SELF_FIRST);
+    $this->assertEqual(iterator_count($it), 8, 'Iterated over all conditions and condition containers');
+    $it = new RecursiveIteratorIterator($rule2->conditions());
+    $this->assertEqual(iterator_count($it), 6, 'Iterated over all conditions');
+    $this->assertEqual(iterator_count($rule2->actions()), 1, 'Iterated over all actions');
+
+    // Delete.
+    $rule2->delete();
+    $this->assertEqual(entity_load('rules_config', FALSE, array('id' => $rule->id)), array(), 'Deleted.');
+
+    // Tests CRUD for tags - making sure the tags are stored properly..
+    $rule = $this->createTestRule();
+    $tag = $this->randomString();
+    $rule->tags = array($tag);
+    $rule->save();
+    $result = db_select('rules_tags')
+      ->fields('rules_tags', array('tag'))
+      ->condition('id', $rule->id)
+      ->execute();
+    $this->assertEqual($result->fetchField(), $tag, 'Associated tag has been saved.');
+    // Try updating.
+    $rule->tags = array($this->randomName(), $this->randomName());
+    $rule->integrityCheck()->save();
+    $result = db_select('rules_tags')
+      ->fields('rules_tags', array('tag'))
+      ->condition('id', $rule->id)
+      ->execute()
+      ->fetchCol();
+    $this->assertTrue(in_array($rule->tags[0], $result) && in_array($rule->tags[1], $result), 'Updated associated tags.');
+    // Try loading multiple rules by tags.
+    $rule2 = $this->createTestRule();
+    $rule2->tags = array($this->randomName());
+    $rule2->save();
+    $loaded = entity_load('rules_config', FALSE, array('tags' => array($rule->tags[0], $rule2->tags[0])));
+    $this->assertTrue($loaded[$rule->id]->id == $rule->id && $loaded[$rule2->id]->id == $rule2->id, 'Loading configs by tags');
+    // Try deleting.
+    $rule->delete();
+    $result = db_select('rules_tags')
+      ->fields('rules_tags', array('tag'))
+      ->condition('id', $rule->id)
+      ->execute();
+    $this->assertEqual($result->fetchField(), FALSE, 'Deleted associated tags.');
+  }
+
+  /**
+   * Tests automatic saving of variables.
+   */
+  public function testActionSaving() {
+    // Test saving a parameter.
+    $action = rules_action('rules_node_publish_action_save');
+    $node = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
+    $action->executeByArgs(array('node' => $node));
+
+    $this->assertEqual($node->status, 1, 'Action executed correctly on node.');
+    // Sync node_load cache with node_save.
+    entity_get_controller('node')->resetCache();
+
+    $node = node_load($node->nid);
+    $this->assertEqual($node->status, 1, 'Node has been saved.');
+
+    // Now test saving a provided variable, which is renamed and modified before
+    // it is saved.
+    $title = $this->randomName();
+    $rule = rule();
+    $rule->action('entity_create', array(
+      'type' => 'node',
+      'param_type' => 'article',
+      'param_author:select' => 'site:current-user',
+      'param_title' => $title,
+      'entity_created:var' => 'node',
+    ));
+    $rule->action('data_set', array(
+      'data:select' => 'node:body',
+      'value' => array('value' => 'body'),
+    ));
+    $rule->integrityCheck();
+    $rule->execute();
+
+    $node = $this->drupalGetNodeByTitle($title);
+    $this->assertTrue(!empty($node) && $node->body[LANGUAGE_NONE][0]['value'] == 'body', 'Saved a provided variable');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests adding a variable and optional parameters.
+   */
+  public function testVariableAdding() {
+    $node = $this->drupalCreateNode();
+    $rule = rule(array('nid' => array('type' => 'integer')));
+    $rule->condition('rules_test_condition_true')
+         ->action('rules_action_load_node')
+         ->action('rules_action_delete_node', array('node:select' => 'node_loaded'))
+         ->execute($node->nid);
+
+    $this->assertEqual(FALSE, node_load($node->nid), 'Variable added and skipped optional parameter.');
+    RulesLog::logger()->checkLog();
+
+    $vars = $rule->conditions()->offsetGet(0)->availableVariables();
+    $this->assertEqual(!isset($vars['node_loaded']), 'Loaded variable is not available to conditions.');
+
+    // Test adding a variable with a custom variable name.
+    $node = $this->drupalCreateNode();
+    $rule = rule(array('nid' => array('type' => 'integer')));
+    $rule->action('rules_action_load_node', array('node_loaded:var' => 'node'))
+         ->action('rules_action_delete_node')
+         ->execute($node->nid);
+
+    $this->assertEqual(FALSE, node_load($node->nid), 'Variable with custom name added.');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests custom access for using component actions/conditions.
+   */
+  public function testRuleComponentAccess() {
+    // Create a normal user.
+    $normal_user = $this->drupalCreateUser();
+    // Create a role for granting access to the rule component.
+    $this->normal_role = $this->drupalCreateRole(array(), 'test_role');
+    $normal_user->roles[$this->normal_role] = 'test_role';
+    user_save($normal_user, array('roles' => $normal_user->roles));
+    // Create an 'action set' rule component making use of a permission.
+    $action_set = rules_action_set(array('node' => array('type' => 'node')));
+    $action_set->access_exposed = TRUE;
+    $action_set->save('rules_test_roles');
+
+    // Set the global user to be the current one as access is checked for the
+    // global user.
+    global $user;
+    $user = user_load($normal_user->uid);
+    $this->assertFalse(rules_action('component_rules_test_roles')->access(), 'Authenticated user without the correct role can\'t use the rule component.');
+
+    // Assign the role that will have permissions for the rule component.
+    user_role_change_permissions($this->normal_role, array('use Rules component rules_test_roles' => TRUE));
+    $this->assertTrue(rules_action('component_rules_test_roles')->access(), 'Authenticated user with the correct role can use the rule component.');
+
+    // Reset global user to anonymous.
+    $user = user_load(0);
+    $this->assertFalse(rules_action('component_rules_test_roles')->access(), 'Anonymous user can\'t use the rule component.');
+  }
+
+  /**
+   * Tests passing arguments by reference to an action.
+   */
+  public function testPassingByReference() {
+    // Keeping references of variables is unsupported, though the
+    // EntityMetadataArrayObject may be used to achieve that.
+    $array = array('foo' => 'bar');
+    $data = new EntityMetadataArrayObject($array);
+    rules_action('rules_action_test_reference')->execute($data);
+    $this->assertTrue($data['changed'], 'Parameter has been passed by reference');
+  }
+
+  /**
+   * Tests sorting rule elements.
+   */
+  public function testSorting() {
+    $rule = $this->createTestRule();
+    $conditions = $rule->conditions();
+    $conditions[0]->weight = 10;
+    $conditions[2]->weight = 10;
+    $id[0] = $conditions[0]->elementId();
+    $id[1] = $conditions[1]->elementId();
+    $id[2] = $conditions[2]->elementId();
+    // For testing use a deep sort, even if not necessary here.
+    $rule->sortChildren(TRUE);
+    $conditions = $rule->conditions();
+    $this->assertEqual($conditions[0]->elementId(), $id[1], 'Condition sorted correctly.');
+    $this->assertEqual($conditions[1]->elementId(), $id[0], 'Condition sorted correctly.');
+    $this->assertEqual($conditions[2]->elementId(), $id[2], 'Condition sorted correctly.');
+  }
+
+  /**
+   * Tests using data selectors.
+   */
+  public function testDataSelectors() {
+    $body[LANGUAGE_NONE][0] = array('value' => '<b>The body & nothing.</b>');
+    $node = $this->drupalCreateNode(array('body' => $body, 'type' => 'page', 'summary' => ''));
+
+    $rule = rule(array('nid' => array('type' => 'integer')));
+    $rule->action('rules_action_load_node')
+         ->action('drupal_message', array('message:select' => 'node_loaded:body:value'))
+         ->execute($node->nid);
+
+    RulesLog::logger()->checkLog();
+    $msg = drupal_get_messages('status');
+    $last_msg = array_pop($msg['status']);
+    $wrapper = entity_metadata_wrapper('node', $node);
+    $this->assertEqual($last_msg, $wrapper->body->value->value(array('sanitize' => TRUE)), 'Data selector for getting parameter applied.');
+
+    // Get a "reference" on the same object as returned by node_load().
+    $node = node_load($node->nid);
+    $rule = rule(array('nid' => array('type' => 'integer')));
+    $rule->action('rules_action_load_node')
+         ->action('data_set', array('data:select' => 'node_loaded:title', 'value' => 'Test title'))
+         // Use two actions and make sure the node get saved only once.
+         ->action('data_set', array('data:select' => 'node_loaded:title', 'value' => 'Test title2'))
+         ->execute($node->nid);
+
+    $wrapper = entity_metadata_wrapper('node', $node);
+    $this->assertEqual('Test title2', $wrapper->title->value(), 'Data has been modified and saved.');
+
+    RulesLog::logger()->checkLog();
+    $text = RulesLog::logger()->render();
+    $msg = RulesTestCase::t('Saved %node_loaded of type %node.', array('node_loaded', 'node'));
+    if ($pos1 = strpos($text, $msg)) {
+      $pos2 = strpos($text, $msg, $pos1 + 1);
+    }
+    $this->assertTrue($pos1 && $pos2 === FALSE, 'Data has been saved only once.');
+
+    // Test validation.
+    try {
+      rules_action('data_set', array('data' => 'no-selector', 'value' => ''))->integrityCheck();
+      $this->fail("Validation hasn't created an exception.");
+    }
+    catch (RulesIntegrityException $e) {
+      $this->pass("Validation error correctly detected: " . $e);
+    }
+
+    // Test auto creation of nested data structures, like the node body field.
+    // I.e. if $node->body is not set, it is automatically initialized to an
+    // empty array, so that the nested value can be set and the wrappers do not
+    // complain about missing parent data structures.
+    $rule = rule();
+    $rule->action('entity_create', array(
+      'type' => 'node',
+      'param_type' => 'page',
+      'param_title' => 'foo',
+      'param_author' => $GLOBALS['user'],
+    ));
+    $rule->action('data_set', array('data:select' => 'entity_created:body:value', 'value' => 'test content'))
+         ->execute();
+    try {
+      RulesLog::logger()->checkLog();
+      $this->pass('Auto creation of nested data structures.');
+    }
+    catch (Exception $e) {
+      $this->fail('Auto creation of nested data structures.');
+    }
+
+    // Make sure variables that are passed wrapped work.
+    $result = rules_condition('rules_test_condition_node_wrapped')->execute($node->nid);
+    $this->assertTrue($result, 'Condition receiving wrapped parameter.');
+
+    // Make sure wrapped parameters are checked for containing NULL values.
+    $rule = rule(array('node' => array('type' => 'node', 'optional' => TRUE)));
+    $rule->condition('rules_test_condition_node_wrapped', array('node:select' => 'node'));
+    $rule->execute(entity_metadata_wrapper('node'));
+    $text = RulesLog::logger()->render();
+    $msg = RulesTestCase::t('The variable or parameter %node is empty.', array('node'));
+    $this->assertTrue(strpos($text, $msg) !== FALSE, 'Evaluation aborted due to an empty argument value.');
+  }
+
+  /**
+   * Tests making use of rule sets.
+   */
+  public function testRuleSets() {
+    $set = rules_rule_set(array(
+      'node' => array('type' => 'node', 'label' => 'node'),
+    ));
+    $set->rule(rule()->action('drupal_message', array('message:select' => 'node:title')))
+        ->rule(rule()->condition('rules_condition_content_is_published')
+                     ->action('drupal_message', array('message' => 'Node is published.'))
+               );
+    $set->integrityCheck()->save('rules_test_set_1');
+
+    $node = $this->drupalCreateNode(array('title' => 'The title.', 'status' => 1));
+    // Execute.
+    rules_invoke_component('rules_test_set_1', $node);
+
+    $msg = drupal_get_messages();
+    $this->assertEqual($msg['status'][0], 'The title.', 'First rule evaluated.');
+    $this->assertEqual($msg['status'][1], 'Node is published.', 'Second rule evaluated.');
+
+    // Test a condition set.
+    $set = rules_or(array(
+      'node' => array('type' => 'node', 'label' => 'node'),
+    ));
+    $set->condition('data_is', array('data:select' => 'node:author:name', 'value' => 'notthename'))
+        ->condition('data_is', array('data:select' => 'node:nid', 'value' => $node->nid))
+        ->integrityCheck()
+        ->save('test', 'rules_test');
+    // Load and execute condition set.
+    $set = rules_config_load('test');
+    $this->assertTrue($set->execute($node), 'Set has been correctly evaluated.');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests invoking components from the action.
+   */
+  public function testComponentInvocations() {
+    $set = rules_rule_set(array(
+      'node1' => array('type' => 'node', 'label' => 'node'),
+    ));
+    $set->rule(rule()->condition('node_is_published', array('node:select' => 'node1'))
+                     ->action('node_unpublish', array('node:select' => 'node1'))
+               );
+    $set->integrityCheck()->save('rules_test_set_2');
+
+    // Use different names for the variables to ensure they are properly mapped
+    // when taking over the variables to be saved.
+    $rule = rule(array(
+      'node2' => array('type' => 'node', 'label' => 'node'),
+    ));
+    $rule->action('component_rules_test_set_2', array('node1:select' => 'node2'));
+    $rule->action('node_make_sticky', array('node:select' => 'node2'));
+
+    $node = $this->drupalCreateNode(array('title' => 'The title.', 'status' => 1, 'sticky' => 0));
+    $rule->execute($node);
+
+    $node = node_load($node->nid, NULL, TRUE);
+    $this->assertFalse($node->status, 'The component changes have been saved correctly.');
+    $this->assertTrue($node->sticky, 'The action changes have been saved correctly.');
+
+    // Check that we have saved the changes only once.
+    $text = RulesLog::logger()->render();
+    // Make sure both saves are handled in one save operation.
+    $this->assertEqual(substr_count($text, 'Saved'), 1, 'Changes have been saved in one save operation.');
+    RulesLog::logger()->checkLog();
+
+    // Test recursion prevention on components by invoking the component from
+    // itself, what should be prevented.
+    $set->action('component_rules_test_set_2', array('node1:select' => 'node1'))
+        ->save();
+
+    $rule->execute($node);
+    $text1 = RulesLog::logger()->render();
+    $text2 = RulesTestCase::t('Not evaluating rule set %rules_test_set_2 to prevent recursion.', array('rules_test_set_2'));
+    $this->assertTrue((strpos($text1, $text2) !== FALSE), "Recursion of component invocation prevented.");
+
+    // Test executing the component provided in code via the action. This makes
+    // sure the component in code has been properly picked up.
+    $node->status = 0;
+    node_save($node);
+    rules_action('component_rules_test_action_set')->execute($node);
+    $this->assertTrue($node->status == 1, 'Component provided in code has been executed.');
+  }
+
+  /**
+   * Tests asserting metadata.
+   *
+   * Customizes action info and makes sure integrity is checked.
+   */
+  public function testMetadataAssertion() {
+    $action = rules_action('rules_node_make_sticky_action');
+
+    // Test failing integrity check.
+    try {
+      $rule = rule(array('node' => array('type' => 'entity')));
+      $rule->action($action);
+      // Fails due to the 'node' variable not matching the node type.
+      $rule->integrityCheck();
+      $this->fail('Integrity check has not thrown an exception.');
+    }
+    catch (RulesIntegrityException $e) {
+      $this->pass('Integrity check has thrown exception: ' . $e->getMessage());
+    }
+
+    // Test asserting additional metadata.
+    $rule = rule(array('node' => array('type' => 'node')));
+    // Customize action info using the settings.
+    $rule->condition('data_is', array('data:select' => 'node:type', 'value' => 'page'))
+         // Configure an condition using the body. As the body is a field,
+         // this requires the bundle to be correctly asserted.
+         ->condition(rules_condition('data_is', array('data:select' => 'node:body:value', 'value' => 'foo'))->negate())
+         // The action also requires the page bundle in order to work.
+         ->action($action);
+    // Make sure the integrity check doesn't throw an exception.
+    $rule->integrityCheck();
+    // Test the rule.
+    $node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
+    $rule->execute($node);
+    $this->assertTrue($node->sticky, 'Rule with asserted metadata executed.');
+
+    // Test asserting metadata on a derived property, i.e. not a variable.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('entity_is_of_type', array('entity:select' => 'node:reference', 'type' => 'node'))
+         ->condition('data_is', array('data:select' => 'node:reference:type', 'value' => 'page'))
+         ->action('rules_node_page_make_sticky_action', array('node:select' => 'node:reference'));
+    $rule->integrityCheck();
+    $rule->execute($node);
+
+    // Test asserting an entity field.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('entity_has_field', array('entity:select' => 'node:reference', 'field' => 'field_tags'))
+         ->action('data_set', array('data:select' => 'node:reference:field-tags', 'value' => array()));
+    $rule->integrityCheck();
+    $rule->execute($node);
+
+    // Make sure an asserted bundle can be used as argument.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('entity_is_of_type', array('entity:select' => 'node:reference', 'type' => 'node'))
+         ->condition('node_is_of_type', array('node:select' => 'node:reference', 'type' => array('page')))
+         ->action('rules_node_page_make_sticky_action', array('node:select' => 'node:reference'));
+    $rule->integrityCheck();
+    $rule->execute($node);
+
+    // Test asserting metadata on a derived property being a list item.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('node_is_of_type', array('node:select' => 'node:ref-nodes:0', 'type' => array('article')))
+         ->action('data_set', array('data:select' => 'node:ref-nodes:0:field-tags', 'value' => array()));
+    $rule->integrityCheck();
+    $rule->execute($node);
+
+    // Give green lights if there were no exceptions and check rules-log errors.
+    $this->pass('Rules asserting metadata on a derived property pass integrity checks.');
+    RulesLog::logger()->checkLog();
+
+    // Make sure assertions of a one list item are not valid for another item.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('node_is_of_type', array('node:select' => 'node:ref-nodes:0', 'type' => array('article')))
+         ->action('data_set', array('data:select' => 'node:ref-nodes:1:field-tags', 'value' => array()));
+    try {
+      $rule->integrityCheck();
+      $this->fail('Assertion of a list item is not valid for another item.');
+    }
+    catch (RulesException $e) {
+      $this->pass('Assertion of a list item is not valid for another item.');
+    }
+  }
+
+  /**
+   * Tests using loops.
+   */
+  public function testLoops() {
+    // Test passing the list parameter as argument to ensure that is working
+    // generally for plugin container too.
+    drupal_get_messages(NULL, TRUE);
+    $loop = rules_loop();
+    $loop->action('drupal_message', array('message' => 'test'));
+    $arg_info = $loop->parameterInfo();
+    $this->assert($arg_info['list']['type'] == 'list', 'Argument info contains list.');
+    $loop->execute(array(1, 2));
+
+    // Ensure the action has been executed twice, once for each list item.
+    $msg = drupal_get_messages();
+    $this->assert($msg['status'][0] == 'test' && $msg['status'][1], 'Loop has been properly executed');
+
+    // Now test looping over nodes.
+    $node1 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
+    $node2 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
+    $node3 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
+
+    $rule = rule(array(
+      'list' => array(
+        'type' => 'list<node>',
+        'label' => 'A list of nodes',
+      ),
+    ));
+    $loop = rules_loop(array('list:select' => 'list', 'item:var' => 'node'));
+    $loop->action('data_set', array('data:select' => 'node:sticky', 'value' => TRUE));
+    $rule->action($loop);
+    // Test using a list with data selectors, just output the last nodes type.
+    $rule->action('drupal_message', array('message:select' => 'list:2:type'));
+
+    $rule->execute(array($node1->nid, $node2->nid, $node3->nid));
+    $text = RulesLog::logger()->render();
+    $save_msg = RulesTestCase::t('Saved %node of type %node.', array('node', 'node'));
+    $this->assertTrue(substr_count($text, $save_msg) == 3, 'List item variables have been saved.');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests access checks.
+   */
+  public function testAccessCheck() {
+    $rule = rule();
+    // Try to set a property which is provided by the test module and is not
+    // accessible, so the access check has to return FALSE.
+    $rule->action('data_set', array('data:select' => 'site:no-access-user', 'value' => 'foo'));
+    $this->assertTrue($rule->access() === FALSE, 'Access check is working.');
+  }
+
+  /**
+   * Tests returning provided variables.
+   */
+  public function testReturningVariables() {
+    $node = $this->drupalCreateNode();
+    $action = rules_action('entity_fetch', array('type' => 'node', 'id' => $node->nid));
+    list($node2) = $action->execute();
+    $this->assertTrue($node2->nid == $node->nid, 'Action returned a variable.');
+
+    // Create a simple set that just passed through the given node.
+    $set = rules_rule_set(array('node' => array('type' => 'node')), array('node'));
+    $set->integrityCheck()->save('rules_test_set_1');
+
+    $provides = $set->providesVariables();
+    $this->assertTrue($provides['node']['type'] == 'node', 'Rule set correctly passed through the node.');
+
+    list($node2) = $set->execute($node);
+    $this->assertTrue($node2->nid == $node->nid, 'Rule set returned a variable.');
+
+    // Create an action set returning a variable that is no parameter.
+    $set = rules_action_set(array(
+      'node' => array(
+        'type' => 'node',
+        'parameter' => FALSE,
+      )), array('node'));
+    $set->action('entity_fetch', array('type' => 'node', 'id' => $node->nid))
+        ->action('data_set', array('data:select' => 'node', 'value:select' => 'entity_fetched'));
+    $set->integrityCheck();
+    list($node3) = $set->execute();
+    $this->assertTrue($node3->nid == $node->nid, 'Action set returned a variable that has not been passed as parameter.');
+
+    // Test the same again with a variable holding a not wrapped data type.
+    $set = rules_action_set(array(
+      'number' => array(
+        'type' => 'integer',
+        'parameter' => FALSE,
+      )), array('number'));
+    $set->action('data_set', array('data:select' => 'number', 'value' => 3));
+    $set->integrityCheck();
+    list($number) = $set->execute();
+    $this->assertTrue($number == 3, 'Actions set returned a number.');
+  }
+
+  /**
+   * Tests using input evaluators.
+   */
+  public function testInputEvaluators() {
+    $node = $this->drupalCreateNode(array('title' => '<b>The body & nothing.</b>', 'type' => 'page'));
+
+    $rule = rule(array('nid' => array('type' => 'integer')));
+    $rule->action('rules_action_load_node')
+         ->action('drupal_message', array('message' => 'Title: [node_loaded:title]'))
+         ->execute($node->nid);
+
+    RulesLog::logger()->checkLog();
+    $msg = drupal_get_messages();
+    $this->assertEqual(array_pop($msg['status']), 'Title: ' . check_plain('<b>The body & nothing.</b>'), 'Token input evaluator applied.');
+
+    // Test token replacements on a list of text values.
+    $component = rules_action_set(array('var' => array('type' => 'list<text>', 'label' => 'var')), array('var'));
+    $component->save('rules_test_input');
+
+    $action = rules_action('component_rules_test_input', array('var' => array('uid: [site:current-user:uid]')));
+    list($var) = $action->execute();
+    $uid = $GLOBALS['user']->uid;
+    $this->assertEqual(array("uid: $uid"), $var, 'Token replacements on a list of values applied.');
+  }
+
+  /**
+   * Tests importing and exporting a rule.
+   */
+  public function testRuleImportExport() {
+    $rule = rule(array('nid' => array('type' => 'integer')));
+    $rule->name = "rules_export_test";
+    $rule->action('rules_action_load_node')
+         ->action('drupal_message', array('message' => 'Title: [node_loaded:title]'));
+
+    $export =
+'{ "rules_export_test" : {
+    "PLUGIN" : "rule",
+    "REQUIRES" : [ "rules_test", "rules" ],
+    "USES VARIABLES" : { "nid" : { "type" : "integer" } },
+    "DO" : [
+      { "rules_action_load_node" : { "PROVIDE" : { "node_loaded" : { "node_loaded" : "Loaded content" } } } },
+      { "drupal_message" : { "message" : "Title: [node_loaded:title]" } }
+    ]
+  }
+}';
+    $this->assertEqual($export, $rule->export(), 'Rule has been exported correctly.');
+
+    // Test importing a rule which makes use of almost all features.
+    $export = _rules_export_get_test_export();
+    $rule = rules_import($export);
+    $this->assertTrue(!empty($rule) && $rule->integrityCheck(), 'Rule has been imported.');
+
+    // Test loading the same export provided as default rule.
+    $rule = rules_config_load('rules_export_test');
+    $this->assertTrue(!empty($rule) && $rule->integrityCheck(), 'Export has been provided in code.');
+
+    // Export it and make sure the same export is generated again.
+    $this->assertEqual($export, $rule->export(), 'Export of imported rule equals original export.');
+
+    // Now try importing a rule set.
+    $export =
+'{ "rules_test_set" : {
+    "LABEL" : "Test set",
+    "PLUGIN" : "rule set",
+    "REQUIRES" : [ "rules" ],
+    "USES VARIABLES" : { "node" : { "label" : "Test node", "type" : "node" } },
+    "RULES" : [
+      { "RULE" : {
+          "IF" : [ { "NOT data_is" : { "data" : [ "node:title" ], "value" : "test" } } ],
+          "DO" : [ { "data_set" : { "data" : [ "node:title" ], "value" : "test" } } ],
+          "LABEL" : "Test Rule"
+        }
+      },
+      { "RULE" : {
+          "DO" : [ { "drupal_message" : { "message" : "hi" } } ],
+          "LABEL" : "Test Rule 2"
+        }
+      }
+    ]
+  }
+}';
+    $set = rules_import($export);
+    $this->assertTrue(!empty($set) && $set->integrityCheck(), 'Rule set has been imported.');
+    // Export it and make sure the same export is generated again.
+    $this->assertEqual($export, $set->export(), 'Export of imported rule set equals original export.');
+
+    // Try executing the imported rule set.
+    $node = $this->drupalCreateNode();
+    $set->execute($node);
+    $this->assertEqual($node->title, 'test', 'Imported rule set has been executed.');
+    RulesLog::logger()->checkLog();
+
+    // Try import / export for a rule component providing a variable.
+    $rule = rule(array(
+      'number' => array(
+        'type' => 'integer',
+        'label' => 'Number',
+        'parameter' => FALSE,
+      )), array('number'));
+    $rule->action('data_set', array('data:select' => 'number', 'value' => 3));
+    $rule->name = 'rules_test_provides';
+
+    $export = '{ "rules_test_provides" : {
+    "PLUGIN" : "rule",
+    "REQUIRES" : [ "rules" ],
+    "USES VARIABLES" : { "number" : { "type" : "integer", "label" : "Number", "parameter" : false } },
+    "DO" : [ { "data_set" : { "data" : [ "number" ], "value" : 3 } } ],
+    "PROVIDES VARIABLES" : [ "number" ]
+  }
+}';
+    $this->assertEqual($export, $rule->export(), 'Rule 2 has been exported correctly.');
+    $imported_rule = rules_import($rule->export());
+
+    $this->assertTrue(!empty($imported_rule) && $imported_rule->integrityCheck(), 'Rule 2 has been imported.');
+    $this->assertEqual($export, $imported_rule->export(), 'Export of imported rule 2 equals original export.');
+
+    // Test importing a negated condition component.
+    $export = '{ "rules_negated_component" : {
+    "LABEL" : "negated_component",
+    "PLUGIN" : "or",
+    "REQUIRES" : [ "rules" ],
+    "NOT OR" : [ { "data_is_empty" : { "data" : [ "site:slogan" ] } } ]
+  }
+}';
+    $or = rules_import($export);
+    $this->assertTrue($or->integrityCheck() && $or->isNegated(), 'Negated condition component imported.');
+  }
+
+  /**
+   * Tests the named parameter mode.
+   */
+  public function testNamedParameters() {
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->action('rules_action_node_set_title', array('title' => 'foo'));
+    $rule->integrityCheck();
+
+    // Test the rule.
+    $node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0));
+    $rule->execute($node);
+    $this->assertTrue($node->title == 'foo', 'Action with named parameters has been correctly executed.');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Makes sure Rules aborts when NULL values are used.
+   */
+  public function testAbortOnNULLValues() {
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->action('drupal_message', array('message:select' => 'node:log'));
+    $rule->integrityCheck();
+
+    // Test the rule.
+    $node = $this->drupalCreateNode();
+    $node->log = NULL;
+    $rule->execute($node);
+
+    $text = RulesLog::logger()->render();
+    $msg = RulesTestCase::t('The variable or parameter %message is empty.', array('message'));
+    $this->assertTrue(strpos($text, $msg) !== FALSE, 'Evaluation aborted due to an empty argument value.');
+  }
+
+}
+
+/**
+ * Test rules data wrappers.
+ */
+class RulesTestDataCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules Data tests',
+      'description' => 'Tests rules data saving and type matching.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules', 'rules_test');
+    variable_set('rules_debug_log', TRUE);
+    // Make sure we don't ran over issues with the node_load static cache.
+    entity_get_controller('node')->resetCache();
+  }
+
+  /**
+   * Tests intelligently saving data.
+   */
+  public function testDataSaving() {
+    $node = $this->drupalCreateNode();
+    $state = new RulesState(rule());
+    $state->addVariable('node', $node, array('type' => 'node'));
+    $wrapper = $state->get('node');
+    $node->title = 'test';
+    $wrapper->set($node);
+    $state->saveChanges('node', $wrapper, FALSE);
+
+    $this->assertFalse($this->drupalGetNodeByTitle('test'), 'Changes have not been saved.');
+    $state->saveChanges('node', $wrapper, TRUE);
+    $this->assertTrue($this->drupalGetNodeByTitle('test'), 'Changes have been saved.');
+
+    // Test skipping saving.
+    $state->addVariable('node2', $node, array(
+      'type' => 'node',
+      'skip save' => TRUE,
+    ));
+    $wrapper = $state->get('node2');
+    $node->title = 'test2';
+    $wrapper->set($node);
+    $state->saveChanges('node2', $wrapper, TRUE);
+    $this->assertFalse($this->drupalGetNodeByTitle('test2'), 'Changes have not been saved.');
+
+    // Try saving a non-entity wrapper, which should result in saving the
+    // parent entity containing the property.
+    $wrapper = $state->get('node');
+    $wrapper->title->set('test3');
+    $state->saveChanges('node:title', $wrapper, TRUE);
+    $this->assertTrue($this->drupalGetNodeByTitle('test3'), 'Parent entity has been saved.');
+  }
+
+  /**
+   * Tests type matching.
+   */
+  public function testTypeMatching() {
+    $entity = array('type' => 'entity');
+    $node = array('type' => 'node');
+    $this->assertTrue(RulesData::typesMatch($node, $entity), 'Types match.');
+    $this->assertFalse(RulesData::typesMatch($entity, $node), 'Types don\'t match.');
+
+    $this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $node), 'Types match.');
+    $this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $entity), 'Types match.');
+    $this->assertTrue(RulesData::typesMatch(array('type' => 'list<node>'), array('type' => 'list')), 'Types match.');
+    $this->assertTrue(RulesData::typesMatch($node + array('bundle' => 'page'), $node + array('bundles' => array('page', 'story'))), 'Types match.');
+    $this->assertFalse(RulesData::typesMatch($node, $node + array('bundles' => array('page', 'story'))), 'Types don\'t match.');
+
+    // Test that a type matches its grand-parent type (text > decimal > integer)
+    $this->assertTrue(RulesData::typesMatch(array('type' => 'integer'), array('type' => 'text')), 'Types match.');
+    $this->assertFalse(RulesData::typesMatch(array('type' => 'text'), array('type' => 'integer')), 'Types don\'t match.');
+  }
+
+  /**
+   * Tests making use of custom wrapper classes.
+   */
+  public function testCustomWrapperClasses() {
+    // Test loading a vocabulary by name, which is done by a custom wrapper.
+    $set = rules_action_set(array('vocab' => array('type' => 'taxonomy_vocabulary')), array('vocab'));
+    $set->action('drupal_message', array('message:select' => 'vocab:name'));
+    $set->integrityCheck();
+    list($vocab) = $set->execute('tags');
+    $this->assertTrue($vocab->machine_name == 'tags', 'Loaded vocabulary by name.');
+
+    // Now test wrapper creation for a direct input argument value.
+    $set = rules_action_set(array('term' => array('type' => 'taxonomy_term')));
+    $set->action('data_set', array('data:select' => 'term:vocabulary', 'value' => 'tags'));
+    $set->integrityCheck();
+
+    $vocab = entity_create('taxonomy_vocabulary', array(
+      'name' => 'foo',
+      'machine_name' => 'foo',
+    ));
+    entity_save('taxonomy_vocabulary', $vocab);
+    $term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
+      'name' => $this->randomName(),
+      'vocabulary' => $vocab,
+    ))->save();
+    $set->execute($term_wrapped);
+    $this->assertEqual($term_wrapped->vocabulary->machine_name->value(), 'tags', 'Vocabulary name used as direct input value.');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Makes sure the RulesIdentifiableDataWrapper is working correctly.
+   */
+  public function testRulesIdentifiableDataWrapper() {
+    $node = $this->drupalCreateNode();
+    $wrapper = new RulesTestTypeWrapper('rules_test_type', $node);
+    $this->assertTrue($wrapper->value() == $node, 'Data correctly wrapped.');
+
+    // Test serializing and make sure only the id is stored.
+    $this->assertTrue(strpos(serialize($wrapper), $node->title) === FALSE, 'Data has been correctly serialized.');
+    $this->assertEqual(unserialize(serialize($wrapper))->value()->title, $node->title, 'Serializing works right.');
+
+    $wrapper2 = unserialize(serialize($wrapper));
+    // Test serializing the unloaded wrapper.
+    $this->assertEqual(unserialize(serialize($wrapper2))->value()->title, $node->title, 'Serializing works right.');
+
+    // Test loading a not more existing node.
+    $s = serialize($wrapper2);
+    node_delete($node->nid);
+    $this->assertFalse(node_load($node->nid), 'Node deleted.');
+    try {
+      unserialize($s)->value();
+      $this->fail("Loading hasn't created an exception.");
+    }
+    catch (EntityMetadataWrapperException $e) {
+      $this->pass("Exception was thrown: " . $e->getMessage());
+    }
+
+    // Test saving a savable custom, identifiable wrapper.
+    $action = rules_action('test_type_save');
+    $node = $this->drupalCreateNode(array('status' => 0, 'type' => 'page'));
+    $node->status = 1;
+    $action->execute($node);
+
+    // Load the node fresh from the db.
+    $node = node_load($node->nid, NULL, TRUE);
+    $this->assertEqual($node->status, 1, 'Savable non-entity has been saved.');
+  }
+
+}
+
+/**
+ * Test triggering rules.
+ */
+class RulesTriggerTestCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Reaction Rules',
+      'description' => 'Tests triggering reactive rules.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules', 'rules_test');
+    RulesLog::logger()->clear();
+    variable_set('rules_debug_log', TRUE);
+  }
+
+  /**
+   * Helper function to create a test Rule.
+   */
+  protected function createTestRule($action = TRUE, $event = 'node_presave') {
+    $rule = rules_reaction_rule();
+    $rule->event($event)
+         ->condition(rules_condition('data_is', array('data:select' => 'node:status', 'value' => TRUE))->negate())
+         ->condition('data_is', array('data:select' => 'node:type', 'value' => 'page'));
+    if ($action) {
+      $rule->action('rules_action_delete_node');
+    }
+    return $rule;
+  }
+
+  /**
+   * Tests CRUD for reaction rules - making sure the events are stored properly.
+   */
+  public function testReactiveRuleCreation() {
+    $rule = $this->createTestRule();
+    $rule->save();
+    $result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
+    $this->assertEqual($result->fetchField(), 'node_presave', 'Associated event has been saved.');
+    // Try updating.
+    $rule->removeEvent('node_presave');
+    $rule->event('node_insert');
+    $rule->event('node_update');
+    $rule->active = FALSE;
+    $rule->integrityCheck()->save();
+    $result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
+    $this->assertEqual($result->fetchCol(), array_values($rule->events()), 'Updated associated events.');
+    // Try deleting.
+    $rule->delete();
+    $result = db_query("SELECT event FROM {rules_trigger} WHERE id = :id", array(':id' => $rule->id));
+    $this->assertEqual($result->fetchField(), FALSE, 'Deleted associated events.');
+  }
+
+  /**
+   * Tests creating and triggering a basic reaction rule.
+   */
+  public function testBasicReactionRule() {
+    $node = $this->drupalCreateNode(array('type' => 'page'));
+    $rule = $this->createTestRule();
+    $rule->integrityCheck()->save();
+    // Test the basics of the event set work right.
+    $event = rules_get_cache('event_node_presave');
+    $this->assertEqual(array_keys($event->parameterInfo()), array('node'), 'EventSet returns correct argument info.');
+
+    // Trigger the rule by updating the node.
+    $nid = $node->nid;
+    $node->status = 0;
+    node_save($node);
+
+    RulesLog::logger()->checkLog();
+    $this->assertFalse(node_load($nid), 'Rule successfully triggered and executed');
+    // debug(RulesLog::logger()->render());
+  }
+
+  /**
+   * Tests a rule using a handler to load a variable.
+   */
+  public function testVariableHandler() {
+    $node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
+    $rule = $this->createTestRule(FALSE, 'node_update');
+    $rule->action('rules_node_publish_action_save', array('node:select' => 'node_unchanged'));
+    // Test without recursion prevention to make sure recursive invocations
+    // work right too. This rule won't ran in an infinite loop anyway.
+    $rule->recursion = TRUE;
+    $rule->label = 'rule 1';
+    $rule->integrityCheck()->save();
+
+    $node->status = 0;
+    $node->sticky = 1;
+    node_save($node);
+
+    RulesLog::logger()->checkLog();
+    entity_get_controller('node')->resetCache();
+    $node = node_load($node->nid);
+
+    $this->assertFalse($node->sticky, 'Parameter has been loaded and saved.');
+    $this->assertTrue($node->status, 'Action has been executed.');
+
+    // Ensure the rule was evaluated a second time.
+    $text = RulesLog::logger()->render();
+    $msg = RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1'));
+    $pos = strpos($text, $msg);
+    $pos = ($pos !== FALSE) ? strpos($text, $msg, $pos) : FALSE;
+    $this->assertTrue($pos !== FALSE, "Recursion prevented.");
+    // debug(RulesLog::logger()->render());
+  }
+
+  /**
+   * Tests aborting silently when handlers are not able to load.
+   */
+  public function testVariableHandlerFailing() {
+    $rule = $this->createTestRule(FALSE, 'node_presave');
+    $rule->action('rules_node_publish_action_save', array('node:select' => 'node_unchanged'));
+    $rule->integrityCheck()->save();
+
+    // On insert it's not possible to get the unchanged node during presave.
+    $node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
+
+    // debug(RulesLog::logger()->render());
+    $text = RulesTestCase::t('Unable to load variable %node_unchanged, aborting.', array('node_unchanged'));
+    $this->assertTrue(strpos(RulesLog::logger()->render(), $text) !== FALSE, "Aborted evaluation.");
+  }
+
+  /**
+   * Tests preventing recursive rule invocations.
+   *
+   * Creates a rule that reacts on node-update then generates a node update
+   * that would trigger it itself.
+   */
+  public function testRecursionPrevention() {
+    $rule = $this->createTestRule(FALSE, 'node_update');
+    $rule->action('rules_node_make_sticky_action');
+    $rule->integrityCheck()->save();
+
+    // Now trigger the rule.
+    $node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
+    node_save($node);
+
+    $text = RulesTestCase::t('Not evaluating reaction rule %label to prevent recursion.', array('label' => $rule->name));
+    // debug(RulesLog::logger()->render());
+    $this->assertTrue((strpos(RulesLog::logger()->render(), $text) !== FALSE), "Recursion prevented.");
+    // debug(RulesLog::logger()->render());
+  }
+
+  /**
+   * Tests recursion prevention with altered arguments.
+   *
+   * Ensure the recursion prevention still allows the rule to trigger again
+   * during evaluation of the same event set, if the event isn't caused by the
+   * rule itself - thus we won't run in an infinite loop.
+   */
+  public function testRecursionOnDifferentArguments() {
+    // Create rule1 - which might recurse.
+    $rule = $this->createTestRule(FALSE, 'node_update');
+    $rule->action('rules_node_make_sticky_action');
+    $rule->label = 'rule 1';
+    $rule->integrityCheck()->save();
+
+    // Create rule2 - which triggers rule1 on another node.
+    $node2 = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
+    $rule2 = $this->createTestRule(FALSE, 'node_update');
+    $rule2->action('rules_action_load_node', array('nid' => $node2->nid))
+          ->action('rules_node_make_sticky_action', array('node:select' => 'node_loaded'));
+    $rule2->label = 'rule 2';
+    $rule2->save();
+
+    // Now trigger both rules by generating the event.
+    $node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
+    node_save($node);
+
+    // debug(RulesLog::logger()->render());
+    $text = RulesLog::logger()->render();
+    $pos = strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1')));
+    $pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 2', array('rule 2')), $pos) : FALSE;
+    $pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %node_loaded of type %node.', array('node_loaded', 'node')), $pos) : FALSE;
+    $pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating conditions of rule %rule 1', array('rule 1')), $pos) : FALSE;
+    $pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Not evaluating reaction rule %rule 2 to prevent recursion', array('rule 2')), $pos) : FALSE;
+    $this->assertTrue($pos !== FALSE, 'Rule1 was triggered on the event caused by Rule2.');
+  }
+
+  /**
+   * Tests the provided default rule 'rules_test_default_1'.
+   */
+  public function testDefaultRule() {
+    $rule = rules_config_load('rules_test_default_1');
+    $this->assertTrue($rule->status & ENTITY_IN_CODE && !($rule->status & ENTITY_IN_DB), 'Default rule can be loaded and has the right status.');
+    $this->assertTrue($rule->tags == array('Admin', 'Tag2'), 'Default rule has correct tags.');
+    // Enable.
+    $rule->active = TRUE;
+    $rule->save();
+
+    // Create a node that triggers the rule.
+    $node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
+    // Clear messages.
+    drupal_get_messages();
+    // Let event node_update occur.
+    node_save($node);
+
+    $msg = drupal_get_messages();
+    $this->assertEqual($msg['status'][0], 'A node has been updated.', 'Default rule has been triggered.');
+  }
+
+  /**
+   * Tests creating and triggering a reaction rule with event settings.
+   */
+  public function testEventSettings() {
+    $rule = rules_reaction_rule();
+    $rule->event('node_presave', array('bundle' => 'article'))
+      ->condition('data_is_empty', array('data:select' => 'node:field-tags'))
+      ->action('node_publish', array('node:select' => 'node'));
+    $rule->integrityCheck()->save();
+
+    $node = $this->drupalCreateNode(array('type' => 'page', 'status' => 0));
+    $this->assertEqual($node->status, 0, 'Rule has not been triggered.');
+    $node = $this->drupalCreateNode(array('type' => 'article', 'status' => 0));
+    $this->assertEqual($node->status, 1, 'Rule has been triggered.');
+    RulesLog::logger()->checkLog();
+
+    // Make sure an invalid bundle raises integrity problems.
+    $rule->event('node_presave', array('bundle' => 'invalid'));
+    try {
+      $rule->integrityCheck();
+      $this->fail('Integrity check failed.');
+    }
+    catch (RulesIntegrityException $e) {
+      $this->pass('Integrity check failed: ' . $e);
+    }
+  }
+
+}
+
+/**
+ * Tests provided module integration.
+ */
+class RulesIntegrationTestCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules Core Integration',
+      'description' => 'Tests provided integration for drupal core.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules', 'rules_test', 'php', 'path');
+    RulesLog::logger()->clear();
+    variable_set('rules_debug_log', TRUE);
+  }
+
+  /**
+   * Just makes sure the access callback run without errors.
+   */
+  public function testAccessCallbacks() {
+    $cache = rules_get_cache();
+    foreach (array('action', 'condition', 'event') as $type) {
+      foreach (rules_fetch_data($type . '_info') as $name => $info) {
+        if (isset($info['access callback'])) {
+          $info['access callback']($type, $name);
+        }
+      }
+    }
+  }
+
+  /**
+   * Tests data integration.
+   */
+  public function testDataIntegration() {
+    // Test data_create action.
+    $action = rules_action('data_create', array(
+      'type' => 'log_entry',
+      'param_type' => 'rules_test',
+      'param_message' => 'Rules test log message',
+      'param_severity' => WATCHDOG_WARNING,
+      'param_request_uri' => 'http://example.com',
+      'param_link' => '',
+    ));
+    $action->access();
+    $action->execute();
+    $text = RulesLog::logger()->render();
+    $pos = strpos($text, RulesTestCase::t('Added the provided variable %data_created of type %log_entry', array('data_created', 'log_entry')));
+    $this->assertTrue($pos !== FALSE, 'Data of type log entry has been created.');
+
+    // Test variable_add action.
+    $action = rules_action('variable_add', array(
+      'type' => 'text_formatted',
+      'value' => array(
+        'value' => 'test text',
+        'format' => 1,
+      ),
+    ));
+    $action->access();
+    $action->execute();
+    $text = RulesLog::logger()->render();
+    $pos = strpos($text, RulesTestCase::t('Added the provided variable %variable_added of type %text_formatted', array('variable_added', 'text_formatted')));
+    $this->assertTrue($pos !== FALSE, 'Data of type text formatted has been created.');
+
+    // Test using the list actions.
+    $rule = rule(array(
+      'list' => array(
+        'type' => 'list<text>',
+        'label' => 'A list of text',
+      ),
+    ));
+    $rule->action('list_add', array('list:select' => 'list', 'item' => 'bar2'));
+    $rule->action('list_add', array('list:select' => 'list', 'item' => 'bar', 'pos' => 'start'));
+    $rule->action('list_add', array('list:select' => 'list', 'item' => 'bar', 'unique' => TRUE));
+    $rule->action('list_remove', array('list:select' => 'list', 'item' => 'bar2'));
+    $list = entity_metadata_wrapper('list', array('foo', 'foo2'));
+    $rule->execute($list);
+    RulesLog::logger()->checkLog();
+    $this->assertEqual($list->value(), array('bar', 'foo', 'foo2'), 'List items removed and added.');
+    $this->assertFalse(rules_condition('list_contains')->execute($list, 'foo-bar'), 'Condition "List item contains" evaluates to FALSE');
+    $this->assertTrue(rules_condition('list_contains')->execute($list, 'foo'), 'Condition "List item contains" evaluates to TRUE');
+    // debug(RulesLog::logger()->render());
+
+    // Test data_is condition with IN operation.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('data_is', array('data:select' => 'node:title', 'op' => 'IN', 'value' => array('foo', 'bar')));
+    $rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
+    $rule->integrityCheck();
+
+    $node = $this->drupalCreateNode(array('title' => 'foo'));
+    $rule->execute($node);
+    $this->assertEqual($node->title, 'bar', "Data comparison using IN operation evaluates to TRUE.");
+
+    // Test Condition: Data is empty.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('data_is_empty', array('data:select' => 'node:title'));
+    $rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
+    $rule->integrityCheck();
+
+    // Data is empty condition evaluates to TRUE
+    // for node with empty title, action sets title to 'bar'.
+    $node = $this->drupalCreateNode(array('title' => '', 'type' => 'article'));
+    $rule->execute($node);
+    $this->assertEqual($node->title, 'bar', "Data is empty condition evaluates to TRUE for node with empty title, action sets title to 'bar'.");
+
+    // Data is empty condition evaluates to FALSE
+    // for node with title 'foo', action is not executed.
+    $node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article'));
+    $rule->execute($node);
+    $this->assertEqual($node->title, 'foo', "Data is empty condition evaluates to FALSE for node with title 'foo', action is not executed.");
+
+    // Data is empty condition evaluates to TRUE for the parent of a
+    // not existing term in the tags field of the node.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('node_is_of_type', array('type' => array('article')));
+    $rule->condition('data_is_empty', array('data:select' => 'node:field-tags:0:parent'));
+    $rule->action('data_set', array('data:select' => 'node:title', 'value' => 'bar'));
+    $rule->integrityCheck();
+    $node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article'));
+    $rule->execute($node);
+    $this->assertEqual($node->title, 'bar', "Data is empty condition evaluates to TRUE for not existing data structures");
+
+    // Test Action: Calculate a value.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->action('data_calc', array('input_1:select' => 'node:nid', 'op' => '*', 'input_2' => 2));
+    $rule->action('data_set', array('data:select' => 'node:title', 'value:select' => 'result'));
+    $rule->integrityCheck();
+    $rule->execute($node);
+    $this->assertEqual($node->title, $node->nid * 2, "Value has been calculated.");
+
+    // Test moving a date.
+    $action_set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
+    $action_set->action('data_calc', array('input_1:select' => 'date', 'op' => '+', 'input_2' => 3600))
+               ->action('data_set', array('data:select' => 'date', 'value:select' => 'result'));
+    $action_set->integrityCheck();
+    list($result) = $action_set->execute(REQUEST_TIME);
+    $this->assertEqual($result, REQUEST_TIME + 3600, 'Used data calculation action to move a date by an hour.');
+
+    // Test data type conversion action.
+    $set = rules_action_set(array('result' => array('type' => 'text', 'parameter' => FALSE)), array('result'));
+    $set->action('data_convert', array('type' => 'text', 'value:select' => 'site:login-url'));
+    $set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
+    list($result) = $set->execute();
+    $set->integrityCheck();
+    $this->assertEqual($result, url('user', array('absolute' => TRUE)), 'Converted URI to text.');
+
+    $set = rules_action_set(array(
+      'result' => array('type' => 'integer', 'parameter' => FALSE),
+      'source' => array('type' => 'text'),
+    ), array('result'));
+    $set->action('data_convert', array('type' => 'integer', 'value:select' => 'source'));
+    $set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
+    list($result) = $set->execute('9.4');
+    $this->assertEqual($result, 9, 'Converted decimal to integer using rounding.');
+
+    $set = rules_action_set(array(
+      'result' => array('type' => 'integer', 'parameter' => FALSE),
+      'source' => array('type' => 'text'),
+    ), array('result'));
+    $set->action('data_convert', array('type' => 'integer', 'value:select' => 'source', 'rounding_behavior' => 'down'));
+    $set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
+    list($result) = $set->execute('9.6');
+    $this->assertEqual($result, 9, 'Converted decimal to integer using rounding behavior down.');
+
+    $set = rules_action_set(array(
+      'result' => array('type' => 'integer', 'parameter' => FALSE),
+      'source' => array('type' => 'text'),
+    ), array('result'));
+    $set->action('data_convert', array('type' => 'integer', 'value:select' => 'source', 'rounding_behavior' => 'up'));
+    $set->action('data_set', array('data:select' => 'result', 'value:select' => 'conversion_result'));
+    list($result) = $set->execute('9.4');
+    $this->assertEqual($result, 10, 'Converted decimal to integer using rounding behavior up.');
+
+    // Test text matching condition.
+    $result = rules_condition('text_matches')->execute('my-text', 'text', 'contains');
+    $result2 = rules_condition('text_matches')->execute('my-text', 'tex2t', 'contains');
+    $this->assertTrue($result && !$result2, 'Text matching condition using operation contain evaluated.');
+
+    $result = rules_condition('text_matches')->execute('my-text', 'my', 'starts');
+    $result2 = rules_condition('text_matches')->execute('my-text', 'text', 'starts');
+    $this->assertTrue($result && !$result2, 'Text matching condition using operation starts evaluated.');
+
+    $result = rules_condition('text_matches')->execute('my-text', 'text', 'ends');
+    $result2 = rules_condition('text_matches')->execute('my-text', 'my', 'ends');
+    $this->assertTrue($result && !$result2, 'Text matching condition using operation ends evaluated.');
+
+    $result = rules_condition('text_matches')->execute('my-text', 'me?y-texx?t', 'regex');
+    $result2 = rules_condition('text_matches')->execute('my-text', 'me+y-texx?t', 'regex');
+    $this->assertTrue($result && !$result2, 'Text matching condition using operation regex evaluated.');
+  }
+
+  /**
+   * Tests entity related integration.
+   */
+  public function testEntityIntegration() {
+    global $user;
+
+    $page = $this->drupalCreateNode(array('type' => 'page'));
+    $article = $this->drupalCreateNode(array('type' => 'article'));
+
+    $result = rules_condition('entity_field_access')
+      ->execute(entity_metadata_wrapper('node', $article), 'field_tags');
+    $this->assertTrue($result);
+
+    // Test entity_is_of_bundle condition.
+    $result = rules_condition('entity_is_of_bundle', array(
+      'type' => 'node',
+      'bundle' => array('article'),
+    ))->execute(entity_metadata_wrapper('node', $page));
+    $this->assertFalse($result, 'Entity is of bundle condition has not been met.');
+    $result = rules_condition('entity_is_of_bundle', array(
+      'type' => 'node',
+      'bundle' => array('article'),
+    ))->execute(entity_metadata_wrapper('node', $article));
+    $this->assertTrue($result, 'Entity is of bundle condition has been met.');
+
+    // Also test a full rule so the integrity check must work.
+    $term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
+      'name' => $this->randomName(),
+      'vocabulary' => 1,
+    ))->save();
+    $rule = rule(array(
+      'node' => array('type' => 'node'),
+    ));
+    $rule->condition('entity_is_of_bundle', array(
+      'entity:select' => 'node',
+      'bundle' => array('article'),
+    ));
+    $rule->action('data_set', array('data:select' => 'node:field_tags', 'value' => array($term_wrapped->getIdentifier())));
+    $rule->integrityCheck();
+    $rule->execute($article);
+    $this->assertEqual($term_wrapped->getIdentifier(), $article->field_tags[LANGUAGE_NONE][0]['tid'], 'Entity is of bundle condition has been met.');
+
+    // Test again using an entity variable.
+    $article = $this->drupalCreateNode(array('type' => 'article'));
+    $rule = rule(array(
+      'entity' => array('type' => 'entity'),
+    ));
+    $rule->condition('entity_is_of_bundle', array(
+      'entity:select' => 'entity',
+      'type' => 'node',
+      'bundle' => array('article'),
+    ));
+    $rule->action('data_set', array('data:select' => 'entity:field_tags', 'value' => array($term_wrapped->getIdentifier())));
+    $rule->integrityCheck();
+    $rule->execute(entity_metadata_wrapper('node', $article));
+    $this->assertEqual($term_wrapped->getIdentifier(), $article->field_tags[LANGUAGE_NONE][0]['tid'], 'Entity is of bundle condition has been met.');
+
+    // Test CRUD actions.
+    $action = rules_action('entity_create', array(
+      'type' => 'node',
+      'param_type' => 'page',
+      'param_title' => 'foo',
+      'param_author' => $GLOBALS['user'],
+    ));
+    $action->access();
+    $action->execute();
+    $text = RulesLog::logger()->render();
+    $pos = strpos($text, RulesTestCase::t('Added the provided variable %entity_created of type %node', array('entity_created', 'node')));
+    $pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %entity_created of type %node.', array('entity_created', 'node')), $pos) : FALSE;
+    $this->assertTrue($pos !== FALSE, 'Data has been created and saved.');
+
+    $node = $this->drupalCreateNode(array('type' => 'page', 'sticky' => 0, 'status' => 0));
+    $rule = rule();
+    $rule->action('entity_fetch', array('type' => 'node', 'id' => $node->nid, 'entity_fetched:var' => 'node'));
+    $rule->action('entity_save', array('data:select' => 'node', 'immediate' => TRUE));
+    $rule->action('entity_delete', array('data:select' => 'node'));
+    $rule->access();
+    $rule->integrityCheck()->execute();
+
+    $text = RulesLog::logger()->render();
+    $pos = strpos($text, RulesTestCase::t('Evaluating the action %entity_fetch.', array('entity_fetch')));
+    $pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Added the provided variable %node of type %node', array('node')), $pos) : FALSE;
+    $pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Saved %node of type %node.', array('node')), $pos) : FALSE;
+    $pos = ($pos !== FALSE) ? strpos($text, RulesTestCase::t('Evaluating the action %entity_delete.', array('entity_delete')), $pos) : FALSE;
+    $this->assertTrue($pos !== FALSE, 'Data has been fetched, saved and deleted.');
+    // debug(RulesLog::logger()->render());
+
+    $node = entity_property_values_create_entity('node', array(
+      'type' => 'article',
+      'author' => $user,
+      'title' => 'foo',
+    ))->value();
+    $term_wrapped = entity_property_values_create_entity('taxonomy_term', array(
+      'name' => $this->randomName(),
+      'vocabulary' => 1,
+    ))->save();
+
+    // Test asserting the field and using it afterwards.
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->condition('entity_has_field', array('entity:select' => 'node', 'field' => 'field_tags'));
+    $rule->condition('entity_is_new', array('entity:select' => 'node'));
+    $rule->action('list_add', array('list:select' => 'node:field-tags', 'item' => $term_wrapped));
+    $rule->integrityCheck();
+    $rule->execute($node);
+
+    $tid = $term_wrapped->getIdentifier();
+    $this->assertEqual(array_values($node->field_tags[LANGUAGE_NONE]), array(0 => array('tid' => $tid)), 'Entity has field conditions evaluted.');
+
+    // Test loading a non-node entity.
+    $action = rules_action('entity_fetch', array('type' => 'taxonomy_term', 'id' => $tid));
+    list($term) = $action->execute();
+    $this->assertEqual($term->tid, $tid, 'Fetched a taxonomy term using "entity_fetch".');
+
+    // Test the entity is of type condition.
+    $rule = rule(array('entity' => array('type' => 'entity', 'label' => 'entity')));
+    $rule->condition('entity_is_of_type', array('type' => 'node'));
+    $rule->action('data_set', array('data:select' => 'entity:title', 'value' => 'bar'));
+    $rule->integrityCheck();
+    $rule->execute(entity_metadata_wrapper('node', $node));
+
+    $this->assertEqual(entity_metadata_wrapper('node', $node->nid)->title->value(), 'bar', 'Entity is of type condition correctly asserts the entity type.');
+
+    // Test the entity_query action.
+    $node = $this->drupalCreateNode(array('type' => 'page', 'title' => 'foo2'));
+    $rule = rule();
+    $rule->action('entity_query', array('type' => 'node', 'property' => 'title', 'value' => 'foo2'))
+         ->action('data_set', array('data:select' => 'entity_fetched:0:title', 'value' => 'bar'));
+    $rule->access();
+    $rule->integrityCheck();
+    $rule->execute();
+    $node = node_load($node->nid);
+    $this->assertEqual('bar', $node->title, 'Fetched a node by title and modified it.');
+
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests integration for the taxonomy module.
+   */
+  public function testTaxonomyIntegration() {
+    $term = entity_property_values_create_entity('taxonomy_term', array(
+      'name' => $this->randomName(),
+      'vocabulary' => 1,
+    ))->value();
+    $term2 = clone $term;
+    taxonomy_term_save($term);
+    taxonomy_term_save($term2);
+
+    $tags[LANGUAGE_NONE][0]['tid'] = $term->tid;
+    $node = $this->drupalCreateNode(array('title' => 'foo', 'type' => 'article', 'field_tags' => $tags));
+
+    // Test assigning and remove a term from an article.
+    $rule = rule(array('node' => array('type' => 'node', 'bundle' => 'article')));
+    $term_wrapped = rules_wrap_data($term->tid, array('type' => 'taxonomy_term'));
+    $term_wrapped2 = rules_wrap_data($term2->tid, array('type' => 'taxonomy_term'));
+    $rule->action('list_add', array('list:select' => 'node:field-tags', 'item' => $term_wrapped2));
+    $rule->action('list_remove', array('list:select' => 'node:field-tags', 'item' => $term_wrapped));
+    $rule->execute($node);
+    RulesLog::logger()->checkLog();
+    $this->assertEqual(array_values($node->field_tags[LANGUAGE_NONE]), array(0 => array('tid' => $term2->tid)), 'Term removed and added from a node.');
+
+    // Test using the taxonomy term reference field on a term object.
+    $field_name = drupal_strtolower($this->randomName() . '_field_name');
+    $field = field_create_field(array(
+      'field_name' => $field_name,
+      'type' => 'taxonomy_term_reference',
+      // Set cardinality to unlimited for tagging.
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+      'settings' => array(
+        'allowed_values' => array(
+          array(
+            'vocabulary' => 'tags',
+            'parent' => 0,
+          ),
+        ),
+      ),
+    ));
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'taxonomy_term',
+      'bundle' => 'tags', // Machine name of vocabulary.
+      'label' => $this->randomName() . '_label',
+      'description' => $this->randomName() . '_description',
+      'weight' => mt_rand(0, 127),
+      'widget' => array(
+        'type' => 'taxonomy_autocomplete',
+        'weight' => -4,
+      ),
+      'display' => array(
+        'default' => array(
+          'type' => 'taxonomy_term_reference_link',
+          'weight' => 10,
+        ),
+      ),
+    );
+    field_create_instance($instance);
+
+    $term1 = entity_property_values_create_entity('taxonomy_term', array(
+      'name' => $this->randomName(),
+      'vocabulary' => 1,
+    ))->save();
+    $term2 = entity_property_values_create_entity('taxonomy_term', array(
+      'name' => $this->randomName(),
+      'vocabulary' => 1,
+    ))->save();
+
+    // Test asserting the term reference field and using it afterwards.
+    $rule = rule(array('taxonomy_term' => array('type' => 'taxonomy_term')));
+    $rule->condition('entity_has_field', array('entity:select' => 'taxonomy-term', 'field' => $field_name));
+    // Add $term2 to $term1 using the term reference field.
+    $selector = str_replace('_', '-', 'taxonomy_term:' . $field_name);
+    $rule->action('list_add', array('list:select' => $selector, 'item' => $term2));
+    $rule->integrityCheck();
+    $rule->execute($term1);
+
+    RulesLog::logger()->checkLog();
+    $this->assertEqual($term1->{$field_name}[0]->getIdentifier(), $term2->getIdentifier(), 'Rule appended a term to the term reference field on a term.');
+
+    // Test an action set for merging term parents, which is provided as default
+    // config.
+    $term = entity_property_values_create_entity('taxonomy_term', array(
+      'name' => $this->randomName(),
+      'vocabulary' => 1,
+      'parent' => array($term1->value()),
+    ))->save();
+
+    $action = rules_action('component_rules_retrieve_term_parents');
+    list($parents) = $action->execute(array($term->getIdentifier()));
+    $this->assertTrue($parents[0]->tid == $term1->getIdentifier(), 'Invoked component to retrieve term parents.');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests integration for the node module.
+   */
+  public function testNodeIntegration() {
+    $tests = array(
+      array('node_unpublish', 'node_is_published', 'node_publish', 'status'),
+      array('node_make_unsticky', 'node_is_sticky', 'node_make_sticky', 'sticky'),
+      array('node_unpromote', 'node_is_promoted', 'node_promote', 'promote'),
+    );
+    $node = $this->drupalCreateNode(array('type' => 'page', 'status' => 1, 'sticky' => 1, 'promote' => 1));
+
+    foreach ($tests as $info) {
+      list($action1, $condition, $action2, $property) = $info;
+      rules_action($action1)->execute($node);
+
+      $node = node_load($node->nid, NULL, TRUE);
+      $this->assertFalse($node->$property, 'Action has permanently disabled node ' . $property);
+      $return = rules_condition($condition)->execute($node);
+      $this->assertFalse($return, 'Condition determines node ' . $property . ' is disabled.');
+
+      rules_action($action2)->execute($node);
+      $node = node_load($node->nid, NULL, TRUE);
+      $this->assertTrue($node->$property, 'Action has permanently enabled node ' . $property);
+      $return = rules_condition($condition)->execute($node);
+      $this->assertTrue($return, 'Condition determines node ' . $property . ' is enabled.');
+    }
+
+    $return = rules_condition('node_is_of_type', array('type' => array('page', 'article')))->execute($node);
+    $this->assertTrue($return, 'Condition determines node is of type page.');
+    $return = rules_condition('node_is_of_type', array('type' => array('article')))->execute($node);
+    $this->assertFalse($return, 'Condition determines node is not of type article.');
+
+    // Test auto saving of a new node after it has been inserted into the DB.
+    $rule = rules_reaction_rule();
+    $rand = $this->randomName();
+    $rule->event('node_insert')
+         ->action('data_set', array('data:select' => 'node:title', 'value' => $rand));
+    $rule->save('test');
+    $node = $this->drupalCreateNode();
+    $node = node_load($node->nid);
+    $this->assertEqual($node->title, $rand, 'Node title is correct.');
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests integration for the user module.
+   */
+  public function testUserIntegration() {
+    $rid = $this->drupalCreateRole(array('administer nodes'), 'foo');
+    $user = $this->drupalCreateUser();
+
+    // Test assigning a role with the list_add action.
+    $rule = rule(array('user' => array('type' => 'user')));
+    $rule->action('list_add', array('list:select' => 'user:roles', 'item' => $rid));
+    $rule->execute($user);
+    $this->assertTrue(isset($user->roles[$rid]), 'Role assigned to user.');
+
+    // Test removing a role with the list_remove action.
+    $rule = rule(array('user' => array('type' => 'user')));
+    $rule->action('list_remove', array('list:select' => 'user:roles', 'item' => $rid));
+    $rule->execute($user);
+    $this->assertTrue(!isset($user->roles[$rid]), 'Role removed from user.');
+
+    // Test assigning a role with user_add_role action.
+    $rule = rule(array('user' => array('type' => 'user')));
+    $rule->action('user_add_role', array('account:select' => 'user', 'roles' => array($rid)));
+    $rule->execute($user);
+
+    $user = user_load($user->uid, TRUE);
+    $result = rules_condition('user_has_role', array('roles' => array($rid)))->execute($user);
+    $this->assertTrue($result, 'Role assigned to user.');
+
+    // Test removing a role with the user_remove_role action.
+    $rule = rule(array('user' => array('type' => 'user')));
+    $rule->action('user_remove_role', array('account:select' => 'user', 'roles' => array($rid)));
+    $rule->execute($user);
+
+    $user = user_load($user->uid, TRUE);
+    $result = rules_condition('user_has_role', array('roles' => array($rid)))->execute($user);
+    $this->assertFalse($result, 'Role removed from user.');
+
+    // Test user blocking.
+    rules_action('user_block')->execute($user);
+    $user = user_load($user->uid, TRUE);
+    $this->assertTrue(rules_condition('user_is_blocked')->execute($user), 'User has been blocked.');
+
+    rules_action('user_unblock')->execute($user);
+    $user = user_load($user->uid, TRUE);
+    $this->assertFalse(rules_condition('user_is_blocked')->execute($user), 'User has been unblocked.');
+
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests integration for the php module.
+   */
+  public function testPHPIntegration() {
+    $node = $this->drupalCreateNode(array('title' => 'foo'));
+    $rule = rule(array('var_name' => array('type' => 'node')));
+    $rule->condition('php_eval', array('code' => 'return TRUE;'))
+         ->action('php_eval', array('code' => 'drupal_set_message("Executed-" . $var_name->title);'))
+         ->action('drupal_message', array('message' => 'Title: <?php echo $var_name->title; ?> Token: [var_name:title]'));
+
+    $rule->execute($node);
+    $rule->access();
+    RulesLog::logger()->checkLog();
+    $msg = drupal_get_messages();
+    $this->assertEqual(array_pop($msg['status']), "Title: foo Token: foo", 'PHP input evaluation has been applied.');
+    $this->assertEqual(array_pop($msg['status']), "Executed-foo", 'PHP code condition and action have been evaluated.');
+
+    // Test PHP data processor.
+    $rule = rule(array('var_name' => array('type' => 'node')));
+    $rule->action('drupal_message', array(
+      'message:select' => 'var_name:title',
+      'message:process' => array(
+        'php' => array('code' => 'return "Title: $value";'),
+      ),
+    ));
+    $rule->execute($node);
+    $rule->access();
+    RulesLog::logger()->checkLog();
+    $msg = drupal_get_messages();
+    $this->assertEqual(array_pop($msg['status']), "Title: foo", 'PHP data processor has been applied.');
+  }
+
+  /**
+   * Tests the "rules_core" integration.
+   */
+  public function testRulesCoreIntegration() {
+    // Make sure the date input evaluator evaluates properly using strtotime().
+    $node = $this->drupalCreateNode(array('title' => 'foo'));
+    $rule = rule(array('node' => array('type' => 'node')));
+    $rule->action('data_set', array('data:select' => 'node:created', 'value' => '+1 day'));
+
+    $rule->execute($node);
+    RulesLog::logger()->checkLog();
+    $node = node_load($node->nid, NULL, TRUE);
+    $now = RulesDateInputEvaluator::gmstrtotime('now');
+    // Tolerate a difference of a second.
+    $this->assertTrue(abs($node->created - $now - 86400) <= 1, 'Date input has been evaluated.');
+
+    // Test using a numeric offset.
+    $rule = rule(array('number' => array('type' => 'decimal')), array('number'));
+    $rule->action('data_set', array(
+      'data:select' => 'number',
+      'value:select' => 'number',
+      'value:process' => array(
+        'num_offset' => array('value' => 1),
+      ),
+    ));
+    $rule->integrityCheck();
+    list($result) = $rule->execute(10);
+    $this->assertTrue($result == 11, 'Numeric offset has been applied');
+
+    // Test using a date offset.
+    $set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
+    $set->action('data_set', array(
+      'data:select' => 'date',
+      'value:select' => 'date',
+      'value:process' => array(
+        'date_offset' => array('value' => 1000),
+      ),
+    ));
+    $date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
+    list($result) = $set->execute($date);
+    $this->assertEqual($result, $date + 1000, 'Date offset in seconds has been added.');
+
+    // Test using a negative offset of 2 months.
+    $set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
+    $set->action('data_set', array(
+      'data:select' => 'date',
+      'value:select' => 'date',
+      'value:process' => array(
+        'date_offset' => array('value' => - 86400 * 30 * 2),
+      ),
+    ));
+    $date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
+    list($result) = $set->execute($date);
+    $this->assertEqual($result, date_create("14 Jan 1984 10:19:23 +01:00")->format('U'), 'Date offset of -2 months has been added.');
+
+    // Test using a positive offset of 1 year 6 months and 30 minutes.
+    $set = rules_action_set(array('date' => array('type' => 'date')), array('date'));
+    $set->action('data_set', array(
+      'data:select' => 'date',
+      'value:select' => 'date',
+      'value:process' => array(
+        'date_offset' => array('value' => 86400 * 30 * 18 + 30 * 60),
+      ),
+    ));
+    $date = date_create("14 Mar 1984 10:19:23 +01:00")->format('U');
+    list($result) = $set->execute($date);
+    $this->assertEqual($result, date_create("14 Sep 1985 10:49:23 +01:00")->format('U'), 'Date offset of 1 year 6 months and 30 minutes has been added.');
+
+    RulesLog::logger()->checkLog();
+  }
+
+  /**
+   * Tests site/system integration.
+   */
+  public function testSystemIntegration() {
+    // Test using the 'site' variable.
+    $condition = rules_condition('data_is', array('data:select' => 'site:current-user:name', 'value' => $GLOBALS['user']->name));
+    $this->assertTrue($condition->execute(), 'Retrieved the current user\'s name.');
+    // Another test using a token replacement.
+    $condition = rules_condition('data_is', array('data:select' => 'site:current-user:name', 'value' => '[site:current-user:name]'));
+    $this->assertTrue($condition->execute(), 'Replaced the token for the current user\'s name.');
+
+    // Test breadcrumbs and drupal set message.
+    $rule = rules_reaction_rule();
+    $rule->event('init')
+         ->action('breadcrumb_set', array('titles' => array('foo'), 'paths' => array('bar')))
+         ->action('drupal_message', array('message' => 'A message.'));
+    $rule->save('test');
+
+    $this->drupalGet('node');
+    $this->assertLink('foo', 0, 'Breadcrumb has been set.');
+    $this->assertText('A message.', 'Drupal message has been shown.');
+
+    // Test the page redirect.
+    $node = $this->drupalCreateNode();
+    $rule = rules_reaction_rule();
+    $rule->event('node_view')
+         ->action('redirect', array('url' => 'user'));
+    $rule->save('test2');
+
+    $this->drupalGet('node/' . $node->nid);
+    $this->assertEqual($this->getUrl(), url('user', array('absolute' => TRUE)), 'Redirect has been issued.');
+
+    // Also test using a url including a fragment.
+    $actions = $rule->actions();
+    $actions[0]->settings['url'] = 'user#fragment';
+    $rule->save();
+
+    $this->drupalGet('node/' . $node->nid);
+    $this->assertEqual($this->getUrl(), url('user', array('absolute' => TRUE, 'fragment' => 'fragment')), 'Redirect has been issued.');
+
+    // Test sending mail.
+    $settings = array('to' => 'mail@example.com', 'subject' => 'subject', 'message' => 'hello.');
+    rules_action('mail', $settings)->execute();
+    $this->assertMail('to', 'mail@example.com', 'Mail has been sent.');
+    $this->assertMail('from', variable_get('site_mail', ini_get('sendmail_from')), 'Default from address has been used');
+
+    rules_action('mail', $settings + array('from' => 'sender@example.com'))->execute();
+    $this->assertMail('from', 'sender@example.com', 'Specified from address has been used');
+
+    // Test sending mail to all users of a role. First clear the mail
+    // collector to remove the mail sent in the previous line of code.
+    variable_set('drupal_test_email_collector', array());
+
+    // Now make sure there is a custom role and two users with that role.
+    $user1 = $this->drupalCreateUser(array('administer nodes'));
+    $roles = $user1->roles;
+    // Remove the authenticated role so we only use the new role created by
+    // drupalCreateUser().
+    unset($roles[DRUPAL_AUTHENTICATED_RID]);
+
+    // Now create a second user with the same role.
+    $user2 = $this->drupalCreateUser();
+    user_save($user2, array('roles' => $roles));
+
+    // Now create a third user without the same role - this user should NOT
+    // receive the role email.
+    $user3 = $this->drupalCreateUser(array('administer blocks'));
+    $additional_roles = $user3->roles;
+    unset($additional_roles[DRUPAL_AUTHENTICATED_RID]);
+
+    // Execute action and check that only two mails were sent.
+    rules_action('mail_to_users_of_role', $settings + array('roles' => array_keys($roles)))->execute();
+    $mails = $this->drupalGetMails();
+    $this->assertEqual(count($mails), 2, '2 e-mails were sent to users of a role.');
+
+    // Check each mail to ensure that only $user1 and $user2 got the mail.
+    $mail = array_pop($mails);
+    $this->assertTrue($mail['to'] == $user2->mail, 'Mail to user of a role has been sent.');
+    $mail = array_pop($mails);
+    $this->assertTrue($mail['to'] == $user1->mail, 'Mail to user of a role has been sent.');
+
+    // Execute action again, this time to send mail to both roles.
+    // This time check that three mails were sent - one for each user..
+    variable_set('drupal_test_email_collector', array());
+    rules_action('mail_to_users_of_role', $settings + array('roles' => array_keys($roles + $additional_roles)))->execute();
+    $mails = $this->drupalGetMails();
+    $this->assertEqual(count($mails), 3, '3 e-mails were sent to users of multiple roles.');
+
+    // Test reacting on new log entries and make sure the log entry is usable.
+    $rule = rules_reaction_rule();
+    $rule->event('watchdog');
+    $rule->action('drupal_message', array('message:select' => 'log_entry:message'));
+    $rule->integrityCheck()->save('test_watchdog');
+
+    watchdog('php', 'test %message', array('%message' => 'message'));
+    $msg = drupal_get_messages();
+    $this->assertEqual(array_pop($msg['status']), t('test %message', array('%message' => 'message')), 'Watchdog event occurred and log entry properties can be used.');
+  }
+
+  /**
+   * Tests the path module integration.
+   */
+  public function testPathIntegration() {
+    rules_action('path_alias')->execute('foo', 'bar');
+    $path = path_load('foo');
+    $this->assertTrue($path['alias'] == 'bar', 'URL alias has been created.');
+
+    $alias_exists = rules_condition('path_alias_exists', array('alias' => 'bar'))->execute();
+    $this->assertTrue($alias_exists, 'Created URL alias exists.');
+
+    $has_alias = rules_condition('path_has_alias', array('source' => 'foo'))->execute();
+    $this->assertTrue($has_alias, 'System path has an alias.');
+
+    // Test node alias action.
+    $node = $this->drupalCreateNode();
+    rules_action('node_path_alias')->execute($node, 'test');
+    $path = path_load("node/$node->nid");
+    $this->assertTrue($path['alias'] == 'test', 'Node URL alias has been created.');
+
+    // Test term alias action.
+    $term = entity_property_values_create_entity('taxonomy_term', array(
+      'name' => $this->randomName(),
+      'vocabulary' => 1,
+    ))->value();
+    rules_action('taxonomy_term_path_alias')->execute($term, 'term-test');
+    $path = path_load("taxonomy/term/$term->tid");
+    $this->assertTrue($path['alias'] == 'term-test', 'Term URL alias has been created.');
+
+    RulesLog::logger()->checkLog();
+  }
+
+}
+
+/**
+ * Tests event dispatcher functionality.
+ */
+class RulesEventDispatcherTestCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules event dispatchers',
+      'description' => 'Tests event dispatcher functionality.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('rules', 'rules_test');
+  }
+
+  /**
+   * Tests start and stop functionality.
+   */
+  public function testStartAndStop() {
+    $handler = rules_get_event_handler('rules_test_event');
+    $rule = rules_reaction_rule();
+    $rule->event('rules_test_event');
+
+    // The handler should not yet be watching.
+    $this->assertFalse($handler->isWatching());
+
+    // Once saved, the event cache rebuild should start the watcher.
+    $rule->save();
+    RulesEventSet::rebuildEventCache();
+    $this->assertTrue($handler->isWatching());
+
+    // Deleting should stop the watcher.
+    $rule->delete();
+    $this->assertFalse($handler->isWatching());
+  }
+
+  /**
+   * Tests start and stop functionality when used with multiple events.
+   */
+  public function testStartAndStopMultiple() {
+    $handler = rules_get_event_handler('rules_test_event');
+
+    // Initially, the task handler should not be watching.
+    $this->assertFalse($handler->isWatching());
+
+    // Set up five rules that all use the same event.
+    $rules = array();
+    foreach (array(1, 2, 3, 4, 5) as $key) {
+      $rules[$key] = rules_reaction_rule();
+      $rules[$key]->event('rules_test_event');
+      $rules[$key]->save();
+    }
+
+    // Once saved, the event cache rebuild should start the watcher.
+    RulesEventSet::rebuildEventCache();
+    $this->assertTrue($handler->isWatching());
+
+    // It should continue watching until all events are deleted.
+    foreach ($rules as $key => $rule) {
+      $rule->delete();
+      $this->assertEqual($key !== 5, $handler->isWatching());
+    }
+  }
+
+}
+
+/**
+ * Test early bootstrap Rules invocation.
+ */
+class RulesInvocationEnabledTestCase extends DrupalWebTestCase {
+
+  /**
+   * Declares test metadata.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rules invocation enabled',
+      'description' => 'Tests that Rules events are enabled during menu item loads.',
+      'group' => 'Rules',
+    );
+  }
+
+  /**
+   * Overrides DrupalWebTestCase::setUp().
+   */
+  protected function setUp() {
+    parent::setUp('dblog', 'rules', 'rules_test', 'rules_test_invocation');
+  }
+
+  /**
+   * Tests that a Rules event is triggered on node menu item loading.
+   *
+   * @see rules_test_invocation_node_load()
+   */
+  public function testInvocationOnNodeMenuLoading() {
+    // Create a test node.
+    $node = $this->drupalCreateNode(array('title' => 'Test'));
+    // Enable Rules logging on the INFO level so that entries are written to
+    // dblog.
+    variable_set('rules_log_errors', RulesLog::INFO);
+    // Create an empty rule that will fire in our node load hook.
+    $rule = rules_reaction_rule();
+    $rule->event('rules_test_event');
+    $rule->save('test_rule');
+
+    // Visit the node page which should trigger the load hook.
+    $this->drupalGet('node/' . $node->nid);
+    $result = db_query("SELECT * FROM {watchdog} WHERE type = 'rules' AND message = 'Reacting on event %label.'")->fetch();
+    $this->assertFalse(empty($result), 'Rules event was triggered and logged.');
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/tests/rules_test.info b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.info
new file mode 100644
index 0000000000000000000000000000000000000000..740f0a62868c36978d1fae2db3fc38e95289b8d1
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.info
@@ -0,0 +1,12 @@
+name = "Rules Tests"
+description = "Support module for the Rules tests."
+package = Testing
+core = 7.x
+files[] = rules_test.rules.inc
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2019-01-24
+version = "7.x-2.12"
+core = "7.x"
+project = "rules"
+datestamp = "1548305586"
diff --git a/profiles/wcm_base/modules/contrib/rules/tests/rules_test.module b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.module
new file mode 100644
index 0000000000000000000000000000000000000000..4c54fafe748c7d8326386f405b505cc0d7f590a3
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.module
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Rules test module.
+ */
+
+/**
+ * Implements hook_entity_property_info_alter() to add a property without
+ * access.
+ */
+function rules_test_entity_property_info_alter(&$info) {
+  $properties =& $info['site']['properties'];
+
+  $properties['no_access_user'] = array(
+    'label' => t("Logged in user"),
+    'description' => t("The currently logged in user."),
+    'getter callback' => 'entity_metadata_system_get_properties',
+    'access callback' => 'rules_test_no_access',
+    'type' => 'user',
+  );
+
+  $properties =& $info['node']['properties'];
+
+  $properties['reference'] = array(
+    'label' => t("Referenced entity"),
+    'getter callback' => 'rules_test_get_referenced_entity',
+    'type' => 'entity',
+  );
+  $properties['ref_nodes'] = array(
+    'label' => t("Referenced nodes"),
+    'getter callback' => 'rules_test_get_referenced_node',
+    'type' => 'list<node>',
+  );
+}
+
+/**
+ * Getter callback to get the referenced-entity property.
+ */
+function rules_test_get_referenced_entity($node) {
+  // For testing purposes we just return the node itself as property value.
+  return entity_metadata_wrapper('node', $node);
+}
+
+/**
+ * Getter callback to get the referenced-node list-property.
+ */
+function rules_test_get_referenced_node($node) {
+  // For testing purposes we just return the node itself as property value.
+  return array($node->nid);
+}
+
+/**
+ * Access callback. Returns TRUE except when $op == 'edit'.
+ */
+function rules_test_no_access($op) {
+  return $op == 'edit' ? FALSE : TRUE;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/tests/rules_test.rules.inc b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..3f302f46368f278cd6a5a9aa4b9457f9b415b751
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.rules.inc
@@ -0,0 +1,382 @@
+<?php
+
+/**
+ * @file
+ * Includes any rules integration provided by the module.
+ */
+
+/**
+ * Implements hook_rules_event_info().
+ */
+function rules_test_rules_event_info() {
+  return array(
+    'rules_test_event' => array(
+      'label' => t('Test event'),
+      'class' => 'RulesTestEventHandler',
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_file_info().
+ */
+function rules_test_rules_file_info() {
+  return array('rules_test.test');
+}
+
+/**
+ * Implements hook_rules_condition_info().
+ */
+function rules_test_rules_condition_info() {
+  $items = array();
+  $defaults = array(
+    'parameter' => array(
+      'node' => array('type' => 'node', 'label' => t('Content')),
+    ),
+    'group' => t('Node'),
+  );
+  $items['rules_condition_content_is_type'] = array(
+    'label' => t('Content has type'),
+    'parameter' => array(
+      'node' => array('type' => 'node', 'label' => t('Content')),
+      'type' => array('type' => 'list<text>', 'label' => t('Content types')),
+    ),
+    'help' => t('Evaluates to TRUE, if the given content has one of the selected content types.'),
+  ) + $defaults;
+  $items['rules_condition_content_is_published'] = $defaults + array(
+    'label' => t('Content is published'),
+  );
+  $items['rules_test_condition_true'] = array(
+    'label' => t('Test condition returning true'),
+    'group' => t('Rules test'),
+  );
+  $items['rules_test_condition_false'] = array(
+    'label' => t('Test condition returning false'),
+    'group' => t('Rules test'),
+  );
+  $items['rules_test_condition_apostrophe'] = array(
+    'label' => t("Test use of an apostrophe (') in a condition label"),
+    'group' => t('Rules test'),
+  );
+  // A condition for testing passing entities wrapped.
+  $items['rules_test_condition_node_wrapped'] = array(
+    'label' => t('Content is published'),
+    'parameter' => array(
+      'node' => array(
+        'type' => 'node',
+        'label' => t('Content'),
+        'wrapped' => TRUE,
+      ),
+    ),
+    'group' => t('Node'),
+  );
+  return $items;
+}
+
+/**
+ * Condition implementation returning true.
+ */
+function rules_test_condition_true($settings, $state, $element) {
+  if (!$element instanceof RulesCondition) {
+    throw new Exception('Rules element has not been passed to condition.');
+  }
+  rules_log('condition true called');
+  return TRUE;
+}
+
+/**
+ * Condition implementation returning false.
+ */
+function rules_test_condition_false() {
+  rules_log('condition false called');
+  return FALSE;
+}
+
+/**
+ * Condition testing use of an apostrophe in a condition label.
+ *
+ * Specifically, we want to ensure that special characters do not show up as
+ * HTML-encoded in the user interface.
+ */
+function rules_test_condition_apostrophe($settings, $state, $element) {
+  if (!$element instanceof RulesCondition) {
+    throw new Exception('Rules element has not been passed to condition.');
+  }
+  rules_log('condition apostrophe called');
+  return TRUE;
+}
+
+/**
+ * Condition implementation receiving the node wrapped.
+ */
+function rules_test_condition_node_wrapped($wrapper) {
+  return $wrapper instanceof EntityMetadataWrapper;
+}
+
+/**
+ * Implements hook_rules_action_info().
+ */
+function rules_test_rules_action_info() {
+  $items['rules_test_action'] = array(
+    'label' => t('Test action'),
+    'group' => t('Rules test'),
+  );
+  return $items + array(
+    'rules_node_publish_action' => array(
+      'label' => t('Publish content, but do not save'),
+      'parameter' => array(
+        'node' => array('type' => 'node', 'label' => t('Content')),
+      ),
+      'callbacks' => array(
+        'help' => 'rules_test_custom_help',
+      ),
+      'base' => 'node_publish_action',
+    ),
+    'rules_node_publish_action_save' => array(
+      'label' => t('Publish content'),
+      'parameter' => array(
+        'node' => array(
+          'type' => 'node',
+          'label' => t('Content'),
+          'save' => TRUE,
+        ),
+      ),
+      'base' => 'node_publish_action',
+    ),
+    'rules_node_make_sticky_action' => array(
+      'label' => t('Make content sticky'),
+      'parameter' => array(
+        'node' => array(
+          'type' => 'node',
+          'label' => t('Content'),
+          'save' => TRUE,
+        ),
+      ),
+      'base' => 'node_make_sticky_action',
+    ),
+    // The same action again requiring a 'page' node.
+    'rules_node_page_make_sticky_action' => array(
+      'label' => t('Mage page content sticky'),
+      'parameter' => array(
+        'node' => array(
+          'type' => 'node',
+          'label' => t('Content'),
+          'save' => TRUE,
+          'bundles' => array('page'),
+        ),
+      ),
+      'base' => 'node_make_sticky_action',
+    ),
+    'rules_action_test_reference' => array(
+      'label' => t('Change argument passed by reference'),
+      'parameter' => array(
+         // For references working right, we need a data type with a wrapper.
+        'arg' => array('type' => 'test'),
+      ),
+    ),
+    'rules_action_load_node' => array(
+      'label' => t('Fetch content by id'),
+      'parameter' => array(
+        'nid' => array('type' => 'integer', 'label' => t('Content ID')),
+        'vid' => array(
+          'type' => 'integer',
+          'label' => t('Content Revision ID'),
+          'description' => t("If you want to fetch a specific revision, specify it's revision id. Else leave it empty to fetch the currently active revision."),
+          'optional' => TRUE,
+        ),
+      ),
+      'provides' => array(
+        'node_loaded' => array(
+          'type' => 'node',
+          'label' => t('Loaded content'),
+          'label callback' => 'rules_action_load_node_variable_label',
+        ),
+      ),
+      'group' => t('Node'),
+      'access callback' => 'rules_node_integration_access',
+    ),
+    'rules_action_delete_node' => array(
+      'label' => t('Delete content'),
+      'parameter' => array(
+        'node' => array('type' => 'node', 'label' => t('Content')),
+      ),
+      'group' => t('Node'),
+      'access callback' => 'rules_node_integration_access',
+    ),
+    // An action for testing named parameters.
+    'rules_action_node_set_title' => array(
+      'label' => t('Content'),
+      'parameter' => array(
+        'node' => array('type' => 'node', 'label' => t('Content')),
+        'title' => array('type' => 'text', 'label' => t('Text')),
+      ),
+      'named parameter' => TRUE,
+      'group' => t('Node'),
+      'access callback' => 'rules_node_integration_access',
+    ),
+    // Tests automatic saving with a non-entity data type.
+    'test_type_save' => array(
+      'base' => 'rules_test_type_save',
+      'label' => t('Save test type'),
+      'parameter' => array(
+        'node' => array(
+          'type' => 'rules_test_type',
+          'label' => t('Test content'),
+          'save' => TRUE,
+        ),
+      ),
+      'group' => t('Node'),
+    ),
+  );
+}
+
+/**
+ * Test action doing nothing exception logging it has been called.
+ */
+function rules_test_action() {
+  rules_log('action called');
+}
+
+/**
+ * Action for testing writing class-based actions.
+ */
+class RulesTestClassAction extends RulesActionHandlerBase {
+
+  /**
+   * Defines the action.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'rules_test_class_action',
+      'label' => t('Test class based action'),
+      'group' => t('Node'),
+      'parameter' => array(
+        'node' => array(
+          'type' => 'node',
+          'label' => t('Node'),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Executes the action.
+   */
+  public function execute($node) {
+    rules_log('Action called with node ' . $node->nid);
+  }
+
+}
+
+/**
+ * Implements hook_rules_data_info().
+ */
+function rules_test_rules_data_info() {
+  return array(
+    'rules_test_type' => array(
+      'label' => t('test type'),
+      'wrap' => TRUE,
+      'wrapper class' => 'RulesTestTypeWrapper',
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_data_info_alter().
+ */
+function rules_test_rules_data_info_alter(&$data_info) {
+  $data_info['log_entry']['creation callback'] = 'rules_action_data_create_array';
+}
+
+/**
+ * The custom wrapper class for the rules test type.
+ *
+ * For testing we internally just make use of nodes.
+ */
+class RulesTestTypeWrapper extends RulesIdentifiableDataWrapper implements RulesDataWrapperSavableInterface {
+
+  /**
+   * Overrides RulesIdentifiableDataWrapper::extractIdentifier().
+   */
+  protected function extractIdentifier($data) {
+    return $data->nid;
+  }
+
+  /**
+   * Overrides RulesIdentifiableDataWrapper::load().
+   */
+  protected function load($id) {
+    return node_load($id);
+  }
+
+  /**
+   * Implements RulesDataWrapperSavableInterface::save().
+   */
+  public function save() {
+    node_save($this->value());
+  }
+
+}
+
+/**
+ * Implements hook_rules_plugin_info().
+ */
+function rules_test_rules_plugin_info() {
+  return array(
+    'rules test container' => array(
+      'label' => t('Test container'),
+      'class' => 'RulesTestContainer',
+      'embeddable' => 'RulesActionContainer',
+    ),
+  );
+}
+
+/**
+ * Test container plugin.
+ */
+class RulesTestContainer extends RulesContainerPlugin {
+  protected $itemName = 'rules test container';
+
+  /**
+   * Evaluate the element on a given rules evaluation state.
+   */
+  public function evaluate(RulesState $state) {
+    // Do nothing.
+  }
+
+}
+
+/**
+ * Test event handler class.
+ */
+class RulesTestEventHandler extends RulesEventDefaultHandler implements RulesEventDispatcherInterface {
+
+  /**
+   * Name of the variable in which to store the state of the event handler.
+   *
+   * @var string
+   */
+  protected $variableName = 'rules_test_event_handler_watch';
+
+  /**
+   * Implements RulesEventDispatcherInterface::startWatching().
+   */
+  public function startWatching() {
+    variable_set($this->variableName, TRUE);
+  }
+
+  /**
+   * Implements RulesEventDispatcherInterface::stopWatching().
+   */
+  public function stopWatching() {
+    variable_set($this->variableName, FALSE);
+  }
+
+  /**
+   * Implements RulesEventDispatcherInterface::isWatching().
+   */
+  public function isWatching() {
+    return (bool) variable_get($this->variableName);
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/tests/rules_test.rules_defaults.inc b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.rules_defaults.inc
new file mode 100644
index 0000000000000000000000000000000000000000..8e9f0f137b63f76660e75ffe74a6052f38b9bd22
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.rules_defaults.inc
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * @file
+ * Includes any Rules integration provided by the module.
+ */
+
+/**
+ * Implements hook_default_rules_configuration().
+ */
+function rules_test_default_rules_configuration() {
+  $rule = rules_reaction_rule();
+  $rule->label = 'example default rule';
+  // Add rules tags.
+  $rule->tags = array('Admin', 'Tag2');
+  $rule->active = FALSE;
+  $rule->event('node_update')
+       ->condition(rules_condition('data_is', array('data:select' => 'node:status', 'value' => TRUE))->negate())
+       ->condition('data_is', array('data:select' => 'node:type', 'value' => 'page'))
+       ->action('drupal_message', array('message' => 'A node has been updated.'));
+
+  $configs['rules_test_default_1'] = $rule;
+
+  $action_set = rules_action_set(array('node' => array('type' => 'node', 'label' => 'Content')));
+  $action_set->action('node_publish');
+  $configs['rules_test_action_set'] = $action_set;
+
+  // Test providing a rule using an export.
+  $configs['rules_export_test'] = rules_import(_rules_export_get_test_export());
+
+  // An action set used to test merging term parents.
+  $configs['rules_retrieve_term_parents'] = rules_import('{ "rules_retrieve_term_parents" : {
+    "LABEL" : "Retrieve term parents",
+    "PLUGIN" : "action set",
+    "REQUIRES" : [ "rules" ],
+    "USES VARIABLES" : {
+      "terms" : { "label" : "Terms", "type" : "list\u003ctaxonomy_term\u003e" },
+      "term_parents" : {
+        "label" : "Term parents",
+        "type" : "list\u003ctaxonomy_term\u003e",
+        "parameter" : false
+      }
+    },
+    "ACTION SET" : [
+      { "LOOP" : {
+          "USING" : { "list" : [ "terms" ] },
+          "ITEM" : { "current_term" : "Current term" },
+          "DO" : [
+            { "LOOP" : {
+                "USING" : { "list" : [ "current-term:parent" ] },
+                "ITEM" : { "current_parent" : "Current parent" },
+                "DO" : [
+                  { "list_add" : {
+                      "list" : [ "term-parents" ],
+                      "item" : [ "current-parent" ],
+                      "unique" : 1
+                    }
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      }
+    ],
+    "PROVIDES VARIABLES" : [ "term_parents" ]
+  }
+}');
+
+  return $configs;
+}
+
+/**
+ * Defines the export of rule for testing import/export.
+ */
+function _rules_export_get_test_export() {
+  return '{ "rules_export_test" : {
+    "LABEL" : "Test import rule2",
+    "PLUGIN" : "reaction rule",
+    "WEIGHT" : "-1",
+    "ACTIVE" : false,
+    "OWNER" : "rules",
+    "TAGS" : [ "bar", "baz", "foo" ],
+    "REQUIRES" : [ "rules", "comment" ],
+    "ON" : { "comment_insert" : [] },
+    "IF" : [
+      { "OR" : [
+          { "NOT node_is_sticky" : { "node" : [ "comment:node" ] } },
+          { "node_is_of_type" : {
+              "node" : [ "comment:node" ],
+              "type" : { "value" : { "page" : "page" } }
+            }
+          },
+          { "NOT AND" : [ { "OR" : [] } ] }
+        ]
+      }
+    ],
+    "DO" : [
+      { "data_set" : {
+          "data" : [ "comment:node:created" ],
+          "value" : { "select" : "site:current-date", "date_offset" : { "value" : -604800 } }
+        }
+      },
+      { "node_make_sticky" : { "node" : [ "comment:node" ] } },
+      { "variable_add" : {
+          "USING" : { "type" : "token", "value" : "error" },
+          "PROVIDE" : { "variable_added" : { "level" : "Error level" } }
+        }
+      },
+      { "drupal_message" : {
+          "message" : "fein, [comment:node:title] has been made sticky!",
+          "type" : [ "level" ]
+        }
+      },
+      { "LOOP" : {
+          "USING" : { "list" : [ "site:current-user:roles" ] },
+          "ITEM" : { "current_role" : "Current role" },
+          "DO" : [ { "drupal_message" : { "message" : [ "current-role" ] } } ]
+        }
+      }
+    ]
+  }
+}';
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/tests/rules_test.test.inc b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.test.inc
new file mode 100644
index 0000000000000000000000000000000000000000..7c242438bb4a14d83bd8ca917a601769b8f34bfa
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/tests/rules_test.test.inc
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Include file for testing file inclusion.
+ */
+
+/**
+ * Extender for the node data type.
+ */
+function rules_test_custom_node_save($object) {
+  throw new RulesEvaluationException('Custom save method invoked.');
+}
+
+/**
+ * Custom help callback for the rules_node_publish_action().
+ */
+function rules_test_custom_help() {
+  return 'custom';
+}
+
+/**
+ * Action callback.
+ */
+function rules_action_test_reference($data) {
+  $data['changed'] = TRUE;
+  return array('arg' => $data);
+}
+
+/**
+ * Condition: Check for selected content types.
+ */
+function rules_condition_content_is_type($node, $type) {
+  return in_array($node->type, $type);
+}
+
+/**
+ * Condition: Check if the node is published.
+ */
+function rules_condition_content_is_published($node, $settings) {
+  return $node->status == 1;
+}
+
+/**
+ * Loads a node.
+ */
+function rules_action_load_node($nid, $vid = NULL) {
+  return array('node_loaded' => node_load($nid, $vid ? $vid : NULL));
+}
+
+/**
+ * Action "Delete a node".
+ */
+function rules_action_delete_node($node) {
+  node_delete($node->nid);
+}
+
+/**
+ * An action making use of named parameters.
+ */
+function rules_action_node_set_title($arguments) {
+  // Make sure the data is unwrapped.
+  if ($arguments['node'] instanceof EntityMetadataWrapper) {
+    throw new Exception('Argument has not been correctly unwrapped.');
+  }
+  $arguments['node']->title = $arguments['title'];
+  return $arguments;
+}
+
+/**
+ * Action: Test saving - nothing to do here.
+ */
+function rules_test_type_save($node) {
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/tests/rules_test_invocation.info b/profiles/wcm_base/modules/contrib/rules/tests/rules_test_invocation.info
new file mode 100644
index 0000000000000000000000000000000000000000..22b53a8df3407058a2ee6bfdf1fdf175f523d8a3
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/tests/rules_test_invocation.info
@@ -0,0 +1,11 @@
+name = "Rules Test invocation"
+description = "Helper module to test Rules invocations."
+package = Testing
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2019-01-24
+version = "7.x-2.12"
+core = "7.x"
+project = "rules"
+datestamp = "1548305586"
diff --git a/profiles/wcm_base/modules/contrib/rules/tests/rules_test_invocation.module b/profiles/wcm_base/modules/contrib/rules/tests/rules_test_invocation.module
new file mode 100644
index 0000000000000000000000000000000000000000..5664c5eaf6c558d32046dc5093a3f338e96fb3f0
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/tests/rules_test_invocation.module
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file
+ * Helper module for Rules invocation testing.
+ */
+
+/**
+ * Implements hook_node_load().
+ */
+function rules_test_invocation_node_load($nodes, $types) {
+  rules_invoke_event('rules_test_event');
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/rules.autocomplete.js b/profiles/wcm_base/modules/contrib/rules/ui/rules.autocomplete.js
new file mode 100644
index 0000000000000000000000000000000000000000..a6ac99327696e259a060af619b7de1a6b0a57d14
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/rules.autocomplete.js
@@ -0,0 +1,202 @@
+
+// Registers the rules namespace.
+Drupal.rules = Drupal.rules || {};
+
+(function($) {
+  Drupal.behaviors.rules_autocomplete = {
+    attach: function(context) {
+      var autocomplete_settings = Drupal.settings.rules_autocomplete;
+
+      $('input.rules-autocomplete').once(function() {
+        var input = this;
+        new Drupal.rules.autocomplete(input, autocomplete_settings[$(input).attr('id')]);
+      });
+    }
+  };
+
+  /**
+   * Rules autocomplete object.
+   */
+  Drupal.rules.autocomplete = function(input, settings) {
+    this.id = settings.inputId;
+    this.uri = settings.source;
+    this.jqObject = $('#' + this.id);
+    this.cache = new Array();
+    this.jqObject.addClass('ui-corner-left');
+
+    this.opendByFocus = false;
+    this.focusOpens = true;
+    this.groupSelected = false;
+
+    this.button = $('<span>&nbsp;</span>');
+    this.button.attr( {
+      'tabIndex': -1,
+      'title': 'Show all items'
+    });
+    this.button.insertAfter(this.jqObject);
+
+    this.button.button( {
+      icons: {
+        primary: 'ui-icon-triangle-1-s'
+      },
+      text: false
+    });
+
+    // Don't round the left corners.
+    this.button.removeClass('ui-corner-all');
+    this.button.addClass('ui-corner-right ui-button-icon rules-autocomplete-button');
+
+    this.jqObject.autocomplete();
+    this.jqObject.autocomplete("option", "minLength", 0);
+    // Add a custom class, so we can style the autocomplete box without
+    // interfering with other jquery autocomplete widgets.
+    this.jqObject.autocomplete("widget").addClass('rules-autocomplete');
+
+    // Save the current rules_autocomplete object, so it can be used in
+    // handlers.
+    var instance = this;
+
+    // Event handlers
+    this.jqObject.focus(function() {
+      if (instance.focusOpens) {
+        instance.toggle();
+        instance.opendByFocus = true;
+      }
+      else {
+        instance.focusOpens = true;
+      }
+    });
+
+    // Needed when the window is closed but the textfield has the focus.
+    this.jqObject.click(function() {
+      // Since the focus event happens earlier then the focus event, we need to
+      // check here, if the window should be opened.
+      if (!instance.opendByFocus) {
+        instance.toggle();
+      }
+      else {
+        instance.opendByFocus = false;
+      }
+    });
+
+    this.jqObject.bind("autocompleteselect", function(event, ui) {
+      // If a group was selected then set the groupSelected to true for the
+      // overridden close function from jquery autocomplete.
+      if (ui.item.value.substring(ui.item.value.length - 1, ui.item.value.length) == ":") {
+        instance.groupSelected = true;
+      }
+      instance.focusOpens = false;
+      instance.opendByFocus = false;
+    });
+
+    this.jqObject.autocomplete("option", "source", function(request, response) {
+      if (request.term in instance.cache) {
+        response(instance.cache[request.term]);
+        return;
+      }
+      $.ajax( {
+        url: instance.uri + '/' + request.term,
+        dataType: "json",
+        success: function(data) {
+          instance.success(data, request, response);
+        }
+      });
+    });
+
+    // Newer versions of jQuery UI use element.data('ui-autocomplete'), older
+    // versions use element.data('autocomplete').
+    var autocompleteDataKey = typeof(this.jqObject.data('autocomplete')) === 'object' ? 'autocomplete' : 'ui-autocomplete';
+
+    // Since jquery autocomplete by default strips html text by using .text()
+    // we need our own _renderItem function to display html content.
+    this.jqObject.data(autocompleteDataKey)._renderItem = function(ul, item) {
+      return $("<li></li>").data("item.autocomplete", item).append("<a>" + item.label + "</a>").appendTo(ul);
+    };
+
+    // Override close function
+    this.jqObject.data(autocompleteDataKey).close = function (event) {
+      var value = this.element.val();
+      // If the selector is not a group, then trigger the close event an and
+      // hide the menu.
+      if (value === undefined || instance.groupSelected === false) {
+        clearTimeout(this.closing);
+        if (this.menu.element.is(":visible")) {
+          this._trigger("close", event);
+          this.menu.element.hide();
+          // Use deactivate for older versions of jQuery UI.
+          if (typeof(this.menu.deactivate) === 'function') {
+            this.menu.deactivate();
+          }
+        }
+      }
+      else {
+        // Else keep all open and trigger a search for the group.
+        instance.jqObject.autocomplete("search", instance.jqObject.val());
+        // After the suggestion box was opened again, we want to be able to
+        // close it.
+        instance.groupSelected = false;
+      }
+    };
+
+    this.button.click(function() {
+      instance.toggle();
+    });
+
+  };
+
+  /**
+   * Success function for Rules autocomplete object.
+   */
+  Drupal.rules.autocomplete.prototype.success = function(data, request, response) {
+    var list = new Array();
+    jQuery.each(data, function(index, value) {
+      list.push( {
+        label: value,
+        value: index
+      });
+    });
+
+    this.cache[request.term] = list;
+    response(list);
+  };
+
+  /**
+   * Open the autocomplete window.
+   * @param searchFor The term for will be searched for. If undefined then the
+   *                  entered input text will be used.
+   */
+  Drupal.rules.autocomplete.prototype.open = function(searchFor) {
+    // If searchFor is undefined, we want to search for the passed argument.
+    this.jqObject.autocomplete("search", ((searchFor === undefined) ? this.jqObject.val() : searchFor));
+    this.button.addClass("ui-state-focus");
+  };
+
+  /**
+   * Close the autocomplete window.
+   */
+  Drupal.rules.autocomplete.prototype.close = function() {
+    this.jqObject.autocomplete("close");
+    this.button.removeClass("ui-state-focus");
+  };
+
+  /**
+   * Toggle the autocomplete window.
+   */
+  Drupal.rules.autocomplete.prototype.toggle = function() {
+    if (this.jqObject.autocomplete("widget").is(":visible")) {
+      this.close();
+      this.focusOpens = true;
+    }
+    else {
+      var groups = this.jqObject.val().split(":");
+      var selector = "";
+      for (var i=0; i<groups.length-1; i++) {
+        selector = selector.concat(groups[i]) + ":";
+      }
+      this.focusOpens = false;
+      this.jqObject.focus();
+      this.open(selector);
+    }
+  };
+
+})(jQuery);
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/rules.debug.js b/profiles/wcm_base/modules/contrib/rules/ui/rules.debug.js
new file mode 100644
index 0000000000000000000000000000000000000000..f337bdcc35d4d563e11fd09bbc39c64f529d8d0a
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/rules.debug.js
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * Adds the collapsible functionality to the rules debug log.
+ */
+
+// Registers the rules namespace.
+Drupal.rules = Drupal.rules || {};
+
+(function($) {
+  Drupal.behaviors.rules_debug_log = {
+    attach: function(context) {
+      $('.rules-debug-open').click(function () {
+        var icon = $(this).children('span.ui-icon');
+        if ($(this).next().is(':hidden')) {
+          Drupal.rules.changeDebugIcon(icon, true);
+        }
+        else {
+          Drupal.rules.changeDebugIcon(icon, false);
+        }
+        $(this).next().toggle();
+      }).next().hide();
+
+      $('.rules-debug-open-main').click(function () {
+        var icon = $(this).children('span.ui-icon');
+        if ($(this).parent().next().is(':hidden')) {
+          Drupal.rules.changeDebugIcon(icon, true);
+          $(this).parent().children('.rules-debug-open-all').text(Drupal.t('-Close all-'));
+        }
+        else {
+          Drupal.rules.changeDebugIcon(icon, false);
+          $(this).parent().children('.rules-debug-open-all').text(Drupal.t('-Open all-'));
+        }
+        $(this).parent().next().toggle();
+      }).parent().next().hide();
+
+      $('.rules-debug-open-all').click(function() {
+        if ($('.rules-debug-open-main').parent().next().is(':hidden')) {
+          $('.rules-debug-open').next().show();
+          Drupal.rules.changeDebugIcon($('.rules-debug-open').children('span.ui-icon'), true);
+          $('.rules-debug-open-main').parent().next().show();
+          Drupal.rules.changeDebugIcon($(this).prev().children('span.ui-icon'), true);
+          $(this).text(Drupal.t('-Close all-'));
+        }
+        else {
+          $('.rules-debug-open-main').parent().next().hide();
+          Drupal.rules.changeDebugIcon($('.rules-debug-open-main').children('span.ui-icon'), false);
+          $(this).text(Drupal.t('-Open all-'));
+          $('.rules-debug-open').next().hide();
+          Drupal.rules.changeDebugIcon($(this).prev().children('span.ui-icon'), false);
+        }
+      });
+    }
+  };
+
+  /**
+   * Changes the icon of a collapsible div.
+   */
+  Drupal.rules.changeDebugIcon = function(item, open) {
+    if (open == true) {
+      item.removeClass('ui-icon-triangle-1-e');
+      item.addClass('ui-icon-triangle-1-s');
+    }
+    else {
+      item.removeClass('ui-icon-triangle-1-s');
+      item.addClass('ui-icon-triangle-1-e');
+    }
+  }
+})(jQuery);
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/rules.ui.css b/profiles/wcm_base/modules/contrib/rules/ui/rules.ui.css
new file mode 100644
index 0000000000000000000000000000000000000000..aa7d2aef8c0b444cd37db8fc09d8e0c0ebafa1d3
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/rules.ui.css
@@ -0,0 +1,198 @@
+@charset "utf-8";
+
+.rules-show-js,
+html.js .rules-hide-js {
+  display: none;
+}
+
+.rules-hide-js,
+html.js .rules-show-js {
+  display: block;
+}
+
+.rules-elements-table ul.action-links {
+  margin: 0px;
+  padding: 0;
+}
+
+.rules-elements-table ul.rules-operations li {
+  list-style: none;
+  float: left;
+}
+
+.rules-elements-table ul.rules-operations a {
+  background: none;
+  padding-left: 0px;
+}
+
+table tr.rules-elements-add {
+  background-color: #e5eff4;
+}
+
+.rules-elements-table ul.rules-operations-add a {
+  line-height: 1em;
+}
+
+
+tr.rules-elements-add td {
+  padding-top: 2px;
+  padding-bottom: 2px;
+}
+
+ul.rules-operations-add li {
+  float: left;
+  list-style-position: inside;
+}
+
+.rules-elements-table {
+  margin-bottom: 3em;
+}
+/* We cannot set a positive margin-top for rules tables as the table drag link
+   should be positioned directly on top of the table. Thus we use a large bottom
+   margin and fix the upper most margin: */
+#rules-form-wrapper:first-child {
+  margin-top: 1.5em;
+}
+
+/* Fix table drag weights to don't take extra space */
+.rules-elements-table .tabledrag-toggle-weight-wrapper {
+  position: absolute;
+  right: 0px;
+}
+
+.rules-elements-table caption,
+.rules-overview-table caption {
+  font-size: 110%;
+  font-weight: bold;
+  padding-bottom: 0.5em;
+  text-align: left;
+}
+
+.rules-overview-table {
+  margin: 1em 0;
+}
+
+.rules-content-group-integrity-error {
+  color: #df0101;
+}
+
+.rules-debug-log {
+  font: 81.3% "Lucida Grande","Lucida Sans Unicode",sans-serif;
+  background-color: #eeeeee;
+  border: 1px solid #cccccc;
+  color: #333333;
+  padding: 5px;
+  margin: 1.5em 0em;
+}
+
+.rules-debug-collapsible-link {
+  position: relative;
+  cursor: pointer;
+  /* The span element with the icon which opens the log, has a whitespace.
+     Since we don't want the user to mark this white space, we prevent this
+     using the this code.*/
+  -moz-user-select: -moz-none;
+  -khtml-user-select: none;
+  -webkit-user-select: none;
+  -o-user-select: none;
+  user-select: none;
+}
+
+.rules-debug-log-head {
+  font-weight: bold;
+}
+
+div.rules-debug-log-head {
+  margin: 0.5em 0em;
+}
+
+.rules-debug-icon-open {
+  position: relative;
+  float: left;
+}
+
+.rules-debug-open-all {
+  position: relative;
+  float: right;
+}
+
+.rules-debug-log ul {
+  padding-left: 2em;
+}
+
+.rules-debug-log .rules-debug-warn {
+  color: #df0101;
+}
+
+.rules-debug-log .rules-debug-error {
+  font-weight: bold;
+  color: #df0101;
+}
+
+#rules-filter-form {
+  margin-bottom: 1.5em;
+}
+
+.rules-parameter-label {
+  font-style: italic;
+}
+
+#rules-plugin-add-help {
+  margin-bottom: 1em;
+}
+
+.rules-element-content {
+  float: left;
+}
+
+form input.rules-switch-button {
+  -moz-border-radius: 5px 5px 5px 5px;
+  cursor: pointer;
+  font-size: 0.8em;
+  font-weight: normal;
+  margin-bottom: 1em;
+  padding: 2px;
+  text-align: center;
+}
+
+.rules-form-heading {
+  margin-top: 3em;
+}
+
+.rules-autocomplete-button {
+  top: 3px;
+  height: 22px;
+}
+
+ul.rules-autocomplete {
+  max-height: 23em;
+  overflow-y: auto;
+}
+
+ul.rules-autocomplete div {
+  padding-left: 5px;
+}
+
+ul.rules-autocomplete a.ui-corner-all {
+  padding: 0px;
+}
+
+ul.rules-autocomplete .rules-dsac-group {
+  background-color: #eee;
+}
+
+ul.rules-autocomplete .ui-corner-all {
+  -moz-border-radius: 0px;
+}
+
+/**
+ * Do not display the hide/show descriptions link above the permissions matrix.
+ */
+#rules-form-wrapper #edit-settings-access-permissions .compact-link {
+  display: none;
+}
+
+/* IE 6 hack for max-height. */
+* html ul.rule-autocomplete {
+  height: 23em;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/rules.ui.seven.css b/profiles/wcm_base/modules/contrib/rules/ui/rules.ui.seven.css
new file mode 100644
index 0000000000000000000000000000000000000000..e019d007eca3aed9033be2ac8eb70b3dbee159db
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/rules.ui.seven.css
@@ -0,0 +1,95 @@
+/**
+ * JQuery UI style sheet fix for the seven theme.
+ */
+
+.ui-button {
+  border: 1px solid #cccccc;
+  background: #e6e6e6;
+}
+
+.ui-state-hover,
+.ui-state-focus {
+  border: 1px solid #bbbbbb;
+}
+
+.ui-button.ui-state-active {
+  border: 1px solid #777777;
+  font-weight: bold;
+}
+
+/**
+ * Corner radius
+ */
+.ui-corner-tl {
+  -moz-border-radius-topleft: 4px;
+  -webkit-border-top-left-radius: 4px;
+  border-top-left-radius: 4px;
+}
+
+.ui-corner-tr {
+  -moz-border-radius-topright: 4px;
+  -webkit-border-top-right-radius: 4px;
+  border-top-right-radius: 4px;
+}
+
+.ui-corner-bl {
+  -moz-border-radius-bottomleft: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+}
+
+.ui-corner-br {
+  -moz-border-radius-bottomright: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+  border-bottom-right-radius: 4px;
+}
+
+.ui-corner-top {
+  -moz-border-radius-topleft: 4px;
+  -moz-border-radius-topright: 4px;
+  -webkit-border-top-left-radius: 4px;
+  -webkit-border-top-right-radius: 4px;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+}
+
+.ui-corner-bottom {
+  -moz-border-radius-bottomleft: 4px;
+  -moz-border-radius-bottomright: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+  border-bottom-left-radius: 4px;
+  border-bottom-right-radius: 4px;
+}
+
+.ui-corner-right {
+  -moz-border-radius-bottomright: 4px;
+  -moz-border-radius-topright: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+  -webkit-border-top-right-radius: 4px;
+  border-bottom-right-radius: 4px;
+  border-top-right-radius: 4px;
+}
+
+.ui-corner-left {
+  -moz-border-radius-bottomleft: 4px;
+  -moz-border-radius-topleft: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+  -webkit-border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+  border-top-left-radius: 4px;
+}
+
+.ui-corner-all {
+  -moz-border-radius: 4px;
+  -webkit-border-radius: 4px;
+  border-radius: 4px;
+}
+
+/**
+ * Fix the position of the core-autocomplete popup when shown in the settings
+ * fieldset.
+ */
+.form-item-settings-tags {
+  position: relative;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/ui.controller.inc b/profiles/wcm_base/modules/contrib/rules/ui/ui.controller.inc
new file mode 100644
index 0000000000000000000000000000000000000000..e2d1c1155f25beb0bbb3b34cca3b7abe1017892f
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/ui.controller.inc
@@ -0,0 +1,328 @@
+<?php
+
+/**
+ * @file
+ * Contains the UI controller for Rules.
+ */
+
+/**
+ * Controller class for the Rules UI.
+ *
+ * The Rules UI controller defines the methods other modules may use in order
+ * to easily re-use the UI, regardless whether the rules admin module is
+ * enabled.
+ */
+class RulesUIController {
+
+  /**
+   * Generates menu items to manipulate rules configurations.
+   *
+   * @param $base_path
+   *   The path to the overview page from where the configurations are edited.
+   */
+  public function config_menu($base_path) {
+    $items = array();
+    $base_count = count(explode('/', $base_path));
+    $items[$base_path . '/manage/%rules_config'] = array(
+      'title callback' => 'rules_get_title',
+      'title arguments' => array('Editing !plugin "!label"', $base_count + 1),
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_form_edit_rules_config', $base_count + 1, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'type' => MENU_VISIBLE_IN_BREADCRUMB,
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/add/%rules_element'] = array(
+      // Adding another part to the path would hit the menu path-part-limit
+      // for base paths like admin/config/workflow/rules. Therefore we have to
+      // use this ugly way for setting the title.
+      'title callback' => 'rules_menu_add_element_title',
+      // Wrap the integer in an array, so it is passed as is.
+      'title arguments' => array(array($base_count + 4)),
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_add_element', $base_count + 1, $base_count + 4, $base_count + 3, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'load arguments' => array($base_count + 1),
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/add/event'] = array(
+      'title callback' => 'rules_get_title',
+      'title arguments' => array('Adding event to !plugin "!label"', $base_count + 1),
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_add_event_page', $base_count + 1, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'load arguments' => array($base_count + 1),
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/delete/event'] = array(
+      // @todo Improve title.
+      'title' => 'Remove event',
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_remove_event', $base_count + 1, $base_count + 4, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'description' => 'Remove an event from a reaction rule.',
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/edit/%rules_element'] = array(
+      'title callback' => 'rules_get_title',
+      'title arguments' => array('Editing !plugin "!label"', $base_count + 3),
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_edit_element', $base_count + 1, $base_count + 3, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'load arguments' => array($base_count + 1),
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/autocomplete'] = array(
+      'page callback' => 'rules_ui_form_data_selection_auto_completion',
+      'page arguments' => array($base_count + 3, $base_count + 4, $base_count + 5),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'type' => MENU_CALLBACK,
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/delete/%rules_element'] = array(
+      'title callback' => 'rules_get_title',
+      'title arguments' => array('Editing !plugin "!label"', $base_count + 3),
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_delete_element', $base_count + 1, $base_count + 3, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'load arguments' => array($base_count + 1),
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/%'] = array(
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_form_rules_config_confirm_op', $base_count + 1, $base_count + 2, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/clone'] = array(
+      'title callback' => 'rules_get_title',
+      'title arguments' => array('Cloning !plugin "!label"', $base_count + 1),
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_form_clone_rules_config', $base_count + 1, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/export'] = array(
+      'title callback' => 'rules_get_title',
+      'title arguments' => array('Export of !plugin "!label"', $base_count + 1),
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_form_export_rules_config', $base_count + 1, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('view', $base_count + 1),
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    $items[$base_path . '/manage/%rules_config/execute'] = array(
+      'title callback' => 'rules_get_title',
+      'title arguments' => array('Executing !plugin "!label"', $base_count + 1),
+      'page callback' => 'drupal_get_form',
+      'page arguments' => array('rules_ui_form_execute_rules_config', $base_count + 1, $base_path),
+      'access callback' => 'rules_config_access',
+      'access arguments' => array('update', $base_count + 1),
+      'file' => 'ui/ui.forms.inc',
+      'file path' => drupal_get_path('module', 'rules'),
+    );
+    drupal_alter('rules_ui_menu', $items, $base_path, $base_count);
+
+    if (module_exists('rules_scheduler')) {
+      $items[$base_path . '/manage/%rules_config/schedule'] = array(
+        'title callback' => 'rules_get_title',
+        'title arguments' => array('Schedule !plugin "!label"', $base_count + 1),
+        'page callback' => 'drupal_get_form',
+        'page arguments' => array('rules_scheduler_schedule_form', $base_count + 1, $base_path),
+        'access callback' => 'rules_config_access',
+        'access arguments' => array('update', $base_count + 1),
+        'file' => 'rules_scheduler.admin.inc',
+        'file path' => drupal_get_path('module', 'rules_scheduler'),
+      );
+    }
+    return $items;
+  }
+
+  /**
+   * Generates the render array for an overview configuration table.
+   *
+   * Generates the render array for an overview configuration table for
+   * arbitrary rule configs that match the given conditions.
+   *
+   * Note: The generated overview table contains multiple links for editing the
+   * rule configurations. For the links to properly work use
+   * RulesUIController::config_menu($base_path) to generate appropriate menu
+   * items for the path at which the overview table is displayed.
+   *
+   * @param $conditions
+   *   An array of conditions as needed by rules_config_load_multiple().
+   * @param $options
+   *   An array with optional options. Known keys are:
+   *   - 'hide status op': If set to TRUE, enable/disable links are not added.
+   *     Defaults to FALSE.
+   *   - 'show plugin': If set to FALSE, the plugin is not shown. Defaults to
+   *     TRUE.
+   *   - 'show events': If set to TRUE, the event column is shown. Defaults to
+   *     TRUE if only reaction rules are listed.
+   *   - 'show execution op': If set to TRUE an operation for execution a
+   *     component is shown for components, as well as a link to schedule a
+   *     component if the rules scheduler module is enabled.
+   *   - 'base path': Optionally, a different base path to use instead of the
+   *     currently set RulesPluginUI::$basePath. If no base path has been set
+   *     yet, the current path is used by default.
+   *
+   * @return array
+   *   A renderable array.
+   */
+  public function overviewTable($conditions = array(), $options = array()) {
+    $options += array(
+      'hide status op' => FALSE,
+      'show plugin' => TRUE,
+      'show events' => isset($conditions['plugin']) && $conditions['plugin'] == 'reaction rule',
+      'show execution op' => !(isset($conditions['plugin']) && $conditions['plugin'] == 'reaction rule'),
+    );
+    // By default show only configurations owned by rules.
+    $conditions += array(
+      'owner' => 'rules',
+    );
+    if (!empty($options['base path'])) {
+      RulesPluginUI::$basePath = $options['base path'];
+    }
+    elseif (!isset(RulesPluginUI::$basePath)) {
+      // Default to the current path, only if no path has been set yet.
+      RulesPluginUI::$basePath = current_path();
+    }
+
+    $entities = entity_load('rules_config', FALSE, $conditions);
+    ksort($entities);
+
+    // Prepare some variables used by overviewTableRow().
+    $this->event_info = rules_fetch_data('event_info');
+    $this->cache = rules_get_cache();
+
+    $rows = array();
+    foreach ($entities as $id => $entity) {
+      if (user_access('bypass rules access') || $entity->access()) {
+        $rows[] = $this->overviewTableRow($conditions, $id, $entity, $options);
+      }
+    }
+    // Assemble the right table header.
+    $header = array(t('Name'), t('Event'), t('Plugin'), t('Status'), array('data' => t('Operations')));
+    if (!$options['show events']) {
+      // Remove the event heading as there is no such column.
+      unset($header[1]);
+    }
+    if (!$options['show plugin']) {
+      unset($header[2]);
+    }
+    // Fix the header operation column colspan.
+    $num_cols = isset($rows[0]) ? count($rows[0]) : 0;
+    if (($addition = $num_cols - count($header)) > 0) {
+      $header[4]['colspan'] = $addition + 1;
+    }
+
+    $table = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#empty' => t('None.'),
+    );
+    $table['#attributes']['class'][] = 'rules-overview-table';
+    $table['#attached']['css'][] = drupal_get_path('module', 'rules') . '/ui/rules.ui.css';
+
+    // @todo Hide configs where access() is FALSE.
+    return $table;
+  }
+
+  /**
+   * Generates the row for a single rules config.
+   *
+   * @param $additional_cols
+   *   Additional columns to be added after the entity label column.
+   */
+  protected function overviewTableRow($conditions, $name, $config, $options) {
+    // Build content includes the label, as well as a short overview including
+    // the machine name.
+    $row[] = array('data' => $config->buildContent());
+
+    // Add events if the configs are assigned to events.
+    if ($options['show events']) {
+      $events = array();
+      if ($config instanceof RulesTriggerableInterface) {
+        foreach ($config->events() as $event_name) {
+          $event_handler = rules_get_event_handler($event_name, $config->getEventSettings($event_name));
+          $events[] = $event_handler->summary();
+        }
+      }
+      $row[] = implode(", ", $events);
+    }
+    if ($options['show plugin']) {
+      $plugin = $config->plugin();
+      $row[] = isset($this->cache['plugin_info'][$plugin]['label']) ? $this->cache['plugin_info'][$plugin]['label'] : $plugin;
+    }
+
+    $row[] = array('data' => array(
+      '#theme' => 'entity_status',
+      '#status' => $config->status,
+    ));
+
+    // Add operations depending on the options and the exportable status.
+    if (!$config->hasStatus(ENTITY_FIXED)) {
+      $row[] = l(t('edit'), RulesPluginUI::path($name), array('attributes' => array('class' => array('edit', 'action'))));
+      if (module_exists('rules_i18n')) {
+        $row[] = l(t('translate'), RulesPluginUI::path($name, 'translate'), array('attributes' => array('class' => array('translate', 'action'))));
+      }
+    }
+    else {
+      $row[] = '';
+      if (module_exists('rules_i18n')) {
+        $row[] = '';
+      }
+    }
+
+    if (!$options['hide status op']) {
+      // Add either an enable or disable link.
+      $text = $config->active ? t('disable') : t('enable');
+      $active_class = $config->active ? 'disable' : 'enable';
+      $link_path = RulesPluginUI::path($name, $active_class);
+      $row[] = $config->hasStatus(ENTITY_FIXED) ? '' : l($text, $link_path, array('attributes' => array('class' => array($active_class, 'action')), 'query' => drupal_get_destination()));
+    }
+    $row[] = l(t('clone'), RulesPluginUI::path($name, 'clone'), array('attributes' => array('class' => array('clone', 'action'))));
+
+    // Add execute link for for components.
+    if ($options['show execution op']) {
+      $row[] = ($config instanceof RulesTriggerableInterface) ? '' : l(t('execute'), RulesPluginUI::path($name, 'execute'), array('attributes' => array('class' => array('execute', 'action')), 'query' => drupal_get_destination()));
+      if (module_exists('rules_scheduler')) {
+        // Add schedule link for action components only.
+        $row[] = $config instanceof RulesActionInterface ? l(t('schedule'), RulesPluginUI::path($name, 'schedule'), array('attributes' => array('class' => array('schedule', 'action')), 'query' => drupal_get_destination())) : '';
+      }
+    }
+
+    if (!$config->hasStatus(ENTITY_IN_CODE) && !$config->hasStatus(ENTITY_FIXED)) {
+      $row[] = l(t('delete'), RulesPluginUI::path($name, 'delete'), array('attributes' => array('class' => array('delete', 'action')), 'query' => drupal_get_destination()));
+    }
+    elseif ($config->hasStatus(ENTITY_OVERRIDDEN) && !$config->hasStatus(ENTITY_FIXED)) {
+      $row[] = l(t('revert'), RulesPluginUI::path($name, 'revert'), array('attributes' => array('class' => array('revert', 'action')), 'query' => drupal_get_destination()));
+    }
+    else {
+      $row[] = '';
+    }
+    $row[] = l(t('export'), RulesPluginUI::path($name, 'export'), array('attributes' => array('class' => array('export', 'action'))));
+    return $row;
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/ui.core.inc b/profiles/wcm_base/modules/contrib/rules/ui/ui.core.inc
new file mode 100644
index 0000000000000000000000000000000000000000..abffb0dcb00594cdfe9ecdc1bd8dc84a79262d50
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/ui.core.inc
@@ -0,0 +1,1297 @@
+<?php
+
+/**
+ * @file
+ * Contains core Rules UI functions.
+ */
+
+/**
+ * Plugin UI Interface.
+ */
+interface RulesPluginUIInterface {
+
+  /**
+   * Adds the whole configuration form of this rules configuration.
+   *
+   * For rule elements that are part of a configuration this method just adds
+   * the elements configuration form.
+   *
+   * @param $form
+   *   The form array where to add the form.
+   * @param $form_state
+   *   The current form state.
+   * @param $options
+   *   An optional array of options with the known keys:
+   *    - 'show settings': Whether to include the 'settings' fieldset for
+   *      editing configuration settings like the label or categories. Defaults
+   *      to FALSE.
+   *    - 'button': Whether a submit button should be added. Defaults to FALSE.
+   *    - 'init': Whether the element is about to be configured the first time
+   *      and the configuration is about to be initialized. Defaults to FALSE.
+   *    - 'restrict plugins: May be used to restrict the list of rules plugins
+   *      that may be added to this configuration. For that set an array of
+   *      valid plugins. Note that conditions and actions are always valid, so
+   *      just set an empty array for just allowing those.
+   *    - 'restrict conditions': Optionally set an array of condition names to
+   *      restrict the conditions that are available for adding.
+   *    - 'restrict actions': Optionally set an array of action names to
+   *      restrict the actions that are available to for adding.
+   *    - 'restrict events': Optionally set an array of event names to restrict
+   *      the events that are available for adding.
+   *
+   * @todo Implement the 'restrict *' options.
+   */
+  public function form(&$form, &$form_state, $options = array());
+
+  /**
+   * Validate the configuration form of this rule element.
+   *
+   * @param $form
+   * @param $form_state
+   */
+  public function form_validate($form, &$form_state);
+
+  /**
+   * Form submit handler for the element configuration form.
+   *
+   * Submit the configuration form of this rule element. This makes sure to
+   * put the updated configuration in the form state. For saving changes
+   * permanently, just call $config->save() afterwards.
+   *
+   * @param $form
+   * @param $form_state
+   */
+  public function form_submit($form, &$form_state);
+
+  /**
+   * Returns a structured array for rendering this element in overviews.
+   */
+  public function buildContent();
+
+  /**
+   * Returns the help text for editing this plugin.
+   */
+  public function help();
+
+  /**
+   * Returns ui operations for this element.
+   */
+  public function operations();
+
+}
+
+/**
+ * Helper object for mapping elements to ids.
+ */
+class RulesElementMap {
+
+  /**
+   * @var RulesPlugin
+   */
+  protected $configuration;
+  protected $index = array();
+  protected $counter = 0;
+
+  public function __construct(RulesPlugin $config) {
+    $this->configuration = $config->root();
+  }
+
+  /**
+   * Makes sure each element has an assigned id.
+   */
+  public function index() {
+    foreach ($this->getUnIndexedElements($this->configuration) as $element) {
+      $id = &$element->property('elementId');
+      $id = ++$this->counter;
+      $this->index[$id] = $element;
+    }
+  }
+
+  protected function getUnIndexedElements($element, &$unindexed = array()) {
+    // Remember unindexed elements.
+    $id = $element->property('elementId');
+    if (!isset($id)) {
+      $unindexed[] = $element;
+    }
+    else {
+      // Make sure $this->counter refers to the highest id.
+      if ($id > $this->counter) {
+        $this->counter = $id;
+      }
+      $this->index[$id] = $element;
+    }
+    // Recurse down the tree.
+    if ($element instanceof RulesContainerPlugin) {
+      foreach ($element as $child) {
+        $this->getUnIndexedElements($child, $unindexed);
+      }
+    }
+    return $unindexed;
+  }
+
+  /**
+   * Looks up the element with the given id.
+   */
+  public function lookup($id) {
+    if (!$this->index) {
+      $this->index();
+    }
+    return isset($this->index[$id]) ? $this->index[$id] : FALSE;
+  }
+
+}
+
+/**
+ * Faces UI extender for all kind of Rules plugins.
+ *
+ * Provides various useful methods for any rules UI.
+ */
+class RulesPluginUI extends FacesExtender implements RulesPluginUIInterface {
+
+  /**
+   * @var RulesPlugin
+   */
+  protected $element;
+
+  /**
+   * The base path determines where a Rules overview UI lives.
+   *
+   * All forms that want to display Rules (overview) forms need to set this
+   * variable. This is necessary in order to get correct operation links,
+   * paths, redirects, breadcrumbs etc. for the form() and overviewTable() methods.
+   *
+   * @see RulesUIController
+   * @see rules_admin_reaction_overview()
+   * @see rules_admin_components_overview()
+   */
+  public static $basePath = NULL;
+
+  /**
+   * Provide $this->element to make the code more meaningful.
+   */
+  public function __construct(FacesExtendable $object) {
+    parent::__construct($object);
+    $this->element = $object;
+  }
+
+  /**
+   * Returns the form values for the given form, possible being only a part of the whole form.
+   *
+   * In case the form is embedded somewhere, this function figures out the
+   * location of its form values and returns them for further use.
+   *
+   * @param $form
+   *   A form array, or an array of form elements to get the value for.
+   * @param $form_state
+   *   The form state as usual.
+   */
+  public static function &getFormStateValues($form, &$form_state) {
+    $values = NULL;
+    if (isset($form_state['values'])) {
+      // Assume the top level if parents are not yet set.
+      $form += array('#parents' => array());
+      $values = &$form_state['values'];
+      foreach ($form['#parents'] as $parent) {
+        $values = &$values[$parent];
+      }
+    }
+    return $values;
+  }
+
+  /**
+   * Implements RulesPluginUIInterface. Generates the element edit form.
+   *
+   * Note: Make sure that you set RulesPluginUI::$basePath before using this
+   * method, otherwise paths, links, redirects etc. won't be correct.
+   */
+  public function form(&$form, &$form_state, $options = array()) {
+    self::formDefaults($form, $form_state);
+    $form_state += array('rules_element' => $this->element);
+
+    // Add the help to the top of the form.
+    $help = $this->element->help();
+    $form['help'] = is_array($help) ? $help : array('#markup' => $help);
+
+    // We use $form_state['element_settings'] to store the settings of both
+    // parameter modes. That way one can switch between the parameter modes
+    // without losing the settings of those.
+    $form_state += array('element_settings' => $this->element->settings);
+    $settings = $this->element->settings + $form_state['element_settings'];
+
+    $form['parameter'] = array(
+      '#tree' => TRUE,
+    );
+
+    foreach ($this->element->pluginParameterInfo() as $name => $parameter) {
+      if ($parameter['type'] == 'hidden') {
+        continue;
+      }
+
+      $form['parameter'][$name] = array(
+        '#type' => 'fieldset',
+        '#title' => check_plain($parameter['label']),
+        '#description' => filter_xss(isset($parameter['description']) ? $parameter['description'] : ''),
+      );
+
+      // Init the parameter input mode.
+      $form_state['parameter_mode'][$name] = !isset($form_state['parameter_mode'][$name]) ? NULL : $form_state['parameter_mode'][$name];
+      $form['parameter'][$name] += $this->getParameterForm($name, $parameter, $settings, $form_state['parameter_mode'][$name]);
+    }
+
+    // Provide a form for editing the label and name of provided variables.
+    $settings = $this->element->settings;
+    foreach ($this->element->pluginProvidesVariables() as $var_name => $var_info) {
+      $form['provides'][$var_name] = array(
+        '#type' => 'fieldset',
+        '#title' => check_plain($var_info['label']),
+        '#description' => filter_xss(isset($var_info['description']) ? $var_info['description'] : ''),
+      );
+      $form['provides'][$var_name]['label'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Variable label'),
+        '#default_value' => isset($settings[$var_name . ':label']) ? $settings[$var_name . ':label'] : $var_info['label'],
+        '#required' => TRUE,
+      );
+      $form['provides'][$var_name]['var'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Variable name'),
+        '#default_value' => isset($settings[$var_name . ':var']) ? $settings[$var_name . ':var'] : $var_name,
+        '#description' => t('The variable name must contain only lowercase letters, numbers, and underscores and must be unique in the current scope.'),
+        '#element_validate' => array('rules_ui_element_machine_name_validate'),
+        '#required' => TRUE,
+      );
+    }
+    if (!empty($form['provides'])) {
+      $help = '<div class="description">' . t('Adjust the names and labels of provided variables, but note that renaming of already utilized variables invalidates the existing uses.') . '</div>';
+      $form['provides'] += array(
+        '#tree' => TRUE,
+        '#prefix' => '<h4 class="rules-form-heading">' . t('Provided variables') . '</h4>' . $help,
+      );
+    }
+
+    // Add settings form, if specified.
+    if (!empty($options['show settings'])) {
+      $this->settingsForm($form, $form_state);
+    }
+    // Add submit button, if specified.
+    if (!empty($options['button'])) {
+      $form['submit'] = array(
+        '#type' => 'submit',
+        '#value' => t('Save'),
+        '#weight' => 10,
+      );
+    }
+  }
+
+  /**
+   * Actually generates the parameter form for the given data type.
+   */
+  protected function getParameterForm($name, $info, $settings, &$mode) {
+    $class = $this->getDataTypeClass($info['type'], $info);
+    $supports_input_mode = in_array('RulesDataDirectInputFormInterface', class_implements($class));
+
+    // Init the mode.
+    if (!isset($mode)) {
+      if (isset($settings[$name . ':select'])) {
+        $mode = 'selector';
+      }
+      elseif (isset($settings[$name]) && $supports_input_mode) {
+        $mode = 'input';
+      }
+      elseif (isset($info['restriction'])) {
+        $mode = $info['restriction'];
+      }
+      else {
+        // Allow the parameter to define the 'default mode' and fallback to the
+        // data type default.
+        $mode = !empty($info['default mode']) ? $info['default mode'] : call_user_func(array($class, 'getDefaultMode'));
+      }
+    }
+
+    // For translatable parameters, pre-populate an internal translation source
+    // key so data type forms or input evaluators (i18n) may show a suitable
+    // help message.
+    if (drupal_multilingual() && !empty($info['translatable'])) {
+      $parameter = $this->element->pluginParameterInfo();
+      $info['custom translation language'] = !empty($parameter['language']);
+    }
+
+    // Add the parameter form.
+    if ($mode == 'input' && $supports_input_mode) {
+      $form['settings'] = call_user_func(array($class, 'inputForm'), $name, $info, $settings, $this->element);
+    }
+    else {
+      $form['settings'] = call_user_func(array($class, 'selectionForm'), $name, $info, $settings, $this->element);
+    }
+
+    // Add a link for switching the input mode when JS is enabled and a button
+    // to switch it without JavaScript, in case switching is possible.
+    if ($supports_input_mode && empty($info['restriction'])) {
+      $value = $mode == 'selector' ? t('Switch to the direct input mode') : t('Switch to data selection');
+
+      $form['switch_button'] = array(
+        '#type' => 'submit',
+        '#name' => 'param_' . $name,
+        '#attributes' => array('class' => array('rules-switch-button')),
+        '#parameter' => $name,
+        '#value' => $value,
+        '#submit' => array('rules_ui_parameter_replace_submit'),
+        '#ajax' => rules_ui_form_default_ajax('none'),
+        // Do not validate!
+        '#limit_validation_errors' => array(),
+      );
+    }
+    return $form;
+  }
+
+  /**
+   * Implements RulesPluginUIInterface.
+   */
+  public function form_validate($form, &$form_state) {
+    $this->form_extract_values($form, $form_state);
+    $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
+
+    if (isset($form_values['provides'])) {
+      $vars = $this->element->availableVariables();
+      foreach ($form_values['provides'] as $name => $values) {
+        if (isset($vars[$values['var']])) {
+          form_error($form['provides'][$name]['var'], t('The variable name %name is already taken.', array('%name' => $values['var'])));
+        }
+      }
+    }
+    // Settings have been updated, so process them now.
+    $this->element->processSettings(TRUE);
+
+    // Make sure the current user really has access to configure this element
+    // as well as the used input evaluators and data processors.
+    if (!user_access('bypass rules access') && !$this->element->root()->access()) {
+      form_set_error('', t('Access violation! You have insufficient access permissions to edit this configuration.'));
+    }
+    if (!empty($form['settings'])) {
+      $this->settingsFormValidate($form, $form_state);
+    }
+  }
+
+  /**
+   * Applies the values of the form to the element.
+   */
+  public function form_extract_values($form, &$form_state) {
+    $this->element->settings = array();
+    $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
+    if (isset($form_values['parameter'])) {
+      foreach ($form_values['parameter'] as $name => $values) {
+        $this->element->settings += $values['settings'];
+      }
+    }
+    if (isset($form_values['provides'])) {
+      foreach ($form_values['provides'] as $name => $values) {
+        $this->element->settings[$name . ':label'] = $values['label'];
+        $this->element->settings[$name . ':var'] = $values['var'];
+      }
+    }
+    if (!empty($form['settings'])) {
+      $this->settingsFormExtractValues($form, $form_state);
+    }
+  }
+
+  /**
+   * Implements RulesPluginUIInterface.
+   */
+  public function form_submit($form, &$form_state) {
+    if (!empty($form['settings'])) {
+      $this->settingsFormSubmit($form, $form_state);
+    }
+    $this->element->save();
+  }
+
+  /**
+   * Adds the configuration settings form (label, tags, description, ..).
+   */
+  public function settingsForm(&$form, &$form_state) {
+    $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
+    // Add the settings in a separate fieldset below.
+    $form['settings'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Settings'),
+      '#collapsible' => TRUE,
+      '#collapsed' => empty($form_values['settings']['vars']['more']),
+      '#weight' => 5,
+      '#tree' => TRUE,
+    );
+    $form['settings']['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Name'),
+      '#default_value' => $this->element->label(),
+      '#required' => TRUE,
+      '#weight' => -5,
+    );
+    // @todo For Drupal 8 use "owner" for generating machine names and
+    // module only for the modules providing default configurations.
+    if (!empty($this->element->module) && !empty($this->element->name) && $this->element->module == 'rules' && strpos($this->element->name, 'rules_') === 0) {
+      // Remove the Rules module prefix from the machine name.
+      $machine_name = substr($this->element->name, strlen($this->element->module) + 1);
+    }
+    else {
+      $machine_name = $this->element->name;
+    }
+    $form['settings']['name'] = array(
+      '#type' => 'machine_name',
+      '#default_value' => isset($machine_name) ? $machine_name : '',
+      // The string 'rules_' is pre-pended to machine names, so the
+      // maxlength must be less than the field length of 64 characters.
+      '#maxlength' => 58,
+      '#disabled' => entity_has_status('rules_config', $this->element, ENTITY_IN_CODE) && !(isset($form_state['op']) && $form_state['op'] == 'clone'),
+      '#machine_name' => array(
+        'exists' => 'rules_config_load',
+        'source' => array('settings', 'label'),
+      ),
+      '#required' => TRUE,
+      '#description' => t('The machine-readable name of this configuration is used by rules internally to identify the configuration. This name must contain only lowercase letters, numbers, and underscores and must be unique.'),
+    );
+    $form['settings']['tags'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Tags'),
+      '#default_value' => isset($this->element->tags) ? drupal_implode_tags($this->element->tags) : '',
+      '#autocomplete_path' => 'admin/config/workflow/rules/autocomplete_tags',
+      '#description' => t('Tags associated with this configuration, used for filtering in the admin interface. Separate multiple tags with commas.'),
+    );
+
+    // Show a form for editing variables for components.
+    if (($plugin_info = $this->element->pluginInfo()) && !empty($plugin_info['component'])) {
+      if ($this->element->hasStatus(ENTITY_IN_CODE)) {
+        $description = t('The variables used by the component. They can not be edited for configurations that are provided in code.');
+      }
+      else {
+        $description = t('Variables are normally input <em>parameters</em> for the component – data that should be available for the component to act on. Additionally, action components may <em>provide</em> variables back to the caller. Each variable must have a specified data type, a label and a unique machine readable name containing only lowercase alphanumeric characters and underscores. See <a href="@url">the online documentation</a> for more information about variables.',
+          array('@url' => rules_external_help('variables'))
+        );
+      }
+      $form['settings']['vars'] = array(
+        '#prefix' => '<div id="rules-component-variables">',
+        '#suffix' => '</div>',
+        '#tree' => TRUE,
+        '#element_validate' => array('rules_ui_element_variable_form_validate'),
+        '#theme' => 'rules_ui_variable_form',
+        '#title' => t('Variables'),
+        '#description' => $description,
+        // Variables can not be edited on configurations in code.
+        '#disabled' => $this->element->hasStatus(ENTITY_IN_CODE),
+      );
+
+      $weight = 0;
+      $provides = $this->element->providesVariables();
+      foreach ($this->element->componentVariables() as $name => $var_info) {
+        $form['settings']['vars']['items'][$name] = RulesPluginUI::getVariableForm($name, $var_info, isset($provides[$name]));
+        $form['settings']['vars']['items'][$name]['weight']['#default_value'] = $weight++;
+      }
+      // Always add three empty forms.
+      for ($i = 0; $i < 3; $i++) {
+        $form['settings']['vars']['items'][$i] = RulesPluginUI::getVariableForm();
+        $form['settings']['vars']['items'][$i]['weight']['#default_value'] = $weight++;
+      }
+      $form['settings']['vars']['more'] = array(
+        '#type' => 'submit',
+        '#value' => t('Add more'),
+        // Enable AJAX once #756762 is fixed.
+        // '#ajax' => rules_ui_form_default_ajax('none'),
+        '#limit_validation_errors' => array(array('vars')),
+        '#submit' => array('rules_form_submit_rebuild'),
+      );
+      if (!empty($this->element->id)) {
+        // Display a setting to manage access.
+        $form['settings']['access'] = array(
+          '#weight' => 50,
+        );
+        $plugin_type = $this->element instanceof RulesActionInterface ? t('action') : t('condition');
+        $form['settings']['access']['access_exposed'] = array(
+          '#type' => 'checkbox',
+          '#title' => t('Configure access for using this component with a permission.'),
+          '#default_value' => !empty($this->element->access_exposed),
+          '#description' => t('By default, the @plugin-type for using this component may be only used by users that have access to configure the component. If checked, access is determined by a permission instead.', array('@plugin-type' => $plugin_type)),
+        );
+        $form['settings']['access']['permissions'] = array(
+          '#type' => 'container',
+          '#states' => array(
+            'visible' => array(
+              ':input[name="settings[access][access_exposed]"]' => array('checked' => TRUE),
+            ),
+          ),
+        );
+        $form['settings']['access']['permissions']['matrix'] = $this->settingsFormPermissionMatrix();
+      }
+    }
+
+    // @todo Attach field form thus description.
+  }
+
+  /**
+   * Provides a matrix permission for the component based in the existing roles.
+   *
+   * @return
+   *   Form elements with the matrix of permissions for a component.
+   */
+  protected function settingsFormPermissionMatrix() {
+    $form['#theme'] = 'user_admin_permissions';
+    $status = array();
+    $options = array();
+
+    $role_names = user_roles();
+    $role_permissions = user_role_permissions($role_names);
+    $component_permission = rules_permissions_by_component(array($this->element));
+    $component_permission_name = key($component_permission);
+
+    $form['permission'][$component_permission_name] = array(
+      '#type' => 'item',
+      '#markup' => $component_permission[$component_permission_name]['title'],
+    );
+    $options[$component_permission_name] = '';
+    foreach ($role_names as $rid => $name) {
+      if (isset($role_permissions[$rid][$component_permission_name])) {
+        $status[$rid][] = $component_permission_name;
+      }
+    }
+
+    // Build the checkboxes for each role.
+    foreach ($role_names as $rid => $name) {
+      $form['checkboxes'][$rid] = array(
+        '#type' => 'checkboxes',
+        '#options' => $options,
+        '#default_value' => isset($status[$rid]) ? $status[$rid] : array(),
+        '#attributes' => array('class' => array('rid-' . $rid)),
+      );
+      $form['role_names'][$rid] = array('#markup' => check_plain($name), '#tree' => TRUE);
+    }
+
+    // Attach the default permissions page JavaScript.
+    $form['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.permissions.js';
+
+    return $form;
+  }
+
+  public function settingsFormExtractValues($form, &$form_state) {
+    $form_values = RulesPluginUI::getFormStateValues($form['settings'], $form_state);
+    $this->element->label = $form_values['label'];
+    // If the name was changed we have to redirect to the URL that contains
+    // the new name, instead of rebuilding on the old URL with the old name.
+    if ($form['settings']['name']['#default_value'] != $form_values['name']) {
+      $module = isset($this->element->module) ? $this->element->module : 'rules';
+      $this->element->name = $module . '_' . $form_values['name'];
+      $form_state['redirect'] = RulesPluginUI::path($this->element->name, 'edit', $this->element);
+    }
+    $this->element->tags = empty($form_values['tags']) ? array() : drupal_explode_tags($form_values['tags']);
+
+    if (isset($form_values['vars']['items'])) {
+      $vars = &$this->element->componentVariables();
+      $vars = array();
+      if ($this->element instanceof RulesActionContainer) {
+        $provides = &$this->element->componentProvidesVariables();
+        $provides = array();
+      }
+
+      usort($form_values['vars']['items'], 'rules_element_sort_helper');
+      foreach ($form_values['vars']['items'] as $item) {
+        if ($item['type'] && $item['name'] && $item['label']) {
+          $vars[$item['name']] = array('label' => $item['label'], 'type' => $item['type']);
+          if (!$item['usage'][0]) {
+            $vars[$item['name']]['parameter'] = FALSE;
+          }
+          if ($item['usage'][1] && isset($provides)) {
+            $provides[] = $item['name'];
+          }
+        }
+      }
+      // Disable FAPI persistence for the variable form so renumbering works.
+      $input = &$form_state['input'];
+      foreach ($form['settings']['#parents'] as $parent) {
+        $input = &$input[$parent];
+      }
+      unset($input['vars']);
+    }
+    $this->element->access_exposed = isset($form_values['access']['access_exposed']) ? $form_values['access']['access_exposed'] : FALSE;
+  }
+
+  public function settingsFormValidate($form, &$form_state) {
+    $form_values = RulesPluginUI::getFormStateValues($form['settings'], $form_state);
+    if ($form['settings']['name']['#default_value'] != $form_values['name'] && rules_config_load($this->element->name)) {
+      form_error($form['settings']['name'], t('The machine-readable name %name is already taken.', array('%name' => $form_values['name'])));
+    }
+  }
+
+  public function settingsFormSubmit($form, &$form_state) {
+    if (isset($form_state['values']['settings']['access']) && !empty($this->element->access_exposed)) {
+      // Save the permission matrix.
+      foreach ($form_state['values']['settings']['access']['permissions']['matrix']['checkboxes'] as $rid => $value) {
+        user_role_change_permissions($rid, $value);
+      }
+    }
+  }
+
+  /**
+   * Returns the form for configuring the info of a single variable.
+   */
+  public function getVariableForm($name = '', $info = array(), $provided = FALSE) {
+    $form['type'] = array(
+      '#type' => 'select',
+      '#options' => array(0 => '--') + RulesPluginUI::getOptions('data'),
+      '#default_value' => isset($info['type']) ? $info['type'] : 0,
+    );
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#size' => 40,
+      '#default_value' => isset($info['label']) ? $info['label'] : '',
+    );
+    $form['name'] = array(
+      '#type' => 'textfield',
+      '#size' => 40,
+      '#default_value' => $name,
+      '#element_validate' => array('rules_ui_element_machine_name_validate'),
+    );
+
+    $usage[0] = !isset($info['parameter']) || $info['parameter'] ? 1 : 0;
+    $usage[1] = $provided ? 1 : 0;
+
+    $form['usage'] = array(
+      '#type' => 'select',
+      '#default_value' => implode('', $usage),
+      '#options' => array(
+        '10' => t('Parameter'),
+        '11' => t('Parameter + Provided'),
+        '01' => t('Provided'),
+      ),
+    );
+    if ($this->element instanceof RulesConditionContainer) {
+      $form['usage']['#disabled'] = TRUE;
+    }
+
+    // Just set the weight #default_value for the returned form.
+    $form['weight'] = array(
+      '#type' => 'weight',
+    );
+    return $form;
+  }
+
+  /**
+   * Returns the name of class for the given data type.
+   *
+   * @param $data_type
+   *   The name of the data type
+   * @param $parameter_info
+   *   (optional) An array of info about the to be configured parameter. If
+   *   given, this array is complemented with data type defaults also.
+   */
+  public function getDataTypeClass($data_type, &$parameter_info = array()) {
+    $cache = rules_get_cache();
+    $data_info = $cache['data_info'];
+    // Add in data-type defaults.
+    if (empty($parameter_info['ui class'])) {
+      $parameter_info['ui class'] = (is_string($data_type) && isset($data_info[$data_type]['ui class'])) ? $data_info[$data_type]['ui class'] : 'RulesDataUI';
+    }
+    if (is_subclass_of($parameter_info['ui class'], 'RulesDataInputOptionsListInterface')) {
+      $parameter_info['options list'] = array($parameter_info['ui class'], 'optionsList');
+    }
+    return $parameter_info['ui class'];
+  }
+
+  /**
+   * Implements RulesPluginUIInterface.
+   *
+   * Shows a preview of the configuration settings.
+   */
+  public function buildContent() {
+    $config_name = $this->element->root()->name;
+    $content['label'] = array(
+      '#type' => 'link',
+      '#title' => $this->element->label(),
+      '#href' => $this->element->isRoot() ? RulesPluginUI::path($config_name) : RulesPluginUI::path($config_name, 'edit', $this->element),
+      '#prefix' => '<div class="rules-element-label">',
+      '#suffix' => '</div>',
+    );
+    // Put the elements below in a "description" div.
+    $content['description'] = array(
+      '#prefix' => '<div class="description">',
+    );
+    $content['description']['parameter'] = array(
+      '#caption' => t('Parameter'),
+      '#theme' => 'rules_content_group',
+    );
+    foreach ($this->element->pluginParameterInfo() as $name => $parameter) {
+      $element = array();
+      if (!empty($this->element->settings[$name . ':select'])) {
+        $element['content'] = array(
+          '#markup' => '[' . $this->element->settings[$name . ':select'] . ']',
+        );
+      }
+      elseif (isset($this->element->settings[$name]) && (!isset($parameter['default value']) || $parameter['default value'] != $this->element->settings[$name])) {
+        $class = $this->getDataTypeClass($parameter['type'], $parameter);
+        $method = empty($parameter['options list']) ? 'render' : 'renderOptionsLabel';
+        // We cannot use method_exists() here as it would trigger a PHP bug.
+        // @see https://www.drupal.org/node/1258284
+        $element = call_user_func(array($class, $method), $this->element->settings[$name], $name, $parameter, $this->element);
+      }
+      // Only add parameters that are really configured / not default.
+      if ($element) {
+        $content['description']['parameter'][$name] = array(
+          '#theme' => 'rules_parameter_configuration',
+          '#info' => $parameter,
+        ) + $element;
+      }
+    }
+    foreach ($this->element->providesVariables() as $name => $var_info) {
+      $content['description']['provides'][$name] = array(
+        '#theme' => 'rules_variable_view',
+        '#info' => $var_info,
+        '#name' => $name,
+      );
+    }
+    if (!empty($content['description']['provides'])) {
+      $content['description']['provides'] += array(
+        '#caption' => t('Provides variables'),
+        '#theme' => 'rules_content_group',
+      );
+    }
+    // Add integrity exception messages if there are any for this element.
+    try {
+      $this->element->integrityCheck();
+      // A configuration is still marked as dirty, but already works again.
+      if (!empty($this->element->dirty)) {
+        rules_config_update_dirty_flag($this->element);
+        $variables = array('%label' => $this->element->label(), '%name' => $this->element->name, '@plugin' => $this->element->plugin());
+        drupal_set_message(t('The @plugin %label (%name) was marked dirty, but passes the integrity check now and is active again.', $variables));
+        rules_clear_cache();
+      }
+    }
+    catch (RulesIntegrityException $e) {
+      $content['description']['integrity'] = array(
+        '#theme' => 'rules_content_group',
+        '#caption' => t('Error'),
+        '#attributes' => array('class' => array('rules-content-group-integrity-error')),
+        'error' => array(
+          '#markup' => filter_xss($e->getMessage()),
+        ),
+      );
+      // Also make sure the rule is marked as dirty.
+      if (empty($this->element->dirty)) {
+        rules_config_update_dirty_flag($this->element);
+        rules_clear_cache();
+      }
+    }
+
+    $content['#suffix'] = '</div>';
+    $content['#type'] = 'container';
+    $content['#attributes']['class'][] = 'rules-element-content';
+    return $content;
+  }
+
+  /**
+   * Implements RulesPluginUIInterface.
+   */
+  public function operations() {
+    $name = $this->element->root()->name;
+    $render = array(
+      '#theme' => 'links__rules',
+    );
+    $render['#attributes']['class'][] = 'rules-operations';
+    $render['#attributes']['class'][] = 'action-links';
+    $render['#links']['edit'] = array(
+      'title' => t('edit'),
+      'href' => RulesPluginUI::path($name, 'edit', $this->element),
+    );
+    $render['#links']['delete'] = array(
+      'title' => t('delete'),
+      'href' => RulesPluginUI::path($name, 'delete', $this->element),
+    );
+    return $render;
+  }
+
+  /**
+   * Implements RulesPluginUIInterface.
+   */
+  public function help() {}
+
+  /**
+   * Deprecated by the controllers overviewTable() method.
+   */
+  public static function overviewTable($conditions = array(), $options = array()) {
+    return rules_ui()->overviewTable($conditions, $options);
+  }
+
+  /**
+   * Generates an operation path.
+   *
+   * Generates a path using the given operation for the element with the given
+   * id of the configuration with the given name.
+   */
+  public static function path($name, $op = NULL, RulesPlugin $element = NULL, $parameter = FALSE) {
+    $element_id = isset($element) ? $element->elementId() : FALSE;
+    if (isset(self::$basePath)) {
+      $base_path = self::$basePath;
+    }
+    // Default to the paths used by 'rules_admin', so modules can easily re-use
+    // its UI.
+    else {
+      $base_path = isset($element) && $element instanceof RulesTriggerableInterface ? 'admin/config/workflow/rules/reaction' : 'admin/config/workflow/rules/components';
+    }
+
+    // Only append the '/manage' path if it is not already present.
+    if (substr($base_path, -strlen('/manage')) != '/manage') {
+      $base_path .= '/manage';
+    }
+
+    return implode('/', array_filter(array($base_path, $name, $op, $element_id, $parameter)));
+  }
+
+  /**
+   * Determines the default redirect target for an edited/deleted element.
+   *
+   * This is a parent element which is either a rule or the configuration root.
+   */
+  public static function defaultRedirect(RulesPlugin $element) {
+    while (!$element->isRoot()) {
+      if ($element instanceof Rule) {
+        return self::path($element->root()->name, 'edit', $element);
+      }
+      $element = $element->parentElement();
+    }
+    return self::path($element->name);
+  }
+
+  /**
+   * @see RulesUICategory::getOptions()
+   */
+  public static function getOptions($item_type, $items = NULL) {
+    return RulesUICategory::getOptions($item_type, $items = NULL);
+  }
+
+  public static function formDefaults(&$form, &$form_state) {
+    form_load_include($form_state, 'inc', 'rules', 'ui/ui.forms');
+    // Add our own css.
+    $form['#attached']['css'][] = drupal_get_path('module', 'rules') . '/ui/rules.ui.css';
+    // Workaround for problems with jquery css in seven theme and the core
+    // autocomplete.
+    if ($GLOBALS['theme'] == 'seven') {
+      $form['#attached']['css'][] = drupal_get_path('module', 'rules') . '/ui/rules.ui.seven.css';
+    }
+
+    // Specify the wrapper div used by #ajax.
+    $form['#prefix'] = '<div id="rules-form-wrapper">';
+    $form['#suffix'] = '</div>';
+
+    // Preserve the base path in the form state. The after build handler will
+    // set self::$basePath again for cached forms.
+    if (isset(self::$basePath)) {
+      $form_state['_rules_base_path'] = RulesPluginUI::$basePath;
+      $form['#after_build'][] = 'rules_form_after_build_restore_base_path';
+    }
+  }
+
+  public static function getTags() {
+    $result = db_select('rules_tags')
+      ->distinct()
+      ->fields('rules_tags', array('tag'))
+      ->groupBy('tag')
+      ->execute()
+      ->fetchCol('tag');
+    return drupal_map_assoc($result);
+  }
+
+}
+
+/**
+ * UI for abstract plugins (conditions & actions).
+ */
+class RulesAbstractPluginUI extends RulesPluginUI {
+
+  /**
+   * Overrides RulesPluginUI::form().
+   *
+   * Overridden to invoke the abstract plugins form alter callback and to add
+   * the negation checkbox for conditions.
+   */
+  public function form(&$form, &$form_state, $options = array()) {
+    parent::form($form, $form_state, $options);
+
+    if ($this->element instanceof RulesCondition) {
+      $form['negate'] = array(
+        '#title' => t('Negate'),
+        '#type' => 'checkbox',
+        '#description' => t('If checked, the condition result is negated such that it returns TRUE if it evaluates to FALSE.'),
+        '#default_value' => $this->element->isNegated(),
+        '#weight' => 5,
+      );
+    }
+    $this->element->call('form_alter', array(&$form, &$form_state, $options));
+  }
+
+  public function form_extract_values($form, &$form_state) {
+    parent::form_extract_values($form, $form_state);
+    $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
+    if ($this->element instanceof RulesCondition && isset($form_values['negate'])) {
+      $this->element->negate($form_values['negate']);
+    }
+  }
+
+  public function form_validate($form, &$form_state) {
+    parent::form_validate($form, $form_state);
+    // Validate the edited element and throw validation errors if it fails.
+    try {
+      $this->element->integrityCheck();
+    }
+    catch (RulesIntegrityException $e) {
+      form_set_error(implode('][', $e->keys), $e->getMessage());
+    }
+  }
+
+}
+
+/**
+ * UI for Rules Container.
+ */
+class RulesContainerPluginUI extends RulesPluginUI {
+
+  /**
+   * Generates a table for editing the contained elements.
+   */
+  public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
+    parent::form($form, $form_state, $options);
+    $form['elements'] = array(
+      // Hide during creation or for embedded elements.
+      '#access' => empty($options['init']) && $this->element->isRoot(),
+      '#tree' => TRUE,
+      '#theme' => 'rules_elements',
+      '#empty' => t('None'),
+      '#caption' => t('Elements'),
+    );
+    $form['elements']['#attributes']['class'][] = 'rules-container-plugin';
+
+    // Recurse over all element children or use the provided iterator.
+    $iterator = isset($iterator) ? $iterator : $this->element->elements();
+    $root_depth = $this->element->depth();
+    foreach ($iterator as $key => $child) {
+      $id = $child->elementId();
+
+      // Do not render rules as container element when displayed in a rule set.
+      $is_container = $child instanceof RulesContainerPlugin && !($child instanceof Rule);
+      $form['elements'][$id] = array(
+        '#depth' => $child->depth() - $root_depth - 1,
+        '#container' => $is_container,
+      );
+      $form['elements'][$id]['label'] = $child->buildContent();
+      $form['elements'][$id]['weight'] = array(
+        '#type' => 'weight',
+        '#default_value' => $child->weight,
+        '#delta' => 50,
+      );
+      $form['elements'][$id]['parent_id'] = array(
+        '#type' => 'hidden',
+        // If another iterator is passed in, the child parent may not equal
+        // the current element. Thus ask the child for its parent.
+        '#default_value' => $child->parentElement()->elementId(),
+      );
+      $form['elements'][$id]['element_id'] = array(
+        '#type' => 'hidden',
+        '#default_value' => $id,
+      );
+      $form['elements'][$id]['operations'] = $child->operations();
+    }
+
+    // Alter the submit button label.
+    if (!empty($options['button']) && !empty($options['init'])) {
+      $form['submit']['#value'] = t('Continue');
+    }
+    elseif (!empty($options['button']) && $this->element->isRoot()) {
+      $form['submit']['#value'] = t('Save changes');
+    }
+  }
+
+  /**
+   * Applies the values of the form to the given rule configuration.
+   */
+  public function form_extract_values($form, &$form_state) {
+    parent::form_extract_values($form, $form_state);
+    $values = RulesPluginUI::getFormStateValues($form, $form_state);
+    // Now apply the new hierarchy.
+    if (isset($values['elements'])) {
+      foreach ($values['elements'] as $id => $data) {
+        $child = $this->element->elementMap()->lookup($id);
+        $child->weight = $data['weight'];
+        $parent = $this->element->elementMap()->lookup($data['parent_id']);
+        $child->setParent($parent ? $parent : $this->element);
+      }
+      $this->element->sortChildren(TRUE);
+    }
+  }
+
+  public function operations() {
+    $ops = parent::operations();
+    $add_ops = self::addOperations();
+    $ops['#links'] += $add_ops['#links'];
+    return $ops;
+  }
+
+  /**
+   * Gets the Add-* operations for the given element.
+   */
+  public function addOperations() {
+    $name = $this->element->root()->name;
+    $render = array(
+      '#theme' => 'links__rules',
+    );
+    $render['#attributes']['class'][] = 'rules-operations-add';
+    $render['#attributes']['class'][] = 'action-links';
+    foreach (rules_fetch_data('plugin_info') as $plugin => $info) {
+      if (!empty($info['embeddable']) && $this->element instanceof $info['embeddable']) {
+        $render['#links']['add_' . $plugin] = array(
+          'title' => t('Add !name', array('!name' => $plugin)),
+          'href' => RulesPluginUI::path($name, 'add', $this->element, $plugin),
+        );
+      }
+    }
+    return $render;
+  }
+
+  public function buildContent() {
+    $content = parent::buildContent();
+    // Don't link the title for embedded container plugins, except for rules.
+    if (!$this->element->isRoot() && !($this->element instanceof Rule)) {
+      $content['label']['#markup'] = check_plain($content['label']['#title']);
+      unset($content['label']['#title']);
+    }
+    elseif ($this->element->isRoot()) {
+      $content['description']['settings'] = array(
+        '#theme' => 'rules_content_group',
+        '#weight' => -4,
+        'machine_name' => array(
+          '#markup' => t('Machine name') . ': ' . $this->element->name,
+        ),
+        'weight' => array(
+          '#access' => $this->element instanceof RulesTriggerableInterface,
+          '#markup' => t('Weight') . ': ' . $this->element->weight,
+        ),
+      );
+      if (!empty($this->element->tags)) {
+        $content['description']['tags'] = array(
+          '#theme' => 'rules_content_group',
+          '#caption' => t('Tags'),
+          'tags' => array(
+            '#markup' => check_plain(drupal_implode_tags($this->element->tags)),
+          ),
+        );
+      }
+      if ($vars = $this->element->componentVariables()) {
+        $content['description']['variables'] = array(
+          '#caption' => t('Parameter'),
+          '#theme' => 'rules_content_group',
+        );
+        foreach ($vars as $name => $info) {
+          if (!isset($info['parameter']) || $info['parameter']) {
+            $content['description']['variables'][$name] = array(
+              '#theme' => 'rules_variable_view',
+              '#info' => $info,
+              '#name' => $name,
+            );
+          }
+        }
+      }
+    }
+    return $content;
+  }
+
+}
+
+/**
+ * UI for Rules condition container.
+ */
+class RulesConditionContainerUI extends RulesContainerPluginUI {
+
+  public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
+    parent::form($form, $form_state, $options, $iterator);
+    // Add the add-* operation links.
+    $form['elements']['#add'] = self::addOperations();
+    $form['elements']['#attributes']['class'][] = 'rules-condition-container';
+    $form['elements']['#caption'] = t('Conditions');
+
+    // By default skip.
+    if (!empty($options['init']) && !$this->element->isRoot()) {
+      $config = $this->element->root();
+      $form['init_help'] = array(
+        '#type' => 'container',
+        '#id' => 'rules-plugin-add-help',
+        'content' => array(
+          '#markup' => t('You are about to add a new @plugin to the @config-plugin %label. Use indentation to make conditions a part of this logic group. See <a href="@url">the online documentation</a> for more information on condition sets.',
+            array('@plugin' => $this->element->plugin(),
+                  '@config-plugin' => $config->plugin(),
+                  '%label' => $config->label(),
+                  '@url' => rules_external_help('condition-components'))),
+        ),
+      );
+    }
+    $form['negate'] = array(
+      '#title' => t('Negate'),
+      '#type' => 'checkbox',
+      '#description' => t('If checked, the condition result is negated such that it returns TRUE if it evaluates to FALSE.'),
+      '#default_value' => $this->element->isNegated(),
+      '#weight' => 5,
+    );
+  }
+
+  public function form_extract_values($form, &$form_state) {
+    parent::form_extract_values($form, $form_state);
+    $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
+    if (isset($form_values['negate'])) {
+      $this->element->negate($form_values['negate']);
+    }
+  }
+
+}
+
+/**
+ * UI for Rules action container.
+ */
+class RulesActionContainerUI extends RulesContainerPluginUI {
+
+  public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
+    parent::form($form, $form_state, $options, $iterator);
+    // Add the add-* operation links.
+    $form['elements']['#add'] = self::addOperations();
+    $form['elements']['#attributes']['class'][] = 'rules-action-container';
+    $form['elements']['#caption'] = t('Actions');
+  }
+
+}
+
+/**
+ * Class holding category related methods.
+ */
+class RulesUICategory {
+
+  /**
+   * Gets info about all available categories, or about a specific category.
+   *
+   * @return array
+   */
+  public static function getInfo($category = NULL) {
+    $data = rules_fetch_data('category_info');
+    if (isset($category)) {
+      return $data[$category];
+    }
+    return $data;
+  }
+
+  /**
+   * Returns a group label, e.g. as usable for opt-groups in a select list.
+   *
+   * @param array $item_info
+   *   The info-array of an item, e.g. an entry of hook_rules_action_info().
+   * @param bool $in_category
+   *   (optional) Whether group labels for grouping inside a category should be
+   *   return. Defaults to FALSE.
+   *
+   * @return string|bool
+   *   The group label to use, or FALSE if none can be found.
+   */
+  public static function getItemGroup($item_info, $in_category = FALSE) {
+    if (isset($item_info['category']) && !$in_category) {
+      return self::getCategory($item_info, 'label');
+    }
+    elseif (!empty($item_info['group'])) {
+      return $item_info['group'];
+    }
+    return FALSE;
+  }
+
+  /**
+   * Gets the category for the given item info array.
+   *
+   * @param array $item_info
+   *   The info-array of an item, e.g. an entry of hook_rules_action_info().
+   * @param string|null $key
+   *   (optional) The key of the category info to return, e.g. 'label'. If none
+   *   is given the whole info array is returned.
+   *
+   * @return array|mixed|false
+   *   Either the whole category info array or the value of the given key. If
+   *   no category can be found, FALSE is returned.
+   */
+  public static function getCategory($item_info, $key = NULL) {
+    if (isset($item_info['category'])) {
+      $info = self::getInfo($item_info['category']);
+      return isset($key) ? $info[$key] : $info;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Returns an array of options to use with a select.
+   *
+   * Returns an array of options to use with a selectfor the items specified
+   * in the given hook.
+   *
+   * @param $item_type
+   *   The item type to get options for. One of 'data', 'event', 'condition' and
+   *   'action'.
+   * @param $items
+   *   (optional) An array of items to restrict the options to.
+   *
+   * @return array
+   *   An array of options.
+   */
+  public static function getOptions($item_type, $items = NULL) {
+    $sorted_data = array();
+    $ungrouped = array();
+    $data = $items ? $items : rules_fetch_data($item_type . '_info');
+    foreach ($data as $name => $info) {
+      // Verify the current user has access to use it.
+      if (!user_access('bypass rules access') && !empty($info['access callback']) && !call_user_func($info['access callback'], $item_type, $name)) {
+        continue;
+      }
+      if ($group = RulesUICategory::getItemGroup($info)) {
+        $sorted_data[drupal_ucfirst($group)][$name] = drupal_ucfirst($info['label']);
+      }
+      else {
+        $ungrouped[$name] = drupal_ucfirst($info['label']);
+      }
+    }
+    asort($ungrouped);
+    foreach ($sorted_data as $key => $choices) {
+      asort($choices);
+      $sorted_data[$key] = $choices;
+    }
+
+    // Sort the grouped data by category weights, defaulting to weight 0 for
+    // groups without a respective category.
+    $sorted_groups = array();
+    foreach (array_keys($sorted_data) as $label) {
+      $sorted_groups[$label] = array('weight' => 0, 'label' => $label);
+    }
+    // Add in category weights.
+    foreach (RulesUICategory::getInfo() as $info) {
+      if (isset($sorted_groups[$info['label']])) {
+        $sorted_groups[$info['label']] = $info;
+      }
+    }
+    uasort($sorted_groups, '_rules_ui_sort_categories');
+
+    // Now replace weights with group content.
+    foreach ($sorted_groups as $group => $weight) {
+      $sorted_groups[$group] = $sorted_data[$group];
+    }
+    return $ungrouped + $sorted_groups;
+  }
+
+}
+
+/**
+ * Helper for sorting categories.
+ */
+function _rules_ui_sort_categories($a, $b) {
+  // @see element_sort()
+  $a_weight = isset($a['weight']) ? $a['weight'] : 0;
+  $b_weight = isset($b['weight']) ? $b['weight'] : 0;
+  if ($a_weight == $b_weight) {
+    // @see element_sort_by_title()
+    $a_title = isset($a['label']) ? $a['label'] : '';
+    $b_title = isset($b['label']) ? $b['label'] : '';
+    return strnatcasecmp($a_title, $b_title);
+  }
+  return ($a_weight < $b_weight) ? -1 : 1;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/ui.data.inc b/profiles/wcm_base/modules/contrib/rules/ui/ui.data.inc
new file mode 100644
index 0000000000000000000000000000000000000000..3516587898e06f02323dc7e085af765f74184ba3
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/ui.data.inc
@@ -0,0 +1,702 @@
+<?php
+
+/**
+ * @file
+ * Contains data type related forms.
+ */
+
+/**
+ * Interface for data types providing a direct input form.
+ */
+interface RulesDataDirectInputFormInterface {
+
+  /**
+   * Constructs the direct input form.
+   *
+   * @return array
+   *   The direct input form.
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element);
+
+  /**
+   * Render the configured value.
+   *
+   * @return array
+   *   A renderable array.
+   */
+  public static function render($value);
+
+}
+
+/**
+ * Interface for data UI classes providing an options list.
+ */
+interface RulesDataInputOptionsListInterface extends RulesDataDirectInputFormInterface {
+
+  /**
+   * Returns the options list for the data type.
+   *
+   * For retrieving information about the used data type and parameter, the
+   * helper RulesDataUI::getTypeInfo() may be used as following:
+   * @code
+   *   list($type, $parameter_info) = RulesDataUI::getTypeInfo($element, $name);
+   * @endcode
+   *
+   * @param RulesPlugin $element
+   *   The rules element to get the options for.
+   * @param string $name
+   *   The name of the parameter for which to get options.
+   *
+   * @return array
+   *   An array of options as used by hook_options_list().
+   */
+  public static function optionsList(RulesPlugin $element, $name);
+
+}
+
+/**
+ * Default UI related class for data types.
+ */
+class RulesDataUI {
+
+  /**
+   * Specifies the default input mode per data type.
+   */
+  public static function getDefaultMode() {
+    return 'selector';
+  }
+
+  /**
+   * Provides the selection form for a parameter.
+   */
+  public static function selectionForm($name, $info, $settings, RulesPlugin $element) {
+    if (!isset($settings[$name . ':select'])) {
+      $settings[$name . ':select'] = '';
+      $vars = $element->availableVariables();
+      // Default to variables with the same name as the parameter.
+      if (isset($vars[$name])) {
+        $settings[$name . ':select'] = $name;
+      }
+      // If there is only one match, use it by default.
+      elseif (count($matches = RulesData::matchingDataSelector($vars, $info, '', 1, FALSE)) == 1) {
+        $settings[$name . ':select'] = rules_array_key($matches);
+      }
+    }
+    $form[$name . ':select'] = array(
+      '#type' => 'rules_data_selection',
+      '#title' => t('Data selector'),
+      '#default_value' => $settings[$name . ':select'],
+      '#required' => empty($info['optional']),
+      '#autocomplete_path' => RulesPluginUI::path($element->root()->name, 'autocomplete' . '/' . $name),
+      // Make the autocomplete textfield big enough so that it can display
+      // descriptions without word wraps.
+      '#size' => 75,
+      '#description' => t("The data selector helps you drill down into the data available to Rules. <em>To make entity fields appear in the data selector, you may have to use the condition 'entity has field' (or 'content is of type').</em> More useful tips about data selection is available in <a href='@url'>the online documentation</a>.",
+        array('@url' => rules_external_help('data-selection'))),
+    );
+
+    $cache = rules_get_cache();
+    $form['types_help'] = array(
+      '#theme' => 'rules_settings_help',
+      '#heading' => t('Data types'),
+    );
+    if ($info['type'] == '*') {
+      $type_labels[] = t('any');
+    }
+    else {
+      $types = is_array($info['type']) ? $info['type'] : array($info['type']);
+      $type_labels = array();
+      foreach ($types as $type) {
+        $type_labels[] = drupal_ucfirst(isset($cache['data_info'][$type]['label']) ? $cache['data_info'][$type]['label'] : $type);
+      }
+    }
+    $form['types_help']['#text'] = format_plural(count($type_labels), 'Select data of the type %types.', 'Select data of the types %types.', array('%types' => implode(', ', $type_labels)));
+
+    if (!empty($info['translatable'])) {
+      if (empty($info['custom translation language'])) {
+        $text = t('If a multilingual data source (i.e. a translatable field) is given, the argument is translated to the current interface language.');
+      }
+      else {
+        $text = t('If a multilingual data source (i.e. a translatable field) is given, the argument is translated to the configured language.');
+      }
+      $form['translation'] = array(
+        '#theme' => 'rules_settings_help',
+        '#text' => $text,
+        '#heading' => t('Translation'),
+      );
+    }
+    $form['help'] = array(
+      '#theme' => 'rules_data_selector_help',
+      '#variables' => $element->availableVariables(),
+      '#parameter' => $info,
+    );
+
+    // Add data processor.
+    $settings += array($name . ':process' => array());
+    $form[$name . ':process'] = array();
+    RulesDataProcessor::attachForm($form[$name . ':process'], $settings[$name . ':process'], $info, $element->availableVariables());
+    return $form;
+  }
+
+  /**
+   * Renders the value with a label if an options list is available.
+   *
+   * Used for data UI classes implementing the
+   * RulesDataDirectInputFormInterface.
+   *
+   * In case an options list is available, the the usual render() method won't
+   * be invoked, instead the selected entry is rendered via this method.
+   *
+   * @todo for Drupal 8: Refactor to avoid implementations have to care about
+   * option lists when generating the form, but not when rendering values.
+   */
+  public static function renderOptionsLabel($value, $name, $info, RulesPlugin $element) {
+    if (!empty($info['options list'])) {
+      $element->call('loadBasicInclude');
+      $options = entity_property_options_flatten(call_user_func($info['options list'], $element, $name));
+      if (!is_array($value) && isset($options[$value])) {
+        $value = $options[$value];
+      }
+      elseif (is_array($value)) {
+        foreach ($value as $key => $single_value) {
+          if (isset($options[$single_value])) {
+            $value[$key] = $options[$single_value];
+          }
+        }
+        $value = implode(', ', $value);
+      }
+      return array(
+        'content' => array('#markup' => check_plain($value)),
+        '#attributes' => array('class' => array('rules-parameter-options-entry')),
+      );
+    }
+  }
+
+  /**
+   * Returns the data type and parameter information for the given arguments.
+   *
+   * This helper may be used by options list callbacks operation at data-type
+   * level, see RulesDataInputOptionsListInterface.
+   */
+  public static function getTypeInfo(RulesPlugin $element, $name) {
+    $parameters = $element->pluginParameterInfo();
+    return array($parameters[$name]['type'], $parameters[$name]);
+  }
+
+}
+
+/**
+ * UI for textual data.
+ */
+class RulesDataUIText extends RulesDataUI implements RulesDataDirectInputFormInterface {
+
+  /**
+   * Overrides RulesDataUI::getDefaultMode().
+   */
+  public static function getDefaultMode() {
+    return 'input';
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    if (!empty($info['options list'])) {
+      // Make sure the .rules.inc of the providing module is included as the
+      // options list callback may reside there.
+      $element->call('loadBasicInclude');
+      $form[$name] = array(
+        '#type' => 'select',
+        '#options' => call_user_func($info['options list'], $element, $name),
+      );
+    }
+    else {
+      $form[$name] = array(
+        '#type' => 'textarea',
+        '#rows' => 3,
+      );
+      RulesDataInputEvaluator::attachForm($form, $settings, $info, $element->availableVariables());
+    }
+    $settings += array($name => isset($info['default value']) ? $info['default value'] : NULL);
+    $form[$name] += array(
+      '#title' => t('Value'),
+      '#default_value' => $settings[$name],
+      '#required' => empty($info['optional']),
+      '#after_build' => array('rules_ui_element_fix_empty_after_build'),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::render().
+   */
+  public static function render($value) {
+    return array(
+      'content' => array('#markup' => check_plain($value)),
+      '#attributes' => array('class' => array('rules-parameter-text')),
+    );
+  }
+
+}
+
+/**
+ * UI for text tokens.
+ */
+class RulesDataUITextToken extends RulesDataUIText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    if ($form[$name]['#type'] == 'textarea') {
+      $form[$name]['#element_validate'][] = 'rules_ui_element_token_validate';
+      $form[$name]['#description'] = t('May only contain lowercase letters, numbers, and underscores and has to start with a letter.');
+      $form[$name]['#rows'] = 1;
+    }
+    return $form;
+  }
+
+}
+
+/**
+ * UI for formatted text.
+ */
+class RulesDataUITextFormatted extends RulesDataUIText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    $settings += array($name => isset($info['default value']) ? $info['default value'] : array('value' => NULL, 'format' => NULL));
+
+    $form[$name]['#type'] = 'text_format';
+    $form[$name]['#base_type'] = 'textarea';
+    $form[$name]['#default_value'] = $settings[$name]['value'];
+    $form[$name]['#format'] = $settings[$name]['format'];
+    return $form;
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::render().
+   */
+  public static function render($value) {
+    return array(
+      'content' => array('#markup' => check_plain($value['value'])),
+      '#attributes' => array('class' => array('rules-parameter-text-formatted')),
+    );
+  }
+
+}
+
+/**
+ * UI for decimal data.
+ */
+class RulesDataUIDecimal extends RulesDataUIText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    if (empty($info['options list'])) {
+      $form[$name]['#type'] = 'textfield';
+    }
+    $form[$name]['#element_validate'][] = 'rules_ui_element_decimal_validate';
+    $form[$name]['#rows'] = 1;
+    return $form;
+  }
+
+}
+
+/**
+ * UI for integers.
+ */
+class RulesDataUIInteger extends RulesDataUIText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    if (empty($info['options list'])) {
+      $form[$name]['#type'] = 'textfield';
+    }
+    $form[$name]['#element_validate'][] = 'rules_ui_element_integer_validate';
+    return $form;
+  }
+
+}
+
+/**
+ * UI for IP addresses.
+ */
+class RulesDataUIIPAddress extends RulesDataUIText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    if (empty($info['options list'])) {
+      $form[$name]['#type'] = 'textfield';
+      $form[$name]['#description'] = t('If not provided, the IP address of the current user will be used.');
+    }
+    $form[$name]['#element_validate'][] = 'rules_ui_element_ip_address_validate';
+    $form[$name]['#rows'] = 1;
+    return $form;
+  }
+
+}
+
+/**
+ * UI for boolean data.
+ */
+class RulesDataUIBoolean extends RulesDataUI implements RulesDataDirectInputFormInterface {
+
+  /**
+   * Overrides RulesDataUI::getDefaultMode().
+   */
+  public static function getDefaultMode() {
+    return 'input';
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $settings += array($name => isset($info['default value']) ? $info['default value'] : NULL);
+    // Note: Due to the checkbox even optional parameter always receive a value.
+    $form[$name] = array(
+      '#type' => 'radios',
+      '#default_value' => $settings[$name],
+      '#options' => array(
+        TRUE => t('@label: True.', array('@label' => $info['label'])),
+        FALSE => t('@label: False.', array('@label' => $info['label'])),
+      ),
+    );
+    return $form;
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::render().
+   */
+  public static function render($value) {
+    return array(
+      'content' => array('#markup' => !empty($value) ? t('true') : t('false')),
+      '#attributes' => array('class' => array('rules-parameter-boolean')),
+    );
+  }
+
+}
+
+/**
+ * UI for dates.
+ */
+class RulesDataUIDate extends RulesDataUIText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $settings += array($name => isset($info['default value']) ? $info['default value'] : (empty($info['optional']) ? gmdate('Y-m-d H:i:s', time()) : NULL));
+
+    // Convert any configured timestamp into a readable format.
+    if (is_numeric($settings[$name])) {
+      $settings[$name] = gmdate('Y-m-d H:i:s', $settings[$name]);
+    }
+    $form = parent::inputForm($name, $info, $settings, $element);
+    $form[$name]['#type'] = 'textfield';
+    $form[$name]['#element_validate'][] = 'rules_ui_element_date_validate';
+    // Note that the date input evaluator takes care for parsing dates using
+    // strtotime() into a timestamp, which is the internal date format.
+    $form[$name]['#description'] = t('The date in GMT. You may enter a fixed time (like %format) or any other values in GMT known by the PHP !strtotime function (like "+1 day"). Relative dates like "+1 day" or "now" relate to the evaluation time.',
+      array('%format' => gmdate('Y-m-d H:i:s', time() + 86400),
+            '!strtotime' => l('strtotime()', 'http://php.net/strtotime')));
+
+    // @todo Leverage the jquery datepicker+timepicker once a module providing
+    // The timepicker is available.
+    return $form;
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::render().
+   */
+  public static function render($value) {
+    $value = is_numeric($value) ? format_date($value, 'short') : check_plain($value);
+    return array(
+      'content' => array('#markup' => $value),
+      '#attributes' => array('class' => array('rules-parameter-date')),
+    );
+  }
+
+}
+
+/**
+ * UI for duration type parameter.
+ */
+class RulesDataUIDuration extends RulesDataUIText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    $form[$name]['#type'] = 'rules_duration';
+    $form[$name]['#after_build'][] = 'rules_ui_element_duration_after_build';
+    return $form;
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::render().
+   */
+  public static function render($value) {
+    $value = is_numeric($value) ? format_interval($value) : check_plain($value);
+    return array(
+      'content' => array('#markup' => $value),
+      '#attributes' => array('class' => array('rules-parameter-duration')),
+    );
+  }
+
+}
+
+/**
+ * UI for the URI type parameter.
+ */
+class RulesDataUIURI extends RulesDataUIText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    $form[$name]['#rows'] = 1;
+    $form[$name]['#description'] = t('You may enter relative URLs like %url as well as absolute URLs like %absolute-url.', array('%url' => 'user/login?destination=node', '%absolute-url' => 'https://www.drupal.org'));
+    return $form;
+  }
+
+}
+
+/**
+ * UI for lists of textual data.
+ */
+class RulesDataUIListText extends RulesDataUIText {
+
+  /**
+   * Overrides RulesDataUI::getDefaultMode().
+   */
+  public static function getDefaultMode() {
+    return 'input';
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   *
+   * @todo This does not work for inputting textual values including "\n".
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $settings += array($name => isset($info['default value']) ? $info['default value'] : NULL);
+    $form = parent::inputForm($name, $info, $settings, $element);
+
+    if ($form[$name]['#type'] == 'textarea') {
+      // Fix up the value to be an array during after build.
+      $form[$name]['#delimiter'] = "\n";
+      $form[$name]['#after_build'][] = 'rules_ui_list_textarea_after_build';
+      $form[$name]['#pre_render'][] = 'rules_ui_list_textarea_pre_render';
+      $form[$name]['#default_value'] = !empty($settings[$name]) ? implode("\n", $settings[$name]) : NULL;
+      $form[$name]['#description'] = t('A list of values, one on each line.');
+    }
+    else {
+      $form[$name]['#multiple'] = TRUE;
+    }
+    return $form;
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::render().
+   */
+  public static function render($value) {
+    return array(
+      'content' => array('#markup' => check_plain(implode(', ', $value))),
+      '#attributes' => array('class' => array('rules-parameter-list')),
+    );
+  }
+
+}
+
+/**
+ * UI for lists of integers.
+ */
+class RulesDataUIListInteger extends RulesDataUIListText {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $settings += array($name => isset($info['default value']) ? $info['default value'] : NULL);
+    $form = parent::inputForm($name, $info, $settings, $element);
+
+    if ($form[$name]['#type'] == 'textarea') {
+      $form[$name]['#description'] = t('A list of integers, separated by commas. E.g. enter "1, 2, 3".');
+      $form[$name]['#delimiter'] = ',';
+      $form[$name]['#default_value'] = !empty($settings[$name]) ? implode(", ", $settings[$name]) : NULL;
+      $form[$name]['#element_validate'][] = 'rules_ui_element_integer_list_validate';
+      $form[$name]['#rows'] = 1;
+    }
+    return $form;
+  }
+
+}
+
+/**
+ * UI for lists of tokens.
+ */
+class RulesDataUIListToken extends RulesDataUIListInteger {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+
+    if ($form[$name]['#type'] == 'textarea') {
+      $form[$name]['#description'] = t('A list of text tokens, separated by commas. E.g. enter "one, two, three".');
+      $form[$name]['#element_validate'] = array('rules_ui_element_token_list_validate');
+    }
+    return $form;
+  }
+
+}
+
+/**
+ * UI for entity-based data types.
+ */
+class RulesDataUIEntity extends RulesDataUIText {
+
+  /**
+   * Overrides RulesDataUI::getDefaultMode().
+   */
+  public static function getDefaultMode() {
+    return 'selector';
+  }
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    if (empty($info['options list'])) {
+      $form[$name]['#type'] = 'textfield';
+
+      $entity_info = entity_get_info($info['type']);
+      if (empty($entity_info['entity keys']['name'])) {
+        $form[$name]['#element_validate'][] = 'rules_ui_element_integer_validate';
+      }
+      $form[$name]['#title'] = t('@entity identifier', array('@entity' => $entity_info['label']));
+      $entity_label = strtolower($entity_info['label'][0]) . substr($entity_info['label'], 1);
+      $form[$name]['#description'] = t('Specify an identifier of a @entity.', array('@entity' => $entity_label));
+    }
+    return $form;
+  }
+
+}
+
+/**
+ * UI for exportable entity-based data types.
+ */
+class RulesDataUIEntityExportable extends RulesDataUIEntity {
+
+  /**
+   * Overrides RulesDataUI::getDefaultMode().
+   */
+  public static function getDefaultMode() {
+    return 'input';
+  }
+
+}
+
+/**
+ * Data UI variant displaying a select list of available bundle entities.
+ *
+ * This is used for "bundle entities" implemented via the 'bundle of' feature
+ * of entity.module.
+ */
+class RulesDataUIBundleEntity extends RulesDataUIEntity implements RulesDataInputOptionsListInterface {
+
+  /**
+   * Overrides RulesDataUI::getDefaultMode().
+   */
+  public static function getDefaultMode() {
+    return 'input';
+  }
+
+  /**
+   * Implements RulesDataInputOptionsListInterface::optionsList().
+   */
+  public static function optionsList(RulesPlugin $element, $name) {
+    list($data_type, $parameter_info) = RulesDataUI::getTypeInfo($element, $name);
+    $bundles = array();
+    $entity_info = entity_get_info();
+    $bundle_of_type = $entity_info[$data_type]['bundle of'];
+    if (isset($entity_info[$bundle_of_type]['bundles'])) {
+      foreach ($entity_info[$bundle_of_type]['bundles'] as $bundle_name => $bundle_info) {
+        $bundles[$bundle_name] = $bundle_info['label'];
+      }
+    }
+    return $bundles;
+  }
+
+}
+
+/**
+ * UI for taxonomy vocabularies.
+ *
+ * @see RulesTaxonomyVocabularyWrapper
+ */
+class RulesDataUITaxonomyVocabulary extends RulesDataUIEntity implements RulesDataInputOptionsListInterface {
+
+  /**
+   * Overrides RulesDataUI::getDefaultMode().
+   */
+  public static function getDefaultMode() {
+    return 'input';
+  }
+
+  /**
+   * Implements RulesDataInputOptionsListInterface::optionsList().
+   */
+  public static function optionsList(RulesPlugin $element, $name) {
+    $options = array();
+    foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocab) {
+      $options[$machine_name] = $vocab->name;
+    }
+    return $options;
+  }
+
+}
+
+/**
+ * UI for lists of entity-based data types.
+ */
+class RulesDataUIListEntity extends RulesDataUIListInteger {
+
+  /**
+   * Implements RulesDataDirectInputFormInterface::inputForm().
+   */
+  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
+    $form = parent::inputForm($name, $info, $settings, $element);
+    if (empty($info['options list'])) {
+
+      $entity_info = entity_get_info(entity_property_list_extract_type($info['type']));
+      if (!empty($entity_info['entity keys']['name'])) {
+        $form[$name]['#element_validate'] = array('rules_ui_element_token_list_validate');
+      }
+      $form[$name]['#title'] = t('@entity identifiers', array('@entity' => $entity_info['label']));
+      $entity_label = strtolower($entity_info['label'][0]) . substr($entity_info['label'], 1);
+      $form[$name]['#description'] = t('Specify a comma-separated list of identifiers of @entity entities.', array('@entity' => $entity_label));
+    }
+    return $form;
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/ui.forms.inc b/profiles/wcm_base/modules/contrib/rules/ui/ui.forms.inc
new file mode 100644
index 0000000000000000000000000000000000000000..b20f8990b4aa2aef1122508c0940220f6038da49
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/ui.forms.inc
@@ -0,0 +1,992 @@
+<?php
+
+/**
+ * @file
+ * Rules User Interface forms.
+ */
+
+/**
+ * Ajax callback for reloading the whole form.
+ */
+function rules_ui_form_ajax_reload_form($form, $form_state) {
+  return $form;
+}
+
+/**
+ * Defines #ajax properties.
+ */
+function rules_ui_form_default_ajax($effect = 'slide') {
+  return array(
+    'callback' => 'rules_ui_form_ajax_reload_form',
+    'wrapper' => 'rules-form-wrapper',
+    'effect' => $effect,
+    'speed' => 'fast',
+  );
+}
+
+/**
+ * Submit handler for switching the parameter input mode.
+ */
+function rules_ui_parameter_replace_submit($form, &$form_state) {
+  if (isset($form_state['triggering_element'])) {
+    $name = $form_state['triggering_element']['#parameter'];
+    $form_state['parameter_mode'][$name] = $form_state['parameter_mode'][$name] == 'selector' ? 'input' : 'selector';
+  }
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * General form submit handler, that rebuilds the form.
+ */
+function rules_form_submit_rebuild($form, &$form_state) {
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Edit a rules configuration.
+ */
+function rules_ui_form_edit_rules_config($form, &$form_state, $rules_config, $base_path) {
+  RulesPluginUI::$basePath = $base_path;
+  $form_state += array('rules_element' => $rules_config);
+  // Add the rule configuration's form.
+  $rules_config->form($form, $form_state, array('show settings' => TRUE, 'button' => TRUE));
+  $form['#validate'] = array('rules_ui_form_rules_config_validate');
+  return $form;
+}
+
+/**
+ * General rules configuration form validation callback.
+ *
+ * Also populates the rules configuration with the form values.
+ */
+function rules_ui_form_rules_config_validate($form, &$form_state) {
+  $form_state['rules_element']->form_validate($form, $form_state);
+}
+
+/**
+ * Edit a rules configuration form submit callback.
+ */
+function rules_ui_form_edit_rules_config_submit($form, &$form_state) {
+  $form_state['rules_element']->form_submit($form, $form_state);
+  drupal_set_message(t('Your changes have been saved.'));
+  if (empty($form_state['redirect'])) {
+    $form_state['redirect'] = RulesPluginUI::defaultRedirect($form_state['rules_element']);
+  }
+}
+
+/**
+ * Clone a rules configuration form.
+ */
+function rules_ui_form_clone_rules_config($form, &$form_state, $rules_config, $base_path) {
+  RulesPluginUI::$basePath = $base_path;
+  $rules_config = clone $rules_config;
+  $rules_config->id = NULL;
+  $rules_config->name = '';
+  $rules_config->label .= ' (' . t('cloned') . ')';
+  $rules_config->status = ENTITY_CUSTOM;
+
+  $form['#validate'][] = 'rules_ui_form_rules_config_validate';
+  $form['#submit'][] = 'rules_ui_form_edit_rules_config_submit';
+  $form_state += array('rules_element' => $rules_config, 'op' => 'clone');
+
+  // Add the rule configuration's form.
+  $rules_config->form($form, $form_state, array('show settings' => TRUE, 'button' => TRUE, 'init' => TRUE));
+
+  // Open the settings fieldset so altering the name is easier.
+  $form['settings']['#collapsed'] = FALSE;
+  return $form;
+}
+
+/**
+ * A simple form just showing a textarea with the export.
+ */
+function rules_ui_form_export_rules_config($form, &$form_state, $rules_config, $base_path) {
+  $form['export'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Export'),
+    '#description' => t('For importing copy the content of the text area and paste it into the import page.'),
+    '#rows' => 25,
+    '#default_value' => $rules_config->export(),
+  );
+  return $form;
+}
+
+/**
+ * Configuration form to directly execute a rules configuration.
+ */
+function rules_ui_form_execute_rules_config($form, &$form_state, $rules_config, $base_path) {
+  // Only components can be executed.
+  if (!($rules_config instanceof RulesTriggerableInterface)) {
+    RulesPluginUI::$basePath = $base_path;
+    // Create either the appropriate action or condition element.
+    $element = rules_plugin_factory($rules_config instanceof RulesActionInterface ? 'action' : 'condition', 'component_' . $rules_config->name);
+    $form['exec_help'] = array(
+      '#prefix' => '<p>',
+      '#markup' => t('This form allows you to manually trigger the execution of the @plugin "%label". If this component requires any parameters, input the suiting execution arguments below.', array('@plugin' => $rules_config->plugin(), '%label' => $rules_config->label())),
+      '#suffix' => '</p>',
+    );
+    $element->form($form, $form_state);
+
+    // For conditions hide the option to negate them.
+    if (isset($form['negate'])) {
+      $form['negate']['#access'] = FALSE;
+    }
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Execute'),
+      '#weight' => 20,
+    );
+    // Re-use the validation callback, which will also populate the action with
+    // the configuration settings in the form.
+    $form['#validate'] = array('rules_ui_form_rules_config_validate');
+    return $form;
+  }
+  drupal_not_found();
+  exit;
+}
+
+/**
+ * Submit callback for directly executing a component.
+ */
+function rules_ui_form_execute_rules_config_submit($form, &$form_state) {
+  $element = $form_state['rules_element'];
+  $result = $element->execute();
+  if ($element instanceof RulesActionInterface) {
+    drupal_set_message(t('Component %label has been executed.', array('%label' => $element->label())));
+  }
+  else {
+    drupal_set_message(t('Component %label evaluated to %result.', array('%label' => $element->label(), '%result' => $result ? 'true' : 'false')));
+  }
+}
+
+/**
+ * Gets the confirmation question for valid operations, or else FALSE.
+ */
+function rules_ui_confirm_operations($op, $rules_config) {
+  $vars = array('%plugin' => $rules_config->plugin(), '%label' => $rules_config->label());
+
+  switch ($op) {
+    case 'enable':
+      return array(
+        t('Are you sure you want to enable the %plugin %label?', $vars),
+        '',
+      );
+
+    case 'disable':
+      return array(
+        t('Are you sure you want to disable the %plugin %label?', $vars),
+        '',
+      );
+
+    case 'revert':
+      return array(
+        t('Are you sure you want to revert the %plugin %label?', $vars),
+        t('This action cannot be undone.'),
+      );
+
+    case 'delete':
+      return array(
+        t('Are you sure you want to delete the %plugin %label?', $vars),
+        t('This action cannot be undone.'),
+      );
+
+    default:
+      return FALSE;
+  }
+}
+
+/**
+ * Confirmation form for applying the operation to the config.
+ */
+function rules_ui_form_rules_config_confirm_op($form, &$form_state, $rules_config, $op, $base_path) {
+  if (list($confirm_question, $description) = rules_ui_confirm_operations($op, $rules_config)) {
+    RulesPluginUI::$basePath = $base_path;
+    $form_state += array('rules_config' => $rules_config, 'op' => $op);
+    return confirm_form($form, $confirm_question, $base_path, $description, t('Confirm'), t('Cancel'));
+  }
+  else {
+    drupal_not_found();
+    exit;
+  }
+}
+
+/**
+ * Applies the operation and returns the message to show to the user.
+ *
+ * The operation is also logged to the watchdog. Note that the string is
+ * defined two times so that the translation extractor can find it.
+ */
+function rules_ui_confirm_operation_apply($op, $rules_config) {
+  $vars = array('%plugin' => $rules_config->plugin(), '%label' => $rules_config->label());
+  $edit_link = l(t('edit'), RulesPluginUI::path($rules_config->name));
+
+  switch ($op) {
+    case 'enable':
+      $rules_config->active = TRUE;
+      $rules_config->save();
+      watchdog('rules', 'Enabled %plugin %label.', $vars, WATCHDOG_NOTICE, $edit_link);
+      return t('Enabled %plugin %label.', $vars);
+
+    case 'disable':
+      $rules_config->active = FALSE;
+      $rules_config->save();
+      watchdog('rules', 'Disabled %plugin %label.', $vars, WATCHDOG_NOTICE, $edit_link);
+      return t('Disabled %plugin %label.', $vars);
+
+    case 'revert':
+      $rules_config->delete();
+      watchdog('rules', 'Reverted %plugin %label to the defaults.', $vars, WATCHDOG_NOTICE, $edit_link);
+      return t('Reverted %plugin %label to the defaults.', $vars);
+
+    case 'delete':
+      $rules_config->delete();
+      watchdog('rules', 'Deleted %plugin %label.', $vars);
+      return t('Deleted %plugin %label.', $vars);
+  }
+}
+
+/**
+ * Rule config deletion form submit callback.
+ */
+function rules_ui_form_rules_config_confirm_op_submit($form, &$form_state) {
+  if ($form_state['values']['confirm']) {
+    $msg = rules_ui_confirm_operation_apply($form_state['op'], $form_state['rules_config']);
+    drupal_set_message($msg);
+  }
+}
+
+/**
+ * Add a new element a rules configuration.
+ */
+function rules_ui_add_element($form, &$form_state, $rules_config, $plugin_name, RulesContainerPlugin $parent, $base_path) {
+  $cache = rules_get_cache();
+  if (!isset($cache['plugin_info'][$plugin_name]['class'])) {
+    drupal_not_found();
+    exit;
+  }
+  RulesPluginUI::$basePath = $base_path;
+  $plugin_is_abstract = in_array('RulesAbstractPlugin', class_parents($cache['plugin_info'][$plugin_name]['class']));
+  // In the first step create the element and in the second step show its edit
+  // form.
+  if ($plugin_is_abstract && !isset($form_state['rules_element'])) {
+    RulesPluginUI::formDefaults($form, $form_state);
+    $form_state += array('parent_element' => $parent, 'plugin' => $plugin_name);
+
+    $form['element_name'] = array(
+      '#type' => 'select',
+      '#title' => t('Select the %element to add', array('%element' => $plugin_name)),
+      '#options' => RulesPluginUI::getOptions($plugin_name),
+      '#ajax' => rules_ui_form_default_ajax() + array(
+        'trigger_as' => array('name' => 'continue'),
+      ),
+    );
+    $form['continue'] = array(
+      '#type' => 'submit',
+      '#name' => 'continue',
+      '#value' => t('Continue'),
+      '#ajax' => rules_ui_form_default_ajax(),
+    );
+  }
+  elseif (!$plugin_is_abstract) {
+    // Create the initial, empty element.
+    $element = rules_plugin_factory($plugin_name);
+    // Always add the new element at the bottom, thus set an appropriate weight.
+    $iterator = $parent->getIterator();
+    if ($sibling = end($iterator)) {
+      $element->weight = $sibling->weight + 1;
+    }
+    $element->setParent($parent);
+    $form_state['rules_element'] = $element;
+  }
+
+  if (isset($form_state['rules_element'])) {
+    $form_state['rules_element']->form($form, $form_state, array('button' => TRUE, 'init' => TRUE));
+    $form['#validate'][] = 'rules_ui_edit_element_validate';
+    $form['#submit'][] = 'rules_ui_edit_element_submit';
+  }
+  return $form;
+}
+
+/**
+ * Add element submit callback.
+ *
+ * Used for "abstract plugins" to create the initial element object with the
+ * given implementation name and rebuild the form.
+ */
+function rules_ui_add_element_submit($form, &$form_state) {
+  $element = rules_plugin_factory($form_state['plugin'], $form_state['values']['element_name']);
+
+  // Always add the new element at the bottom, thus set an appropriate weight.
+  $iterator = $form_state['parent_element']->getIterator();
+  if ($sibling = end($iterator)) {
+    $element->weight = $sibling->weight + 1;
+  }
+  // Clear the element settings so they won't be processed on serialization as
+  // there is nothing to be processed yet.
+  $element->settings = array();
+  $element->setParent($form_state['parent_element']);
+
+  $form_state['rules_element'] = $element;
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Delete elements.
+ */
+function rules_ui_delete_element($form, &$form_state, $rules_config, $rules_element, $base_path) {
+  RulesPluginUI::$basePath = $base_path;
+
+  if (empty($form_state['rules_config'])) {
+    // Before modifying the rules config we have to clone it, so any
+    // modifications won't appear in the static cache of the loading controller.
+    $rules_config = clone $rules_config;
+    // Also get the element from the cloned config.
+    $rules_element = $rules_config->elementMap()->lookup($rules_element->elementId());
+
+    $form_state['rules_config'] = $rules_config;
+    $form_state['rules_element'] = $rules_element;
+    $form_state['element_parent'] = $rules_element->parentElement();
+  }
+
+  // Try deleting the element and warn the user if something breaks, but
+  // save the parent for determining the right redirect target on submit.
+  $removed_plugin = $form_state['rules_element']->plugin();
+  $rules_element->delete();
+
+  if (empty($rules_config->dirty) && empty($form_state['input'])) {
+    try {
+      $rules_config->integrityCheck();
+    }
+    catch (RulesIntegrityException $e) {
+      $args = array(
+        '@plugin' => $e->element->plugin(),
+        '%label' => $e->element->label(),
+        '@removed-plugin' => $removed_plugin,
+        '!url' => url(RulesPluginUI::path($form_state['rules_config']->name, 'edit', $e->element)),
+      );
+      drupal_set_message(t('Deleting this @removed-plugin would break your configuration as some of its provided variables are utilized by the @plugin <a href="!url">%label</a>.', $args), 'warning');
+    }
+  }
+
+  $confirm_question = t('Are you sure you want to delete the %element_plugin %element_name?', array(
+    '%element_plugin' => $rules_element->plugin(),
+    '%element_name' => $rules_element->label(),
+  ));
+  return confirm_form($form, $confirm_question, RulesPluginUI::path($rules_config->name), t('This action cannot be undone.'), t('Delete'), t('Cancel'));
+}
+
+/**
+ * Rule config deletion form submit callback.
+ */
+function rules_ui_delete_element_submit($form, &$form_state) {
+  $rules_config = $form_state['rules_config'];
+  $rules_config->save();
+  if (empty($form_state['redirect'])) {
+    $form_state['redirect'] = RulesPluginUI::defaultRedirect($form_state['element_parent']);
+  }
+}
+
+/**
+ * Configure a rule element.
+ */
+function rules_ui_edit_element($form, &$form_state, $rules_config, $element, $base_path) {
+  RulesPluginUI::$basePath = $base_path;
+  $form_state += array('rules_element' => $element);
+  $form_state['rules_element']->form($form, $form_state, array('button' => TRUE));
+  return $form;
+}
+
+/**
+ * Validate the element configuration.
+ */
+function rules_ui_edit_element_validate($form, &$form_state) {
+  $form_state['rules_element']->form_validate($form, $form_state);
+}
+
+/**
+ * Submit the element configuration.
+ */
+function rules_ui_edit_element_submit($form, &$form_state) {
+  $form_state['rules_element']->form_submit($form, $form_state);
+  drupal_set_message(t('Your changes have been saved.'));
+  if (empty($form_state['redirect'])) {
+    $form_state['redirect'] = RulesPluginUI::defaultRedirect($form_state['rules_element']);
+  }
+}
+
+/**
+ * Form builder for the "add event" page.
+ */
+function rules_ui_add_event_page($form, &$form_state, RulesTriggerableInterface $rules_config, $base_path) {
+  RulesPluginUI::$basePath = $base_path;
+  RulesPluginUI::formDefaults($form, $form_state);
+  $form = rules_ui_add_event($form, $form_state, $rules_config, $base_path);
+  $form['#validate'][] = 'rules_ui_add_event_validate';
+  return $form;
+}
+
+/**
+ * Submit the event configuration.
+ */
+function rules_ui_add_event_page_submit($form, &$form_state) {
+  rules_ui_add_event_apply($form, $form_state);
+  $rules_config = $form_state['rules_config'];
+
+  // Tell the user if this breaks something, but let him proceed.
+  if (empty($rules_config->dirty)) {
+    try {
+      $rules_config->integrityCheck();
+    }
+    catch (RulesIntegrityException $e) {
+      $warning = TRUE;
+      drupal_set_message(t('Added the event, but it does not provide all variables utilized.'), 'warning');
+    }
+  }
+  $rules_config->save();
+  if (!isset($warning)) {
+    $events = rules_fetch_data('event_info');
+    $label = $events[$form_state['values']['event']]['label'];
+    drupal_set_message(t('Added event %event.', array('%event' => $label)));
+  }
+}
+
+/**
+ * Add a new event.
+ */
+function rules_ui_add_event($form, &$form_state, RulesReactionRule $rules_config, $base_path) {
+  $form_state += array('rules_config' => $rules_config);
+  $events = array_diff_key(rules_fetch_data('event_info'), array_flip($rules_config->events()));
+
+  $form['help'] = array(
+    '#markup' => t('Select the event to add. However note that all added events need to provide all variables that should be available to your rule.'),
+  );
+  $form['event'] = array(
+    '#type' => 'select',
+    '#title' => t('React on event'),
+    '#options' => RulesPluginUI::getOptions('event', $events),
+    '#description' => t('Whenever the event occurs, rule evaluation is triggered.'),
+    '#ajax' => rules_ui_form_default_ajax(),
+    '#required' => TRUE,
+  );
+  if (!empty($form_state['values']['event'])) {
+    $handler = rules_get_event_handler($form_state['values']['event']);
+    $form['event_settings'] = $handler->buildForm($form_state);
+  }
+  else {
+    $form['event_settings'] = array();
+  }
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add'),
+  );
+  $form_state['redirect'] = RulesPluginUI::path($rules_config->name);
+  return $form;
+}
+
+/**
+ * Validation callback for adding an event.
+ */
+function rules_ui_add_event_validate($form, $form_state) {
+  $handler = rules_get_event_handler($form_state['values']['event']);
+  $handler->extractFormValues($form['event_settings'], $form_state);
+  try {
+    $handler->validate();
+  }
+  catch (RulesIntegrityException $e) {
+    form_set_error(implode('][', $e->keys), $e->getMessage());
+  }
+}
+
+/**
+ * Submit callback that just adds the selected event.
+ *
+ * @see rules_admin_add_reaction_rule()
+ */
+function rules_ui_add_event_apply($form, &$form_state) {
+  $handler = rules_get_event_handler($form_state['values']['event']);
+  $handler->extractFormValues($form['event_settings'], $form_state);
+  $form_state['rules_config']->event($form_state['values']['event'], $handler->getSettings());
+}
+
+/**
+ * Form to remove an event from a rule.
+ */
+function rules_ui_remove_event($form, &$form_state, $rules_config, $event, $base_path) {
+  RulesPluginUI::$basePath = $base_path;
+  $form_state += array('rules_config' => $rules_config, 'rules_event' => $event);
+  $event_info = rules_get_event_info($event);
+  $form_state['event_label'] = $event_info['label'];
+  $confirm_question = t('Are you sure you want to remove the event?');
+  return confirm_form($form, $confirm_question, RulesPluginUI::path($rules_config->name), t('You are about to remove the event %event.', array('%event' => $form_state['event_label'])), t('Remove'), t('Cancel'));
+}
+
+/**
+ * Submit the event configuration.
+ */
+function rules_ui_remove_event_submit($form, &$form_state) {
+  $rules_config = $form_state['rules_config'];
+  $rules_config->removeEvent($form_state['rules_event']);
+  // Tell the user if this breaks something, but let him proceed.
+  if (empty($rules_config->dirty)) {
+    try {
+      $rules_config->integrityCheck();
+    }
+    catch (RulesIntegrityException $e) {
+      $warning = TRUE;
+      drupal_set_message(t('Removed the event, but it had provided some variables which are now missing.'), 'warning');
+    }
+  }
+  $rules_config->save();
+  if (!isset($warning)) {
+    drupal_set_message(t('Event %event has been removed.', array('%event' => $form_state['event_label'])));
+  }
+  $form_state['redirect'] = RulesPluginUI::path($rules_config->name);
+}
+
+/**
+ * Import form for rule configurations.
+ */
+function rules_ui_import_form($form, &$form_state, $base_path) {
+  RulesPluginUI::$basePath = $base_path;
+  RulesPluginUI::formDefaults($form, $form_state);
+  $form['import'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Import'),
+    '#description' => t('Paste an exported Rules configuration here.'),
+    '#rows' => 20,
+  );
+  $form['overwrite'] = array(
+    '#title' => t('Overwrite'),
+    '#type' => 'checkbox',
+    '#description' => t('If checked, any existing configuration with the same identifier will be replaced by the import.'),
+    '#default_value' => FALSE,
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Import'),
+  );
+  return $form;
+}
+
+/**
+ * Validation callback for the import form.
+ */
+function rules_ui_import_form_validate($form, &$form_state) {
+  if ($rules_config = rules_import($form_state['values']['import'], $error_msg)) {
+    // Store the successfully imported entity in $form_state.
+    $form_state['rules_config'] = $rules_config;
+    if (!$form_state['values']['overwrite']) {
+      // Check for existing entities with the same identifier.
+      if (rules_config_load($rules_config->name)) {
+        $vars = array('@entity' => t('Rules configuration'), '%label' => $rules_config->label());
+        form_set_error('import', t('Import of @entity %label failed, a @entity with the same machine name already exists. Check the overwrite option to replace it.', $vars));
+      }
+    }
+    try {
+      $rules_config->integrityCheck();
+    }
+    catch (RulesIntegrityException $e) {
+      form_set_error('import', t('Integrity check for the imported configuration failed. Error message: %message.', array('%message' => $e->getMessage())));
+    }
+    if (!user_access('bypass rules access') && !$rules_config->access()) {
+      form_set_error('import', t('You have insufficient access permissions for importing this Rules configuration.'));
+    }
+  }
+  else {
+    form_set_error('import', t('Import failed.'));
+    if ($error_msg) {
+      drupal_set_message($error_msg, 'error');
+    }
+  }
+}
+
+/**
+ * Submit callback for the import form.
+ */
+function rules_ui_import_form_submit($form, &$form_state) {
+  $rules_config = $form_state['rules_config'];
+
+  if ($existing_config = rules_config_load($rules_config->name)) {
+    // Copy DB id and remove the new indicator to overwrite the existing record.
+    $rules_config->id = $existing_config->id;
+    unset($rules_config->is_new);
+  }
+  $rules_config->save();
+  $vars = array('@entity' => t('Rules configuration'), '%label' => $rules_config->label());
+  watchdog('rules_config', 'Imported @entity %label.', $vars);
+  drupal_set_message(t('Imported @entity %label.', $vars));
+  $form_state['redirect'] = RulesPluginUI::$basePath;
+}
+
+/**
+ * FAPI process callback for the data selection widget.
+ *
+ * This finalises the auto completion callback path by appending the form build
+ * id.
+ */
+function rules_data_selection_process($element, &$form_state, $form) {
+  $element['#autocomplete_path'] .= '/' . $form['#build_id'];
+  $form_state['cache'] = TRUE;
+  return $element;
+}
+
+/**
+ * Autocomplete data selection results.
+ */
+function rules_ui_form_data_selection_auto_completion($parameter, $form_build_id, $string = '') {
+  // Get the form and its state from the cache to get the currently edited
+  // or created element.
+  $form_state = form_state_defaults();
+  $form = form_get_cache($form_build_id, $form_state);
+  if (!isset($form_state['rules_element'])) {
+    return;
+  }
+  $element = $form_state['rules_element'];
+
+  $params = $element->pluginParameterInfo();
+  $matches = array();
+  if (isset($params[$parameter])) {
+    $parts = explode(':', $string);
+    // Remove the last part as it might be unfinished.
+    $last_part = array_pop($parts);
+    $selector = implode(':', $parts);
+
+    // Start with the partly given selector or from scratch.
+    $result = array();
+    if ($selector && $wrapper = $element->applyDataSelector($selector)) {
+      $result = RulesData::matchingDataSelector($wrapper, $params[$parameter], $selector . ':', 0);
+    }
+    elseif (!$selector) {
+      $result = RulesData::matchingDataSelector($element->availableVariables(), $params[$parameter], '', 0);
+    }
+
+    foreach ($result as $selector => $info) {
+      // If we have an uncomplete last part, take it into account now.
+      $attributes = array();
+      if (!$last_part || strpos($selector, $string) === 0) {
+        $attributes['class'][] = 'rules-dsac-item';
+        $attributes['title'] = isset($info['description']) ? strip_tags($info['description']) : '';
+        if ($selector[strlen($selector) - 1] == ':') {
+          $attributes['class'][] = 'rules-dsac-group';
+          $text = check_plain($selector) . '... (' . check_plain($info['label']) . ')';
+        }
+        else {
+          $text = check_plain($selector) . ' (' . check_plain($info['label']) . ')';
+        }
+        $matches[$selector] = "<div" . drupal_attributes($attributes) . ">$text</div>";
+      }
+    }
+  }
+  drupal_json_output($matches);
+}
+
+/**
+ * FAPI validation of an integer element.
+ *
+ * Copy of the core Drupal private function _element_validate_integer().
+ */
+function rules_ui_element_integer_validate($element, &$form_state) {
+  $value = $element['#value'];
+  if (isset($value) && $value !== '' && (!is_numeric($value) || intval($value) != $value)) {
+    form_error($element, t('%name must be an integer value.', array('%name' => isset($element['#title']) ? $element['#title'] : t('Element'))));
+  }
+}
+
+/**
+ * FAPI validation of a decimal element.
+ *
+ * Improved version of the private function _element_validate_number().
+ */
+function rules_ui_element_decimal_validate($element, &$form_state) {
+  // Substitute the decimal separator ",".
+  $value = strtr($element['#value'], ',', '.');
+  if ($value != '' && !is_numeric($value)) {
+    form_error($element, t('%name must be a number.', array('%name' => $element['#title'])));
+  }
+  elseif ($value != $element['#value']) {
+    form_set_value($element, $value, $form_state);
+  }
+}
+
+/**
+ * FAPI callback to validate an IP address.
+ */
+function rules_ui_element_ip_address_validate($element, &$form_state) {
+  $value = $element['#value'];
+  if ($value != '' && !filter_var($value, FILTER_VALIDATE_IP)) {
+    form_error($element, t('%name is not a valid IP address.', array('%name' => $element['#title'])));
+  }
+}
+
+/**
+ * FAPI validation of a date element.
+ *
+ * Makes sure the specified date format is correct and converts date values
+ * specify a fixed (= non relative) date to a timestamp. Relative dates are
+ * handled by the date input evaluator.
+ */
+function rules_ui_element_date_validate($element, &$form_state) {
+  $value = $element['#value'];
+  if ($value == '' || (is_numeric($value) && intval($value) == $value)) {
+    // The value is a timestamp.
+    return;
+  }
+  elseif (is_string($value) && RulesDateInputEvaluator::gmstrtotime($value) === FALSE) {
+    form_error($element, t('Wrong date format. Specify the date in the format %format.', array('%format' => gmdate('Y-m-d H:i:s', time() + 86400))));
+  }
+  elseif (is_string($value) && RulesDateInputEvaluator::isFixedDateString($value)) {
+    // As the date string specifies a fixed format, we can convert it now.
+    $value = RulesDateInputEvaluator::gmstrtotime($value);
+    form_set_value($element, $value, $form_state);
+  }
+}
+
+/**
+ * FAPI process callback for the duration element type.
+ */
+function rules_ui_element_duration_process($element, &$form_state) {
+  $element['value'] = array(
+    '#type' => 'textfield',
+    '#size' => 8,
+    '#element_validate' => array('rules_ui_element_integer_validate'),
+    '#default_value' => $element['#default_value'],
+    '#required' => !empty($element['#required']),
+  );
+  $element['multiplier'] = array(
+    '#type' => 'select',
+    '#options' => rules_ui_element_duration_multipliers(),
+    '#default_value' => 1,
+  );
+
+  // Put the child elements in a container-inline div.
+  $element['value']['#prefix'] = '<div class="rules-duration container-inline">';
+  $element['multiplier']['#suffix'] = '</div>';
+
+  // Set an appropriate multiplier.
+  if (!empty($element['value']['#default_value'])) {
+    foreach (array_keys(rules_ui_element_duration_multipliers()) as $m) {
+      if ($element['value']['#default_value'] % $m == 0) {
+        $element['multiplier']['#default_value'] = $m;
+      }
+    }
+    // Divide value by the multiplier, so the display is correct.
+    $element['value']['#default_value'] /= $element['multiplier']['#default_value'];
+  }
+  return $element;
+}
+
+/**
+ * Defines possible duration multiplier.
+ */
+function rules_ui_element_duration_multipliers() {
+  return array(
+    1 => t('seconds'),
+    60 => t('minutes'),
+    3600 => t('hours'),
+    // Just use approximate numbers for days (might last 23h on DST change),
+    // months and years.
+    86400 => t('days'),
+    86400 * 30 => t('months'),
+    86400 * 30 * 12 => t('years'),
+  );
+}
+
+/**
+ * Helper function a rules duration form element.
+ *
+ * Determines the value for a rules duration form element.
+ */
+function rules_ui_element_duration_value($element, $input = FALSE) {
+  // This runs before child elements are processed, so we cannot calculate the
+  // value here. But we have to make sure the value is an array, so the form
+  // API is able to process the children to set their values in the array. Thus
+  // once the form API has finished processing the element, the value is an
+  // array containing the child element values. Then finally the after build
+  // callback converts it back to the numeric value and sets that.
+  return array();
+}
+
+/**
+ * FAPI after build callback for the duration parameter type form.
+ *
+ * Fixes up the form value by applying the multiplier.
+ */
+function rules_ui_element_duration_after_build($element, &$form_state) {
+  if ($element['value']['#value'] !== '') {
+    $element['#value'] = $element['value']['#value'] * $element['multiplier']['#value'];
+    form_set_value($element, $element['#value'], $form_state);
+  }
+  else {
+    $element['#value'] = NULL;
+    form_set_value($element, NULL, $form_state);
+  }
+  return $element;
+}
+
+/**
+ * FAPI after build callback to ensure empty form elements result in no value.
+ */
+function rules_ui_element_fix_empty_after_build($element, &$form_state) {
+  if (isset($element['#value']) && $element['#value'] === '') {
+    $element['#value'] = NULL;
+    form_set_value($element, NULL, $form_state);
+  }
+  // Work-a-round for the text_format element.
+  elseif ($element['#type'] == 'text_format' && !isset($element['value']['#value'])) {
+    form_set_value($element, NULL, $form_state);
+  }
+  return $element;
+}
+
+/**
+ * FAPI after build callback for specifying a list of values.
+ *
+ * Turns the textual value in an array by splitting the text in chunks using the
+ * delimiter set at $element['#delimiter'].
+ */
+function rules_ui_list_textarea_after_build($element, &$form_state) {
+  $element['#value'] = $element['#value'] ? explode($element['#delimiter'], $element['#value']) : array();
+  $element['#value'] = array_map('trim', $element['#value']);
+  form_set_value($element, $element['#value'], $form_state);
+  return $element;
+}
+
+/**
+ * FAPI pre render callback. Turns the value back to a string for rendering.
+ *
+ * @see rules_ui_list_textarea_after_build()
+ */
+function rules_ui_list_textarea_pre_render($element) {
+  $element['#value'] = implode($element['#delimiter'], $element['#value']);
+  return $element;
+}
+
+/**
+ * FAPI callback to validate a list of integers.
+ */
+function rules_ui_element_integer_list_validate($element, &$form_state) {
+  foreach ($element['#value'] as $value) {
+    if ($value !== '' && (!is_numeric($value) || intval($value) != $value)) {
+      form_error($element, t('Each value must be an integer.'));
+    }
+  }
+}
+
+/**
+ * FAPI callback to validate a token.
+ */
+function rules_ui_element_token_validate($element) {
+  $value = $element['#value'];
+  if (isset($value) && $value !== '' && !entity_property_verify_data_type($value, 'token')) {
+    form_error($element, t('%name may only contain lowercase letters, numbers, and underscores and has to start with a letter.', array('%name' => isset($element['#title']) ? $element['#title'] : t('Element'))));
+  }
+}
+
+/**
+ * FAPI callback to validate a list of tokens.
+ */
+function rules_ui_element_token_list_validate($element, &$form_state) {
+  foreach ($element['#value'] as $value) {
+    if ($value !== '' && !entity_property_verify_data_type($value, 'token')) {
+      form_error($element, t('Each value may only contain lowercase letters, numbers, and underscores and has to start with a letter.'));
+    }
+  }
+}
+
+/**
+ * FAPI callback to validate a machine readable name.
+ */
+function rules_ui_element_machine_name_validate($element, &$form_state) {
+  if ($element['#value'] && !preg_match('!^[a-z0-9_]+$!', $element['#value'])) {
+    form_error($element, t('Machine-readable names must contain only lowercase letters, numbers, and underscores.'));
+  }
+}
+
+/**
+ * FAPI callback to validate the form for editing variable info.
+ *
+ * @see RulesPluginUI::getVariableForm()
+ */
+function rules_ui_element_variable_form_validate($elements, &$form_state) {
+  $names = array();
+  foreach (element_children($elements['items']) as $item_key) {
+    $element = &$elements['items'][$item_key];
+    if ($element['name']['#value'] || $element['type']['#value'] || $element['label']['#value']) {
+      foreach (array('name' => t('Machine name'), 'label' => t('Label'), 'type' => t('Data type')) as $key => $title) {
+        if (!$element[$key]['#value']) {
+          form_error($element[$key], t('!name field is required.', array('!name' => $title)));
+        }
+      }
+      if (isset($names[$element['name']['#value']])) {
+        form_error($element['name'], t('The machine-readable name %name is already taken.', array('%name' => $element['name']['#value'])));
+      }
+      $names[$element['name']['#value']] = TRUE;
+    }
+  }
+}
+
+/**
+ * Helper to sort elements by their 'weight' key.
+ */
+function rules_element_sort_helper($a, $b) {
+  $a += array('weight' => 0);
+  $b += array('weight' => 0);
+  if ($a['weight'] == $b['weight']) {
+    return 0;
+  }
+  return ($a['weight'] < $b['weight']) ? -1 : 1;
+}
+
+/**
+ * Form after build handler to set the static base path.
+ *
+ * @see RulesPluginUI::formDefaults()
+ */
+function rules_form_after_build_restore_base_path($form, &$form_state) {
+  if (isset($form_state['_rules_base_path'])) {
+    RulesPluginUI::$basePath = $form_state['_rules_base_path'];
+  }
+  return $form;
+}
+
+/**
+ * AJAX page callback to load tag suggestions.
+ *
+ * Largely copied from taxonomy_autocomplete().
+ */
+function rules_autocomplete_tags($tags_typed = '') {
+  // The user enters a comma-separated list of tags. We only autocomplete the
+  // last tag.
+  $tags_typed = drupal_explode_tags($tags_typed);
+  $tag_last = drupal_strtolower(array_pop($tags_typed));
+
+  $tag_matches = array();
+  if ($tag_last != '') {
+    $query = db_select('rules_tags', 'rt');
+    // Do not select already entered terms.
+    if (!empty($tags_typed)) {
+      $query->condition('rt.tag', $tags_typed, 'NOT IN');
+    }
+    // Select rows that match by tag name.
+    $tags_return = $query
+      ->distinct()
+      ->fields('rt', array('tag'))
+      ->condition('rt.tag', '%' . db_like($tag_last) . '%', 'LIKE')
+      ->groupBy('rt.tag')
+      ->range(0, 10)
+      ->execute()
+      ->fetchCol('rt.tag');
+
+    $prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
+
+    foreach ($tags_return as $name) {
+      $n = $name;
+      // Tag names containing commas or quotes must be wrapped in quotes.
+      if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
+        $n = '"' . str_replace('"', '""', $name) . '"';
+      }
+      $tag_matches[$prefix . $n] = check_plain($name);
+    }
+  }
+  drupal_json_output($tag_matches);
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/ui.plugins.inc b/profiles/wcm_base/modules/contrib/rules/ui/ui.plugins.inc
new file mode 100644
index 0000000000000000000000000000000000000000..d3a061af089dafda4271b850da54456e0d022d0d
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/ui.plugins.inc
@@ -0,0 +1,264 @@
+<?php
+
+/**
+ * @file
+ * Contains UI for diverse plugins provided by Rules.
+ */
+
+/**
+ * Rule specific UI.
+ */
+class RulesRuleUI extends RulesActionContainerUI {
+
+  protected $rule;
+  protected $conditions;
+
+  /**
+   * Constructs a RulesRuleUI object.
+   *
+   * @param FacesExtendable $object
+   */
+  public function __construct(FacesExtendable $object) {
+    parent::__construct($object);
+    $this->rule = $object;
+    $this->conditions = $this->rule->conditionContainer();
+  }
+
+  public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
+    $form_state['rules_element'] = $this->rule;
+    $label = $this->element->label();
+    // Automatically add a counter to unlabelled rules.
+    if ($label == t('unlabeled') && !$this->element->isRoot() && !empty($options['init'])) {
+      $parent = $this->element->parentElement();
+      $label .= ' ' . count($parent->getIterator());
+    }
+    // Components have already a label. If used inside a rule-set add a label
+    // though. It's called 'Name' in the UI though.
+    if (!$this->element->isRoot()) {
+      $form['label'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Name'),
+        '#default_value' => empty($options['init']) ? $label : '',
+        '#required' => TRUE,
+        '#weight' => 5,
+        '#description' => t('A human-readable name shortly describing the rule.'),
+      );
+    }
+
+    $form += array('conditions' => array('#weight' => -5, '#tree' => TRUE));
+    $this->conditions->form($form['conditions'], $form_state);
+    unset($form['conditions']['negate']);
+
+    // Add actions form.
+    $iterator = new RecursiveIteratorIterator($this->rule->actions(), RecursiveIteratorIterator::SELF_FIRST);
+    parent::form($form, $form_state, $options, $iterator);
+    // Hide nested elements during creation.
+    $form['elements']['#access'] = empty($options['init']);
+    $form['conditions']['elements']['#access'] = empty($options['init']);
+
+    $form_state['redirect'] = RulesPluginUI::path($this->element->root()->name, 'edit', $this->element);
+    if (!empty($options['button'])) {
+      $form['submit']['#value'] = t('Save changes');
+    }
+  }
+
+  /**
+   * Applies the values of the form to the rule configuration.
+   */
+  public function form_extract_values($form, &$form_state) {
+    $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
+    // Run condition and action container value extraction.
+    if (isset($form['conditions'])) {
+      $this->conditions->extender('RulesConditionContainerUI')->form_extract_values($form['conditions'], $form_state);
+    }
+    if (!empty($form_values['label'])) {
+      $this->element->label = $form_values['label'];
+    }
+    parent::form_extract_values($form, $form_state);
+  }
+
+  public function operations() {
+    // When rules are listed only show the edit and delete operations.
+    $ops = parent::operations();
+    $ops['#links'] = array_intersect_key($ops['#links'], array_flip(array('edit', 'delete')));
+    return $ops;
+  }
+
+}
+
+/**
+ * Reaction rule specific UI.
+ */
+class RulesReactionRuleUI extends RulesRuleUI {
+
+  public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
+    $form['events'] = array(
+      '#type' => 'container',
+      '#weight' => -10,
+      '#access' => empty($options['init']),
+    );
+
+    $form['events']['table'] = array(
+      '#theme' => 'table',
+      '#caption' => 'Events',
+      '#header' => array(t('Event'), t('Operations')),
+      '#empty' => t('None'),
+    );
+    $form['events']['table']['#attributes']['class'][] = 'rules-elements-table';
+    foreach ($this->rule->events() as $event_name) {
+      $event_handler = rules_get_event_handler($event_name, $this->rule->getEventSettings($event_name));
+
+      $event_operations = array(
+        '#theme' => 'links__rules',
+        '#attributes' => array(
+          'class' => array(
+            'rules-operations',
+            'action-links',
+            'rules_rule_event',
+          ),
+        ),
+        '#links' => array(
+          'delete_event' => array(
+            'title' => t('delete'),
+            'href' => RulesPluginUI::path($this->rule->name, 'delete/event/' . $event_name),
+            'query' => drupal_get_destination(),
+          ),
+        ),
+      );
+
+      $form['events']['table']['#rows'][$event_name] = array(
+        'data' => array(
+          $event_handler->summary(),
+          array('data' => $event_operations),
+        ),
+      );
+    }
+
+    // Add the "add event" row.
+    $cell['colspan'] = 3;
+    $cell['data']['#theme'] = 'links__rules';
+    $cell['data']['#attributes']['class'][] = 'rules-operations-add';
+    $cell['data']['#attributes']['class'][] = 'action-links';
+    $cell['data']['#links']['add_event'] = array(
+      'title' => t('Add event'),
+      'href' => RulesPluginUI::path($this->rule->name, 'add/event'),
+      'query' => drupal_get_destination(),
+    );
+    $form['events']['table']['#rows'][] = array('data' => array($cell), 'class' => array('rules-elements-add'));
+
+    parent::form($form, $form_state, $options);
+    unset($form['label']);
+  }
+
+  /**
+   * Adds the configuration settings form (label, tags, description, ..).
+   */
+  public function settingsForm(&$form, &$form_state) {
+    parent::settingsForm($form, $form_state);
+    $form['settings']['active'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Active'),
+      '#default_value' => !isset($this->rule->active) || $this->rule->active,
+    );
+    $form['settings']['weight'] = array(
+      '#type' => 'weight',
+      '#title' => t('Weight'),
+      '#default_value' => $this->element->weight,
+      '#weight' => 5,
+      '#delta' => 10,
+      '#description' => t('Order rules that react on the same event. Rules with a higher weight are evaluated after rules with less weight.'),
+    );
+    unset($form['settings']['component_provides']);
+  }
+
+  public function settingsFormExtractValues($form, &$form_state) {
+    $form_values = RulesPluginUI::getFormStateValues($form['settings'], $form_state);
+    parent::settingsFormExtractValues($form, $form_state);
+    $this->rule->active = $form_values['active'];
+    $this->rule->weight = $form_values['weight'];
+  }
+
+}
+
+/**
+ * Rule set specific UI.
+ */
+class RulesRuleSetUI extends RulesActionContainerUI {
+
+  public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
+    // Pass an iterator just iterating over the rules, thus no further child
+    // elements will be displayed.
+    parent::form($form, $form_state, $options, $this->element->getIterator());
+    // Only show the add rule link.
+    $form['elements']['#add']['#links'] = array_intersect_key($form['elements']['#add']['#links'], array('add_rule' => 1));
+    $form['elements']['#attributes']['class'][] = 'rules-rule-set';
+    $form['elements']['#caption'] = t('Rules');
+  }
+
+}
+
+/**
+ * UI for Rules loops.
+ */
+class RulesLoopUI extends RulesActionContainerUI {
+
+  public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
+    parent::form($form, $form_state, $options);
+    $settings = $this->element->settings;
+
+    $form['item'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Current list item'),
+      '#description' => t('The variable used for holding each list item in the loop. This variable will be available inside the loop only.'),
+      '#tree' => TRUE,
+    );
+    $form['item']['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Variable label'),
+      '#default_value' => $settings['item:label'],
+      '#required' => TRUE,
+    );
+    $form['item']['var'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Variable name'),
+      '#default_value' => $settings['item:var'],
+      '#description' => t('The variable name must contain only lowercase letters, numbers, and underscores and must be unique in the current scope.'),
+      '#element_validate' => array('rules_ui_element_machine_name_validate'),
+      '#required' => TRUE,
+    );
+  }
+
+  public function form_extract_values($form, &$form_state) {
+    parent::form_extract_values($form, $form_state);
+    $form_values = RulesPluginUI::getFormStateValues($form, $form_state);
+
+    $this->element->settings['item:var'] = $form_values['item']['var'];
+    $this->element->settings['item:label'] = $form_values['item']['label'];
+  }
+
+  public function form_validate($form, &$form_state) {
+    parent::form_validate($form, $form_state);
+
+    $vars = $this->element->availableVariables();
+    $name = $this->element->settings['item:var'];
+    if (isset($vars[$name])) {
+      form_error($form['item']['var'], t('The variable name %name is already taken.', array('%name' => $name)));
+    }
+  }
+
+  public function buildContent() {
+    $content = parent::buildContent();
+
+    $content['description']['item'] = array(
+      '#caption' => t('List item'),
+      '#theme' => 'rules_content_group',
+    );
+    $content['description']['item']['var'] = array(
+      '#theme' => 'rules_variable_view',
+      '#info' => $this->element->listItemInfo(),
+      '#name' => $this->element->settings['item:var'],
+    );
+    return $content;
+  }
+
+}
diff --git a/profiles/wcm_base/modules/contrib/rules/ui/ui.theme.inc b/profiles/wcm_base/modules/contrib/rules/ui/ui.theme.inc
new file mode 100644
index 0000000000000000000000000000000000000000..52212fe3414dd319d1dbb0b6970ec482df60899c
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules/ui/ui.theme.inc
@@ -0,0 +1,306 @@
+<?php
+
+/**
+ * @file
+ * Rules theme functions.
+ */
+
+/**
+ * Themes a tree of rule elements in a draggable table.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_elements($variables) {
+  $form = $variables['element'];
+  $form['#theme'] = 'table';
+  $form['#header'] = array(t('Elements'), t('Weight'), t('Operations'));
+  $form['#attributes']['id'] = 'rules-' . drupal_html_id($form['#caption']) . '-id';
+
+  foreach (element_children($form) as $element_id) {
+    $element = &$form[$element_id];
+
+    // Add special classes to be used for tabledrag.js.
+    $element['parent_id']['#attributes']['class'] = array('rules-parent-id');
+    $element['element_id']['#attributes']['class'] = array('rules-element-id');
+    $element['weight']['#attributes']['class'] = array('rules-element-weight');
+
+    $row = array();
+    $row[] = theme('indentation', array('size' => $element['#depth'])) . drupal_render($element['label']);
+
+    $row[] = drupal_render($element['weight']) . drupal_render($element['parent_id']) . drupal_render($element['element_id']);
+    $row[] = array('class' => 'rules-operations', 'data' => $element['operations']);
+
+    $row = array('data' => $row) + $element['#attributes'];
+    $row['class'][] = 'draggable';
+    if (!$element['#container']) {
+      $row['class'][] = 'tabledrag-leaf';
+    }
+    $form['#rows'][] = $row;
+  }
+  if (!empty($form['#rows'])) {
+    drupal_add_tabledrag($form['#attributes']['id'], 'match', 'parent', 'rules-parent-id', 'rules-parent-id', 'rules-element-id', TRUE, 10);
+    drupal_add_tabledrag($form['#attributes']['id'], 'order', 'sibling', 'rules-element-weight');
+  }
+  else {
+    $form['#rows'][] = array(array('data' => t('None'), 'colspan' => 3));
+  }
+  if (!empty($form['#add'])) {
+    $row = array();
+    $row[] = array('data' => $form['#add'], 'colspan' => 3);
+    $form['#rows'][] = array('data' => $row, 'class' => array('rules-elements-add'));
+  }
+
+  // Add a wrapping div.
+  return '<div class="rules-elements-table">' . drupal_render($form) . '</div>';
+}
+
+/**
+ * Themes the rules form for editing the used variables.
+ *
+ * @see RulesPluginUI::getVariableForm()
+ *
+ * @ingroup themeable
+ */
+function theme_rules_ui_variable_form($variables) {
+  $elements = $variables['element'];
+
+  $table['#theme'] = 'table';
+  $table['#header'] = array(
+    t('Data type'),
+    t('Label'),
+    t('Machine name'),
+    t('Usage'),
+    array('data' => t('Weight'), 'class' => array('tabledrag-hide')),
+  );
+  $table['#attributes']['id'] = 'rules-' . drupal_html_id($elements['#title']) . '-id';
+
+  foreach (element_children($elements['items']) as $key) {
+    $element = &$elements['items'][$key];
+    // Add special classes to be used for tabledrag.js.
+    $element['weight']['#attributes']['class'] = array('rules-element-weight');
+
+    $row = array();
+    $row[] = array('data' => $element['type']);
+    $row[] = array('data' => $element['label']);
+    $row[] = array('data' => $element['name']);
+    $row[] = array('data' => $element['usage']);
+    $row[] = array('data' => $element['weight']);
+    $row = array('data' => $row) + $element['#attributes'];
+    $row['class'][] = 'draggable';
+    $table['#rows'][] = $row;
+  }
+  $elements['items']['#printed'] = TRUE;
+  if (!empty($table['#rows'])) {
+    drupal_add_tabledrag($table['#attributes']['id'], 'order', 'sibling', 'rules-element-weight');
+  }
+
+  // Theme it like a form item, but with the description above the content.
+  $attributes['class'][] = 'form-item';
+  $attributes['class'][] = 'rules-variables-form';
+
+  $output = '<div' . drupal_attributes($attributes) . '>' . "\n";
+  $output .= theme('form_element_label', $variables);
+  if (!empty($elements['#description'])) {
+    $output .= ' <div class="description">' . $elements['#description'] . "</div>\n";
+  }
+  $output .= ' ' . drupal_render($table) . "\n";
+  // Add in any further children elements.
+  foreach (element_children($elements, TRUE) as $key) {
+    $output .= drupal_render($elements[$key]);
+  }
+  $output .= "</div>\n";
+  return $output;
+}
+
+/**
+ * Themes a view of multiple configuration items.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_content_group($variables) {
+  $element = $variables['element'];
+  $output = array();
+  foreach (element_children($element) as $key) {
+    $output[] = drupal_render($element[$key]);
+  }
+  $output = array_filter($output);
+  $heading = !empty($element['#caption']) ? "<span class='rules-content-heading'>" . $element['#caption'] . ': </span>' : '';
+  if (!empty($output)) {
+    $element['#attributes']['class'][] = 'rules-element-content-group';
+    return '<div' . drupal_attributes($element['#attributes']) . '>' . $heading . implode(', ', $output) . '</div>';
+  }
+}
+
+/**
+ * Themes the view of a single parameter configuration.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_parameter_configuration($variables) {
+  $element = $variables['element'];
+  $content = drupal_render_children($element);
+  // Add the full content to the span's title, but don't use drupal_attributes
+  // for that as this would invoke check_plain() again.
+  $title = strip_tags($content);
+  $element['#attributes']['class'][] = 'rules-parameter-configuration';
+  $attributes = drupal_attributes($element['#attributes']) . " title='$title'";
+
+  $label_attributes['class'][] = 'rules-parameter-label';
+  if (!empty($element['#info']['description'])) {
+    $label_attributes['title'] = $element['#info']['description'];
+  }
+  $label_attributes = drupal_attributes($label_attributes);
+
+  $output = "<span $label_attributes>" . check_plain($element['#info']['label']) . ': </span>';
+  $output .= "<span $attributes>" . truncate_utf8($content, 30, TRUE, TRUE) . "</span>";
+  return $output;
+}
+
+/**
+ * Themes info about variables.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_variable_view($variables) {
+  $element = $variables['element'];
+
+  $label_attributes['class'][] = 'rules-variable-label';
+  $label_attributes['title'] = '';
+  if (!empty($element['#info']['description'])) {
+    $label_attributes['title'] = $element['#info']['description'] . ' ';
+  }
+  $label_attributes['title'] .= t('Data type: !type', array('!type' => $element['#info']['type']));
+  $label_attributes = drupal_attributes($label_attributes);
+
+  $output = check_plain($element['#info']['label']);
+  $output .= ' (' . check_plain($element['#name']) . ')';
+  return "<span $label_attributes>" . $output . '</span>';
+}
+
+/**
+ * Themes help for using the data selector.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_data_selector_help($variables) {
+  $variables_info = $variables['variables'];
+  $param_info = $variables['parameter'];
+
+  $render = array(
+    '#type' => 'fieldset',
+    '#title' => t('Data selectors'),
+    '#pre_render' => array(),
+    '#attributes' => array(),
+  );
+  // Make it manually collapsible as we cannot use #collapsible without the
+  // FAPI element processor.
+  $render['#attached']['js'][] = 'misc/collapse.js';
+  $render['#attributes']['class'][] = 'collapsible';
+  $render['#attributes']['class'][] = 'collapsed';
+
+  $render['table'] = array(
+    '#theme' => 'table',
+    '#header' => array(t('Selector'), t('Label'), t('Description')),
+  );
+  foreach (RulesData::matchingDataSelector($variables_info, $param_info) as $selector => $info) {
+    $info += array('label' => '', 'description' => '');
+    $render['table']['#rows'][] = array(
+      check_plain($selector),
+      check_plain(drupal_ucfirst($info['label'])),
+      check_plain($info['description']),
+    );
+  }
+  return drupal_render($render);
+}
+
+/**
+ * Themes the rules log debug output.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_log($variables) {
+  $element = $variables['element'];
+  drupal_add_css(drupal_get_path('module', 'rules') . '/ui/rules.ui.css');
+  // Add jquery ui core css and functions, which are needed for the icons.
+  drupal_add_library('system', 'ui');
+  drupal_add_js(drupal_get_path('module', 'rules') . '/ui/rules.debug.js');
+
+  $output = '<div class="rules-debug-log">';
+
+  $output .= '<h3 class="rules-debug-log-head">';
+  $output .= '<span class="rules-debug-open-main rules-debug-collapsible-link">';
+  $output .= '<span unselectable="on" class="ui-icon ui-icon-triangle-1-e rules-debug-icon-open">&nbsp;</span>';
+  $output .= t('Rules evaluation log');
+  $output .= '</span>';
+  $output .= '</span>';
+  $output .= '<span class="rules-debug-open-all rules-debug-collapsible-link">-' . t('Open all') . '-<span>';
+  $output .= '</h3>';
+
+  $output .= '<div>';
+  $output .= $element['#children'];
+  $output .= '</div>';
+  $output .= '</div>';
+
+  return $output;
+}
+
+/**
+ * Theme rules debug log elements.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_debug_element($variables) {
+  $output = '<div class="rules-debug-block">';
+  $output .= '<div class="rules-debug-log-head rules-debug-open rules-debug-collapsible-link">"';
+  $output .= '<span unselectable="on" class="ui-icon ui-icon-triangle-1-e rules-debug-icon-open">&nbsp;</span>';
+  $output .= $variables['head'];
+  if (isset($variables['link'])) {
+    $output .= ' ' . $variables['link'];
+  }
+  $output .= '</div>';
+  $output .= $variables['log'];
+  $output .= '</div>';
+  return $output;
+}
+
+/**
+ * Themes rules autocomplete forms.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_autocomplete($variables) {
+  $element = $variables['element'];
+  drupal_add_js(drupal_get_path('module', 'rules') . '/ui/rules.autocomplete.js');
+  drupal_add_library('system', 'ui.autocomplete');
+  drupal_add_library('system', 'ui.button');
+
+  $element['#attributes']['type'] = 'text';
+  element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength'));
+  _form_set_class($element, array('form-text', 'rules-autocomplete'));
+
+  $id = $element['#attributes']['id'];
+  $id_button = drupal_html_id($id . '-button');
+
+  $js_vars['rules_autocomplete'][$id] = array(
+    'inputId' => $id,
+    'source' => url($element['#autocomplete_path'], array('absolute' => TRUE)),
+  );
+  drupal_add_js($js_vars, 'setting');
+
+  $output = '<div class="rules-autocomplete">';
+  $output .= '<input' . drupal_attributes($element['#attributes']) . ' />';
+  $output .= '</div>';
+
+  return $output;
+}
+
+/**
+ * General theme function for displaying settings related help.
+ *
+ * @ingroup themeable
+ */
+function theme_rules_settings_help($variables) {
+  $text = $variables['text'];
+  $heading = $variables['heading'];
+  return "<p class=\"rules-settings-help\"><strong>$heading:</strong> $text</p>";
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/LICENSE.txt b/profiles/wcm_base/modules/contrib/rules_conditional/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/LICENSE.txt
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.core.inc b/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.core.inc
new file mode 100644
index 0000000000000000000000000000000000000000..1cb2eb28d35130552640c016ae906bfbe6a8a647
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.core.inc
@@ -0,0 +1,523 @@
+<?php
+/**
+ * @file
+ * Conditional Rules framework implementation.
+ */
+
+/**
+ * Base conditional statement plugin implementation.
+ */
+abstract class RulesConditionalContainer extends RulesContainerPlugin implements RulesActionInterface {
+  /**
+   * Magic methods to intercept.
+   * @var array
+   */
+  protected $interceptMethods = array();
+
+  /**
+   * @var RulesActionContainer
+   */
+  protected $fluentElement;
+
+  protected $providesVariables;
+
+  public function label() {
+    $info = $this->pluginInfo();
+    $label = isset($info['label']) ? $info['label'] : t('unlabeled');
+    return $label;
+  }
+
+  /**
+   * Intercepts calls to magic methods, possibly using reserved keywords.
+   */
+  public function __call($name, $arguments = array()) {
+    if (in_array($name, $this->interceptMethods) && method_exists($this, $mapMethod = 'call_' . $name)) {
+      return call_user_func_array(array($this, $mapMethod), $arguments);
+    }
+    else {
+      return parent::__call($name, $arguments);
+    }
+  }
+
+  /**
+   * Adds an action to the active fluent statement.
+   *
+   * Pass either an instance of the RulesActionInterface or the arguments as
+   * needed by rules_action().
+   *
+   * @param $name
+   * @param array $settings
+   * @return $this
+   *   Returns $this for fluent interface.
+   */
+  public function action($name, array $settings = array()) {
+    if (isset($this->fluentElement)) {
+      $this->fluentElement->action($name, $settings);
+    }
+    return $this;
+  }
+
+  /**
+   * Evaluates the conditional statement.
+   */
+  public function evaluate(RulesState $state) {
+    // Evaluate selected branches.
+    $branches = $this->selectBranches($state);
+    foreach ($branches as $branch) {
+      $branch->evaluate($state);
+    }
+  }
+
+  /**
+   * Asserts no variables (since a conditional is *conditionally* evaluated).
+   */
+  protected function variableInfoAssertions() {
+    return array();
+  }
+
+  /**
+   * Declares only parent state variables for individual branches.
+   *
+   * By definition, divergent branches should not have each other's variables.
+   */
+  protected function stateVariables($element = NULL) {
+    return $this->availableVariables();
+  }
+
+  /**
+   * Provides intersections of variables in all branches, at least one default.
+   */
+  public function providesVariables() {
+    if (!isset($this->providesVariables)) {
+      $this->providesVariables = parent::providesVariables();
+
+      if (!$this->isRoot()) {
+        // Collect variables.
+        $hasDefault = FALSE;
+        $childVariables = array();
+        /** @var $child RulesConditionalElement */
+        $isEmpty = FALSE;
+        foreach ($this->children as $child) {
+          $hasDefault = $hasDefault || $child->isDefault();
+          if ($childProvides = $child->providesVariables()) {
+            $childVariables[] = $childProvides;
+          }
+          else {
+            // Mark as empty if any branch does not provide variables. This is
+            // to avoid having to perform intersections over empty sets.
+            $isEmpty = TRUE;
+            break;
+          }
+        }
+
+        if ($hasDefault && !$isEmpty) {
+          // Collect intersection of variable names.
+          $names = NULL;
+          foreach ($childVariables as $variables) {
+            $newNames = array_keys($variables);
+            $names = isset($names) ? array_intersect($names, $newNames) : $newNames;
+          }
+          // Add variables.
+          if (isset($names)) {
+            foreach ($names as $name) {
+              // Determine if variable types are consistent.
+              $type = NULL;
+              foreach ($childVariables as $variables) {
+                if (isset($type) && $type != $variables[$name]['type']) {
+                  continue 2;
+                }
+                else {
+                  $type = $variables[$name]['type'];
+                }
+              }
+              // Add compatible variable.
+              if (isset($type)) {
+                $lastVariables = end($childVariables);
+                $this->providesVariables[$name] = $lastVariables[$name];
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return $this->providesVariables;
+  }
+
+  /**
+   * Selects the branches to evaluate for this conditional.
+   *
+   * @param RulesState $state
+   *   Rules state to use.
+   * @return RulesConditionalElement[]
+   *   An array of branches to evaluate.
+   */
+  abstract protected function selectBranches(RulesState $state);
+
+  /**
+   * Deletes the container and its children.
+   */
+  public function delete($keep_children = FALSE) {
+    parent::delete($keep_children);
+  }
+
+  public function dependencies() {
+    $modules = array('rules_conditional' => 1);
+    $modules += array_flip(parent::dependencies());
+    return array_keys($modules);
+  }
+
+  protected function exportSettings() {
+    $export = parent::exportSettings();
+    // Remove provided variables as plugin is only a container.
+    if (isset($export['PROVIDE'])) {
+      unset($export['PROVIDE']);
+    }
+    return $export;
+  }
+
+  public function resetInternalCache() {
+    parent::resetInternalCache();
+    $this->providesVariables = NULL;
+  }
+}
+
+/**
+ * Base conditional element plugin implementation.
+ */
+abstract class RulesConditionalElement extends RulesActionContainer implements RulesActionInterface {
+  /**
+   * The parent conditional.
+   * @var RulesConditionalContainer
+   */
+  protected $parent;
+
+  public function label() {
+    $info = $this->pluginInfo();
+    $label = isset($info['label']) ? $info['label'] : t('unlabeled');
+    return $label;
+  }
+
+  /**
+   * @todo Remove once http://drupal.org/node/1671344 is resolved.
+   */
+  public function setParent(RulesContainerPlugin $parent) {
+    if ($this->parent === $parent) {
+      return;
+    }
+    // Check parent class against the compatible class.
+    $pluginInfo = $this->pluginInfo();
+    if (empty($pluginInfo['embeddable'])) {
+      throw new RulesEvaluationException('This element cannot be embedded.', array(), $this, RulesLog::ERROR);
+    }
+    elseif (!$parent instanceof $pluginInfo['embeddable']) {
+      throw new RulesEvaluationException('The given container is incompatible with this element.', array(), $this, RulesLog::ERROR);
+    }
+
+    if (isset($this->parent) && ($key = array_search($this, $this->parent->children, TRUE)) !== FALSE) {
+      // Remove element from any previous parent.
+      unset($this->parent->children[$key]);
+      $this->parent->resetInternalCache();
+    }
+
+    // Update parent.
+    $this->parent = $parent;
+    $parent->children[] = $this;
+    $this->parent->resetInternalCache();
+  }
+
+  public function providesVariables() {
+    $provides = parent::providesVariables();
+    if (!$this->isRoot()) {
+      foreach ($this->children as $action) {
+        $provides += $action->providesVariables();
+      }
+    }
+    return $provides;
+  }
+
+  /**
+   * Determines whether this branch is default, i.e. covers the remainder of
+   * conditions outside of all non-default branches inside the conditional.
+   */
+  public function isDefault() {
+    return FALSE;
+  }
+
+  /**
+   * Determines whether this branch can be evaluated.
+   *
+   * Non-default plugins should override this method.
+   */
+  public function canEvaluate(RulesState $state) {
+    return $this->isDefault();
+  }
+
+  /**
+   * Gets the previous sibling element.
+   *
+   * @return RulesPlugin
+   */
+  public function getPreviousSibling() {
+    if (isset($this->parent)) {
+      $previous = NULL;
+      foreach ($this->parent->getIterator() as $element) {
+        if ($element === $this) {
+          return $previous;
+        }
+        $previous = $element;
+      }
+    }
+    // Otherwise, return nothing if no previous sibling is applicable.
+    return NULL;
+  }
+
+  /**
+   * Gets the next sibling element.
+   *
+   * @return RulesPlugin
+   */
+  public function getNextSibling() {
+    if (isset($this->parent)) {
+      $previous = NULL;
+      foreach ($this->parent->getIterator() as $element) {
+        if ($previous === $this) {
+          return $element;
+        }
+        $previous = $element;
+      }
+    }
+    // Otherwise, return nothing if no next sibling is applicable.
+    return NULL;
+  }
+
+  public function integrityCheck() {
+    parent::integrityCheck();
+    $this->checkSiblings();
+    return $this;
+  }
+
+  /**
+   * Checks basic conditional element integrity.
+   */
+  protected function checkSiblings() {
+    // Check a default element is the last.
+    if ($this->isDefault() && $this->getNextSibling()) {
+      throw new RulesIntegrityException(t('The "%plugin" cannot precede another element.', array('%plugin' => $this->plugin())), $this);
+    }
+
+    $pluginInfo = $this->pluginInfo();
+    // Check dependent element.
+    if (!empty($pluginInfo['conditional depends'])) {
+      if (!($previous = $this->getPreviousSibling()) || !in_array($previous->plugin(), $pluginInfo['conditional depends'])) {
+        $depends = $pluginInfo['conditional depends'];
+        $list = t('"%plugin"', array('%plugin' => array_shift($depends)));
+        foreach ($depends as $depend) {
+          $list = t('!preceding, "%plugin"', array(
+            '!preceding' => $list,
+            '%plugin' => $depend,
+          ));
+        }
+        throw new RulesIntegrityException(t('The "%plugin" must be preceded by one of: !list.', array('%plugin' => $this->plugin(), '!list' => $list)), $this);
+      }
+    }
+    // Check single element in a conditional container.
+    if (!$this->isRoot() && $this->parentElement() instanceof RulesConditionalContainer && !empty($pluginInfo['conditional single'])) {
+      $plugin = $this->plugin();
+      $previous = $this->getPreviousSibling();
+      $next = $this->getNextSibling();
+      do {
+        if (($previous && $previous->plugin() == $plugin) || ($next && $next->plugin() == $plugin)) {
+          throw new RulesIntegrityException(t('The "%plugin" cannot occur more than once within the enclosing container.', array('%plugin' => $this->plugin())), $this);
+        }
+      }
+      while (($previous && $previous = $previous->getPreviousSibling()) || ($next && $next = $next->getNextSibling()));
+    }
+  }
+
+  /**
+   * Deletes the element and its children.
+   */
+  public function delete($keep_children = FALSE) {
+    parent::delete($keep_children);
+  }
+
+  public function dependencies() {
+    $modules = array('rules_conditional' => 1);
+    $modules += array_flip(parent::dependencies());
+    return array_keys($modules);
+  }
+
+  protected function importChildren($export, $key = NULL) {
+    parent::importChildren($export, 'DO');
+  }
+
+  protected function exportChildren($key = NULL) {
+    return parent::exportChildren('DO');
+  }
+
+  protected function exportSettings() {
+    $export = parent::exportSettings();
+    // Remove provided variables as plugin is only a container.
+    if (isset($export['PROVIDE'])) {
+      unset($export['PROVIDE']);
+    }
+    return $export;
+  }
+}
+
+/**
+ * Base conditional element that uses a predicate.
+ */
+abstract class RulesConditionalPredicateElement extends RulesConditionalElement {
+  /**
+   * @var RulesCondition
+   */
+  protected $predicate;
+
+  public function __construct($predicate = NULL, $settings = array()) {
+    parent::__construct();
+    if (isset($predicate)) {
+      $predicate = is_object($predicate) && $predicate instanceof RulesConditionInterface ? $predicate : rules_condition($predicate, $settings);
+      $this->setPredicate($predicate);
+    }
+  }
+
+  /**
+   * Sets a condition as predicate.
+   */
+  protected function setPredicate($predicate) {
+    $this->predicate = $predicate;
+    $this->predicate->parent = $this;
+    // Set up variables with the new parent.
+    $this->resetInternalCache();
+  }
+
+  public function resetInternalCache() {
+    parent::resetInternalCache();
+    if (isset($this->predicate)) {
+      $this->predicate->resetInternalCache();
+    }
+  }
+
+  public function __sleep() {
+    $array = parent::__sleep();
+    $array['predicate'] = 'predicate';
+    return $array;
+  }
+
+  public function label() {
+    $text = '@plugin';
+    $variables = array('@plugin' => $this->pluginLabel());
+    if (isset($this->predicate)) {
+      $text = '@plugin: @label';
+      $variables['@label'] = $this->predicate->label();
+    }
+    return t($text, $variables);
+  }
+
+  public function pluginLabel() {
+    return parent::label();
+  }
+
+  public function integrityCheck() {
+    if (!isset($this->predicate)) {
+      throw new RulesIntegrityException(t('The conditional "%plugin" does not have a valid predicate.', array('%plugin' => $this->plugin())), $this);
+    }
+    parent::integrityCheck();
+    return $this;
+  }
+
+  /**
+   * Adds predicate assertions to state.
+   */
+  protected function stateVariables($element = NULL) {
+    if (!isset($element) || $element === $this->predicate) {
+      return parent::stateVariables();
+    }
+    else {
+      // Add assertions from the predicate.
+      $variables = parent::stateVariables($element);
+      if (isset($this->predicate) && $assertions = $this->predicate->call('variableInfoAssertions')) {
+        $variables = RulesData::addMetadataAssertions($variables, $assertions);
+      }
+      return $variables;
+    }
+  }
+
+  public function dependencies() {
+    $modules = array_flip(parent::dependencies());
+    if (isset($this->predicate)) {
+      $modules += array_flip($this->predicate->dependencies());
+    }
+    return array_keys($modules);
+  }
+
+  /**
+   * Negates the predicate.
+   */
+  public function negate($negate = TRUE) {
+    $this->predicate->negate($negate);
+    return $this;
+  }
+
+  /**
+   * Returns whether the predicate is negated.
+   */
+  public function isNegated() {
+    return $this->predicate->isNegated();
+  }
+
+  /**
+   * Evaluates the predicate.
+   */
+  public function canEvaluate(RulesState $state) {
+    return $this->predicate->evaluate($state);
+  }
+
+  /**
+   * Imports predicate.
+   */
+  protected function importChildren($export, $key = NULL) {
+    $this->importPredicate($export);
+    parent::importChildren($export, 'DO');
+  }
+
+  /**
+   * Imports predicate.
+   */
+  protected function importPredicate($export, $key = NULL) {
+    if (!isset($key)) {
+      $key = strtoupper($this->plugin());
+    }
+    $predicate = rules_plugin_factory('condition');
+    $this->setPredicate($predicate);
+    $predicate->import($export[$key]);
+  }
+
+  /**
+   * Exports predicate with actions.
+   */
+  protected function exportChildren($key = NULL) {
+    $export = $this->exportPredicate();
+    return $export + parent::exportChildren('DO');
+  }
+
+  /**
+   * Exports predicate.
+   */
+  protected function exportPredicate($key = NULL) {
+    $export = array();
+    if (!isset($key)) {
+      $key = strtoupper($this->plugin());
+    }
+    if (isset($this->predicate)) {
+      $export[$key] = $this->predicate->export();
+    }
+    return $export;
+  }
+
+  protected function exportFlat() {
+    return TRUE;
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.plugin.inc b/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.plugin.inc
new file mode 100644
index 0000000000000000000000000000000000000000..d0974663d0c2bc1b3328c08ff4547c68ccaebcee
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.plugin.inc
@@ -0,0 +1,589 @@
+<?php
+/**
+ * @file
+ * Rules plugin implementation.
+ */
+
+// Load new file to avoid registry rebuild problems.
+// TODO Remove this after a while.
+if (!class_exists('RulesConditionalContainer')) {
+  module_load_include('inc', 'rules_conditional', 'includes/rules_conditional.core');
+}
+
+/**
+ * Default if-else conditional statement.
+ *
+ * @method RulesConditional if() if($predicate, array $settings = array()) Adds an "if" statement.
+ * @method RulesConditional elseIf() elseIf($predicate, array $settings = array()) Adds an "else if" statement. This is an alias for self::if().
+ * @method RulesConditional else() else() Adds an "else" statement.
+ * @method RulesConditional action() action($name, array $settings = array()) Adds an action to the currently active statement. Pass arguments as rules_action() would need.
+ */
+class RulesConditional extends RulesConditionalContainer {
+  protected $itemName = 'conditional';
+
+  /**
+   * Intercepts calls to "if" and "else".
+   * @var array
+   */
+  protected $interceptMethods = array('if', 'elseIf', 'else');
+
+  public function __construct() {
+    parent::__construct();
+  }
+
+  /**
+   * Adds an "if" statement, for use with magic call.
+   */
+  protected function call_if($predicate, array $settings = array()) {
+    $this->fluentElement = $element = rules_conditional_if($predicate, $settings);
+    $element->setParent($this);
+    return $this;
+  }
+
+  /**
+   * Adds an "if" as an "else if" statement, for use with magic call.
+   */
+  protected function call_elseIf($predicate, array $settings = array()) {
+    return $this->call_if($predicate, $settings);
+  }
+
+  /**
+   * Adds an "else" statement, for use with magic call.
+   */
+  protected function call_else() {
+    $this->fluentElement = $element = rules_conditional_else();
+    $element->setParent($this);
+    return $this;
+  }
+
+  /**
+   * Selects the branches to evaluate for this conditional.
+   *
+   * @param RulesState $state
+   *   Rules state to use.
+   * @return RulesConditionalElement[]
+   *   An array of branches to evaluate.
+   */
+  protected function selectBranches(RulesState $state) {
+    /** @var $branch RulesConditionalElement */
+    foreach ($this->children as $branch) {
+      // Select the first matched branch.
+      if ($branch->canEvaluate($state)) {
+        return array($branch);
+      }
+    }
+    // Return no branch if none matched.
+    return array();
+  }
+
+  protected function exportFlat() {
+    return TRUE;
+  }
+}
+
+/**
+ * The "if" clause.
+ */
+class RulesConditionalIf extends RulesConditionalPredicateElement {
+  protected $itemName = 'if';
+
+  public function pluginLabel() {
+    $previous = $this->getPreviousSibling();
+    if ($previous && $previous instanceof RulesConditionalIf) {
+      return t('(Else) If', array(), array('context' => 'conditional rules'));
+    }
+    else {
+      return parent::pluginLabel();
+    }
+  }
+
+  /**
+   * Imports predicate.
+   */
+  protected function importPredicate($export, $key = NULL) {
+    $plugin = strtoupper($this->plugin());
+    $pluginInfo = $this->pluginInfo();
+    $keys = !empty($pluginInfo['import keys']) ? $pluginInfo['import keys'] : array($plugin);
+    foreach ($keys as $key) {
+      if (isset($export[$key])) {
+        parent::importPredicate($export, $key);
+        break;
+      }
+    }
+  }
+
+  /**
+   * Exports predicate.
+   */
+  protected function exportPredicate($key = NULL) {
+    if ($this->getPreviousSibling()) {
+      $key = 'ELSE IF';
+    }
+    return parent::exportPredicate($key);
+  }
+}
+
+/**
+ * The "else" clause.
+ */
+class RulesConditionalElse extends RulesConditionalElement {
+  protected $itemName = 'else';
+
+  public function __construct() {
+    parent::__construct();
+  }
+
+  public function isDefault() {
+    return TRUE;
+  }
+
+  protected function importChildren($export, $key = NULL) {
+    RulesContainerPlugin::importChildren($export);
+  }
+
+  protected function exportChildren($key = NULL) {
+    return RulesContainerPlugin::exportChildren();
+  }
+
+  protected function exportFlat() {
+    return TRUE;
+  }
+}
+
+/**
+ * Switch conditional container.
+ *
+ * @method RulesConditionalSwitch case() case($value, $fallThrough = FALSE, $valueIsSelector = FALSE) Adds a "case" statement.
+ * @method RulesConditionalSwitch defaultCase() defaultCase() Adds a "default case" statement.
+ * @method RulesConditionalSwitch action() action($name, array $settings = array()) Adds an action to the currently active statement. Pass arguments as rules_action() would need.
+ */
+class RulesConditionalSwitch extends RulesConditionalContainer {
+  protected $itemName = 'switch';
+
+  /**
+   * Intercepts calls to "case" and "defaultCase".
+   * @var array
+   */
+  protected $interceptMethods = array('case', 'defaultCase');
+
+  public function __construct($dataSelector = NULL) {
+    parent::__construct();
+    if (isset($dataSelector)) {
+      $this->settings['data:select'] = $dataSelector;
+    }
+  }
+
+  /**
+   * Adds a "case" statement, for use with magic call.
+   */
+  protected function call_case($settings = array(), $fallThrough = FALSE) {
+    $this->fluentElement = $element = rules_conditional_case($settings, $fallThrough);
+    $element->setParent($this);
+    return $this;
+  }
+
+  /**
+   * Adds a "defaultCase" statement, for use with magic call.
+   */
+  protected function call_defaultCase() {
+    $this->fluentElement = $element = rules_conditional_default_case();
+    $element->setParent($this);
+    return $this;
+  }
+
+  public function pluginParameterInfo() {
+    $parameterInfo = array(
+      'data' => array(
+        'type' => '*',
+        'label' => t('Data to match cases against'),
+        'description' => t('The data to be compared, specified by using a data selector, e.g. "node:author:name".'),
+        'restriction' => 'selector',
+        'allow null' => TRUE,
+      ),
+    );
+    return $parameterInfo;
+  }
+
+  /**
+   * Selects the branches to evaluate for this conditional.
+   *
+   * @param RulesState $state
+   *   Rules state to use.
+   * @return RulesConditionalElement[]
+   *   An array of branches to evaluate.
+   */
+  protected function selectBranches(RulesState $state) {
+    $branches = array();
+    // Collect all cases to be evaluated.
+    $fallThrough = FALSE;
+    foreach ($this->children as $case) {
+      /** @var $case RulesConditionalCase */
+      if ($case->canEvaluate($state)) {
+        $branches[] = $case;
+      }
+    }
+    return $branches;
+  }
+
+  protected function importChildren($export, $key = NULL) {
+    parent::importChildren($export, 'DO');
+  }
+
+  protected function exportChildren($key = NULL) {
+    return parent::exportChildren('DO');
+  }
+}
+
+/**
+ * Switch case.
+ */
+class RulesConditionalCase extends RulesConditionalElement {
+  protected $itemName = 'case';
+
+  /**
+   * @var RulesPlugin
+   */
+  protected $condition;
+
+  /**
+   * Evaluated condition results.
+   * @var array
+   */
+  protected $conditionResultCache = array();
+
+  public function __construct($settings = array(), $fallThrough = FALSE) {
+    parent::__construct();
+    if (isset($settings['value'])) {
+      $this->settings['value'] = $settings['value'];
+    }
+    elseif (isset($settings['value:select'])) {
+      $this->settings['value:select'] = $settings['value:select'];
+    }
+    $this->settings += array('fall_through' => $fallThrough);
+  }
+
+  public function __destruct() {
+    unset($this->condition);
+    unset($this->conditionResultCache);
+  }
+
+  public function forceSetUp() {
+    parent::forceSetUp();
+    $this->setUpCondition();
+  }
+
+  public function pluginParameterInfo() {
+    $parameterInfo = array(
+      'value' => array(
+        'type' => '*',
+        'label' => t('Data value'),
+        'description' => t('The value to compare the data with.'),
+        'allow null' => TRUE,
+      ),
+      'fall_through' => array(
+        'type' => 'boolean',
+        'label' => t('Fall through'),
+        'description' => t('Fall through to next case when complete. If this option is checked, the next case is automatically executed (regardless of the case value) when this case is finished. If not, the switch will terminate when the case is finished.'),
+        'optional' => TRUE,
+        'default value' => FALSE,
+        'restriction' => 'input',
+      ),
+    );
+    // Derive parameter info from switch variable selector.
+    $dataSelector = isset($this->parent->settings['data:select']) ? $this->parent->settings['data:select'] : NULL;
+    if ($wrapper = $this->applyDataSelector($dataSelector)) {
+      $parameterInfo['value']['type'] = $wrapper->type();
+    }
+    return $parameterInfo;
+  }
+
+  public function stateVariables($element = NULL) {
+    $this->forceSetUp();
+    if (!isset($element) || $element === $this->condition) {
+      return parent::stateVariables();
+    }
+    else {
+      // Add assertions from the condition.
+      $variables = parent::stateVariables($element);
+      if (isset($this->condition) && $assertions = $this->condition->call('variableInfoAssertions')) {
+        $variables = RulesData::addMetadataAssertions($variables, $assertions);
+      }
+      return $variables;
+    }
+  }
+
+  protected function setUpCondition() {
+    if (!isset($this->condition) && isset($this->parent)) {
+      // Prepare settings for 'data_is' condition.
+      $settings = array(
+        'data:select' => $this->parent->settings['data:select'],
+        'op' => '==',
+      );
+      if (isset($this->settings['value:select'])) {
+        $settings['value:select'] = $this->settings['value:select'];
+      }
+      elseif (isset($this->settings['value'])) {
+        $settings['value'] = $this->settings['value'];
+      }
+      else {
+        // Abort if settings are incomplete.
+        return;
+      }
+      // Set up 'data_is'.
+      $this->condition = rules_condition('data_is', $settings);
+      $this->condition->parent = $this;
+      $this->condition->processSettings();
+    }
+  }
+
+  /**
+   * Returns whether this case should fall through.
+   */
+  public function fallThrough() {
+    return !empty($this->settings['fall_through']);
+  }
+
+  /**
+   * Determines whether this branch can be evaluated.
+   */
+  public function canEvaluate(RulesState $state) {
+    $this->forceSetUp();
+    // Check if this element has fallen through.
+    if ($previous = $this->getPreviousSibling()) {
+      /** @var $previous self */
+      if ($previous instanceof self && $previous->fallThrough() && $previous->canEvaluate($state)) {
+        return TRUE;
+      }
+    }
+    // Evaluate condition for the given state once.
+    $this->conditionResultCache += array('state' => array(), 'result' => array());
+    if (empty($this->conditionResultCache['state']) || !$cacheKey = array_search($state, $this->conditionResultCache['state'], TRUE)) {
+      $cacheKey = count($this->conditionResultCache['state']);
+      $this->conditionResultCache['state'][$cacheKey] = $state;
+      $this->conditionResultCache['result'][$cacheKey] = $this->condition->evaluate($state);
+    }
+    return !empty($this->conditionResultCache['result'][$cacheKey]);
+  }
+
+  public function resetInternalCache() {
+    parent::resetInternalCache();
+    $this->condition = NULL;
+    $this->conditionResultCache = array();
+  }
+}
+
+/**
+ * Switch default case.
+ */
+class RulesConditionalDefaultCase extends RulesConditionalElement {
+  protected $itemName = 'default case';
+
+  public function __construct() {
+    parent::__construct();
+  }
+
+  public function isDefault() {
+    return TRUE;
+  }
+
+  protected function importChildren($export, $key = NULL) {
+    RulesContainerPlugin::importChildren($export);
+  }
+
+  protected function exportChildren($key = NULL) {
+    return RulesContainerPlugin::exportChildren();
+  }
+
+  protected function exportFlat() {
+    return TRUE;
+  }
+}
+
+/**
+ * While loop.
+ */
+class RulesConditionalWhile extends RulesConditionalPredicateElement {
+  protected $itemName = 'while';
+
+  public function providesVariables() {
+    return array();
+  }
+
+  public function evaluate(RulesState $state) {
+    $iteration = 0;
+    $maxIterations = variable_get('rules_conditional_max_iterations', RULES_CONDITIONAL_MAX_ITERATIONS);
+    while ($iteration < $maxIterations && $this->canEvaluate($state)) {
+      // Use a separate state so variables are available in the loop only.
+      $clonedState = clone $state;
+      parent::evaluate($clonedState);
+      $iteration ++;
+
+      // Retrieve variables.
+      foreach ($state->variables as $key => &$value) {
+        if (array_key_exists($key, $clonedState->variables)) {
+          $value = $clonedState->variables[$key];
+        }
+      }
+    }
+  }
+
+  /**
+   * Deletes the element and its children.
+   */
+  public function delete($keep_children = TRUE) {
+    parent::delete($keep_children);
+  }
+}
+
+/**
+ * Rule as condition set.
+ *
+ * A rule condition set evaluates a set of actions before evaluating the result
+ * condition on the new state.
+ */
+class RuleConditionSet extends RulesAnd {
+  protected $itemName = 'rule condition set';
+
+  /** @var RulesActionSet */
+  protected $actions = NULL;
+
+  public function __construct($variables = array()) {
+    parent::__construct($variables);
+
+    if (!isset($this->actions)) {
+      $this->actions = rules_action_set();
+      $this->actions->parent = $this;
+    }
+  }
+
+  public function __sleep() {
+    return parent::__sleep() + drupal_map_assoc(array('actions'));
+  }
+
+  /**
+   * Get an iterator over all contained actions.
+   * @return RulesRecursiveElementIterator
+   */
+  public function actions() {
+    return $this->actions->getIterator();
+  }
+
+  /**
+   * Returns the contained action set to evaluate the result for.
+   */
+  public function actionContainer() {
+    return $this->actions;
+  }
+
+  /**
+   * Adds an action to the rule condition. Pass either an instance of the
+   * RulesActionInterface or the arguments as needed by rules_action().
+   *
+   * This method can be chained.
+   */
+  public function action($name, $settings = array()) {
+    $this->actions->action($name, $settings);
+    return $this;
+  }
+
+  /**
+   * Returns a recursive iterator for result conditions.
+   * @return RulesRecursiveElementIterator
+   */
+  public function conditions() {
+    return parent::getIterator();
+  }
+
+  public function evaluate(RulesState $state) {
+    rules_log('Evaluating actions of rule condition set %label.', array('%label' => $this->label()), RulesLog::INFO, $this);
+    $this->actions->evaluate($state);
+    rules_log('Evaluating result conditions of rule condition set %label.', array('%label' => $this->label()), RulesLog::INFO, $this);
+    return parent::evaluate($state);
+  }
+
+  public function integrityCheck() {
+    parent::integrityCheck();
+    $this->actions->integrityCheck();
+    return $this;
+  }
+
+  public function access() {
+    return $this->actions->access() && parent::access();
+  }
+
+  public function dependencies() {
+    $modules = array('rules_conditional' => 1);
+    $modules += array_flip($this->actions->dependencies());
+    $modules += array_flip(parent::dependencies());
+    return array_keys($modules);
+  }
+
+  public function destroy() {
+    $this->actions->destroy();
+    parent::destroy();
+  }
+
+  /**
+   * @return RulesRecursiveElementIterator
+   */
+  public function getIterator() {
+    $array = array_merge(array($this->actions), $this->children);
+    return new RulesRecursiveElementIterator($array);
+  }
+
+  protected function stateVariables($element = NULL) {
+    $vars = $this->availableVariables();
+    if (isset($element) && $element !== $this->actions) {
+      // Provide action variables for conditions.
+      foreach ($this->actions->children as $child) {
+        $vars += $child->providesVariables();
+      }
+      // Provide condition state variables.
+      foreach ($this->children as $child) {
+        if ($child === $element) {
+          break;
+        }
+        $vars += $child->providesVariables();
+        // Assert variable info from child conditions.
+        if (!$child->isNegated() && ($assertions = $child->variableInfoAssertions())) {
+          $vars = RulesData::addMetadataAssertions($vars, $assertions);
+        }
+      }
+    }
+    return $vars;
+  }
+
+  protected function exportChildren($key = NULL) {
+    $export = array();
+    if ($this->actions->children) {
+      $export += $this->actions->exportChildren('DO');
+    }
+    $export += parent::exportChildren('RESULT');
+    return $export;
+  }
+
+  protected function importChildren($export, $key = NULL) {
+    if (!empty($export['DO'])) {
+      $this->actions->importChildren($export, 'DO');
+    }
+    parent::importChildren($export, 'RESULT');
+  }
+
+  public function __clone() {
+    parent::__clone();
+    $this->actions = clone $this->actions;
+    $this->actions->parent = $this;
+  }
+
+  /**
+   * Deletes nested elements by default.
+   */
+  public function delete($keep_children = FALSE) {
+    parent::delete($keep_children);
+  }
+
+  public function resetInternalCache() {
+    parent::resetInternalCache();
+    $this->actions->resetInternalCache();
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.rules_admin.inc b/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.rules_admin.inc
new file mode 100644
index 0000000000000000000000000000000000000000..f223674d003afa4d00eb6f4bfb4f80a6cac4d9c2
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.rules_admin.inc
@@ -0,0 +1,104 @@
+<?php
+/**
+ * @file
+ * Extensions to Rules UI.
+ */
+
+// Make Rules form functions available.
+module_load_include('inc', 'rules', 'ui/ui.forms');
+
+/**
+ * Provides menu callbacks to extend Rules UI for conditionals.
+ */
+function _rules_conditional_rules_ui_menu_alter(&$items, $base_path, $base_count) {
+  // Use existing "add" menu item as extension base.
+  $add_item = $items[$base_path . '/manage/%rules_config/add/%rules_element'];
+
+  // Extend "add".
+  $items[$base_path . '/manage/%rules_config/add-predicate/%rules_element'] = array(
+    'page arguments' => array('rules_conditional_add_predicate') + $add_item['page arguments'],
+    'file' => 'includes/rules_conditional.rules_admin.inc',
+    'file path' => drupal_get_path('module', 'rules_conditional'),
+  ) + $add_item;
+
+  // Add temporary fix for http://drupal.org/node/1666890.
+  // TODO Remove when http://drupal.org/node/1676998 is fixed.
+  $items[$base_path . '/manage/%rules_config/add-case/%rules_element'] = array(
+    'page arguments' => array('rules_conditional_add_case') + $add_item['page arguments'],
+    'file' => 'includes/rules_conditional.rules_admin.inc',
+    'file path' => drupal_get_path('module', 'rules_conditional'),
+  ) + $add_item;
+}
+
+/**
+ * Provides a variant of rules_ui_add_element() to create a condition as the
+ * predicate of a plugin.
+ */
+function rules_conditional_add_predicate($form, &$form_state, $rules_config, $plugin_name, RulesContainerPlugin $parent, $base_path) {
+  $cache = rules_get_cache();
+  if (!isset($cache['plugin_info'][$plugin_name]['class'])) {
+    drupal_not_found();
+    exit;
+  }
+
+  // Build condition form.
+  RulesPluginUI::$basePath = $base_path;
+  if (!isset($form_state['rules_element'])) {
+    RulesPluginUI::formDefaults($form, $form_state);
+    $form_state += array('parent_element' => $parent, 'plugin' => $plugin_name);
+
+    $form['element_name'] = array(
+      '#type' => 'select',
+      '#title' => t('Select the condition to add for %element', array('%element' => $plugin_name)),
+      '#options' => RulesPluginUI::getOptions('condition'),
+      '#ajax' => rules_ui_form_default_ajax() + array(
+        'trigger_as' => array('name' => 'continue'),
+      ),
+    );
+    $form['continue'] = array(
+      '#type' => 'submit',
+      '#name' => 'continue',
+      '#value' => t('Continue'),
+      '#ajax' => rules_ui_form_default_ajax(),
+    );
+  }
+
+  // Build element form.
+  if (isset($form_state['rules_element'])) {
+    $form_state['rules_element']->form($form, $form_state, array('button' => TRUE, 'init' => TRUE));
+    $form['#validate'][] = 'rules_ui_edit_element_validate';
+    $form['#submit'][] = 'rules_ui_edit_element_submit';
+  }
+  else {
+    $form['#submit'][] = 'rules_ui_add_element_submit';
+  }
+  return $form;
+}
+
+/**
+ * Provides a variant of rules_ui_add_element() to create a case.
+ * @todo Remove when http://drupal.org/node/1676998 is fixed.
+ */
+function rules_conditional_add_case($form, &$form_state, $rules_config, $plugin_name, RulesContainerPlugin $parent, $base_path) {
+  // Build condition form.
+  RulesPluginUI::$basePath = $base_path;
+  if (!isset($form_state['rules_element'])) {
+    RulesPluginUI::formDefaults($form, $form_state);
+    $form_state += array('parent_element' => $parent, 'plugin' => $plugin_name);
+
+    $element = rules_plugin_factory($plugin_name);
+    // Always add the new element at the bottom, thus set an appropriate weight.
+    $iterator = $parent->getIterator();
+    if ($sibling = end($iterator)) {
+      $element->weight = $sibling->weight + 1;
+    }
+    $element->setParent($parent);
+    $form_state['rules_element'] = $element;
+  }
+
+  // Build element form.
+  $form_state['rules_element']->form($form, $form_state, array('button' => TRUE, 'init' => TRUE));
+  $form['#validate'][] = 'rules_ui_edit_element_validate';
+  $form['#submit'][] = 'rules_ui_edit_element_submit';
+  return $form;
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.ui.inc b/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.ui.inc
new file mode 100644
index 0000000000000000000000000000000000000000..9a1ac7b880d651f5b3700ba534a4bc85d394ff83
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/includes/rules_conditional.ui.inc
@@ -0,0 +1,275 @@
+<?php
+/**
+ * @file
+ * Plugin UI implementation.
+ */
+
+/**
+ * Base UI for providing customized operation links.
+ */
+class RulesConditionalPluginUI extends RulesContainerPluginUI {
+  public function operations() {
+    $ops = parent::operations();
+    $elementChildrenTypes = array();
+    foreach ($this->element->getIterator() as $element) {
+      $plugin = $element->plugin();
+      $elementChildrenTypes[$plugin] = $plugin;
+    }
+    foreach (rules_fetch_data('plugin_info') as $plugin => $info) {
+      // Remove operations as specified in the plugin info.
+      $remove = FALSE;
+      if (!empty($info['conditional single']) && isset($elementChildrenTypes[$plugin])) {
+        $remove = TRUE;
+      }
+      elseif (!empty($info['conditional depends']) && !array_intersect($elementChildrenTypes, $info['conditional depends'])) {
+        $remove = TRUE;
+      }
+      // Remove link.
+      if ($remove) {
+        unset($ops['#links']['add_' . $plugin]);
+      }
+    }
+    return $ops;
+  }
+}
+
+/**
+ * Empty UI for doing nothing with the plugin.
+ */
+class RulesConditionalEmptyUI extends RulesConditionalPluginUI {
+  public function form(&$form, &$form_state, $options = array()) {
+    // Save element.
+    $this->element->save();
+    // Redirect right away.
+    $path = RulesPluginUI::defaultRedirect($this->element);
+    drupal_goto($path);
+  }
+
+  public function operations() {
+    $ops = parent::operations();
+    // Get rid of the 'edit' link.
+    if (isset($ops['#links']['edit'])) {
+      unset($ops['#links']['edit']);
+    }
+    return $ops;
+  }
+}
+
+/**
+ * UI for configuring the predicate in a conditional branch.
+ */
+class RulesConditionalPredicateUI extends RulesContainerPluginUI {
+  /**
+   * @var RulesPlugin
+   */
+  protected $predicate;
+
+  public function __construct(FacesExtendable $object) {
+    parent::__construct($object);
+    if (!$this->element instanceof RulesConditionalPredicateElement) {
+      return;
+    }
+    $this->predicate = $this->property('predicate');
+  }
+
+  /**
+   * Delegates the form to the predicate.
+   */
+  public function form(&$form, &$form_state, $options = array()) {
+    $baseCount = count(explode('/', RulesPluginUI::$basePath));
+    $op = arg($baseCount + 2);
+    if ($op == 'add') {
+      // Redirect to extended path.
+      $pathAddPredicate = RulesPluginUI::path($this->element->root()->name, 'add-predicate', $this->element->parentElement(), $this->element->plugin());
+      drupal_goto($pathAddPredicate);
+    }
+
+    if (isset($this->predicate)) {
+      // Build form for predicate.
+      $form_state['rules_element'] = $this->predicate;
+      $this->predicate->form($form, $form_state, $options);
+      $heading = '<h4 class="rules-form-heading">';
+      $heading .= t('Condition');
+      $heading .= '</h4>';
+      $form['parameter']['#prefix'] = $heading;
+    }
+  }
+
+  /**
+   * Delegates the form validator to the predicate.
+   */
+  public function form_validate($form, &$form_state) {
+    if (isset($this->predicate)) {
+      // Validate form for predicate.
+      $form_state['rules_element'] = $this->predicate;
+      $this->predicate->form_validate($form, $form_state);
+    }
+  }
+
+  /**
+   * Delegates the form submit handler to the predicate.
+   */
+  public function form_submit($form, &$form_state) {
+    if (isset($this->predicate)) {
+      // Handle form submission for predicate.
+      $form_state['rules_element'] = $this->predicate;
+      $this->predicate->form_submit($form, $form_state);
+    }
+  }
+
+  public function buildContent() {
+    $content = NULL;
+    if (isset($this->predicate)) {
+      // Build default content.
+      $content = parent::buildContent();
+      // Build parameter description from predicate.
+      $predicateContent = $this->predicate->buildContent();
+      if (isset($predicateContent['description']['parameter'])) {
+        $content['description']['parameter'] = $predicateContent['description']['parameter'];
+      }
+    }
+    return $content;
+  }
+}
+
+/**
+ * UI for supporting option lists in the case value.
+ */
+class RulesConditionalCaseUI extends RulesConditionalPluginUI {
+  public function form(&$form, &$form_state, $options = array(), $iterator = NULL) {
+    // TODO Remove when http://drupal.org/node/1676998 is fixed.
+    $baseCount = count(explode('/', RulesPluginUI::$basePath));
+    $op = arg($baseCount + 2);
+    if ($op == 'add') {
+      // Redirect to extended path.
+      $pathAddPredicate = RulesPluginUI::path($this->element->root()->name, 'add-case', $this->element->parentElement(), $this->element->plugin());
+      drupal_goto($pathAddPredicate);
+    }
+
+    $options['init'] = FALSE;
+    parent::form($form, $form_state, $options, $iterator);
+  }
+
+  protected function getParameterForm($name, $info, $settings, &$mode) {
+    $form = parent::getParameterForm($name, $info, $settings, $mode);
+    if ($name == 'value' && $mode == 'input') {
+      $labels = $this->getValueOptionLabels();
+      if (isset($labels)) {
+        $form['settings']['value']['#type'] = 'select';
+        $form['settings']['value']['#options'] = $labels;
+        $form['settings']['value']['#empty_value'] = '';
+      }
+    }
+    return $form;
+  }
+
+  public function buildContent() {
+    $content = parent::buildContent();
+    // Use option label for text.
+    if (isset($this->element->settings['value'])) {
+      $value = $this->element->settings['value'];
+      $labels = $this->getValueOptionLabels();
+      $content['label']['#markup'] = t('@plugin: @case', array(
+        '@plugin' => $this->element->label(),
+        '@case' => isset($labels[$value]) ? $labels[$value] : $value,
+      ));
+      unset($content['description']['parameter']['value']);
+    }
+    return $content;
+  }
+
+  public function getValueOptionLabels() {
+    $parent = $this->element->parentElement();
+    if (isset($parent->settings['data:select'])) {
+      $dataSelector = $parent->settings['data:select'];
+      if ($wrapper = $this->element->applyDataSelector($dataSelector)) {
+        $dataInfo = $wrapper->info();
+        if (!empty($dataInfo['options list'])) {
+          return $wrapper->optionsList('view');
+        }
+      }
+    }
+  }
+}
+
+/**
+ * UI for while loops to add extra description.
+ */
+class RulesConditionalWhileUI extends RulesConditionalPredicateUI {
+  public function form(&$form, &$form_state, $options = array()) {
+    parent::form($form, $form_state, $options);
+
+    // Display help for while loop.
+    $form['while_help'] = array(
+      '#weight' => -5,
+    );
+    $form['while_help']['text'] = array(
+      '#prefix' => '<p>',
+      '#markup' => t('Configure the condition for this while loop to continue. This loop will not end until the condition evaluates to false, or the maximum limit on the number of iterations has been reached.'),
+      '#suffix' => '</p>',
+    );
+
+    // Display help to change iteration limit.
+    $maxIterations = variable_get('rules_conditional_max_iterations', RULES_CONDITIONAL_MAX_ITERATIONS);
+    $form['while_help']['description'] = array(
+      '#prefix' => '<div class="description"><p>',
+      '#markup' => t('The current limit is @limit. Note that this cannot be changed in the user interface. Although it can be changed by setting the %variable variable to another value, raising this limit must be done with care since Rules evaluation can be resource-intensive.', array('@limit' => $maxIterations, '%variable' => 'rules_conditional_max_iterations')),
+      '#suffix' => '</p></div>',
+    );
+  }
+}
+
+/**
+ * UI for rule condition set.
+ *
+ * This UI is adapted from RulesRuleUI by swapping conditions for actions.
+ */
+class RuleConditionSetUI extends RulesConditionContainerUI {
+  /** @var RulesActionContainer */
+  protected $actions;
+  /** @var RuleConditionSet */
+  protected $set;
+
+  public function __construct(FacesExtendable $object) {
+    parent::__construct($object);
+    $this->set = $object;
+    /** @var RuleConditionSet $object  */
+    $this->actions = $object->actionContainer();
+  }
+
+  public function form(&$form, &$form_state, $options = array()) {
+    $form_state['rules_element'] = $this->set;
+
+    $form += array('actions' => array('#weight' => -5, '#tree' => TRUE));
+    $this->actions->form($form['actions'], $form_state);
+
+    // Add condition result form.
+    $iterator = new RecursiveIteratorIterator($this->set->conditions(), RecursiveIteratorIterator::SELF_FIRST);
+    parent::form($form, $form_state, $options, $iterator);
+    $form['elements']['#caption'] = t('Result conditions');
+    $form['negate']['#weight'] = 1;
+    // Hide nested elements during creation.
+    $form['elements']['#access'] = empty($options['init']);
+    $form['actions']['elements']['#access'] = empty($options['init']);
+
+    $form['help']['#markup'] = t('A rule condition set evaluates the actions first. Variables provided by the actions then can be used to evaluate the result conditions (as an "AND", i.e. evaluating to TRUE if conditions are empty).');
+    $form['help']['#weight'] = -5;
+
+    $form_state['redirect'] = RulesPluginUI::path($this->element->root()->name);
+    if (!empty($options['button'])) {
+      $form['submit']['#value'] = t('Save changes');
+    }
+  }
+
+  /**
+   * Applies the values of the form to the rule configuration.
+   */
+  function form_extract_values($form, &$form_state) {
+    // Extract action container values.
+    if (isset($form['actions'])) {
+      $this->actions->extender('RulesActionContainerUI')->form_extract_values($form['actions'], $form_state);
+    }
+    // Extract condition container values.
+    parent::form_extract_values($form, $form_state);
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.info b/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.info
new file mode 100644
index 0000000000000000000000000000000000000000..511ed70851f5070a1bd1b62eef5949f6266b819f
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.info
@@ -0,0 +1,16 @@
+name = Conditional Rules
+description = Provides conditional control elements within action containers.
+core = 7.x
+dependencies[] = rules
+package = Rules
+files[] = includes/rules_conditional.core.inc
+files[] = includes/rules_conditional.plugin.inc
+files[] = includes/rules_conditional.ui.inc
+files[] = tests/rules_conditional.test
+
+; Information added by drupal.org packaging script on 2012-11-12
+version = "7.x-1.0-beta2"
+core = "7.x"
+project = "rules_conditional"
+datestamp = "1352752992"
+
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.install b/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.install
new file mode 100644
index 0000000000000000000000000000000000000000..706cd62e55cfb0d7f1265bc43ec0a05bdbf747df
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.install
@@ -0,0 +1,12 @@
+<?php
+/**
+ * @file
+ * Module installation file.
+ */
+
+/**
+ * Implements hook_uninstall().
+ */
+function rules_conditional_uninstall() {
+  variable_del('rules_conditional_max_iterations');
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.module b/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.module
new file mode 100644
index 0000000000000000000000000000000000000000..bc7aa525ba45c10d2984c967de8bedb8c4ae28b4
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.module
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @file
+ * Main module file.
+ */
+
+/**
+ * Maximum number of loop iterations (effectively infinite).
+ */
+define('RULES_CONDITIONAL_MAX_ITERATIONS', 65535);
+
+/**
+ * Implements hook_rules_ui_menu_alter().
+ */
+function rules_conditional_rules_ui_menu_alter(&$items, $base_path, $base_count) {
+  module_load_include('inc', 'rules_conditional', 'includes/rules_conditional.rules_admin');
+  _rules_conditional_rules_ui_menu_alter($items, $base_path, $base_count);
+}
+
+/**
+ * Creates a conditional.
+ */
+function rules_conditional() {
+  return new RulesConditional();
+}
+
+/**
+ * Creates an "if".
+ */
+function rules_conditional_if($predicate, array $settings = array()) {
+  return new RulesConditionalIf($predicate, $settings);
+}
+
+/**
+ * Creates an "else".
+ */
+function rules_conditional_else() {
+  return new RulesConditionalElse();
+}
+
+/**
+ * Creates a "switch".
+ */
+function rules_conditional_switch($data_selector) {
+  return new RulesConditionalSwitch($data_selector);
+}
+
+/**
+ * Creates a "case".
+ */
+function rules_conditional_case(array $settings = array(), $fall_through = FALSE) {
+  return new RulesConditionalCase($settings, $fall_through);
+}
+
+/**
+ * Creates a "default case".
+ */
+function rules_conditional_default_case() {
+  return new RulesConditionalDefaultCase();
+}
+
+/**
+ * Creates a "while".
+ */
+function rules_conditional_while($predicate, array $settings = array()) {
+  return new RulesConditionalWhile($predicate, $settings);
+}
+
+/**
+ * Creates a rule condition set.
+ */
+function rule_condition_set(array $variables = array()) {
+  return new RuleConditionSet($variables);
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.rules.inc b/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..d8e1e91141eea42d834a9fcd04fe3bb6c2d04670
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/rules_conditional.rules.inc
@@ -0,0 +1,99 @@
+<?php
+/**
+ * @file
+ * Rules integration file.
+ */
+
+/**
+ * Implements hook_rules_plugin_info().
+ */
+function rules_conditional_rules_plugin_info() {
+  return array(
+    'conditional' => array(
+      'label' => t('Conditional', array(), array('context' => 'conditional rules')),
+      'class' => 'RulesConditional',
+      'embeddable' => 'RulesActionContainer',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalEmptyUI',
+        ),
+      ),
+    ),
+    'if' => array(
+      'label' => t('If', array(), array('context' => 'conditional rules')),
+      'class' => 'RulesConditionalIf',
+      'embeddable' => 'RulesConditional',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalPredicateUI',
+        ),
+      ),
+      'import keys' => array('IF', 'ELSE IF'),
+    ),
+    'else' => array(
+      'label' => t('Else', array(), array('context' => 'conditional rules')),
+      'class' => 'RulesConditionalElse',
+      'embeddable' => 'RulesConditional',
+      'conditional single' => TRUE,
+      'conditional depends' => array('if'),
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalEmptyUI',
+        ),
+      ),
+    ),
+    'switch' => array(
+      'label' => t('Switch', array(), array('context' => 'conditional rules')),
+      'class' => 'RulesConditionalSwitch',
+      'embeddable' => 'RulesActionContainer',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalPluginUI',
+        ),
+      ),
+    ),
+    'case' => array(
+      'label' => t('Case', array(), array('context' => 'conditional rules')),
+      'class' => 'RulesConditionalCase',
+      'embeddable' => 'RulesConditionalSwitch',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalCaseUI',
+        ),
+      ),
+    ),
+    'default case' => array(
+      'label' => t('Default case', array(), array('context' => 'conditional rules')),
+      'class' => 'RulesConditionalDefaultCase',
+      'embeddable' => 'RulesConditionalSwitch',
+      'conditional single' => TRUE,
+      'conditional depends' => array('case'),
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalEmptyUI',
+        ),
+      ),
+    ),
+    'while' => array(
+      'label' => t('While', array(), array('context' => 'conditional rules')),
+      'class' => 'RulesConditionalWhile',
+      'embeddable' => 'RulesActionContainer',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalWhileUI',
+        ),
+      ),
+    ),
+    'rule condition set' => array(
+      'label' => t('Condition set (rule)', array(), array('context' => 'conditional rules')),
+      'class' => 'RuleConditionSet',
+      'embeddable' => FALSE,
+      'component' => TRUE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RuleConditionSetUI',
+        ),
+      ),
+    ),
+  );
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/tests/conditional_test_export.txt b/profiles/wcm_base/modules/contrib/rules_conditional/tests/conditional_test_export.txt
new file mode 100644
index 0000000000000000000000000000000000000000..03ae10581843485e9fc170859f268d33795b6d32
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/tests/conditional_test_export.txt
@@ -0,0 +1,20 @@
+{ "conditional_test" : {
+    "PLUGIN" : "action set",
+    "REQUIRES" : [ "rules_conditional", "rules_conditional_test", "rules" ],
+    "USES VARIABLES" : { "node" : { "type" : "node", "label" : "Node" } },
+    "ACTION SET" : [
+      { "CONDITIONAL" : [
+          {
+            "IF" : { "data_is" : { "data" : [ "node:title" ], "value" : "if" } },
+            "DO" : [ { "rules_conditional_test_throw" : { "message" : "if" } } ]
+          },
+          {
+            "ELSE IF" : { "data_is" : { "data" : [ "node:title" ], "value" : "else if" } },
+            "DO" : [ { "rules_conditional_test_throw" : { "message" : "else if" } } ]
+          },
+          { "ELSE" : [ { "rules_conditional_test_throw" : { "message" : "else" } } ] }
+        ]
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/tests/rule_condition_set_test_export.txt b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rule_condition_set_test_export.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c1108eb3f946f0308a30f66497be82f1cdae10b2
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rule_condition_set_test_export.txt
@@ -0,0 +1,15 @@
+{ "rule_condition_set_test" : {
+    "PLUGIN" : "rule condition set",
+    "REQUIRES" : [ "rules_conditional", "rules" ],
+    "USES VARIABLES" : { "test" : { "type" : "text", "label" : "Test" } },
+    "DO" : [
+      { "variable_add" : {
+          "USING" : { "type" : "text" },
+          "PROVIDE" : { "variable_added" : { "test2" : "Test 2" } }
+        }
+      },
+      { "data_set" : { "data" : [ "test2" ], "value" : [ "test" ] } }
+    ],
+    "RESULT" : [ { "data_is" : { "data" : [ "test2" ], "value" : "condition set" } } ]
+  }
+}
\ No newline at end of file
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional.test b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional.test
new file mode 100644
index 0000000000000000000000000000000000000000..e3fe1f44232906ea8fe507fb962d7b4f990a8674
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional.test
@@ -0,0 +1,825 @@
+<?php
+/**
+ * @file
+ * SimpleTest testing suites.
+ */
+
+/**
+ * Base class for conditional rules testing.
+ */
+abstract class RulesConditionalBaseTestCase extends DrupalWebTestCase {
+  /**
+   * Asserts a test message from the execution of a rule configuration.
+   *
+   * @param $error
+   * @param RulesPlugin $config
+   * @param array $args
+   * @param string $message
+   */
+  protected function assertExecution($error, $config, array $args = array(), $message = '') {
+    if (empty($message)) {
+      $message = t('Message "@error" captured by executing rule configuration.', array('@error' => $error));
+    }
+    try {
+      $config->executeByArgs($args);
+      return $this->fail($message);
+    }
+    catch (RulesConditionalTestException $ex) {
+      return $this->assertEqual($error, $ex->getMessage(), $message);
+    }
+  }
+
+  /**
+   * Retrieves a file in the test directory.
+   */
+  protected function getFileContents($fileName) {
+    $filePath = drupal_get_path('module', 'rules_conditional_test') . '/' . $fileName;
+    return file_get_contents($filePath);
+  }
+}
+
+/**
+ * Framework tests.
+ */
+class RulesConditionalFrameworkTestCase extends RulesConditionalBaseTestCase {
+  /**
+   * Returns test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Framework',
+      'description' => 'Test the core conditional plugin framework.',
+      'group' => 'Conditional Rules',
+    );
+  }
+
+  /**
+   * Sets up test case.
+   */
+  protected function setUp() {
+    parent::setUp('rules_conditional_test');
+  }
+
+  /**
+   * Tests plugin label.
+   */
+  public function testPluginLabel() {
+    $container = new RulesConditionalTestStubContainer();
+    $label = $container->label();
+    $this->assertEqual('Stub conditional', $label, 'Default conditional container label uses the plugin label.');
+
+    $branch = new RulesConditionalTestStubElement();
+    $label = $branch->label();
+    $this->assertEqual('Stub conditional element', $label, 'Default conditional element label uses the plugin label.');
+  }
+
+  /**
+   * Tests intercepting a method.
+   */
+  public function testInterceptMethod() {
+    $message = 'Test magic method is intercepted.';
+    try {
+      $container = new RulesConditionalTestStubContainer();
+      $container->test();
+      $this->fail($message);
+    }
+    catch (Exception $ex) {
+      $this->assertEqual('intercept', $ex->getMessage(), $message);
+    }
+  }
+
+  /**
+   * Tests fluent interface.
+   */
+  public function testFluentInterface() {
+    $container = new RulesConditionalTestStubContainer();
+    $actions1 = new RulesActionSet();
+    $container->fluent($actions1)->action('drupal_message', array('message' => '[site:name]'));
+    $this->assertEqual(1, count($actions1->elements()), 'Fluent interface adds action to the active element.');
+    $actions2 = new RulesActionSet();
+    $container->fluent($actions2)->action('drupal_message', array('message' => '[site:name]'));
+    $this->assertEqual(1, count($actions1->elements()), 'Fluent interface does not add action to a previously active element.');
+  }
+
+  /**
+   * Tests branch sibling methods.
+   */
+  public function testBranchSibling() {
+    // Set up stub objects.
+    $container = new RulesConditionalTestStubContainer();
+    $branch1 = new RulesConditionalTestStubElement();
+    $branch1->setParent($container);
+    $branch2 = new RulesConditionalTestStubElement();
+    $branch2->setParent($container);
+    $orphanBranch = new RulesConditionalTestStubElement();
+
+    // Test obtaining siblings.
+    $this->assertIdentical($branch2, $branch1->getNextSibling(), 'Next sibling branch can be obtained.');
+    $this->assertIdentical($branch1, $branch2->getPreviousSibling(), 'Previous sibling branch can be obtained.');
+    $this->assertNull($branch1->getPreviousSibling(), 'First branch has no previous sibling.');
+    $this->assertNull($branch2->getNextSibling(), 'Last branch has no next sibling.');
+
+    // Test obtaining siblings from an orphan element.
+    $this->assertNull($orphanBranch->getNextSibling(), 'Orphan branch has no next sibling.');
+    $this->assertNull($orphanBranch->getPreviousSibling(), 'Orphan branch has no previous sibling.');
+  }
+
+  /**
+   * Tests integrity check.
+   */
+  public function testIntegrityCheck() {
+    $container = new RulesConditionalTestStubContainer();
+
+    $message = 'Dependent element does not validate without a required preceding element.';
+    $dependent = new RulesConditionalTestStubDependentElement();
+    $dependent->setParent($container);
+    try {
+      $dependent->integrityCheck();
+      $this->fail($message);
+    }
+    catch (RulesIntegrityException $ex) {
+      $element = new RulesConditionalTestStubElement();
+      $element->weight = -1;
+      $element->setParent($container);
+      $container->sortChildren();
+      $dependent->integrityCheck();
+      $this->pass($message);
+    }
+
+    $message = 'A single element does not validate if it occurs more than once in a conditional container.';
+    $single = new RulesConditionalTestStubSingleElement();
+    $single->setParent($container);
+    try {
+      $single->integrityCheck();
+      $single2 = new RulesConditionalTestStubSingleElement();
+      $single2->setParent($container);
+      try {
+        $single->integrityCheck();
+        $this->fail($message);
+      }
+      catch (RulesIntegrityException $ex) {
+        try {
+          $single2->integrityCheck();
+          $this->fail($message);
+        }
+        catch (RulesIntegrityException $ex) {
+          $single2->delete();
+          try {
+            $single->integrityCheck();
+            $this->pass($message);
+          }
+          catch (RulesIntegrityException $ex) {
+            $this->fail($message);
+          }
+        }
+      }
+    }
+    catch (RulesIntegrityException $ex) {
+      $this->fail($message);
+    }
+
+    $message = 'A default element does not validate if it precedes any element.';
+    $default = new RulesConditionalTestStubDefaultElement();
+    try {
+      $default->setParent($container);
+      $default->integrityCheck();
+      try {
+        $element = new RulesConditionalTestStubElement();
+        $element->setParent($container);
+        $default->integrityCheck();
+        $this->fail($message);
+      }
+      catch (RulesIntegrityException $ex) {
+        $element->delete();
+        try {
+          $default->integrityCheck();
+          $this->pass($message);
+        }
+        catch (RulesIntegrityException $ex) {
+          $this->fail($message);
+        }
+      }
+    }
+    catch (RulesIntegrityException $ex) {
+      $this->fail($message);
+    }
+  }
+
+  /**
+   * Tests basic evaluation.
+   */
+  public function testEvaluate() {
+    // Set up stub objects.
+    $container = new RulesConditionalTestStubContainer();
+    $branch = new RulesConditionalTestStubElement();
+    $branch->action('rules_conditional_test_throw', array('message' => 'evaluate'))
+      ->setParent($container);
+    $defaultBranch = new RulesConditionalTestStubDefaultElement();
+    $defaultBranch->action('rules_conditional_test_throw', array('message' => 'evaluate default'))
+      ->setParent($container);
+
+    // Evaluate an element.
+    $this->assertExecution('evaluate', $container, array(), 'Evaluating container evaluates elements.');
+    $branch->setPass(FALSE);
+    $this->assertExecution('evaluate default', $container, array(), 'Evaluating container evaluates default elements.');
+  }
+
+  /**
+   * Tests container provided variables.
+   */
+  public function testProvidesVariables() {
+    rules_action_set(array())->action($container = new RulesConditionalTestStubContainer());
+    $textVariable = array('variable_added:var' => 'text', 'type' => 'text');
+    $mixedVariable1 = array('variable_added:var' => 'mixed', 'type' => 'text');
+    $mixedVariable2 = array('variable_added:var' => 'mixed', 'type' => 'token');
+
+    $branch1 = new RulesConditionalTestStubElement();
+    $branch1->action('variable_add', $textVariable)
+      ->setParent($container);
+    $this->assertIdentical(array(), $container->providesVariables(), 'Container does not provide variables without a default branch.');
+
+    $defaultBranch = new RulesConditionalTestStubDefaultElement();
+    $defaultBranch->action('variable_add', $textVariable)
+      ->setParent($container);
+    $this->assertIdentical(array('text'), array_keys($container->providesVariables()), 'Container provides common variables in complete branches.');
+
+    $branch1->action('variable_add', $mixedVariable1);
+    $defaultBranch->action('variable_add', $mixedVariable2);
+    $this->assertIdentical(array('text'), array_keys($container->providesVariables()), 'Container does not provide variables with mixed types.');
+
+    $branch2 = new RulesConditionalTestStubElement();
+    $branch2->setParent($container);
+    $this->assertIdentical(array(), $container->providesVariables(), 'Container provides no variables if one branch provides none.');
+  }
+
+  /**
+   * Tests the base predicate element.
+   */
+  public function testPredicateElement() {
+    $predicateElement = new RulesConditionalTestStubPredicateElement();
+
+    // Test integrity check.
+    $message = 'Predicate element does not validate without predicate.';
+    try {
+      $predicateElement->integrityCheck();
+      $this->fail($message);
+    }
+    catch (RulesIntegrityException $ex) {
+      $expectedExceptionMessage = t('The conditional "%plugin" does not have a valid predicate.', array('%plugin' => $predicateElement->plugin()));
+      $this->assertEqual($expectedExceptionMessage, $ex->getMessage(), $message);
+    }
+
+    // Test variable assertion using a field in a content type.
+    field_create_field(array(
+      'field_name' => 'field_test',
+      'type' => 'text',
+    ));
+    field_create_instance(array(
+      'field_name' => 'field_test',
+      'entity_type' => 'node',
+      'bundle' => 'page',
+    ));
+    $settings = array(
+      'type' => 'page',
+      'field_test' => array(LANGUAGE_NONE => array(array('value' => 'test value'))),
+    );
+    $node = $this->drupalCreateNode($settings);
+
+    $predicateElement = new RulesConditionalTestStubPredicateElement('data_is', array(
+      'data:select' => 'node:type',
+      'op' => '==',
+      'value' => 'page',
+    ));
+    $actionSet = rules_action_set(array('node' => array('type' => 'node', 'label' => 'Node')))
+      ->action($predicateElement->action('rules_conditional_test_throw', array(
+        'message:select' => 'node:field_test',
+      )));
+    $this->assertExecution('test value', $actionSet->integrityCheck(), array($node), 'Predicate element correctly adds predicate assertions to state.');
+  }
+}
+
+/**
+ * Default if-else tests.
+ */
+class RulesConditionalTestCase extends RulesConditionalBaseTestCase {
+  /**
+   * Returns test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Conditional',
+      'description' => 'Test the if-else plugins.',
+      'group' => 'Conditional Rules',
+    );
+  }
+
+  /**
+   * Sets up test case.
+   */
+  protected function setUp() {
+    parent::setUp('rules_conditional_test');
+  }
+
+  /**
+   * Tests evaluation.
+   */
+  public function testEvaluate() {
+    // Create test objects.
+    $comp = $this->createTestComponent();
+    $this->assert($this->doTestEvaluate($comp), 'Conditional correctly evaluates.');
+  }
+
+  /**
+   * Tests exporting.
+   */
+  public function testExport() {
+    $comp = $this->createTestComponent();
+    $comp->name = 'conditional_test';
+    $export = $this->getFileContents('conditional_test_export.txt');
+    $this->assertEqual($export, $comp->export(), 'Conditional is correctly exported.');
+  }
+
+  /**
+   * Tests importing.
+   */
+  public function testImport() {
+    $export = $this->getFileContents('conditional_test_export.txt');
+    $comp = entity_import('rules_config', $export);
+    $this->assert($this->doTestEvaluate($comp), 'Imported conditional correctly evaluates.');
+  }
+
+  /**
+   * Creates an action set to test a conditional.
+   */
+  protected function createTestComponent() {
+    return rules_action_set(array('node' => array('type' => 'node', 'label' => 'Node')))
+      ->action(rules_conditional()
+        ->if('data_is', array('data:select' => 'node:title', 'value' => 'if'))
+        ->action('rules_conditional_test_throw', array('message' => 'if'))
+        ->elseIf('data_is', array('data:select' => 'node:title', 'value' => 'else if'))
+        ->action('rules_conditional_test_throw', array('message' => 'else if'))
+        ->else()
+        ->action('rules_conditional_test_throw', array('message' => 'else')));
+  }
+
+  /**
+   * Tests evaluating a conditional.
+   */
+  public function doTestEvaluate($comp) {
+    $node = $this->drupalCreateNode();
+    $result = TRUE;
+
+    // Test "if".
+    $node->title = 'if';
+    $result = $this->assertExecution('if', $comp, array($node)) && $result;
+
+    // Test "else if".
+    $node->title = 'else if';
+    $result = $this->assertExecution('else if', $comp, array($node)) && $result;
+
+    // Test "else".
+    $node->title = 'else';
+    $result = $this->assertExecution('else', $comp, array($node)) && $result;
+
+    return $result;
+  }
+}
+
+/**
+ * Switch tests.
+ */
+class RulesConditionalSwitchTestCase extends RulesConditionalBaseTestCase {
+  /**
+   * Returns test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Switch',
+      'description' => 'Test the switch plugins.',
+      'group' => 'Conditional Rules',
+    );
+  }
+
+  /**
+   * Sets up test case.
+   */
+  protected function setUp() {
+    parent::setUp('rules_conditional_test');
+  }
+
+  /**
+   * Tests evaluation.
+   */
+  public function testEvaluate() {
+    $comp = $this->createTestComponent();
+    $this->assert($this->doTestEvaluate($comp), 'Switch correctly evaluates.');
+  }
+
+  /**
+   * Tests exporting.
+   */
+  public function testExport() {
+    $comp = $this->createTestComponent();
+    $comp->name = 'switch_test';
+    $export = $this->getFileContents('switch_test_export.txt');
+    $this->assertEqual($export, $comp->export(), 'Switch is correctly exported.');
+  }
+
+  /**
+   * Tests importing.
+   */
+  public function testImport() {
+    $export = $this->getFileContents('switch_test_export.txt');
+    $comp = entity_import('rules_config', $export);
+    $this->assert($this->doTestEvaluate($comp), 'Imported switch correctly evaluates.');
+  }
+
+  /**
+   * Creates an action set to test a conditional.
+   */
+  protected function createTestComponent() {
+    return rules_action_set(array('node' => array('type' => 'node', 'label' => 'Node')))
+      ->action(rules_conditional_switch('node:title')
+        ->case(array('value' => 'case 1'))
+        ->action('rules_conditional_test_throw', array('message' => 'case'))
+        ->case(array('value' => 'case 2'), TRUE)
+        ->action('data_set', array('data:select' => 'node:title', 'value' => 'fall through'))
+        ->case(array('value' => 'case 3'))
+        ->action('rules_conditional_test_throw', array('message' => 'case 3'))
+        ->defaultCase()
+        ->action('rules_conditional_test_throw', array('message' => 'default')));
+  }
+
+  /**
+   * Tests evaluating a conditional.
+   */
+  public function doTestEvaluate($comp) {
+    $node = $this->drupalCreateNode();
+    $result = TRUE;
+
+    // Test basic "case".
+    $node->title = 'case 1';
+    $result = $this->assertExecution('case', $comp, array($node)) && $result;
+
+    // Test fall-through "case".
+    $node->title = 'case 2';
+    $result = $this->assertExecution('case 3', $comp, array($node)) && $result;
+    $result = $this->assertEqual('fall through', $node->title) && $result;
+
+    // Test "default case".
+    $node->title = 'anything';
+    $result = $this->assertExecution('default', $comp, array($node)) && $result;
+
+    return $result;
+  }
+}
+
+/**
+ * While tests.
+ */
+class RulesConditionalWhileTestCase extends RulesConditionalBaseTestCase {
+  /**
+   * Returns test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'While loop',
+      'description' => 'Test the while loop plugin.',
+      'group' => 'Conditional Rules',
+    );
+  }
+
+  /**
+   * Sets up test case.
+   */
+  protected function setUp() {
+    parent::setUp('rules_conditional_test');
+  }
+
+  /**
+   * Tests evaluation.
+   */
+  public function testEvaluate() {
+    // Cap iteration limit to prevent long-running tests.
+    variable_set('rules_conditional_max_iterations', 100);
+
+    $comp = $this->createTestComponent();
+    $this->assert($this->doTestEvaluate($comp), 'While loop correctly evaluates.');
+
+    // Constrain iteration limit and test again.
+    variable_set('rules_conditional_max_iterations', 5);
+    $this->assert($this->doTestEvaluate($comp, 5), 'While loop correctly evaluates with limited number of iterations.');
+  }
+
+  /**
+   * Tests exporting.
+   */
+  public function testExport() {
+    $comp = $this->createTestComponent();
+    $comp->name = 'while_test';
+    $export = $this->getFileContents('while_test_export.txt');
+    $this->assertEqual($export, $comp->export(), 'While loop is correctly exported.');
+  }
+
+  /**
+   * Tests importing.
+   */
+  public function testImport() {
+    // Cap iteration limit to prevent long-running tests.
+    variable_set('rules_conditional_max_iterations', 100);
+
+    $export = $this->getFileContents('while_test_export.txt');
+    $comp = entity_import('rules_config', $export);
+    $this->assert($this->doTestEvaluate($comp), 'Imported while loop correctly evaluates.');
+  }
+
+  /**
+   * Creates an action set to test a conditional.
+   */
+  protected function createTestComponent() {
+    return rules_action_set(array())
+      ->action('variable_add', array('type' => 'integer', 'value' => 0, 'variable_added:var' => 'count', 'variable_added:label' => 'Count'))
+      ->action(rules_conditional_while('data_is', array('data:select' => 'count', 'op' => '>', 'value' => 10))->negate()
+        ->action('data_set', array(
+          'data:select' => 'count',
+          'value:select' => 'count',
+          'value:process' => array(
+            'num_offset' => array('value' => 1),
+          ),
+        )))
+      ->action('rules_conditional_test_throw', array('message:select' => 'count'));
+  }
+
+  /**
+   * Tests evaluating a conditional.
+   */
+  public function doTestEvaluate($comp, $expectedCount = 11) {
+    return $this->assertExecution($expectedCount, $comp);
+  }
+}
+
+/**
+ * Rule condition set tests.
+ */
+class RuleConditionSetTestCase extends RulesConditionalBaseTestCase {
+  /**
+   * Returns test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Rule condition set',
+      'description' => 'Test the rule condition set plugin.',
+      'group' => 'Conditional Rules',
+    );
+  }
+
+  /**
+   * Sets up test case.
+   */
+  protected function setUp() {
+    parent::setUp('rules_conditional_test');
+  }
+
+  /**
+   * Tests evaluation.
+   */
+  public function testEvaluate() {
+    $set = $this->createConditionSet();
+    $this->assert($this->doTestEvaluate($set), 'Rule condition set correctly evaluates.');
+
+    // Test evaluating in a component.
+    $set->save('rule_condition_set_test');
+    $comp = $this->createTestComponent($set);
+    $comp->execute('test');
+    $this->pass('Rule condition set evaluates to FALSE as a condition.');
+    $this->assertExecution('condition set', $comp, array('condition set'), 'Rule condition set evaluates to TRUE as a condition.');
+  }
+
+  /**
+   * Tests exporting.
+   */
+  public function testExport() {
+    $comp = $this->createConditionSet();
+    $comp->name = 'rule_condition_set_test';
+    $export = $this->getFileContents('rule_condition_set_test_export.txt');
+    $this->assertEqual($export, $comp->export(), 'Rule condition set is correctly exported.');
+  }
+
+  /**
+   * Tests importing.
+   */
+  public function testImport() {
+    $export = $this->getFileContents('rule_condition_set_test_export.txt');
+    $comp = entity_import('rules_config', $export);
+    $this->assert($this->doTestEvaluate($comp), 'Imported rule condition set correctly evaluates.');
+  }
+
+  /**
+   * Creates a rule condition set.
+   */
+  protected function createConditionSet() {
+    $conditionVariables = array(
+      'test' => array('type' => 'text', 'label' => 'Test'),
+    );
+    $set = rule_condition_set($conditionVariables)
+      ->action('variable_add', array('type' => 'text', 'variable_added:var' => 'test2', 'variable_added:label' => 'Test 2'))
+      ->action('data_set', array('data:select' => 'test2', 'value:select' => 'test'))
+      ->condition('data_is', array('data:select' => 'test2', 'value' => 'condition set'));
+    $set->component = TRUE;
+    return $set;
+  }
+
+  /**
+   * Creates an action set to test a rule condition set.
+   */
+  protected function createTestComponent(RuleConditionSet $conditionSet) {
+    return rule(array('test' => array('type' => 'text', 'label' => 'Text')))
+      ->condition('component_' . $conditionSet->name, array('test:select' => 'test'))
+      ->action('rules_conditional_test_throw', array('message' => 'condition set'));
+  }
+
+  /**
+   * Tests evaluating a rule condition set.
+   */
+  public function doTestEvaluate(RulesPlugin $comp) {
+    $result = TRUE;
+    $result = $this->assertTrue($comp->execute('condition set')) && $result;
+    $result = $this->assertFalse($comp->execute('wrong value')) && $result;
+    return $result;
+  }
+}
+
+/**
+ * UI tests.
+ */
+class RulesConditionalUITestCase extends RulesConditionalBaseTestCase {
+  /**
+   * Returns test info.
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'User interface',
+      'description' => 'Test the user interface implementations for conditional plugins.',
+      'group' => 'Conditional Rules',
+    );
+  }
+
+  /**
+   * Sets up test case.
+   */
+  protected function setUp() {
+    parent::setUp('rules_conditional_test', 'rules_admin');
+    $user = $this->drupalCreateUser(array('administer rules'));
+    $this->drupalLogin($user);
+  }
+
+  /**
+   * Tests RulesConditionalPluginUI.
+   */
+  public function testBaseUI() {
+    // Create component.
+    $componentName = 'base_test';
+    $comp = rules_action_set(array())
+      ->action($container = new RulesConditionalTestStubContainer());
+    $comp->component = TRUE;
+    $comp->integrityCheck()->save($componentName);
+
+    $componentPath = 'admin/config/workflow/rules/components';
+    RulesPluginUI::$basePath = $componentPath;
+    $managePath = RulesPluginUI::path($componentName);
+    $addSinglePath = RulesPluginUI::path($componentName, 'add', $container, 'stub conditional single element');
+    $addDependentPath = RulesPluginUI::path($componentName, 'add', $container, 'stub conditional dependent element');
+
+    // Test 'conditional depends'.
+    $this->drupalGet($managePath);
+
+    // Check dependent element cannot be added.
+    $this->assertNoLinkByHref(url($addDependentPath), 'Dependent plugin is unavailable to add.');
+
+    // Add element and then check for link to add dependent element.
+    $element = new RulesConditionalTestStubElement();
+    $element->setParent($container);
+    $comp->save();
+    $this->drupalGet($managePath);
+    $this->assertLinkByHref(url($addDependentPath), 0, 'Dependent plugin can be added on fulfilling prerequisite.');
+
+    // Test 'conditional single'.
+    $element = new RulesConditionalTestStubSingleElement();
+    $element->setParent($container);
+    $comp->save();
+    $this->drupalGet($managePath);
+    $this->assertNoLinkByHref(url($addSinglePath), 'Single plugins cannot be added more than once.');
+  }
+
+  /**
+   * Tests RulesConditionalEmptyUI.
+   */
+  public function testEmptyUI() {
+    // Create component.
+    $componentName = 'empty_test';
+    $comp = rules_action_set(array());
+    $comp->component = TRUE;
+    $comp->integrityCheck()->save($componentName);
+
+    $componentPath = 'admin/config/workflow/rules/components';
+    RulesPluginUI::$basePath = $componentPath;
+    $managePath = RulesPluginUI::path($componentName);
+    $addPath = RulesPluginUI::path($componentName, 'add', $comp, 'stub conditional');
+
+    // Test adding with empty UI.
+    $this->drupalGet($addPath);
+    $this->assertUrl($managePath, array(), 'Adding via the empty UI redirects to the parent edit form.');
+  }
+
+  /**
+   * Tests RulesConditionalPredicateUI.
+   */
+  public function testPredicateUI() {
+    // Create component.
+    $componentName = 'predicate_test';
+    $comp = rules_action_set(array(
+      'node' => array(
+        'type' => 'node',
+        'label' => t('Node'),
+      ),
+    ));
+    $comp->component = TRUE;
+    $comp->integrityCheck()->save($componentName);
+
+    $componentPath = 'admin/config/workflow/rules/components';
+    RulesPluginUI::$basePath = $componentPath;
+    $addPath = RulesPluginUI::path($componentName, 'add', $comp, 'stub conditional predicate element');
+    $addPredicatePath = RulesPluginUI::path($componentName, 'add-predicate', $comp, 'stub conditional predicate element');
+
+    // Test adding with predicate UI.
+    $this->drupalGet($addPath);
+    $this->assertUrl($addPredicatePath, array(), 'Adding via the predicate UI redirects to a special creation form.');
+
+    // Add 'data_is'.
+    $edit = array(
+      'element_name' => 'data_is',
+    );
+    $this->drupalPost(NULL, $edit, t('Continue'));
+    $this->assertFieldByName('parameter[data][settings][data:select]', '', 'Creating a predicate shows the condition form.');
+
+    // Save the condition.
+    $edit = array(
+      'parameter[data][settings][data:select]' => 'node:type',
+    );
+    $this->drupalPost(NULL, $edit, t('Continue'));
+    $edit = array(
+      'parameter[value][settings][value]' => 'page',
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    // Reload and execute component.
+    $comp = rules_config_load($componentName);
+    $comp->elements()->current()
+      ->action('data_set', array(
+        'data:select' => 'node:title',
+        'value' => 'evaluate predicate',
+      ));
+    $comp->execute($node = $this->drupalCreateNode());
+    $this->assertEqual('evaluate predicate', $node->title, 'Element created from predicate UI can be evaluated.');
+  }
+
+  /**
+   * Tests RulesConditionalCaseUI.
+   */
+  public function testCaseUI() {
+    // Create component.
+    $componentName = 'case_test';
+    $comp = rules_action_set(array(
+      'node' => array(
+        'type' => 'node',
+        'label' => t('Node'),
+      ),
+    ))->action($switch = rules_conditional_switch('node:type'));
+    $comp->component = TRUE;
+    $comp->integrityCheck()->save($componentName);
+
+    $componentPath = 'admin/config/workflow/rules/components';
+    RulesPluginUI::$basePath = $componentPath;
+    $addPath = RulesPluginUI::path($componentName, 'add', $switch, 'case');
+
+    // Test adding a case.
+    $this->drupalGet($addPath);
+    $this->assertFieldByXPath("//select[@name='parameter[value][settings][value]']", NULL, 'Option case values are shown as select list.');
+    $edit = array(
+      'parameter[value][settings][value]' => 'page',
+    );
+    $this->drupalPost(NULL, $edit, t('Save'));
+
+    // Reload and execute component.
+    $comp = rules_config_load($componentName);
+    // Navigate to switch.
+    $comp->elements()->current()
+      ->elements()->current()
+      ->action('data_set', array(
+        'data:select' => 'node:title',
+        'value' => 'evaluate case',
+      ));
+    $comp->execute($node = $this->drupalCreateNode());
+    $this->assertEqual('evaluate case', $node->title, 'Case element created from case UI can be evaluated.');
+  }
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.inc b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.inc
new file mode 100644
index 0000000000000000000000000000000000000000..f11eefbd78eca77b8ef733b599b8e1eb3e6ebe6b
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.inc
@@ -0,0 +1,109 @@
+<?php
+/**
+ * @file
+ * Testing implementations.
+ */
+
+/**
+ * Test base conditional.
+ *
+ * @method RulesConditional test() test().
+ */
+class RulesConditionalTestStubContainer extends RulesConditionalContainer {
+  protected $itemName = 'stub conditional';
+
+  protected $interceptMethods = array('test');
+
+  /**
+   * Selects the branches to evaluate for this conditional.
+   *
+   * @param RulesState $state
+   *   Rules state to use.
+   * @return RulesConditionalElement[]
+   *   An array of branches to evaluate.
+   */
+  protected function selectBranches(RulesState $state) {
+    // Pick the first element to pass.
+    foreach ($this->children as $element) {
+      if ($element->canEvaluate($state)) {
+        return array($element);
+      }
+    }
+    return array();
+  }
+
+  /**
+   * Intercepts the 'test' method.
+   */
+  protected function call_test() {
+    throw new Exception('intercept');
+  }
+
+  /**
+   * Sets a fluent element.
+   */
+  public function fluent(RulesActionContainer $element) {
+    $this->fluentElement = $element;
+    return $this;
+  }
+}
+
+/**
+ * Test element.
+ */
+class RulesConditionalTestStubElement extends RulesConditionalElement {
+  protected $itemName = 'stub conditional element';
+
+  protected $pass;
+
+  public function __construct($pass = TRUE) {
+    parent::__construct();
+    $this->setPass($pass);
+  }
+
+  /**
+   * Sets whether the element should pass.
+   */
+  public function setPass($pass) {
+    $this->pass = $pass;
+  }
+
+  /**
+   * Determines whether this branch can be evaluated.
+   */
+  public function canEvaluate(RulesState $state) {
+    return $this->pass;
+  }
+}
+
+/**
+ * Test default element.
+ */
+class RulesConditionalTestStubDefaultElement extends RulesConditionalElement {
+  protected $itemName = 'stub conditional default element';
+
+  public function isDefault() {
+    return TRUE;
+  }
+}
+
+/**
+ * Test predicate element.
+ */
+class RulesConditionalTestStubPredicateElement extends RulesConditionalPredicateElement {
+  protected $itemName = 'stub conditional predicate element';
+}
+
+/**
+ * Test single element.
+ */
+class RulesConditionalTestStubSingleElement extends RulesConditionalElement {
+  protected $itemName = 'stub conditional single element';
+}
+
+/**
+ * Test dependent element.
+ */
+class RulesConditionalTestStubDependentElement extends RulesConditionalElement {
+  protected $itemName = 'stub conditional dependent element';
+}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.info b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.info
new file mode 100644
index 0000000000000000000000000000000000000000..7973dfbc04695af18c6bf540b069da8f38fe97ea
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.info
@@ -0,0 +1,14 @@
+name = Conditional Rules Test
+core = 7.x
+dependencies[] = rules
+dependencies[] = rules_conditional
+files[] = rules_conditional_test.inc
+package = Rules
+hidden = TRUE
+
+; Information added by drupal.org packaging script on 2012-11-12
+version = "7.x-1.0-beta2"
+core = "7.x"
+project = "rules_conditional"
+datestamp = "1352752992"
+
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.module b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.module
new file mode 100644
index 0000000000000000000000000000000000000000..ff80d28dcd9e52f48c2d05f060f6a1feb9d555a4
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/tests/rules_conditional_test.module
@@ -0,0 +1,105 @@
+<?php
+/**
+ * @file
+ * Testing module.
+ */
+
+/**
+ * Implements hook_rules_plugin_info().
+ */
+function rules_conditional_test_rules_plugin_info() {
+  return array(
+    'stub conditional' => array(
+      'label' => 'Stub conditional',
+      'class' => 'RulesConditionalTestStubContainer',
+      'embeddable' => 'RulesActionContainer',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalEmptyUI',
+        ),
+      ),
+    ),
+    'stub conditional element' => array(
+      'label' => 'Stub conditional element',
+      'class' => 'RulesConditionalTestStubElement',
+      'embeddable' => 'RulesConditionalTestStubContainer',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesPluginUI',
+        ),
+      ),
+    ),
+    'stub conditional default element' => array(
+      'label' => 'Stub conditional default element',
+      'class' => 'RulesConditionalTestStubDefaultElement',
+      'embeddable' => 'RulesConditionalTestStubContainer',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesPluginUI',
+        ),
+      ),
+    ),
+    'stub conditional single element' => array(
+      'label' => 'Stub single element',
+      'class' => 'RulesConditionalTestStubSingleElement',
+      'embeddable' => 'RulesConditionalTestStubContainer',
+      'conditional single' => TRUE,
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesPluginUI',
+        ),
+      ),
+    ),
+    'stub conditional dependent element' => array(
+      'label' => 'Stub dependent element',
+      'class' => 'RulesConditionalTestStubDependentElement',
+      'embeddable' => 'RulesConditionalTestStubContainer',
+      'conditional depends' => array('stub conditional element'),
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesPluginUI',
+        ),
+      ),
+    ),
+    'stub conditional predicate element' => array(
+      'label' => 'Stub predicate element',
+      'class' => 'RulesConditionalTestStubPredicateElement',
+      'embeddable' => 'RulesActionContainer',
+      'extenders' => array(
+        'RulesPluginUIInterface' => array(
+          'class' => 'RulesConditionalPredicateUI',
+        ),
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_rules_action_info().
+ */
+function rules_conditional_test_rules_action_info() {
+  return array(
+    'rules_conditional_test_throw' => array(
+      'label' => t('Throw a test message'),
+      'group' => t('Conditional Rules Test'),
+      'parameter' => array(
+        'message' => array(
+          'label' => t('Message'),
+          'type' => 'text',
+        ),
+      ),
+    ),
+  );
+}
+
+/**
+ * Throws a test exception.
+ */
+function rules_conditional_test_throw($message) {
+  throw new RulesConditionalTestException($message);
+}
+
+/**
+ * Test exception.
+ */
+class RulesConditionalTestException extends Exception {}
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/tests/switch_test_export.txt b/profiles/wcm_base/modules/contrib/rules_conditional/tests/switch_test_export.txt
new file mode 100644
index 0000000000000000000000000000000000000000..50d404b06e8d8cc8f525bc33549b79d704603d82
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/tests/switch_test_export.txt
@@ -0,0 +1,32 @@
+{ "switch_test" : {
+    "PLUGIN" : "action set",
+    "REQUIRES" : [ "rules_conditional", "rules_conditional_test", "rules" ],
+    "USES VARIABLES" : { "node" : { "type" : "node", "label" : "Node" } },
+    "ACTION SET" : [
+      { "SWITCH" : {
+          "USING" : { "data" : [ "node:title" ] },
+          "DO" : [
+            { "CASE" : {
+                "USING" : { "value" : "case 1" },
+                "DO" : [ { "rules_conditional_test_throw" : { "message" : "case" } } ]
+              }
+            },
+            { "CASE" : {
+                "USING" : { "value" : "case 2", "fall_through" : true },
+                "DO" : [
+                  { "data_set" : { "data" : [ "node:title" ], "value" : "fall through" } }
+                ]
+              }
+            },
+            { "CASE" : {
+                "USING" : { "value" : "case 3" },
+                "DO" : [ { "rules_conditional_test_throw" : { "message" : "case 3" } } ]
+              }
+            },
+            { "DEFAULT CASE" : [ { "rules_conditional_test_throw" : { "message" : "default" } } ] }
+          ]
+        }
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/profiles/wcm_base/modules/contrib/rules_conditional/tests/while_test_export.txt b/profiles/wcm_base/modules/contrib/rules_conditional/tests/while_test_export.txt
new file mode 100644
index 0000000000000000000000000000000000000000..cafcd32763c5312c0fc76fb3cb3846c7182e92c0
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/rules_conditional/tests/while_test_export.txt
@@ -0,0 +1,23 @@
+{ "while_test" : {
+    "PLUGIN" : "action set",
+    "REQUIRES" : [ "rules", "rules_conditional", "rules_conditional_test" ],
+    "ACTION SET" : [
+      { "variable_add" : {
+          "USING" : { "type" : "integer", "value" : 0 },
+          "PROVIDE" : { "variable_added" : { "count" : "Count" } }
+        }
+      },
+      {
+        "WHILE" : { "NOT data_is" : { "data" : [ "count" ], "op" : "\u003E", "value" : 10 } },
+        "DO" : [
+          { "data_set" : {
+              "data" : [ "count" ],
+              "value" : { "select" : "count", "num_offset" : { "value" : 1 } }
+            }
+          }
+        ]
+      },
+      { "rules_conditional_test_throw" : { "message" : [ "count" ] } }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/profiles/wcm_base/modules/contrib/slack/LICENSE.txt b/profiles/wcm_base/modules/contrib/slack/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/LICENSE.txt
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/profiles/wcm_base/modules/contrib/slack/includes/pages/slack.admin.inc b/profiles/wcm_base/modules/contrib/slack/includes/pages/slack.admin.inc
new file mode 100644
index 0000000000000000000000000000000000000000..6ac7477b09bfb4bf5177ccf4bd6f2f66bb7b45ba
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/includes/pages/slack.admin.inc
@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * @file
+ * Slack integration module admin functions.
+ */
+
+/**
+ * Slack module admin form.
+ */
+function slack_configure_form($form, &$form_state) {
+  $form['slack_webhook_url'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Webhook URL'),
+    '#description' => t('Enter your Webhook URL from an Incoming WebHooks integration. It looks like https://hooks.slack.com/services/XXXXXXXXX/YYYYYYYYY/ZZZZZZZZZZZZZZZZZZZZZZZZ'),
+    '#default_value' => slack_get_default_webhook_url(),
+    '#required' => TRUE,
+  );
+  $form['slack_channel'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default channel'),
+    '#description' => t('Enter your channel name with # symbol, for example #general (or @username for a private message or a private group name).'),
+    '#default_value' => variable_get('slack_channel'),
+  );
+  $form['slack_username'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default username'),
+    '#description' => t('What would you like to name your Slack bot?'),
+    '#default_value' => variable_get('slack_username'),
+  );
+  $form['slack_icon_type'] = array(
+    '#type' => 'radios',
+    '#title' => t('Type of image'),
+    '#options' => array (
+      'emoji' => t('Emoji'),
+      'image' => t('Image'),
+      'none' => t('None (Use default integration settings)')),
+    '#default_value' => slack_get_default_icon_type(),
+  );
+  $form['slack_icon_emoji'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Emoji code'),
+    '#default_value' => variable_get('slack_icon_emoji'),
+    '#description' => t('What emoji would you use for your SlackBot?'),
+    '#states' => array(
+      'visible' => array(
+        ':input[name="slack_icon_type"]' => array(
+          'value' => 'emoji',
+        ),
+      ),
+    ),
+  );
+  $form['slack_icon_url'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Image URL '),
+    '#default_value' => variable_get('slack_icon_url'),
+    '#description' => t('What icon would you use for your SlackBot?'),
+    '#states' => array(
+      'visible' => array(
+        ':input[name="slack_icon_type"]' => array(
+          'value' => 'image',
+        ),
+      ),
+    ),
+  );
+  $form['slack_attachment_enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Send message with attachment styling'),
+    '#description' => t('Attachments allow further styling of your messages.'),
+    '#default_value' => variable_get('slack_attachment_enabled'),
+  );
+
+  $attachment_state_toggle = array(
+    'visible' => array(
+      ':input[name="slack_attachment_enabled"]' => array(
+        'checked' => TRUE,
+      )
+    )
+  );
+  $form['slack_attachment_color'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default message color'),
+    '#description' => t('What color do you want to use for your slack messages by default?'),
+    '#default_value' => variable_get('slack_attachment_color'),
+    '#states' => $attachment_state_toggle,
+  );
+
+  $form['slack_attachment_pretext'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default pretext'),
+    '#description' => t('Set a pretext of your message to provide meta information..'),
+    '#default_value' => variable_get('slack_attachment_pretext'),
+    '#states' => $attachment_state_toggle,
+  );
+
+  $form['slack_attachment_title'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default message title'),
+    '#description' => t('What title do you want to use for your slack messages by default?'),
+    '#default_value' => variable_get('slack_attachment_title'),
+    '#states' => $attachment_state_toggle,
+  );
+
+  $form['slack_attachment_title_link'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Default link for title'),
+    '#description' => t('Add a link to your message title.'),
+    '#default_value' => variable_get('slack_attachment_title_link'),
+    '#states' => $attachment_state_toggle,
+  );
+  return system_settings_form($form);
+}
diff --git a/profiles/wcm_base/modules/contrib/slack/includes/pages/slack.pages.inc b/profiles/wcm_base/modules/contrib/slack/includes/pages/slack.pages.inc
new file mode 100644
index 0000000000000000000000000000000000000000..948efa6800de16db857264bb20ba1d4b048a8e5d
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/includes/pages/slack.pages.inc
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ * Slack module page functions.
+ */
+
+/**
+ * Slack test message form.
+ */
+function slack_send_test_message_form($form, &$form_state) {
+  $form['slack_test_channel'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Channel'),
+    '#default_value' => variable_get('slack_channel'),
+  );
+
+  $form['slack_attachments'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Attachments options'),
+    '#collapsible' => TRUE,
+    '#collapsed' => !variable_get('slack_attachment_enabled'),
+  );
+  $form['slack_attachments']['slack_attachment_enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable attachments'),
+    '#description' => t('Attachments allow further styling of your messages.'),
+    '#default_value' => variable_get('slack_attachment_enabled'),
+  );
+  $form['slack_attachments']['slack_test_color'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Color'),
+    '#default_value' => variable_get('slack_attachment_color'),
+  );
+  $form['slack_attachments']['slack_test_pretext'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Message pretext'),
+    '#default_value' => variable_get('slack_attachment_pretext'),
+  );
+  $form['slack_attachments']['slack_test_title'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Message title'),
+    '#default_value' => variable_get('slack_attachment_title'),
+  );
+  $form['slack_attachments']['slack_test_title_link'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Message title link'),
+    '#default_value' => variable_get('slack_attachment_title_link'),
+  );
+  $form['slack_test_message'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Message'),
+    '#required' => TRUE,
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Send message'),
+  );
+  return $form;
+}
+
+/**
+ * Submit handler for slack test message form.
+ */
+function slack_send_test_message_form_submit($form, &$form_state) {
+  $webhook_url = slack_get_default_webhook_url();
+  $channel = $form_state['values']['slack_test_channel'];
+  $message = $form_state['values']['slack_test_message'];
+
+  $attachment_options = array();
+  $use_attachments = $form_state['values']['slack_attachment_enabled'];
+  if($use_attachments) {
+    $attachment_options['pretext'] = $form_state['values']['slack_test_pretext'];
+    $attachment_options['title'] = $form_state['values']['slack_test_title'];
+    $attachment_options['title_link'] = $form_state['values']['slack_test_title_link'];
+    $attachment_options['color'] = $form_state['values']['slack_test_color'];
+  }
+  $icon_type = variable_get('slack_icon_type');
+  $icon = variable_get('slack_icon_url');
+  $emoji = variable_get('slack_icon_emoji');
+
+  $icon_options = array('icon'=>$icon, 'emoji'=>$emoji, 'type'=>$icon_type);
+
+  $result = slack_send_message($webhook_url, $message, $channel, '', $icon_options, $attachment_options);
+  if (!$result) {
+    drupal_set_message(t("Message wasn't sent. Please, check slack module configuration."));
+  }
+  elseif (!isset($result->error) && $result->code == SLACK_CODE_OK) {
+    drupal_set_message(t('Message was successfully sent.'));
+  }
+  else {
+    drupal_set_message(t("Message wasn't sent."), 'error');
+    slack_watchdog_sending_error($result);
+  }
+}
+
+/**
+ * Create a log in watchdog containing Slack's response information when message
+ * sending was failed.
+ */
+function slack_watchdog_sending_error($result) {
+  $message = '';
+  $status_message = $result->status_message;
+  if (isset($status_message)) {
+    $message .= "<strong>Error message:</strong> $status_message";
+  }
+
+  $code = $result->code;
+  if (isset($code)) {
+    $message .= "<br /><strong>Code:</strong> $code";
+  }
+
+  $request = $result->request;
+  if (isset($request)) {
+    $message .= "<br /><strong>Request:</strong> $request";
+  }
+
+  $headers = $result->headers;
+  if (isset($headers)) {
+    $message .= "<br /><br /><strong>Headers:</strong><br><ul>";
+    foreach ($headers as $key => $header) {
+      $message .= "<li>$key: $header</li>";
+    }
+    $message .= '</ul>';
+  }
+
+  watchdog('slack', $message, array(), WATCHDOG_ERROR);
+}
\ No newline at end of file
diff --git a/profiles/wcm_base/modules/contrib/slack/includes/slack.api.inc b/profiles/wcm_base/modules/contrib/slack/includes/slack.api.inc
new file mode 100644
index 0000000000000000000000000000000000000000..586d4328f27c10efb9cc4decc05fadcf4291f87f
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/includes/slack.api.inc
@@ -0,0 +1,207 @@
+<?php
+
+/**
+ * @file
+ * Slack integration module API functions.
+ */
+
+/**
+ * Send message to the Slack.
+ *
+ * @param string $message
+ *   The message sent to the channel.
+ * @param string $channel
+ *   The channel in the Slack service to send messages.
+ * @param string $username
+ *   The bot name displayed in the channel.
+ *
+ * @return bool|object
+ *   Slack response.
+ */
+function slack_send_message($webhook_url, $message, $channel = '', $username = '', $icon_options = array(), $attachment_options = array()) {
+  if (!$webhook_url) {
+    drupal_set_message('Please, enter the webhook value and try again', 'error');
+    return FALSE;
+  }
+  if ($channel) {
+    $message_options['channel'] = $channel;
+  }
+  else {
+    $message_options['channel'] = slack_get_default_channel();
+  }
+  if ($username) {
+    $message_options['username'] = $username;
+  }
+  else {
+    $message_options['username'] = slack_get_default_username();
+  }
+
+  if ($icon_options['type'] == 'emoji') {
+    if ($icon_options['emoji']) {
+      $message_options['icon_emoji'] = $icon_options['emoji'];
+    }
+    else {
+      $message_options['icon_emoji'] = slack_get_default_icon_emoji();
+    }
+  }
+  elseif ($icon_options['type'] == 'image') {
+    if ($icon_options['icon']) {
+      $message_options['icon_url'] = $icon_options['icon'];
+    }
+    else {
+      $message_options['icon_url'] = slack_get_default_icon_url();
+    }
+  }
+  $result = _slack_send_message($webhook_url, $message, $message_options, $attachment_options);
+  return $result;
+}
+
+/**
+ * Send message to the Slack with more options.
+ *
+ * @param string $team_name
+ *   Your team name in the Slack.
+ * @param string $team_token
+ *   The token from "Incoming WebHooks" integration in the Slack.
+ * @param string $message
+ *   The message sent to the channel.
+ * @param array $message_options
+ *   An associative array, it can contain:
+ *     - channel: The channel in the Slack service to send messages
+ *     - username: The bot name displayed in the channel
+ *     - icon_emoji: The bot icon displayed in the channel
+ *     - icon_url: The bot icon displayed in the channel
+ * @param array $attachments
+ *   An optional associative array to further style the message
+ *        - title: an optinal title for the message
+ *        - pretext: Text preceding the message
+ *        - color: a color to emphasize the text body
+ *        - title_link: a link associated with the message
+ *
+ * @return object
+ *   Can contain:
+ *                          success      fail          fail
+ *     - data:                ok         No hooks      Invalid channel specified
+ *     - status message:      OK         Not found     Server Error
+ *     - code:                200        404           500
+ *     - error:               -          Not found     Server Error
+ */
+function _slack_send_message($webhook_url, $message, $message_options = array(), $attachment_options = array()) {
+  $headers = array(
+    'Content-Type' => 'application/json',
+  );
+  $message = _slack_process_message($message);
+  if(empty($attachment_options)) {
+    $message_options['text'] = $message;
+  }
+  else {
+    $attachment_options['fallback'] = $message;
+    $attachment_options['text'] = $message;
+
+    $message_options['attachments'] = array(
+      $attachment_options
+    );
+  }
+  $sending_data = json_encode($message_options, JSON_UNESCAPED_SLASHES);
+  $options = array(
+    'method' => 'POST',
+    'data' => $sending_data,
+    'headers' => $headers,
+  );
+  $sending_url = $webhook_url;
+  $result = drupal_http_request($sending_url, $options);
+  return $result;
+}
+
+/**
+ * Replaces links with slack friendly tags. Strips all other html.
+ *
+ * @param string $message
+ *   The message sent to the channel.
+ *
+ * @return string
+ *   Replaces links with slack friendly tags. Strips all other html.
+ */
+function _slack_process_message($message) {
+  $regexp = "<a\s[^>]*href=(\"??)([^\" >]*?)\\1[^>]*>(.*)<\/a>";
+  if (preg_match_all("/$regexp/siU", $message, $matches, PREG_SET_ORDER)) {
+    $i = 1;
+    foreach ($matches as $match) {
+      $new_link = "<$match[2] | $match[3]>";
+      $links['link-' . $i] = $new_link;
+      $message = str_replace($match[0], 'link-' . $i, $message);
+      $i++;
+      $message = strip_tags($message);
+      foreach ($links as $id => $link) {
+        $message = str_replace($id, $link, $message);
+      }
+    }
+  }
+  return $message;
+}
+
+/**
+ * Get default Webhook URL.
+ *
+ * @return string
+ *   Get default Webhook URL.
+ */
+function slack_get_default_webhook_url() {
+  $channel = variable_get('slack_webhook_url');
+  return $channel;
+}
+
+/**
+ * Get default team channel.
+ *
+ * @return string
+ *   Get default team channel.
+ */
+function slack_get_default_channel() {
+  $channel = variable_get('slack_channel');
+  return $channel;
+}
+
+/**
+ * Get default Slack bot username.
+ *
+ * @return string
+ *   Get default Slack bot username.
+ */
+function slack_get_default_username() {
+  $username = variable_get('slack_username');
+  return $username;
+}
+
+/**
+ * Get default Slack bot icon.
+ *
+ * @return string
+ *   Get default Slack bot icon.
+ */
+function slack_get_default_icon_url() {
+  $icon['url'] = variable_get('slack_icon_url');
+  return $icon['url'];
+}
+
+/**
+ * Get default Slack bot icon emoji.
+ *
+ * @return string
+ *   Get default Slack bot icon emoji.
+ */
+function slack_get_default_icon_emoji() {
+  $icon['emoji'] = variable_get('slack_icon_emoji');
+  return $icon['emoji'];
+}
+
+/**
+ * Get default Slack bot image type radios value.
+ *
+ * @return string
+ *   Get default Slack bot image type radios value.
+ */
+function slack_get_default_icon_type() {
+  $select_image = variable_get('slack_icon_type', 'none');
+  return $select_image;
+}
diff --git a/profiles/wcm_base/modules/contrib/slack/slack.info b/profiles/wcm_base/modules/contrib/slack/slack.info
new file mode 100644
index 0000000000000000000000000000000000000000..35b076324f35034c41d8f312ca7c350a6c808a0c
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/slack.info
@@ -0,0 +1,19 @@
+name = Slack
+description = Integration with Slack.com service.
+core = 7.x
+
+configure = admin/config/services/slack
+
+; Information added by Drupal.org packaging script on 2015-10-06
+version = "7.x-1.5"
+core = "7.x"
+project = "slack"
+datestamp = "1444107840"
+
+
+; Information added by Drupal.org packaging script on 2016-03-25
+version = "7.x-1.6"
+core = "7.x"
+project = "slack"
+datestamp = "1458903540"
+
diff --git a/profiles/wcm_base/modules/contrib/slack/slack.install b/profiles/wcm_base/modules/contrib/slack/slack.install
new file mode 100644
index 0000000000000000000000000000000000000000..a70c9c424d31bc309b9d0e66ab46f0ed754a751c
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/slack.install
@@ -0,0 +1,25 @@
+<?php
+
+/*
+ * Implements hook_install().
+ */
+function slack_install() {
+  variable_set('slack_customize_icon', 'none');
+}
+
+/*
+ * Implements hook_uninstall().
+ */
+function slack_uninstall() {
+  variable_del('slack_webhook_url');
+  variable_del('slack_channel');
+  variable_del('slack_username');
+  variable_del('slack_icon_url');
+  variable_del('slack_icon_emoji');
+  variable_del('slack_icon_type');
+  variable_del('slack_enabled_attachment');
+  variable_del('slack_attachment_pretext');
+  variable_del('slack_attachment_color');
+  variable_del('slack_attachment_title');
+  variable_del('slack_attachment_title_link');
+}
diff --git a/profiles/wcm_base/modules/contrib/slack/slack.module b/profiles/wcm_base/modules/contrib/slack/slack.module
new file mode 100644
index 0000000000000000000000000000000000000000..277e880bb2b783cfc5a3990f0af65ad216d3ba99
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/slack.module
@@ -0,0 +1,43 @@
+<?php
+
+define('SLACK_CODE_OK', 200);
+define('SLACK_CODE_NOT_FOUND', 404);
+define('SLACK_CODE_SERVER_ERROR', 500);
+
+require_once 'includes/slack.api.inc';
+require_once 'slack.tokens.inc';
+
+/**
+ * Implements hook_menu().
+ */
+function slack_menu() {
+  $items = array();
+  $slack_module_url = 'admin/config/services/slack';
+  $items[$slack_module_url] = array(
+    'title' => 'Slack',
+    'description' => 'Configure slack module.',
+    'page callback' => 'system_admin_menu_block_page',
+    'access arguments' => array('access administration pages'),
+    'file' => 'system.admin.inc',
+    'file path' => drupal_get_path('module', 'system'),
+  );
+  $items[$slack_module_url . '/config'] = array(
+    'title' => 'Configuration',
+    'description' => 'Adjust slack settings.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('slack_configure_form'),
+    'access arguments' => array('administer site configuration'),
+    'type' => MENU_NORMAL_ITEM,
+    'file' => 'includes/pages/slack.admin.inc',
+  );
+  $items[$slack_module_url . '/test'] = array(
+    'title' => 'Send a message',
+    'description' => 'Allows to send a test message to the Slack.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('slack_send_test_message_form'),
+    'access arguments' => array('administer site configuration'),
+    'type' => MENU_NORMAL_ITEM,
+    'file' => 'includes/pages/slack.pages.inc',
+  );
+  return $items;
+}
diff --git a/profiles/wcm_base/modules/contrib/slack/slack.rules.inc b/profiles/wcm_base/modules/contrib/slack/slack.rules.inc
new file mode 100644
index 0000000000000000000000000000000000000000..f8e0e82295465b468cc60589c47566a4644118e7
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/slack.rules.inc
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Slack integration module rules functions.
+ */
+
+/**
+ * Implements hook_rules_action_info().
+ */
+function slack_rules_action_info() {
+  $actions = array();
+  $actions['slack_send_message'] = array(
+    'base' => 'slack_rules_send_message_action',
+    'label' => t('Slack send message'),
+    'group' => t('Slack'),
+    'parameter' => array(
+      'webhook_url' => array(
+        'type' => 'text',
+        'label' => t('Webhook URL'),
+        'description' => t("It will be using a webhook_url from slack module settings, if you don't enter it here."),
+        'optional' => TRUE,
+      ),
+      'channel' => array(
+        'type' => 'text',
+        'label' => t('Channel'),
+        'description' => t("It will be using a channel from slack module settings, if you don't enter channel here."),
+        'optional' => TRUE,
+      ),
+      'username' => array(
+        'type' => 'text',
+        'label' => t('Username'),
+        'description' => t("It will be using a username from slack module settings, if you don't enter username here."),
+        'optional' => TRUE,
+      ),
+      'message' => array(
+        'type' => 'text',
+        'label' => t('Sending message'),
+      ),
+      'type_token' => array(
+        'type' => 'text',
+        'label' => t('Icon type'),
+        'description' => t("Select needed token for define slackbot icon type, or select nothing to use default"),
+        'optional' => TRUE,
+      ),
+      'emoji' => array(
+        'type' => 'text',
+        'label' => t('Emoji code'),
+        'description' => t("It will be using a emoji code from slack module settings, if you don't enter username here."),
+        'optional' => TRUE,
+      ),
+      'icon' => array(
+        'type' => 'text',
+        'label' => t('Enter icon url'),
+        'description' => t("It will be using a icon url from slack module settings, if you don't enter username here."),
+        'optional' => TRUE,
+      ),
+      'color' => array(
+        'type' => 'text',
+        'label' => t('Message color'),
+        'optional' => TRUE,
+      ),
+      'pretext' => array(
+        'type' => 'text',
+        'label' => t('Message pretext'),
+        'optional' => TRUE,
+      ),
+      'title' => array(
+        'type' => 'text',
+        'label' => t('Message title'),
+        'optional' => TRUE,
+      ),
+      'title_link ' => array(
+        'type' => 'text',
+        'label' => t('Link for message title'),
+        'optional' => TRUE,
+      ),
+    ),
+  );
+  return $actions;
+}
+
+/**
+ * Rules action for sending a message to the Slack.
+ */
+function slack_rules_send_message_action($webhook_url, $channel, $username, $message, $type_token = '', $emoji = '', $icon = '', $color = '', $pretext = '', $title = '', $title_link = '') {
+  if (!$webhook_url) {
+    $webhook_url = slack_get_default_webhook_url();
+  }
+  if (!$channel) {
+    $channel = slack_get_default_channel();
+  }
+  if (!$username) {
+    $username = slack_get_default_username();
+  }
+  if (!$emoji) {
+    $emoji = slack_get_default_icon_emoji();
+  }
+  if (!$icon) {
+    $icon = slack_get_default_icon_url();
+  }
+
+  $attachment_options = array();
+  $attachment_options['color'] = $color;
+  $attachment_options['pretext'] = $pretext;
+  $attachment_options['title'] = $title;
+  $attachment_options['title_link'] = $title_link;
+
+  $icon_options = array('icon' => $icon, 'emoji' => $emoji, 'type' => $type_token);
+  slack_send_message($webhook_url, $message, $channel, $username, $icon_options, $attachment_options);
+}
diff --git a/profiles/wcm_base/modules/contrib/slack/slack.tokens.inc b/profiles/wcm_base/modules/contrib/slack/slack.tokens.inc
new file mode 100644
index 0000000000000000000000000000000000000000..c53578539bedabe03c2a0cbebccd03b066e51cd7
--- /dev/null
+++ b/profiles/wcm_base/modules/contrib/slack/slack.tokens.inc
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * Implements hook_info().
+ */
+function slack_token_info() {
+  $tokens['slack-channel'] = array(
+    'name' => t('Channel'),
+    'description' => t('Name of channel.'),
+    'type' => 'text',
+    'dynamic' => TRUE,
+    'global_types' => TRUE,
+  );
+  $tokens['slack-default-webhook-url'] = array(
+    'name' => t('Webhook URL'),
+    'description' => t('Webhook URL which defined on settings page.'),
+    'type' => 'text',
+    'dynamic' => TRUE,
+    'global_types' => TRUE,
+  );
+  $tokens['slack-icon-url'] = array(
+    'name' => t('Icon URL'),
+    'description' => t('Absolute Url to image what you would like to use for your Slackbot.'),
+    'type' => 'text',
+    'dynamic' => TRUE,
+    'global_types' => TRUE,
+  );
+  $tokens['slack-message'] = array(
+    'name' => t('Message'),
+    'description' => t('User message'),
+    'type' => 'text',
+    'dynamic' => TRUE,
+    'global_types' => TRUE,
+  );
+  $tokens['slack-icon-emoji'] = array(
+    'name' => t('Emoji code'),
+    'description' => t('Emoji code what you would like to use for your Slackbot.'),
+    'type' => 'text',
+    'dynamic' => TRUE,
+    'global_types' => TRUE,
+  );
+  $tokens['slack-username'] = array(
+    'name' => t('Username'),
+    'description' => t('Default username for your Slackbot.'),
+    'type' => 'text',
+    'dynamic' => TRUE,
+    'global_types' => TRUE,
+  );
+
+  return array('tokens' => array('slack' => $tokens));
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function slack_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  $replacements = array();
+  foreach ($tokens as $name => $original) {
+    if ($name == 'slack-icon-url') {
+      $replacements[$original] = 'image';
+    }
+    if ($name == 'slack-icon-emoji') {
+      $replacements[$original] = 'emoji';
+    }
+    if ($name == 'slack-default-webhook-url') {
+      $replacements[$original] = slack_get_default_webhook_url();
+    }
+    if ($name == 'slack-username') {
+      $replacements[$original] = slack_get_default_username();
+    }
+  }
+  return $replacements;
+}
diff --git a/profiles/wcm_base/modules/contrib/views_accordion/README.txt b/profiles/wcm_base/modules/contrib/views_accordion/README.txt
index 5ea5a4351399caececef59176f05123dde9e2071..e05b0ea0cbf3903f76f4e928956ecb3442356f17 100644
--- a/profiles/wcm_base/modules/contrib/views_accordion/README.txt
+++ b/profiles/wcm_base/modules/contrib/views_accordion/README.txt
@@ -6,7 +6,7 @@ It will take the results and display them as a jQuery UI accordion. It supports
 grouping of fields and ajax pagination.
 
 
-/* INSTALATION */
+/* INSTALLATION */
 
 1. Place the views_accordion module in your modules directory (usually under
    /sites/all/modules/).
@@ -21,7 +21,7 @@ Your view must meet the following requirements:
   * Provide at least two fields to show.
 
 Choose Views Accordion in the Style dialog within your view, which will prompt
-you to configure the accodion.
+you to configure the accordion.
 
 *        IMPORTANT       *
 The first field WILL be used as the header for each accordion section, all
@@ -35,7 +35,7 @@ though.
 /* THEMING INFORMATION */
 
 Files included:
-  * views-acordion.css - Just some styles to fix default styling problems in
+  * views-accordion.css - Just some styles to fix default styling problems in
     bartik.
   * views-view-accordion.tpl.php - copy/paste into your theme directory -
     please the comments in this file for requirements/instructions.
diff --git a/profiles/wcm_base/modules/contrib/views_accordion/views-accordion.js b/profiles/wcm_base/modules/contrib/views_accordion/views-accordion.js
index f35c6921798214bd0fde331d51518198ef91ed77..16c722e302caa6c9da9ad1ffdd0bf11af75599d0 100644
--- a/profiles/wcm_base/modules/contrib/views_accordion/views-accordion.js
+++ b/profiles/wcm_base/modules/contrib/views_accordion/views-accordion.js
@@ -1,3 +1,7 @@
+/**
+ * @file
+ * Javascript for views-accordion.
+ */
 Drupal.behaviors.views_accordion = {
   attach: function(context) {
     if(Drupal.settings.views_accordion){
@@ -15,6 +19,9 @@ Drupal.behaviors.views_accordion = {
           var displaySelector = '.view-id-' + viewname + '.view-display-id-' + display + ' > .view-content';
           var headerSelector = this.header;
 
+          /* The row count to be used if Row to display opened on start is set to random */
+          var row_count = 0;
+
           /* Prepare our markup for jquery ui accordion */
           $(displaySelector + ' ' + headerSelector + ':not(.ui-accordion-header)').each(function(i){
         	// Hash to use for accordion navigation option.
@@ -38,8 +45,13 @@ Drupal.behaviors.views_accordion = {
             if (!usegroupheader) {
               $this.siblings().wrapAll('<div></div>');
             }
+            row_count++;
           });
 
+          if (this.rowstartopen == 'random') {
+            this.rowstartopen = Math.floor(Math.random() * row_count);
+          }
+
           var options = {};
           if (this.newoptions) {
             /* jQuery UI accordion options format changed for jquery >= 1.9 */
diff --git a/profiles/wcm_base/modules/contrib/views_accordion/views_accordion.info b/profiles/wcm_base/modules/contrib/views_accordion/views_accordion.info
index 3379d8f33337a27fff21e0a0c5bec6567173fc49..284ab922abc39d7d03fbb5528800a75038092065 100644
--- a/profiles/wcm_base/modules/contrib/views_accordion/views_accordion.info
+++ b/profiles/wcm_base/modules/contrib/views_accordion/views_accordion.info
@@ -6,9 +6,8 @@ dependencies[] = views
 
 files[] = views_accordion_style_plugin.inc
 
-; Information added by Drupal.org packaging script on 2015-01-29
-version = "7.x-1.1"
+; Information added by Drupal.org packaging script on 2018-12-08
+version = "7.x-1.2"
 core = "7.x"
 project = "views_accordion"
-datestamp = "1422545700"
-
+datestamp = "1544299988"
diff --git a/profiles/wcm_base/modules/contrib/views_accordion/views_accordion.module b/profiles/wcm_base/modules/contrib/views_accordion/views_accordion.module
index c647eb797157c221dcde13c39aab8bab13d4e757..bd1d963458504f630a6835f359fac114d57c00d3 100644
--- a/profiles/wcm_base/modules/contrib/views_accordion/views_accordion.module
+++ b/profiles/wcm_base/modules/contrib/views_accordion/views_accordion.module
@@ -31,7 +31,7 @@ function views_accordion_help($path, $arg) {
       $output .= '<li>' . t('<strong>Start with the first row opened</strong>: Wether or not the first row of the view should start opened when the view is first shown. Uncheck this if you would like the accordion to start closed.') . '</li>';
       $output .= '<li>' . t("<strong>Use the module's default styling</strong>:  If you disable this, the CSS file in the module's directory (views-accordion.css) will not be loaded. You can uncheck this if you plan on doing your own CSS styling.") . '</li>';
       $output .= '</ul>';
-      $output .= '<h3>' . t('Theming information')  .'</h3>';
+      $output .= '<h3>' . t('Theming information') . '</h3>';
       $output .= t('This module comes with a default style, which you can disable in the options (see above). Files included for your convinence:');
       $output .= '<ul><li>' . t('<strong>views-acordion.css</strong> - with how the classes the author thought would be best used, mostly empty.') . '</li>';
       $output .= '<li>' . t('<strong>views-view-accordion.tpl.php</strong> - copy/paste into your theme directory - please the comments in this file for requirements/instructions.') . '</li></ul>';
@@ -64,7 +64,7 @@ function template_preprocess_views_view_accordion(&$vars) {
   // The template variable 'view_accordion_id' MUST be the same as $accordion_id
   // in the render() function inside the style plugin.
   // Don't touch it or it will stop working.
-  $vars['view_accordion_id'] = 'views-accordion-' . $vars['view']->name . '-'. $vars['view']->current_display . '-header';
+  $vars['view_accordion_id'] = 'views-accordion-' . $vars['view']->name . '-' . $vars['view']->current_display . '-header';
 
   // Add the css for fixing/preventing accordion issues.
   drupal_add_css(drupal_get_path('module', 'views_accordion') . '/views-accordion.css');
diff --git a/profiles/wcm_base/modules/contrib/views_accordion/views_accordion_style_plugin.inc b/profiles/wcm_base/modules/contrib/views_accordion/views_accordion_style_plugin.inc
index c76630a5b74de40b50dac1745bf0ff0e6e5579d3..5e9cff658d76b71e5c9b0ba28fa1c515a3c7fd36 100644
--- a/profiles/wcm_base/modules/contrib/views_accordion/views_accordion_style_plugin.inc
+++ b/profiles/wcm_base/modules/contrib/views_accordion/views_accordion_style_plugin.inc
@@ -55,9 +55,10 @@ class views_accordion_style_plugin extends views_plugin_style {
     // Setup our array of options for choosing which row should start opened (row-start-open).
     $rsopen_options = array();
     for ($i = 1; $i <= $maxitems; $i++) {
-      $rsopen_options[] = t('Row ') . $i;
+      $rsopen_options[] = t('Row') . $i;
     }
     $rsopen_options['none'] = t('None');
+    $rsopen_options['random'] = t('Random');
 
     $animated_options = array(
       'none' => t('None'),
@@ -213,13 +214,15 @@ class views_accordion_style_plugin extends views_plugin_style {
    */
   public function render() {
     $output = '';
+    $row_plugin_option = $this->display->handler->get_option('row_plugin');
     // Prevent errors if users select content instead of fields as row style.
     // This forces them to use field as row style, no other way that I can think of.
-    if (parent::uses_fields()) {
+    if ($row_plugin_option == 'fields') {
       $output = parent::render();
     }
     else {
-      drupal_set_message('Views accordion requires Fields as row style', 'error');
+      drupal_set_message(t('Views accordion requires Fields as row style, but the view <em>@view</em> is configured with <em>@row_plugin_option</em> as a row style.', array('@view' => $this->view->name, '@row_plugin_option' => $row_plugin_option)), 'error');
+      return;
     }
 
     if ($this->options['disableifone'] == '1') {
@@ -249,8 +252,12 @@ class views_accordion_style_plugin extends views_plugin_style {
     // we do it here so we dont have it run once every group.
     $view_settings['collapsible'] = $this->options['collapsible'];
 
-    // The +0 forces it to add it as an integrer instead of a string - ugly hack?
-    $view_settings['rowstartopen'] = ($this->options['row-start-open'] == 'none') ? FALSE : $this->options['row-start-open'] + 0;
+    if ($this->options['row-start-open'] == 'random') {
+      $view_settings['rowstartopen'] = 'random';
+    }
+    else {
+      $view_settings['rowstartopen'] = ($this->options['row-start-open'] == 'none') ? FALSE : (int) $this->options['row-start-open'];
+    }
     $view_settings['animated'] = ($this->options['animated'] == 'none') ? FALSE : $this->options['animated'];
     $view_settings['autoheight'] = $this->options['autoheight'];
     $view_settings['event'] = $this->options['event'];
@@ -264,7 +271,7 @@ class views_accordion_style_plugin extends views_plugin_style {
     $view_settings['viewname'] = $this->view->name;
     $view_settings['usegroupheader'] = $view_settings['grouping'] ? $this->options['use-grouping-header'] : 0;
 
-    $accordion_id = 'views-accordion-' . $this->view->name .'-' . $this->view->current_display;
+    $accordion_id = 'views-accordion-' . $this->view->name . '-' . $this->view->current_display;
 
     if ($view_settings['usegroupheader'] == 1) {
       $view_settings['header'] = 'h3.' . $accordion_id . '-header';
diff --git a/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/README.md b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..bbe679db029f6ab3d320cfd21df81cdef1cfd9c0
--- /dev/null
+++ b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/README.md
@@ -0,0 +1,5 @@
+# WCM Watchdog to Slack
+
+This is a feature that configures slack to allow messages to our #websites-discussion channel from rules.
+
+There is an e-mail error notification rule included by default, but this feature allows one to set up any number of watchdog rules for Quality Assurance.
diff --git a/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.features.defaultconfig.inc b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.features.defaultconfig.inc
new file mode 100644
index 0000000000000000000000000000000000000000..1200f199e6ff320889fb8fddcd5d51077fa8eafd
--- /dev/null
+++ b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.features.defaultconfig.inc
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @file
+ * wcm_watchdog_to_slack.features.defaultconfig.inc
+ */
+
+/**
+ * Implements hook_defaultconfig_features().
+ */
+function wcm_watchdog_to_slack_defaultconfig_features() {
+  return array(
+    'wcm_watchdog_to_slack' => array(
+      'strongarm' => 'strongarm',
+    ),
+  );
+}
+
+/**
+ * Implements hook_defaultconfig_strongarm().
+ */
+function wcm_watchdog_to_slack_defaultconfig_strongarm() {
+  $export = array();
+
+  $strongarm = new stdClass();
+  $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+  $strongarm->api_version = 1;
+  $strongarm->name = 'slack_channel';
+  $strongarm->value = '#websites-discussion';
+  $export['slack_channel'] = $strongarm;
+
+  $strongarm = new stdClass();
+  $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+  $strongarm->api_version = 1;
+  $strongarm->name = 'slack_webhook_url';
+  $strongarm->value = 'https://hooks.slack.com/services/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';
+  $export['slack_webhook_url'] = $strongarm;
+
+  $strongarm = new stdClass();
+  $strongarm->disabled = FALSE; /* Edit this to true to make a default strongarm disabled initially */
+  $strongarm->api_version = 1;
+  $strongarm->name = 'watchdog_slack_channel';
+  $strongarm->value = '#website-discussion';
+  $export['watchdog_slack_channel'] = $strongarm;
+
+  return $export;
+}
diff --git a/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.info b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.info
new file mode 100644
index 0000000000000000000000000000000000000000..f09ed723b4e82da9e5af54a976977b812216586c
--- /dev/null
+++ b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.info
@@ -0,0 +1,16 @@
+name = WCM Watchdog to Slack
+description = Feature that uses rules and slack to notify of us of errors.
+core = 7.x
+package = WCM Configuration
+version = 7.x-1.0
+dependencies[] = rules
+dependencies[] = rules_admin
+dependencies[] = slack
+dependencies[] = watchdog_rules
+features[defaultconfig][] = strongarm:slack_channel
+features[defaultconfig][] = strongarm:slack_webhook_url
+features[defaultconfig][] = strongarm:watchdog_slack_channel
+features[features_api][] = api:2
+features[rules_config][] = rules_mail_error_to_slack
+features_exclude[dependencies][defaultconfig] = defaultconfig
+features_exclude[dependencies][entity] = entity
diff --git a/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.module b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.module
new file mode 100644
index 0000000000000000000000000000000000000000..44788b071e42418f461724a48cea5cd1f0b6e429
--- /dev/null
+++ b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.module
@@ -0,0 +1,5 @@
+<?php
+/**
+ * @file
+ * Drupal needs this blank file.
+ */
diff --git a/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.rules_defaults.inc b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.rules_defaults.inc
new file mode 100644
index 0000000000000000000000000000000000000000..6fdd00a5282ad88d4b1a1715fa707241cdd2df20
--- /dev/null
+++ b/profiles/wcm_base/modules/custom/wcm_watchdog_to_slack/wcm_watchdog_to_slack.rules_defaults.inc
@@ -0,0 +1,32 @@
+<?php
+/**
+ * @file
+ * wcm_watchdog_to_slack.rules_defaults.inc
+ */
+
+/**
+ * Implements hook_default_rules_configuration().
+ */
+function wcm_watchdog_to_slack_default_rules_configuration() {
+  $items = array();
+  $items['rules_mail_error_to_slack'] = entity_import('rules_config', '{ "rules_mail_error_to_slack" : {
+      "LABEL" : "mail error to slack",
+      "PLUGIN" : "reaction rule",
+      "OWNER" : "rules",
+      "REQUIRES" : [ "rules", "slack", "watchdog_rules" ],
+      "ON" : { "watchdog_rules" : [] },
+      "IF" : [
+        { "data_is" : { "data" : [ "watchdog-type" ], "value" : "mail" } },
+        { "data_is" : { "data" : [ "watchdog-severity" ], "value" : "3" } }
+      ],
+      "DO" : [
+        { "slack_send_message" : {
+            "channel" : "#website-discussion",
+            "message" : "\\u003C!channel\\u003E Mail error on [site:name]. Check logs on [site:url]."
+          }
+        }
+      ]
+    }
+  }');
+  return $items;
+}
diff --git a/profiles/wcm_base/wcm_base.info b/profiles/wcm_base/wcm_base.info
index 18545dc66e3da57b07065284f3ae719c40ac930d..b98138e1e99fe13bfa4692025839903783f024c7 100644
--- a/profiles/wcm_base/wcm_base.info
+++ b/profiles/wcm_base/wcm_base.info
@@ -55,6 +55,7 @@ dependencies[] = ds_ui
 dependencies[] = file_entity_swf
 dependencies[] = flexslider
 dependencies[] = flexslider_views
+dependencies[] = logging_alerts
 dependencies[] = mailsystem
 dependencies[] = mimemail
 dependencies[] = clone
@@ -65,6 +66,9 @@ dependencies[] = prepopulate
 dependencies[] = private
 dependencies[] = private_files_download_permission
 dependencies[] = r4032login
+dependencies[] = rules
+dependencies[] = rules_conditional
+dependencies[] = slack
 dependencies[] = smart_trim
 dependencies[] = smtp
 dependencies[] = telephone
@@ -135,3 +139,4 @@ dependencies[] = wcm_user_contact
 dependencies[] = wcm_user_leadership
 dependencies[] = wcm_user_profile
 dependencies[] = wcm_suppress_headers
+dependencies[] = wcm_watchdog_to_slack
diff --git a/profiles/wcm_base/wcm_base.make b/profiles/wcm_base/wcm_base.make
index 68b6527a61ea659340d0a576ab956187ac785563..8e3c98a74235bd4e97cd33fa298773482e12e8ac 100644
--- a/profiles/wcm_base/wcm_base.make
+++ b/profiles/wcm_base/wcm_base.make
@@ -4,7 +4,7 @@ core = 7.x
 ; **********************************************
 ; ***************** CONTRIB ********************
 
-projects[better_exposed_filters][version] = 3.5
+projects[better_exposed_filters][version] = 3.6
 projects[better_exposed_filters][subdir] = contrib
 projects[better_exposed_filters][patch][2560321] = http://drupal.org/files/issues/better_exposed_filters-placeholder_support-2560321-4.patch
 
@@ -20,7 +20,7 @@ projects[context][subdir] = contrib
 projects[context_omega][version] = 1.1
 projects[context_omega][subdir] = contrib
 
-projects[ctools][version] = 1.14
+projects[ctools][version] = 1.15
 projects[ctools][subdir] = contrib
 
 projects[date][version] = 2.10
@@ -42,7 +42,7 @@ projects[entity][patch][1312374] = http://drupal.org/files/issues/entity-1312374
 projects[ds][version] = 2.16
 projects[ds][subdir] = contrib
 
-projects[features][version] = 2.10
+projects[features][version] = 2.11
 projects[features][subdir] = contrib
 projects[features][patch][986968] = https://drupal.org/files/issues/export_shorcuts_sets-986968-36.patch
 
@@ -52,7 +52,7 @@ projects[features_extra][subdir] = contrib
 projects[field_group][version] = 1.6
 projects[field_group][subdir] = contrib
 
-projects[fieldable_panels_panes][version] = 1.10
+projects[fieldable_panels_panes][version] = 1.11
 projects[fieldable_panels_panes][subdir] = contrib
 
 projects[file_entity][version] = 2.4
@@ -63,7 +63,7 @@ projects[file_entity][patch][1997208] = http://drupal.org/files/issues/use-file_
 projects[file_entity_swf][version] = 1.0-rc2
 projects[file_entity_swf][subdir] = contrib
 
-projects[google_analytics][version] = 2.5
+projects[google_analytics][version] = 2.6
 projects[google_analytics][subdir] = contrib
 
 projects[google_tag][version] = 1.2
@@ -79,10 +79,13 @@ projects[linkchecker][version] = 1.x-dev
 projects[linkchecker][subdir] = contrib
 projects[linkchecker][patch][1946252] = http://drupal.org/files/issues/linkchecker-hooks-1946252-4.patch
 
+projects[logging_alerts][version] = 2.2
+projects[logging_alerts][subdir] = contrib
+
 projects[mailsystem][version] = 2.34
 projects[mailsystem][subdir] = contrib
 
-projects[menu_block][version] = 2.7
+projects[menu_block][version] = 2.8
 projects[menu_block][subdir] = contrib
 projects[menu_block][patch][2076119] = http://drupal.org/files/issues/2018-05-14/menu_block-customize_deltas-2076119-7.patch
 projects[menu_block][patch][2932156] = http://drupal.org/files/issues/2018-06-01/menu_block-rebuild-block-ids-2932156-5.patch
@@ -94,10 +97,10 @@ projects[migrate][2297685] = http://drupal.org/files/issues/migrate-php7-uniform
 projects[mimemail][version] = 1.1
 projects[mimemail][subdir] = contrib
 
-projects[media][version] = 2.19
+projects[media][version] = 2.21
 projects[media][subdir] = contrib
 
-projects[media_youtube][version] = 3.5
+projects[media_youtube][version] = 3.8
 projects[media_youtube][subdir] = contrib
 
 projects[navbar][version] = 1.5
@@ -137,7 +140,7 @@ projects[private][subdir] = contrib
 projects[private_files_download_permission][version] = 2.5
 projects[private_files_download_permission][subdir] = contrib
 
-projects[realname][version] = 1.3
+projects[realname][version] = 1.4
 projects[realname][subdir] = contrib
 
 projects[r4032login][version] = 1.8
@@ -146,10 +149,19 @@ projects[r4032login][subdir] = contrib
 projects[redis][version] = 3.17
 projects[redis][subdir] = contrib
 
+projects[rules][version] = 2.12
+projects[rules][subdir] = contrib
+
+projects[rules_conditional][version] = 1.0-beta2
+projects[rules_conditional][subdir] = contrib
+
 projects[search_api_solr][version] = 1.14
 projects[search_api_solr][subdir] = contrib
 projects[search_api_solr][patch][1414838] = http://drupal.org/files/issues/search_api_solr-partial_word_matching-1414838-21.patch
 
+projects[slack][version] = 1.6
+projects[slack][subdir] = contrib
+
 projects[smart_trim][version] = 1.5
 projects[smart_trim][subdir] = contrib
 
@@ -178,7 +190,7 @@ projects[views][subdir] = contrib
 projects[views][patch][2985178] = http://drupal.org/files/issues/2018-07-15/mysql-group-by-duplication-workaround-2985178-7.patch
 projects[views][patch][2037469] = http://drupal.org/files/issues/views-exposed-sorts-2037469-26.patch
 
-projects[views_accordion][version] = 1.1
+projects[views_accordion][version] = 1.2
 projects[views_accordion][subdir] = contrib
 
 projects[views_conditional][version] = 1.3
@@ -451,6 +463,12 @@ projects[wcm_suppress_headers][download][type] = "git"
 projects[wcm_suppress_headers][download][url] = git@code.osu.edu:ocio_odee_web/wcm_suppress_headers.git
 projects[wcm_suppress_headers][download][branch] = 7.x-1.x
 
+projects[wcm_watchdog_to_slack][type] = module
+projects[wcm_watchdog_to_slack][subdir] = custom
+projects[wcm_watchdog_to_slack][download][type] = "git"
+projects[wcm_watchdog_to_slack][download][url] = git@code.osu.edu:ocio_odee_web/wcm_watchdog_to_slack.git
+projects[wcm_watchdog_to_slack][download][branch] = 7.x-1.x
+
 
 ;themes
 projects[ocio_seven][type] = theme