From fa24b8fb8b81213a3a40d51cd25214e8b0c54352 Sun, 12 Oct 2014 23:42:29 +0200
From: hass <hass@85918.no-reply.drupal.org>
Date: Sun, 12 Oct 2014 23:42:16 +0200
Subject: [PATCH] Issue #2306281: Context module integration

diff --git a/googleanalytics.context.inc b/googleanalytics.context.inc
new file mode 100644
index 0000000..8166540
--- /dev/null
+++ b/googleanalytics.context.inc
@@ -0,0 +1,317 @@
+<?php
+/**
+ * @file
+ * Context reaction for Google Analytics Content Experiments.
+ */
+
+class GoogleAnalyticsReactionContentExperiment extends context_reaction {
+  /**
+   * Allow admins to provide a section title, section subtitle and section class.
+   */
+  function options_form($context) {
+    $values = $this->fetch_from_context($context);
+    $form = array(
+      '#tree' => TRUE,
+      '#title' => t('Google Analytics Content Experiment'),
+      'googleanalytics_content_experiment_key' => array(
+        '#title' => t('Experiment key'),
+        '#description' => t('The experiment key comes from Google Analytics. It should appear under the code that Google Analytics instructs you to paste immediately after the opening of the &lt;head&gt; tag.'),
+        '#type' => 'textfield',
+        '#maxlength' => 255,
+        '#default_value' => isset($values['googleanalytics_content_experiment_key']) ? $values['googleanalytics_content_experiment_key'] : '',
+      ),
+    );
+    return $form;
+  }
+
+  /**
+   * Override of options_form_submit().
+   */
+  function options_form_submit($values) {
+    if (!preg_match('/^\d+-\d+$/', $values['googleanalytics_content_experiment_key'])) {
+      form_set_error('reactions[plugins][googleanalytics_reaction_content_group][googleanalytics_content_experiment_key]', t('Google Analytics Content Experiment - A valid experiment key is formatted like xxxxxxx-yy.'));
+    }
+    return $values;
+  }
+
+  /**
+   * Output a list of active contexts.
+   */
+  function execute(&$variables) {
+    $contexts = $this->get_contexts();
+    foreach ($contexts as $context) {
+      if (!empty($context->reactions[$this->plugin]['googleanalytics_content_experiment_key'])) {
+        $script = '<!-- Google Analytics Content Experiment code -->' . "\n";
+        $script .= '<script>function utmx_section(){}function utmx(){}(function(){var';
+        $script .= 'k=' . drupal_json_encode($context->reactions[$this->plugin]['googleanalytics_content_experiment_key']) .',d=document,l=d.location,c=d.cookie;';
+        $script .= 'if(l.search.indexOf(\'utm_expid=\'+k)>0)return;';
+        $script .= 'function f(n){if(c){var i=c.indexOf(n+\'=\');if(i>-1){var j=c.';
+        $script .= 'indexOf(\';\',i);return escape(c.substring(i+n.length+1,j<0?c.';
+        $script .= 'length:j))}}}var x=f(\'__utmx\'),xx=f(\'__utmxx\'),h=l.hash;d.write(';
+        $script .= '\'<sc\'+\'ript src="\'+\'http\'+(l.protocol==\'https:\'?\'s://ssl\':';
+        $script .= '\'://www\')+\'.google-analytics.com/ga_exp.js?\'+\'utmxkey=\'+k+';
+        $script .= '\'&utmx=\'+(x?x:\'\')+\'&utmxx=\'+(xx?xx:\'\')+\'&utmxtime=\'+new Date().';
+        $script .= 'valueOf()+(h?\'&utmxhash=\'+escape(h.substr(1)):\'\')+';
+        $script .= '\'" type="text/javascript" charset="utf-8"><\/sc\'+\'ript>\')})();';
+        $script .= '</script><script>utmx(\'url\',\'A/B\');</script>' . "\n";
+        $script .= '<!-- End of Google Analytics Content Experiment code -->' . "\n";
+
+        drupal_add_html_head(array('#type' => 'markup', '#markup' => $script), 'googleanalytics_content_experiment');
+      }
+    }
+  }
+}
+
+/**
+ * Context reaction for Google Analytics Content Groups.
+ */
+class GoogleAnalyticsReactionContentGroup extends context_reaction {
+  /**
+   * Allow admins to provide a section title, section subtitle and section class.
+   */
+  function options_form($context) {
+    $values = $this->fetch_from_context($context);
+
+    $form = array(
+      '#tree' => TRUE,
+      '#title' => t('Google Analytics Content Group'),
+      'googleanalytics_content_group_id' => array(
+        '#title' => t('Group ID'),
+        '#description' => t('The content group ID you\'d like to assign the context to.'),
+        '#type' => 'select',
+        '#options' => drupal_map_assoc(array(1,2,3,4,5)),
+        '#empty_option' => t('- Select -'),
+        '#default_value' => isset($values['googleanalytics_content_group_id']) ? $values['googleanalytics_content_group_id'] : '',
+        //'#required' => TRUE,
+      ),
+      'googleanalytics_content_group_name' => array(
+        '#title' => t('Group name'),
+        '#description' => t('The content group name you\'d like to assign the context to.'),
+        '#type' => 'textfield',
+        '#maxlength' => 255,
+        '#default_value' => isset($values['googleanalytics_content_group_name']) ? $values['googleanalytics_content_group_name'] : '',
+      ),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Override of options_form_submit().
+   */
+  function options_form_submit($values) {
+    if (empty($values['googleanalytics_content_group_id'])) {
+      form_set_error('reactions[plugins][googleanalytics_reaction_content_group][googleanalytics_content_group_id]', t('Google Analytics Content Group - %field field is required.', array('%field' => t('Group ID'))));
+    }
+    if (!drupal_strlen($values['googleanalytics_content_group_name'])) {
+      form_set_error('reactions[plugins][googleanalytics_reaction_content_group][googleanalytics_content_group_name]', t('Google Analytics Content Group - %field field is required.', array('%field' => t('Group name'))));
+    }
+    return $values;
+  }
+
+  /**
+   * Output a list of active contexts.
+   */
+  function execute(&$variables) {
+    $contexts = $this->get_contexts();
+    foreach ($contexts as $context) {
+      if (!empty($context->reactions[$this->plugin]['googleanalytics_content_group_id']) && !empty($context->reactions[$this->plugin]['googleanalytics_content_group_name'])) {
+        $variables[$context->reactions[$this->plugin]['googleanalytics_content_group_id']] = $context->reactions[$this->plugin]['googleanalytics_content_group_name'];
+      }
+    }
+  }
+}
+
+/**
+ * Context reaction for Google Analytics Event.
+ */
+class GoogleAnalyticsReactionEvent extends context_reaction {
+  /**
+   * Allow admins to provide a section title, section subtitle and section class.
+   */
+  function options_form($context) {
+    $values = $this->fetch_from_context($context);
+
+    $form = array(
+      '#tree' => TRUE,
+      '#title' => t('Google Analytics Event Tracking'),
+    );
+
+    for ($i = 1; $i <= 5; $i++) {
+      $form['events'][$i] = array(
+        '#collapsed' => (empty($values['events'][$i]['googleanalytics_event_category']) && empty($values['events'][$i]['googleanalytics_event_action'])),
+        '#collapsible' => TRUE,
+        '#type' => 'fieldset',
+        '#title' => t('Event #@number', array('@number' => $i)),
+      );
+      $form['events'][$i]['googleanalytics_event_category'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Category'),
+        '#default_value' => isset($values['events'][$i]['googleanalytics_event_category']) ? $values['events'][$i]['googleanalytics_event_category'] : '',
+        '#description' => t('Typically the object that was interacted with (e.g. button).'),
+        '#maxlength' => 255,
+        '#states' => array(
+          'required' => array(
+            array(':input[name="reactions[plugins][googleanalytics_reaction_event][events]['. $i .'][googleanalytics_event_label]"]' => array('filled' => TRUE)),
+            array(':input[name="reactions[plugins][googleanalytics_reaction_event][events]['. $i .'][googleanalytics_event_value]"]' => array('filled' => TRUE)),
+            array(':input[name="reactions[plugins][googleanalytics_reaction_event][events]['. $i .'][googleanalytics_event_noninteraction]"]' => array('checked' => TRUE)),
+          ),
+        ),
+      );
+      $form['events'][$i]['googleanalytics_event_action'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Action'),
+        '#default_value' => isset($values['events'][$i]['googleanalytics_event_action']) ? $values['events'][$i]['googleanalytics_event_action'] : '',
+        '#description' => t('The type of interaction (e.g. click).'),
+        '#maxlength' => 255,
+        '#states' => array(
+          'required' => array(
+            array(':input[name="reactions[plugins][googleanalytics_reaction_event][events]['. $i .'][googleanalytics_event_label]"]' => array('filled' => TRUE)),
+            array(':input[name="reactions[plugins][googleanalytics_reaction_event][events]['. $i .'][googleanalytics_event_value]"]' => array('filled' => TRUE)),
+            array(':input[name="reactions[plugins][googleanalytics_reaction_event][events]['. $i .'][googleanalytics_event_noninteraction]"]' => array('checked' => TRUE)),
+          ),
+        ),
+      );
+      $form['events'][$i]['googleanalytics_event_label'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Label'),
+        '#default_value' => isset($values['events'][$i]['googleanalytics_event_label']) ? $values['events'][$i]['googleanalytics_event_label'] : '',
+        '#description' => t('Useful for categorizing events (e.g. nav buttons).'),
+        '#maxlength' => 255,
+      );
+      $form['events'][$i]['googleanalytics_event_value'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Value'),
+        '#default_value' => isset($values['events'][$i]['googleanalytics_event_value']) ? $values['events'][$i]['googleanalytics_event_value'] : '',
+        '#description' => t('Values must be non-negative. Useful to pass counts (e.g. 4 times)'),
+        '#maxlength' => 255,
+      );
+      $form['events'][$i]['googleanalytics_event_noninteraction'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Non-interaction event'),
+        '#default_value' => isset($values['events'][$i]['googleanalytics_event_noninteraction']) ? $values['events'][$i]['googleanalytics_event_noninteraction'] : 0,
+        '#description' => t("Enabled, if you want such events to be excluded from bounce rate calculations because they don't track visitor interaction. You can mark these events as non-interaction events, so that they donÃ¢â‚¬â„¢t affect the bounce rate for the page."),
+      );
+    }
+
+    return $form;
+  }
+
+  /**
+   * Override of options_form_submit().
+   *
+   * Reduce array to configured events.
+   */
+  function options_form_submit($values) {
+    if (!empty($values['events'])) {
+      foreach ($values['events'] as $i => $event) {
+        // Force error on incomplete form settings.
+        if (drupal_strlen($event['googleanalytics_event_label']) || drupal_strlen($event['googleanalytics_event_value']) || !empty($event['googleanalytics_event_noninteraction'])) {
+          if (!drupal_strlen($event['googleanalytics_event_category'])) {
+            form_set_error('reactions[plugins][googleanalytics_reaction_event][events]['. $i .'][googleanalytics_event_label]', t('Google Analytics Event Tracking - Event #@number: %field field is required.', array('@number' => $i, '%field' => t('Category'))));
+          }
+          if (!drupal_strlen($event['googleanalytics_event_action'])) {
+            form_set_error('reactions[plugins][googleanalytics_reaction_event][events][' . $i . '][googleanalytics_event_action]', t('Google Analytics Event Tracking - Event #@number: %field field is required.', array('@number' => $i, '%field' => t('Action'))));
+          }
+        }
+
+        if (!drupal_strlen($event['googleanalytics_event_category'])
+          && !drupal_strlen($event['googleanalytics_event_action'])
+          && !drupal_strlen($event['googleanalytics_event_label'])
+          && !drupal_strlen($event['googleanalytics_event_value'])
+          && empty($event['googleanalytics_event_noninteraction'])) {
+          // Remove empty event configurations from settings array.
+          unset($values['events'][$i]);
+        }
+      }
+
+      return $values;
+    }
+  }
+
+  /**
+   * Output a list of active contexts.
+   */
+  function execute(&$variables) {
+    $contexts = $this->get_contexts();
+    foreach ($contexts as $context) {
+      if (!empty($context->reactions[$this->plugin]['events'])) {
+        foreach ($context->reactions[$this->plugin]['events'] as $event) {
+          $event_array = array();
+          // Required values.
+          $event_array['eventCategory'] = $event['googleanalytics_event_category'];
+          $event_array['eventAction'] = $event['googleanalytics_event_action'];
+
+          // Optional values.
+          if (drupal_strlen($event['googleanalytics_event_label'])) {
+            $event_array['eventLabel'] = $event['googleanalytics_event_label'];
+          }
+          if (drupal_strlen($event['googleanalytics_event_value'])) {
+            $event_array['eventValue'] = $event['googleanalytics_event_value'];
+          }
+          if ($event['googleanalytics_event_noninteraction'] == TRUE) {
+            $event_array['nonInteraction'] = $event['googleanalytics_event_noninteraction'];
+          }
+
+          // Do not add invalid event configurations.
+          if (!empty($event_array) && drupal_strlen($event['googleanalytics_event_category']) && drupal_strlen($event['googleanalytics_event_action'])) {
+            $variables['events'][] = array_merge(array('hitType' => 'event'), $event_array);
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Context reaction for Google Analytics code snippets.
+ */
+class GoogleAnalyticsReactionCodeSnippet extends context_reaction {
+  /**
+   * Allow admins to provide a section title, section subtitle and section class.
+   */
+  function options_form($context) {
+    $values = $this->fetch_from_context($context);
+
+    $form = array(
+      '#tree' => TRUE,
+      '#title' => t('Google Analytics Code Snippet'),
+    );
+    $form['codesnippet'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Custom JavaScript code'),
+      '#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')),
+    );
+    $form['codesnippet']['googleanalytics_codesnippet_before'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Code snippet (before)'),
+      '#default_value' => isset($values['codesnippet']['googleanalytics_codesnippet_before']) ? $values['codesnippet']['googleanalytics_codesnippet_before'] : '',
+      '#rows' => 5,
+      '#description' => t('Code in this textarea will be added <strong>before</strong> <code>ga("send", "pageview");</code>.'),
+    );
+    $form['codesnippet']['googleanalytics_codesnippet_after'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Code snippet (after)'),
+      '#default_value' => isset($values['codesnippet']['googleanalytics_codesnippet_after']) ? $values['codesnippet']['googleanalytics_codesnippet_after'] : '',
+      '#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.'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Output a list of active contexts.
+   */
+  function execute(&$variables) {
+    $contexts = $this->get_contexts();
+    foreach ($contexts as $context) {
+      if (drupal_strlen($context->reactions[$this->plugin]['codesnippet']['googleanalytics_codesnippet_before'])) {
+        $variables['before'][] = $context->reactions[$this->plugin]['codesnippet']['googleanalytics_codesnippet_before'];
+      }
+      if (drupal_strlen($context->reactions[$this->plugin]['codesnippet']['googleanalytics_codesnippet_after'])) {
+        $variables['after'][] = $context->reactions[$this->plugin]['codesnippet']['googleanalytics_codesnippet_after'];
+      }
+    }
+  }
+}
diff --git a/googleanalytics.module b/googleanalytics.module
index a22dd6d..93438d5 100644
--- a/googleanalytics.module
+++ b/googleanalytics.module
@@ -110,6 +110,7 @@
   // 4. Ignore pages visibility filter for 404 or 403 status codes.
   if (preg_match('/^UA-\d+-\d+$/', $id) && (_googleanalytics_visibility_pages() || in_array($status, $trackable_status_codes)) && _googleanalytics_visibility_user($user)) {
 
+    $context_exists = module_exists('context');
     $debug = variable_get('googleanalytics_debug', 0);
     $url_custom = '';
 
@@ -148,6 +149,11 @@
       else {
         drupal_add_js(drupal_get_path('module', 'googleanalytics') . '/googleanalytics.js');
       }
+    }
+
+    // Add experiments to the head.
+    if ($context_exists && $plugin = context_get_plugin('reaction', 'googleanalytics_reaction_content_experiment')) {
+      $plugin->execute($variables);
     }
 
     // Add messages tracking.
@@ -330,10 +336,38 @@
       $script .= 'ga("set", "anonymizeIp", true);';
     }
 
+    // Add context dependend content groups.
+    if ($context_exists && $plugin = context_get_plugin('reaction', 'googleanalytics_reaction_content_group')) {
+      $plugin->execute($googleanalytics_context_content_groups);
+      if (!empty($googleanalytics_context_content_groups)) {
+        foreach ($googleanalytics_context_content_groups as $googleanalytics_context_content_group_id => $googleanalytics_context_content_group_name) {
+          $script .= 'ga("set", "contentGroup' . $googleanalytics_context_content_group_id . '", '. drupal_json_encode($googleanalytics_context_content_group_name) . ');';
+        }
+      }
+    }
+    // Add context depended code snippet.
+    if ($context_exists && $plugin = context_get_plugin('reaction', 'googleanalytics_reaction_code_snippet')) {
+      $plugin->execute($googleanalytics_context_codesnippets);
+    }
+    // Add context depended event tracking.
+    if ($context_exists && $plugin = context_get_plugin('reaction', 'googleanalytics_reaction_event')) {
+      $plugin->execute($googleanalytics_context_events);
+      if (!empty($googleanalytics_context_events['events'])) {
+        foreach ($googleanalytics_context_events['events'] as $googleanalytics_context_event) {
+          $message_events .= 'ga("send", ' . json_encode($googleanalytics_context_event) . ');';
+        }
+      }
+    }
+
     if (!empty($custom_var)) {
       $script .= $custom_var;
     }
-    if (!empty($codesnippet_before)) {
+    if (!empty($codesnippet_before) || !empty($googleanalytics_context_codesnippets['before'])) {
+      if (!empty($googleanalytics_context_codesnippets['before'])) {
+        foreach ($googleanalytics_context_codesnippets['before'] as $googleanalytics_context_codesnippet_before) {
+          $script .= $googleanalytics_context_codesnippet_before;
+        }
+      }
       $script .= $codesnippet_before;
     }
     if (!empty($url_custom)) {
@@ -344,7 +378,12 @@
     if (!empty($message_events)) {
       $script .= $message_events;
     }
-    if (!empty($codesnippet_after)) {
+    if (!empty($codesnippet_after) || !empty($googleanalytics_context_codesnippets['after'])) {
+      if (!empty($googleanalytics_context_codesnippets['after'])) {
+        foreach ($googleanalytics_context_codesnippets['after'] as $googleanalytics_context_codesnippet_after) {
+          $script .= $googleanalytics_context_codesnippet_after;
+        }
+      }
       $script .= $codesnippet_after;
     }
 
@@ -668,3 +707,73 @@
 
   return TRUE;
 }
+
+/**
+ * Implements hook_context_plugins().
+ */
+function googleanalytics_context_plugins() {
+  return array(
+    'googleanalytics_reaction_content_experiment' => array(
+      'handler' => array(
+        'path' => drupal_get_path('module', 'googleanalytics'),
+        'file' => 'googleanalytics.context.inc',
+        'class' => 'GoogleAnalyticsReactionContentExperiment',
+        'parent' => 'context_reaction',
+      ),
+    ),
+    'googleanalytics_reaction_content_group' => array(
+      'handler' => array(
+        'path' => drupal_get_path('module', 'googleanalytics'),
+        'file' => 'googleanalytics.context.inc',
+        'class' => 'GoogleAnalyticsReactionContentGroup',
+        'parent' => 'context_reaction',
+      ),
+    ),
+    'googleanalytics_reaction_code_snippet' => array(
+      'handler' => array(
+        'path' => drupal_get_path('module', 'googleanalytics'),
+        'file' => 'googleanalytics.context.inc',
+        'class' => 'GoogleAnalyticsReactionCodeSnippet',
+        'parent' => 'context_reaction',
+      ),
+    ),
+    'googleanalytics_reaction_event' => array(
+      'handler' => array(
+        'path' => drupal_get_path('module', 'googleanalytics'),
+        'file' => 'googleanalytics.context.inc',
+        'class' => 'GoogleAnalyticsReactionEvent',
+        'parent' => 'context_reaction',
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_context_registry().
+ */
+function googleanalytics_context_registry() {
+  return array(
+    'reactions' => array(
+      'googleanalytics_reaction_content_experiment' => array(
+        'title' => t('Google Analytics Content Experiment'),
+        'description' => t('Control page experiments in Google Analytics.'),
+        'plugin' => 'googleanalytics_reaction_content_experiment',
+      ),
+      'googleanalytics_reaction_content_group' => array(
+        'title' => t('Google Analytics Content Group'),
+        'description' => t('Control content groups in Google Analytics.'),
+        'plugin' => 'googleanalytics_reaction_content_group',
+      ),
+      'googleanalytics_reaction_code_snippet' => array(
+        'title' => t('Google Analytics Code Snippet'),
+        'description' => t('Control code snippets added to the Google Analytics tracking code.'),
+        'plugin' => 'googleanalytics_reaction_code_snippet',
+      ),
+      'googleanalytics_reaction_event' => array(
+        'title' => t('Google Analytics Event Tracking'),
+        'description' => t('Control events added to the Google Analytics tracking code.'),
+        'plugin' => 'googleanalytics_reaction_event',
+      ),
+    ),
+  );
+}
diff --git a/googleanalytics.test b/googleanalytics.test
index 0b64bb8..6fa6be0 100644
--- a/googleanalytics.test
+++ b/googleanalytics.test
@@ -719,3 +719,227 @@
   }
 
 }
+
+class GoogleAnalyticsReactionTest extends DrupalWebTestCase {
+  protected $profile = 'testing';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Google Analytics context reaction tests',
+      'description' => 'Test context reaction functionality of Google Analytics module.',
+      'group' => 'Google Analytics',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('googleanalytics', 'context', 'ctools');
+
+    $permissions_admin_user = array(
+      'access administration pages',
+      'administer google analytics',
+      'administer nodes',
+      'administer site configuration'
+    );
+    $this->admin_user = $this->drupalCreateUser($permissions_admin_user);
+    $this->drupalLogin($this->admin_user);
+  }
+
+  function testGoogleAnalyticsContextContentExperiment() {
+    $ua_code = 'UA-123456-1';
+    $edit = array();
+    $edit['googleanalytics_account'] = $ua_code;
+    $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
+
+    $googleanalytics_content_experiment_key = mt_rand() . '-' . mt_rand();
+
+    ctools_include('export');
+    $context = ctools_export_new_object('context');
+    $context->name = 'test1_content_experiment';
+    $context->conditions = array(
+      'path' => array(
+        'values' => array(
+          '<front>' => '<front>',
+        ),
+      ),
+    );
+    $context->reactions = array(
+      'googleanalytics_reaction_content_experiment' => array(
+        'googleanalytics_content_experiment_key' => $googleanalytics_content_experiment_key,
+      ),
+    );
+    $saved = context_save($context);
+    $this->assertTrue($saved, 'Context "' . $context->name . '" has been saved.');
+
+    $this->drupalGet('');
+    $this->assertRaw('function utmx_section(){}function utmx(){}(function(){vark="' . $googleanalytics_content_experiment_key . '",');
+
+    // @FIXME: Set export_type manually. Workaround for bug #2355101.
+    $context->export_type = EXPORT_IN_DATABASE;
+
+    // Cleanup
+    $deleted = context_delete($context);
+    $this->assertTrue($deleted, 'Context "' . $context->name . '" has been deleted.');
+
+    $this->drupalGet('');
+    $this->assertNoRaw('function utmx_section(){}function utmx(){}(function(){vark="' . $googleanalytics_content_experiment_key . '",');
+  }
+
+  function testGoogleAnalyticsContextContentGroup() {
+    $ua_code = 'UA-123456-2';
+    $edit = array();
+    $edit['googleanalytics_account'] = $ua_code;
+    $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
+
+    $googleanalytics_content_group_id = mt_rand(1, 6);
+    $googleanalytics_content_group_name = $this->randomName(8);
+
+    ctools_include('export');
+    $context = ctools_export_new_object('context');
+    $context->name = 'test1_content_group';
+    $context->conditions = array(
+      'path' => array(
+        'values' => array(
+          '<front>' => '<front>',
+        ),
+      ),
+    );
+    $context->reactions = array(
+      'googleanalytics_reaction_content_group' => array(
+        'googleanalytics_content_group_id' => $googleanalytics_content_group_id,
+        'googleanalytics_content_group_name' => $googleanalytics_content_group_name,
+      ),
+    );
+    $saved = context_save($context);
+    $this->assertTrue($saved, 'Context "' . $context->name . '" has been saved.');
+
+    $this->drupalGet('');
+    $this->assertRaw('ga("set", ' . drupal_json_encode('contentGroup' . $googleanalytics_content_group_id) . ', ' . drupal_json_encode($googleanalytics_content_group_name) .');');
+
+    // @FIXME: Set export_type manually. Workaround for bug #2355101.
+    $context->export_type = EXPORT_IN_DATABASE;
+
+    // Cleanup
+    $deleted = context_delete($context);
+    $this->assertTrue($deleted, 'Context "' . $context->name . '" has been deleted.');
+
+    $this->drupalGet('');
+    $this->assertNoRaw('ga("set", ' . drupal_json_encode('contentGroup' . $googleanalytics_content_group_id) . ', ' . drupal_json_encode($googleanalytics_content_group_name) .');');
+  }
+
+  function testGoogleAnalyticsContextEvent() {
+    $ua_code = 'UA-123456-3';
+    $edit = array();
+    $edit['googleanalytics_account'] = $ua_code;
+    $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
+
+    ctools_include('export');
+    $context = ctools_export_new_object('context');
+    $context->name = 'test1_event';
+    $context->conditions = array(
+      'path' => array(
+        'values' => array(
+          '<front>' => '<front>',
+        ),
+      ),
+    );
+    $context->reactions = array(
+      'googleanalytics_reaction_event' => array(
+        'events' => array(
+          1 => array(
+            'googleanalytics_event_category' => 'cat1',
+            'googleanalytics_event_action' => 'action1',
+            'googleanalytics_event_label' => 'label1',
+            'googleanalytics_event_value' => 'value1',
+            'googleanalytics_event_noninteraction' => 1,
+          ),
+          2 => array(
+            'googleanalytics_event_category' => 'cat2',
+            'googleanalytics_event_action' => 'action2',
+            'googleanalytics_event_label' => 'label2',
+            'googleanalytics_event_value' => 'value2',
+            'googleanalytics_event_noninteraction' => 0,
+          ),
+          4 => array(
+            'googleanalytics_event_category' => 'cat4',
+            'googleanalytics_event_action' => 'action4',
+            'googleanalytics_event_label' => 'label4',
+            'googleanalytics_event_value' => 'value4',
+            'googleanalytics_event_noninteraction' => 1,
+          ),
+          5 => array(
+            'googleanalytics_event_category' => 'cat5',
+            'googleanalytics_event_action' => 'action5',
+            'googleanalytics_event_label' => 'label5',
+            'googleanalytics_event_value' => 'value5',
+            'googleanalytics_event_noninteraction' => 0,
+          ),
+        ),
+      ),
+    );
+    $saved = context_save($context);
+    $this->assertTrue($saved, 'Context "' . $context->name . '" has been saved.');
+
+    $this->drupalGet('');
+    $this->assertRaw('ga("send", {"hitType":"event","eventCategory":"cat1","eventAction":"action1","eventLabel":"label1","eventValue":"value1","nonInteraction":1});');
+    $this->assertRaw('ga("send", {"hitType":"event","eventCategory":"cat2","eventAction":"action2","eventLabel":"label2","eventValue":"value2"});');
+    $this->assertRaw('ga("send", {"hitType":"event","eventCategory":"cat4","eventAction":"action4","eventLabel":"label4","eventValue":"value4","nonInteraction":1});');
+    $this->assertRaw('ga("send", {"hitType":"event","eventCategory":"cat5","eventAction":"action5","eventLabel":"label5","eventValue":"value5"});');
+
+    // @FIXME: Set export_type manually. Workaround for bug #2355101.
+    $context->export_type = EXPORT_IN_DATABASE;
+
+    // Cleanup
+    $deleted = context_delete($context);
+    $this->assertTrue($deleted, 'Context "' . $context->name . '" has been deleted.');
+
+    $this->drupalGet('');
+    $this->assertNoRaw('ga("send", {"hitType":"event","eventCategory":"cat1","eventAction":"action1","eventLabel":"label1","eventValue":"value1","nonInteraction":1});');
+    $this->assertNoRaw('ga("send", {"hitType":"event","eventCategory":"cat2","eventAction":"action2","eventLabel":"label2","eventValue":"value2"});');
+  }
+
+  function testGoogleAnalyticsContextCodeSnippet() {
+    $ua_code = 'UA-123456-4';
+    $edit = array();
+    $edit['googleanalytics_account'] = $ua_code;
+    $this->drupalPost('admin/config/system/googleanalytics', $edit, t('Save configuration'));
+
+    ctools_include('export');
+    $context = ctools_export_new_object('context');
+    $context->name = 'test1_code_snippet';
+    $context->conditions = array(
+      'path' => array(
+        'values' => array(
+          '<front>' => '<front>',
+        ),
+      ),
+    );
+    $context->reactions = array(
+      'googleanalytics_reaction_code_snippet' => array(
+        'codesnippet' => array(
+          'googleanalytics_codesnippet_before' => 'ga("set", "expId", "Qp0gahJ3RAO3DJ18b0XoUQ");ga("set", "campaignName", "(direct)");ga("set", "campaignSource", "(direct)");',
+          'googleanalytics_codesnippet_after' => '',
+        ),
+      ),
+    );
+    $saved = context_save($context);
+    $this->assertTrue($saved, 'Context "' . $context->name . '" has been saved.');
+
+    $this->drupalGet('');
+    $this->assertRaw('ga("set", "expId", "Qp0gahJ3RAO3DJ18b0XoUQ");');
+    $this->assertRaw('ga("set", "campaignName", "(direct)");');
+    $this->assertRaw('ga("set", "campaignSource", "(direct)");');
+
+    // @FIXME: Set export_type manually. Workaround for bug #2355101.
+    $context->export_type = EXPORT_IN_DATABASE;
+
+    // Cleanup
+    $deleted = context_delete($context);
+    $this->assertTrue($deleted, 'Context "' . $context->name . '" has been deleted.');
+
+    $this->drupalGet('');
+    $this->assertNoRaw('ga("set", "expId", "Qp0gahJ3RAO3DJ18b0XoUQ");');
+    $this->assertNoRaw('ga("set", "campaignName", "(direct)");');
+    $this->assertNoRaw('ga("set", "campaignSource", "(direct)");');
+  }
+
+}
