From d280bfb5230fe1d07e8d01ec47cfc6cbbbf28987 Mon Sep 17 00:00:00 2001
From: Chris Gross <gross.364@osu.edu>
Date: Wed, 10 Aug 2016 13:03:12 -0400
Subject: [PATCH] weekly build

---
 profiles/wcm_base/CHANGELOG.txt               |  4 ++
 .../contrib/google_analytics/README.txt       | 59 ++++++++++++-----
 .../googleanalytics.admin.inc                 | 46 ++++++++++---
 .../google_analytics/googleanalytics.admin.js |  9 +++
 .../google_analytics/googleanalytics.debug.js | 64 ++++++++++++++-----
 .../google_analytics/googleanalytics.info     |  6 +-
 .../google_analytics/googleanalytics.install  |  9 +--
 .../google_analytics/googleanalytics.js       | 58 +++++++++++++----
 .../google_analytics/googleanalytics.module   | 36 +++++++++--
 .../google_analytics/googleanalytics.test     | 62 ++++++++++++++++--
 .../googleanalytics.variable.inc              |  4 ++
 profiles/wcm_base/wcm_base.make               |  2 +-
 12 files changed, 282 insertions(+), 77 deletions(-)

diff --git a/profiles/wcm_base/CHANGELOG.txt b/profiles/wcm_base/CHANGELOG.txt
index 4b7d4740..9ce5c4b5 100644
--- a/profiles/wcm_base/CHANGELOG.txt
+++ b/profiles/wcm_base/CHANGELOG.txt
@@ -1,3 +1,7 @@
+WCM Base 7.x-1.x, 2016-08-10
+----------------------------
+-WCM Base: Updated Google Analytics to 2.3.
+
 WCM Base 7.x-1.x, 2016-08-03
 ----------------------------
 -WCM Base: Updated Panopoly to 1.38.
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/README.txt b/profiles/wcm_base/modules/contrib/google_analytics/README.txt
index fab2f2ec..25fd22aa 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/README.txt
+++ b/profiles/wcm_base/modules/contrib/google_analytics/README.txt
@@ -12,12 +12,20 @@ Requirements
 
 * Google Analytics user account
 
-
 Installation
 ============
 Copy the 'googleanalytics' module directory in to your Drupal
 sites/all/modules directory as usual.
 
+Upgrading from 6.x-3.x and 7.x-1.x
+==================================
+If you upgrade from 6.x-3.x and 7.x-1.x (ga.js) to 7.x-2.x (analytics.js) you
+should verify if you used custom variables. Write down your settings or make a 
+screenshot. You need to re-configure the settings to use custom dimensions or
+metrics. There is no automatic upgrade path for custom variables feature. All
+other module settings are upgraded automatically.
+
+See https://support.google.com/analytics/answer/2795983?hl=en for more details.
 
 Usage
 =====
@@ -27,12 +35,8 @@ All pages will now have the required JavaScript added to the
 HTML footer can confirm this by viewing the page source from
 your browser.
 
-New approach to page tracking in 5.x-1.5 and 6.x-1.1
-====================================================
-With 5.x-1.5 and 6.x-1.1 there are new settings on the settings page at
-admin/config/system/googleanalytics. The "Page specific tracking" area now
-comes with an interface that copies Drupal's block visibility settings.
-
+Page specific tracking
+======================
 The default is set to "Add to every page except the listed pages". By
 default the following pages are listed for exclusion:
 
@@ -44,23 +48,24 @@ node/*/*
 user/*/*
 
 These defaults are changeable by the website administrator or any other
-user with 'administer google analytics' permission.
+user with 'Administer Google Analytics' permission.
 
-Like the blocks visibility settings in Drupal core, there is now 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.
+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.
 
 Custom dimensions and metrics
 =============================
 One example for custom dimensions tracking is the "User roles" tracking.
 
-1. In the Google Analytics Management Interface 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 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.
 
-2. Enter the below configuration data into the custom dimensions settings form
-   under admin/config/system/googleanalytics. You can also choose another index,
-   but keep it always in sync with the index used in step #1.
+2. Enter the below configuration data into the Drupal custom dimensions settings
+   form under admin/config/system/googleanalytics. You can also choose another
+   index, but keep it always in sync with the index used in step #1.
 
    Index: 1
    Value: [current-user:role-names]
@@ -77,3 +82,23 @@ provided for any customisations you include.
 
 To speed up page loading you may also cache the Google Analytics "analytics.js"
 file locally.
+
+Manual JS debugging
+===================
+For manual debugging of the JS code you are able to create a test node. This
+is the example HTML code for this test node. You need to enable debugging mode
+in your Drupal configuration of Google Analytics settings to see verbose
+messages in your browsers JS console.
+
+Title: Google Analytics test page
+
+Body:
+<ul>
+  <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="/go/test">Go link</a></li>
+</ul>
+
+Text format: Full HTML
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 4449077d..d32939a3 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.inc
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.inc
@@ -224,6 +224,18 @@ function googleanalytics_admin_settings_form($form_state) {
       ),
     ),
   );
+
+  $colorbox_dependencies = '<div class="admin-requirements">';
+  $colorbox_dependencies .= t('Requires: !module-list', array('!module-list' => (module_exists('colorbox') ? t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => 'Colorbox')) : t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => 'Colorbox')))));
+  $colorbox_dependencies .= '</div>';
+
+  $form['tracking']['linktracking']['googleanalytics_trackcolorbox'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Track content in colorbox modal dialogs'),
+    '#default_value' => variable_get('googleanalytics_trackcolorbox', 1),
+    '#description' => t('Enable to track the content shown in colorbox modal windows.') . $colorbox_dependencies,
+    '#disabled' => (module_exists('colorbox') ? FALSE : TRUE),
+  );
   $form['tracking']['linktracking']['googleanalytics_tracklinkid'] = array(
     '#type' => 'checkbox',
     '#title' => t('Track enhanced link attribution'),
@@ -246,7 +258,7 @@ function googleanalytics_admin_settings_form($form_state) {
     '#type' => 'checkboxes',
     '#title' => t('Track messages of type'),
     '#default_value' => variable_get('googleanalytics_trackmessages', array()),
-    '#description' => t('This will track the selected message types shown to users. Tracking of form validation errors may help you identifying usability issues in your site. For each visit (user session), a maximum of approximately 500 combined GATC requests (both events and page views) can be tracked. Every message is tracked as one individual event. Note that - as the number of events in a session approaches the limit - additional events might not be tracked. Messages from excluded pages cannot tracked.'),
+    '#description' => t('This will track the selected message types shown to users. Tracking of form validation errors may help you identifying usability issues in your site. For each visit (user session), a maximum of approximately 500 combined GATC requests (both events and page views) can be tracked. Every message is tracked as one individual event. Note that - as the number of events in a session approaches the limit - additional events might not be tracked. Messages from excluded pages cannot be tracked.'),
     '#options' => array(
       'status' => t('Status message'),
       'warning' => t('Warning message'),
@@ -305,7 +317,7 @@ function googleanalytics_admin_settings_form($form_state) {
   $form['googleanalytics_custom_dimension'] = array(
     '#collapsed' => TRUE,
     '#collapsible' => TRUE,
-    '#description' => t('You can set values for Google Analytics <a href="@custom_var_documentation">Custom Dimensions</a> here. You must have already configured your custom dimensions in the <a href="@setup_documentation">Google Analytics Management Interface</a>. You may use tokens. Global and user tokens are always available; on node pages, node tokens are also available. A dimension <em>value</em> is allowed to have a maximum lenght of 150 bytes. Expect longer values to get trimmed.', array('@custom_var_documentation' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets', '@setup_documentation' => 'https://support.google.com/analytics/answer/2709829')),
+    '#description' => t('You can set values for Google Analytics <a href="@custom_var_documentation">Custom Dimensions</a> here. You must have already configured your custom dimensions in the <a href="@setup_documentation">Google Analytics Management Interface</a>. You may use tokens. Global and user tokens are always available; on node pages, node tokens are also available. A dimension <em>value</em> is allowed to have a maximum length of 150 bytes. Expect longer values to get trimmed.', array('@custom_var_documentation' => 'https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets', '@setup_documentation' => 'https://support.google.com/analytics/answer/2709829')),
     '#theme' => 'googleanalytics_admin_custom_var_table',
     '#title' => t('Custom dimensions'),
     '#tree' => TRUE,
@@ -428,6 +440,8 @@ function googleanalytics_admin_settings_form($form_state) {
     );
   }
 
+  $user_access_add_js_snippets = !user_access('add JS snippets for google analytics');
+  $user_access_add_js_snippets_permission_warning = $user_access_add_js_snippets ? ' <em>' . t('This field has been disabled because you do not have sufficient permissions to edit it.') . '</em>' : '';
   $form['advanced']['codesnippet'] = array(
     '#type' => 'fieldset',
     '#title' => t('Custom JavaScript code'),
@@ -447,15 +461,17 @@ function googleanalytics_admin_settings_form($form_state) {
     '#type' => 'textarea',
     '#title' => t('Code snippet (before)'),
     '#default_value' => variable_get('googleanalytics_codesnippet_before', ''),
+    '#disabled' => $user_access_add_js_snippets,
     '#rows' => 5,
-    '#description' => t('Code in this textarea will be added <strong>before</strong> <code>ga("send", "pageview");</code>.'),
+    '#description' => t('Code in this textarea will be added <strong>before</strong> <code>ga("send", "pageview");</code>.') . $user_access_add_js_snippets_permission_warning,
   );
   $form['advanced']['codesnippet']['googleanalytics_codesnippet_after'] = array(
     '#type' => 'textarea',
     '#title' => t('Code snippet (after)'),
     '#default_value' => variable_get('googleanalytics_codesnippet_after', ''),
+    '#disabled' => $user_access_add_js_snippets,
     '#rows' => 5,
-    '#description' => t('Code in this textarea will be added <strong>after</strong> <code>ga("send", "pageview");</code>. This is useful if you\'d like to track a site in two accounts.'),
+    '#description' => t('Code in this textarea will be added <strong>after</strong> <code>ga("send", "pageview");</code>. This is useful if you\'d like to track a site in two accounts.') . $user_access_add_js_snippets_permission_warning,
   );
 
   $form['advanced']['googleanalytics_debug'] = array(
@@ -541,6 +557,12 @@ function googleanalytics_admin_settings_form_validate($form, &$form_state) {
 
 /**
  * Layout for the custom variables table in the admin settings form.
+ *
+ * @param array $variables
+ *   An array contains the form elements.
+ *
+ * @return string
+ *   The rendered output.
  */
 function theme_googleanalytics_admin_custom_var_table($variables) {
   $form = $variables['form'];
@@ -551,7 +573,7 @@ function theme_googleanalytics_admin_custom_var_table($variables) {
   );
 
   $rows = array();
-  foreach (element_children($form['indexes']) as $key => $id) {
+  foreach (element_children($form['indexes']) as $id) {
     $rows[] = array(
       'data' => array(
         drupal_render($form['indexes'][$id]['index']),
@@ -600,11 +622,18 @@ function googleanalytics_token_element_validate(&$element, &$form_state) {
   return $element;
 }
 
+/**
+ * @param array $value
+ *   An array of token values.
+ *
+ * @return array
+ *   A unique array of invalid tokens.
+ */
 function _googleanalytics_get_forbidden_tokens($value) {
   $invalid_tokens = array();
   $value_tokens = is_string($value) ? token_scan($value) : $value;
 
-  foreach ($value_tokens as $type => $tokens) {
+  foreach ($value_tokens as $tokens) {
     if (array_filter($tokens, '_googleanalytics_contains_forbidden_token')) {
       $invalid_tokens = array_merge($invalid_tokens, array_values($tokens));
     }
@@ -617,8 +646,9 @@ function _googleanalytics_get_forbidden_tokens($value) {
 /**
  * Validate if a string contains forbidden tokens not allowed by privacy rules.
  *
- * @param $token_string
+ * @param string $token_string
  *   A string with one or more tokens to be validated.
+ *
  * @return boolean
  *   TRUE if blacklisted token has been found, otherwise FALSE.
  */
@@ -724,7 +754,7 @@ function _googleanalytics_extract_create_field_values($string) {
   $list = array_map('trim', $list);
   $list = array_filter($list, 'strlen');
 
-  foreach ($list as $position => $text) {
+  foreach ($list as $text) {
     // Check for an explicit key.
     $matches = array();
     if (preg_match('/(.*)\|(.*)/', $text, $matches)) {
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.js b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.js
index 083eeaa3..c81470d7 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.js
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.admin.js
@@ -65,6 +65,15 @@ Drupal.behaviors.trackingSettingsSummary = {
       if ($('input#edit-googleanalytics-trackfiles', context).is(':checked')) {
         vals.push(Drupal.t('Downloads'));
       }
+      if ($('input#edit-googleanalytics-trackcolorbox', context).is(':checked')) {
+        vals.push(Drupal.t('Colorbox'));
+      }
+      if ($('input#edit-googleanalytics-tracklinkid', context).is(':checked')) {
+        vals.push(Drupal.t('Link attribution'));
+      }
+      if ($('input#edit-googleanalytics-trackurlfragments', context).is(':checked')) {
+        vals.push(Drupal.t('URL fragments'));
+      }
       if (!vals.length) {
         return Drupal.t('Not tracked');
       }
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 861f7fdb..a0be81ae 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.debug.js
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.debug.js
@@ -8,16 +8,16 @@ $(document).ready(function() {
   // clicks on all elements.
   $(document.body).bind("mousedown keyup touchstart", function(event) {
     console.group("Running Google Analytics for Drupal.");
-    console.info(event);
+    console.info("Event '%s' has been detected.", event.type);
 
     // Catch the closest surrounding link of a clicked element.
     $(event.target).closest("a,area").each(function() {
-      console.info("Element '%o' has been detected. Link '%s' found.", this, this.href);
+      console.info("Closest element '%o' has been found. URL '%s' extracted.", this, this.href);
 
       // Is the clicked URL internal?
       if (Drupal.googleanalytics.isInternal(this.href)) {
         // Skip 'click' tracking, if custom tracking events are bound.
-        if ($(this).is('.colorbox')) {
+        if ($(this).is('.colorbox') && (Drupal.settings.googleanalytics.trackColorbox)) {
           // Do nothing here. The custom event will handle all tracking.
           console.info("Click on .colorbox item has been detected.");
         }
@@ -25,12 +25,22 @@ $(document).ready(function() {
         else if (Drupal.settings.googleanalytics.trackDownload && Drupal.googleanalytics.isDownload(this.href)) {
           // Download link clicked.
           console.info("Download url '%s' has been found. Tracked download as extension '%s'.", Drupal.googleanalytics.getPageUrl(this.href), Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase());
-          ga("send", "event", "Downloads", Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(), Drupal.googleanalytics.getPageUrl(this.href));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Downloads",
+            "eventAction": Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(),
+            "eventLabel": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.googleanalytics.isInternalSpecial(this.href)) {
           // Keep the internal URL for Google Analytics website overlay intact.
           console.info("Click on internal special link '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(this.href));
-          ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(this.href) });
+          ga("send", {
+            "hitType": "pageview",
+            "page": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else {
           // e.g. anchor in same page or other internal page link
@@ -41,13 +51,25 @@ $(document).ready(function() {
         if (Drupal.settings.googleanalytics.trackMailto && $(this).is("a[href^='mailto:'],area[href^='mailto:']")) {
           // Mailto link clicked.
           console.info("Click on e-mail '%s' has been tracked.", this.href.substring(7));
-          ga("send", "event", "Mails", "Click", this.href.substring(7));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Mails",
+            "eventAction": "Click",
+            "eventLabel": this.href.substring(7),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.settings.googleanalytics.trackOutbound && this.href.match(/^\w+:\/\//i)) {
-          if (Drupal.settings.googleanalytics.trackDomainMode != 2 || (Drupal.settings.googleanalytics.trackDomainMode == 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
+          if (Drupal.settings.googleanalytics.trackDomainMode !== 2 || (Drupal.settings.googleanalytics.trackDomainMode === 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
             // External link clicked / No top-level cross domain clicked.
             console.info("Outbound link '%s' has been tracked.", this.href);
-            ga("send", "event", "Outbound links", "Click", this.href);
+            ga("send", {
+              "hitType": "event",
+              "eventCategory": "Outbound links",
+              "eventAction": "Click",
+              "eventLabel": this.href,
+              "transport": "beacon"
+            });
           }
           else {
             console.info("Internal link '%s' clicked, not tracked.", this.href);
@@ -63,19 +85,27 @@ $(document).ready(function() {
   if (Drupal.settings.googleanalytics.trackUrlFragments) {
     window.onhashchange = function() {
       console.info("Track URL '%s' as pageview. Hash '%s' has changed.", location.pathname + location.search + location.hash, location.hash);
-      ga('send', 'pageview', location.pathname + location.search + location.hash);
-    }
+      ga("send", {
+        "hitType": "pageview",
+        "page": location.pathname + location.search + location.hash
+      });
+    };
   }
 
   // Colorbox: This event triggers when the transition has completed and the
   // newly loaded content has been revealed.
-  $(document).bind("cbox_complete", function () {
-    var href = $.colorbox.element().attr("href");
-    if (href) {
-      console.info("Colorbox transition to url '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(href));
-      ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(href) });
-    }
-  });
+  if (Drupal.settings.googleanalytics.trackColorbox) {
+    $(document).bind("cbox_complete", function () {
+      var href = $.colorbox.element().attr("href");
+      if (href) {
+        console.info("Colorbox transition to url '%s' has been tracked.", Drupal.googleanalytics.getPageUrl(href));
+        ga("send", {
+          "hitType": "pageview",
+          "page": Drupal.googleanalytics.getPageUrl(href)
+        });
+      }
+    });
+  }
 
 });
 
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.info b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.info
index adbf134a..2b814605 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.info
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.info
@@ -5,9 +5,9 @@ package = Statistics
 configure = admin/config/system/googleanalytics
 files[] = googleanalytics.test
 test_dependencies[] = token
-; Information added by Drupal.org packaging script on 2014-11-29
-version = "7.x-2.1"
+; Information added by Drupal.org packaging script on 2016-08-09
+version = "7.x-2.3"
 core = "7.x"
 project = "google_analytics"
-datestamp = "1417276982"
+datestamp = "1470779953"
 
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.install b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.install
index d7b2edfb..a4f4e83a 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.install
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.install
@@ -25,6 +25,7 @@ function googleanalytics_uninstall() {
   variable_del('googleanalytics_roles');
   variable_del('googleanalytics_site_search');
   variable_del('googleanalytics_trackadsense');
+  variable_del('googleanalytics_trackcolorbox');
   variable_del('googleanalytics_trackdoubleclick');
   variable_del('googleanalytics_tracker_anonymizeip');
   variable_del('googleanalytics_trackfiles');
@@ -446,16 +447,16 @@ function googleanalytics_update_7200() {
   if (!empty($googleanalytics_codesnippet_before) && stristr($googleanalytics_codesnippet_before, '_gaq.push(')) {
     variable_set('googleanalytics_codesnippet_before_backup_7200', $googleanalytics_codesnippet_before);
     variable_del('googleanalytics_codesnippet_before');
-    drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet."), 'warning');
-    $messages[] = t('Manual upgrade of custom "before" code snippet is required.');
+    drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet (ga.js) has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet to analytics.js API."), 'warning');
+    $messages[] = t('Manual upgrade of custom "before" code snippet from ja.js to analytics.js API is required.');
   }
 
   $googleanalytics_codesnippet_after = variable_get('googleanalytics_codesnippet_after', '');
   if (!empty($googleanalytics_codesnippet_after) && stristr($googleanalytics_codesnippet_after, '_gaq.push(')) {
     variable_set('googleanalytics_codesnippet_after_backup_7200', $googleanalytics_codesnippet_after);
     variable_del('googleanalytics_codesnippet_after');
-    drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet has been saved in database table '{variable}' as 'googleanalytics_codesnippet_before_backup_7200'. You need to manually upgrade the custom 'before' code snippet."), 'warning');
-    $messages[] = t('Manual upgrade of custom "after" code snippet is required.');
+    drupal_set_message(Database::getConnection()->prefixTables("A backup of your previous Google Analytics code snippet (ga.js) has been saved in database table '{variable}' as 'googleanalytics_codesnippet_after_backup_7200'. You need to manually upgrade the custom 'after' code snippet to analytics.js API."), 'warning');
+    $messages[] = t('Manual upgrade of custom "after" code snippet from ja.js to analytics.js API is required.');
   }
 
   return empty($messages) ? t('No custom code snipped found. Nothing to do.') : implode(' ', $messages);
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.js b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.js
index 1eed3c32..8d5bde1b 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.js
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.js
@@ -14,29 +14,51 @@ $(document).ready(function() {
       // Is the clicked URL internal?
       if (Drupal.googleanalytics.isInternal(this.href)) {
         // Skip 'click' tracking, if custom tracking events are bound.
-        if ($(this).is('.colorbox')) {
+        if ($(this).is('.colorbox') && (Drupal.settings.googleanalytics.trackColorbox)) {
           // Do nothing here. The custom event will handle all tracking.
           //console.info("Click on .colorbox item has been detected.");
         }
         // Is download tracking activated and the file extension configured for download tracking?
         else if (Drupal.settings.googleanalytics.trackDownload && Drupal.googleanalytics.isDownload(this.href)) {
           // Download link clicked.
-          ga("send", "event", "Downloads", Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(), Drupal.googleanalytics.getPageUrl(this.href));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Downloads",
+            "eventAction": Drupal.googleanalytics.getDownloadExtension(this.href).toUpperCase(),
+            "eventLabel": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.googleanalytics.isInternalSpecial(this.href)) {
           // Keep the internal URL for Google Analytics website overlay intact.
-          ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(this.href) });
+          ga("send", {
+            "hitType": "pageview",
+            "page": Drupal.googleanalytics.getPageUrl(this.href),
+            "transport": "beacon"
+          });
         }
       }
       else {
         if (Drupal.settings.googleanalytics.trackMailto && $(this).is("a[href^='mailto:'],area[href^='mailto:']")) {
           // Mailto link clicked.
-          ga("send", "event", "Mails", "Click", this.href.substring(7));
+          ga("send", {
+            "hitType": "event",
+            "eventCategory": "Mails",
+            "eventAction": "Click",
+            "eventLabel": this.href.substring(7),
+            "transport": "beacon"
+          });
         }
         else if (Drupal.settings.googleanalytics.trackOutbound && this.href.match(/^\w+:\/\//i)) {
-          if (Drupal.settings.googleanalytics.trackDomainMode != 2 || (Drupal.settings.googleanalytics.trackDomainMode == 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
+          if (Drupal.settings.googleanalytics.trackDomainMode !== 2 || (Drupal.settings.googleanalytics.trackDomainMode === 2 && !Drupal.googleanalytics.isCrossDomain(this.hostname, Drupal.settings.googleanalytics.trackCrossDomains))) {
             // External link clicked / No top-level cross domain clicked.
-            ga("send", "event", "Outbound links", "Click", this.href);
+            ga("send", {
+              "hitType": "event",
+              "eventCategory": "Outbound links",
+              "eventAction": "Click",
+              "eventLabel": this.href,
+              "transport": "beacon"
+            });
           }
         }
       }
@@ -46,18 +68,26 @@ $(document).ready(function() {
   // Track hash changes as unique pageviews, if this option has been enabled.
   if (Drupal.settings.googleanalytics.trackUrlFragments) {
     window.onhashchange = function() {
-      ga('send', 'pageview', location.pathname + location.search + location.hash);
-    }
+      ga("send", {
+        "hitType": "pageview",
+        "page": location.pathname + location.search + location.hash
+      });
+    };
   }
 
   // Colorbox: This event triggers when the transition has completed and the
   // newly loaded content has been revealed.
-  $(document).bind("cbox_complete", function () {
-    var href = $.colorbox.element().attr("href");
-    if (href) {
-      ga("send", "pageview", { "page": Drupal.googleanalytics.getPageUrl(href) });
-    }
-  });
+  if (Drupal.settings.googleanalytics.trackColorbox) {
+    $(document).bind("cbox_complete", function () {
+      var href = $.colorbox.element().attr("href");
+      if (href) {
+        ga("send", {
+          "hitType": "pageview",
+          "page": Drupal.googleanalytics.getPageUrl(href)
+        });
+      }
+    });
+  }
 
 });
 
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.module b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.module
index 4051b1b9..b45ee103 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.module
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.module
@@ -69,6 +69,11 @@ function googleanalytics_permission() {
       'description' => t('Enter PHP code in the field for tracking visibility settings.'),
       'restrict access' => TRUE,
     ),
+    'add JS snippets for google analytics' => array(
+      'title' => t('Add JavaScript snippets'),
+      'description' => 'Enter JavaScript code snippets for advanced Google Analytics functionality.',
+      'restrict access' => TRUE,
+    ),
   );
 }
 
@@ -125,6 +130,9 @@ function googleanalytics_page_alter(&$page) {
       $link_settings['trackDownload'] = $track_download;
       $link_settings['trackDownloadExtensions'] = $trackfiles_extensions;
     }
+    if (module_exists('colorbox') && ($track_colorbox = variable_get('googleanalytics_trackcolorbox', 1))) {
+      $link_settings['trackColorbox'] = $track_colorbox;
+    }
     if ($track_domain_mode = variable_get('googleanalytics_domain_mode', 0)) {
       $link_settings['trackDomainMode'] = $track_domain_mode;
     }
@@ -294,10 +302,7 @@ function googleanalytics_page_alter(&$page) {
 
     // Track logged in users across all devices.
     if (variable_get('googleanalytics_trackuserid', 0) && user_is_logged_in()) {
-      // The USER_ID value should be a unique, persistent, and non-personally
-      // identifiable string identifier that represents a user or signed-in
-      // account across devices.
-      $create_only_fields['userId'] = drupal_hmac_base64($user->uid, drupal_get_private_key() . drupal_get_hash_salt());
+      $create_only_fields['userId'] = google_analytics_user_id_hash($user->uid);
     }
 
     // Create a tracker.
@@ -352,13 +357,30 @@ function googleanalytics_page_alter(&$page) {
       // Custom tracking. Prepend before all other JavaScript.
       // @TODO: https://support.google.com/adsense/answer/98142
       // sounds like it could be appended to $script.
-      drupal_add_js($googleanalytics_adsense_script, array('type' => 'inline', 'group' => JS_LIBRARY-1));
+      drupal_add_js($googleanalytics_adsense_script, array('type' => 'inline', 'group' => JS_LIBRARY-1, 'requires_jquery' => FALSE));
     }
 
-    drupal_add_js($script, array('scope' => 'header', 'type' => 'inline'));
+    drupal_add_js($script, array('scope' => 'header', 'type' => 'inline', 'requires_jquery' => FALSE));
   }
 }
 
+/**
+ * Generate user id hash to implement USER_ID.
+ *
+ * The USER_ID value should be a unique, persistent, and non-personally
+ * identifiable string identifier that represents a user or signed-in
+ * account across devices.
+ *
+ * @param int $uid
+ *   User id.
+ *
+ * @return string
+ *   User id hash.
+ */
+function google_analytics_user_id_hash($uid) {
+  return drupal_hmac_base64($uid, drupal_get_private_key() . drupal_get_hash_salt());
+}
+
 /**
  * Implements hook_field_extra_fields().
  */
@@ -456,7 +478,7 @@ function googleanalytics_preprocess_search_results(&$variables) {
     // found. But the pager item mumber can tell the number of search results.
     global $pager_total_items;
 
-    drupal_add_js('window.googleanalytics_search_results = ' . intval($pager_total_items[0]) . ';', array('type' => 'inline', 'group' => JS_LIBRARY-1));
+    drupal_add_js('window.googleanalytics_search_results = ' . intval($pager_total_items[0]) . ';', array('type' => 'inline', 'group' => JS_LIBRARY-1, 'requires_jquery' => FALSE));
   }
 }
 
diff --git a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test
index 0b64bb82..745047a8 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.test
@@ -6,6 +6,13 @@
  */
 class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
 
+  /**
+   * User without permissions to edit snippets.
+   *
+   * @var \StdClass
+   */
+  protected $noSnippetUser;
+
   public static function getInfo() {
     return array(
       'name' => 'Google Analytics basic tests',
@@ -25,6 +32,8 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     );
 
     // User to set up google_analytics.
+    $this->noSnippetUser = $this->drupalCreateUser($permissions);
+    $permissions[] = 'add JS snippets for google analytics';
     $this->admin_user = $this->drupalCreateUser($permissions);
     $this->drupalLogin($this->admin_user);
   }
@@ -48,6 +57,26 @@ class GoogleAnalyticsBasicTest extends DrupalWebTestCase {
     $edit['googleanalytics_account'] = $this->randomName(2);
     $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
     $this->assertRaw(t('A valid Google Analytics Web Property ID is case sensitive and formatted like UA-xxxxxxx-yy.'), '[testGoogleAnalyticsConfiguration]: Invalid Web Property ID number validated.');
+
+    // User should have access to code snippets.
+    $this->assertFieldByName('googleanalytics_codesnippet_create');
+    $this->assertFieldByName('googleanalytics_codesnippet_before');
+    $this->assertFieldByName('googleanalytics_codesnippet_after');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_create' and @disabled='disabled']", NULL, '"Create only fields" is enabled.');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_before' and @disabled='disabled']", NULL, '"Code snippet (before)" is enabled.');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_after' and @disabled='disabled']", NULL, '"Code snippet (after)" is enabled.');
+
+    // Login as user without JS permissions.
+    $this->drupalLogin($this->noSnippetUser);
+    $this->drupalGet('admin/config/system/googleanalytics');
+
+    // User should *not* have access to snippets, but create fields.
+    $this->assertFieldByName('googleanalytics_codesnippet_create');
+    $this->assertFieldByName('googleanalytics_codesnippet_before');
+    $this->assertFieldByName('googleanalytics_codesnippet_after');
+    $this->assertNoFieldByXPath("//textarea[@name='googleanalytics_codesnippet_create' and @disabled='disabled']", NULL, '"Create only fields" is enabled.');
+    $this->assertFieldByXPath("//textarea[@name='googleanalytics_codesnippet_before' and @disabled='disabled']", NULL, '"Code snippet (before)" is disabled.');
+    $this->assertFieldByXPath("//textarea[@name='googleanalytics_codesnippet_after' and @disabled='disabled']", NULL, '"Code snippet (after)" is disabled.');
   }
 
   function testGoogleAnalyticsPageVisibility() {
@@ -284,6 +313,7 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
 
     // User to set up google_analytics.
     $this->admin_user = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($this->admin_user);
   }
 
   function testGoogleAnalyticsCustomDimensions() {
@@ -362,34 +392,30 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
       1 => array(
         'index' => 1,
         'value' => '6',
-        'value_expected' => 6,
       ),
       2 => array(
         'index' => 2,
         'value' => '8000',
-        'value_expected' => 8000,
       ),
       3 => array(
         'index' => 3,
         'value' => '7.8654',
-        'value_expected' => 7.8654,
       ),
       4 => array(
         'index' => 4,
         'value' => '1123.4',
-        'value_expected' => 1123.4,
       ),
       5 => array(
         'index' => 5,
         'value' => '5,67',
-        'value_expected' => 5,
       ),
     );
+
     variable_set('googleanalytics_custom_metric', $googleanalytics_custom_metric);
     $this->drupalGet('');
 
     foreach ($googleanalytics_custom_metric as $metric) {
-      $this->assertRaw('ga("set", ' . drupal_json_encode('metric' . $metric['index']) . ', ' . drupal_json_encode($metric['value_expected']) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Metric #' . $metric['index'] . ' is shown.');
+      $this->assertRaw('ga("set", ' . drupal_json_encode('metric' . $metric['index']) . ', ' . drupal_json_encode((float) $metric['value']) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Metric #' . $metric['index'] . ' is shown.');
     }
 
     // Test whether tokens are replaced in custom metric values.
@@ -421,6 +447,30 @@ class GoogleAnalyticsCustomDimensionsAndMetricsTest extends DrupalWebTestCase {
     $this->assertNoRaw('ga("set", ' . drupal_json_encode('metric3') . ', ' . drupal_json_encode('') . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Empty value is not shown.');
     $this->assertRaw('ga("set", ' . drupal_json_encode('metric4') . ', ' . drupal_json_encode(0) . ');', '[testGoogleAnalyticsCustomDimensionsAndMetrics]: Value 0 is shown.');
   }
+
+  /**
+   * Tests if Custom Dimensions token form validation works.
+   */
+  public function testGoogleAnalyticsCustomDimensionsTokenFormValidation() {
+    $ua_code = 'UA-123456-1';
+
+    // Check form validation.
+    $edit['googleanalytics_account'] = $ua_code;
+    $edit['googleanalytics_custom_dimension[indexes][1][value]'] = '[current-user:name]';
+    $edit['googleanalytics_custom_dimension[indexes][2][value]'] = '[current-user:edit-url]';
+    $edit['googleanalytics_custom_dimension[indexes][3][value]'] = '[user:name]';
+    $edit['googleanalytics_custom_dimension[indexes][4][value]'] = '[term:name]';
+    $edit['googleanalytics_custom_dimension[indexes][5][value]'] = '[term:tid]';
+
+    $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
+
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 1)), '@invalid-tokens' => implode(', ', array('[current-user:name]')))));
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 2)), '@invalid-tokens' => implode(', ', array('[current-user:edit-url]')))));
+    $this->assertRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 3)), '@invalid-tokens' => implode(', ', array('[user:name]')))));
+    // BUG #2037595
+    //$this->assertNoRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 4)), '@invalid-tokens' => implode(', ', array('[term:name]')))));
+    //$this->assertNoRaw(t('The %element-title is using the following forbidden tokens with personal identifying information: @invalid-tokens.', array('%element-title' => t('Custom dimension value #@index', array('@index' => 5)), '@invalid-tokens' => implode(', ', array('[term:tid]')))));
+  }
 }
 
 class GoogleAnalyticsStatusMessagesTest extends DrupalWebTestCase {
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 6a657350..9d142a27 100644
--- a/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.variable.inc
+++ b/profiles/wcm_base/modules/contrib/google_analytics/googleanalytics.variable.inc
@@ -40,6 +40,10 @@ function googleanalytics_variable_group_info() {
 
 /**
  * Validate Web Property ID variable.
+ *
+ * @param array $variable
+ *
+ * @return string
  */
 function googleanalytics_validate_googleanalytics_account($variable) {
   // Replace all type of dashes (n-dash, m-dash, minus) with the normal dashes.
diff --git a/profiles/wcm_base/wcm_base.make b/profiles/wcm_base/wcm_base.make
index 88da825b..fbc9d624 100644
--- a/profiles/wcm_base/wcm_base.make
+++ b/profiles/wcm_base/wcm_base.make
@@ -58,7 +58,7 @@ projects[file_entity][patch][2533816] = http://drupal.org/files/issues/2537982-f
 projects[file_entity_swf][version] = 1.0-rc2
 projects[file_entity_swf][subdir] = contrib
 
-projects[google_analytics][version] = 2.1
+projects[google_analytics][version] = 2.3
 projects[google_analytics][subdir] = contrib
 
 projects[jquery_update][version] = 2.7
-- 
GitLab