From b0fbab98c36c47b22fb1af627b83a03e10e9d149 Sun, 20 Jul 2014 15:30:05 +0200
From: hass <hass@85918.no-reply.drupal.org>
Date: Sun, 20 Jul 2014 15:29:55 +0200
Subject: [PATCH] Issue #1686874: Context module integration

diff --git a/googleanalytics.context.inc b/googleanalytics.context.inc
new file mode 100644
index 0000000..8fc6965
--- /dev/null
+++ b/googleanalytics.context.inc
@@ -0,0 +1,295 @@
+<?php
+/**
+ * @file
+ * Context reaction for Google Analytics Content Experiments.
+ */
+
+class GoogleAnalyticsReactionContentExperiment extends context_reaction {
+  /**
+   * Editor form.
+   */
+  function editor_form($context) {
+    $form = $this->options_form($context);
+    return $form;
+  }
+
+  /**
+   * Submit handler for editor form.
+   */
+  function editor_form_submit($context, $values) {
+    return $values;
+  }
+
+  /**
+   * 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;
+  }
+
+  /**
+   * 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 {
+  /**
+   * Editor form.
+   */
+  function editor_form($context) {
+    $form = $this->options_form($context);
+    return $form;
+  }
+
+  /**
+   * Submit handler for editor form.
+   */
+  function editor_form_submit($context, $values) {
+    return $values;
+  }
+
+  /**
+   * 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;
+  }
+
+  /**
+   * 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 {
+  /**
+   * Editor form.
+   */
+  function editor_form($context) {
+    $form = $this->options_form($context);
+    return $form;
+  }
+
+  /**
+   * Submit handler for editor form.
+   */
+  function editor_form_submit($context, $values) {
+    return $values;
+  }
+
+  /**
+   * 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'),
+    );
+    $form['event'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Event'),
+    );
+    $form['event']['googleanalytics_event_category'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Category'),
+      '#default_value' => isset($values['event']['googleanalytics_event_category']) ? $values['event']['googleanalytics_event_category'] : '',
+      '#description' => t('Typically the object that was interacted with (e.g. button).'),
+      '#maxlength' => 255,
+    );
+    $form['event']['googleanalytics_event_action'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Action'),
+      '#default_value' => isset($values['event']['googleanalytics_event_action']) ? $values['event']['googleanalytics_event_action'] : '',
+      '#description' => t('The type of interaction (e.g. click).'),
+      '#maxlength' => 255,
+    );
+    $form['event']['googleanalytics_event_label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Label'),
+      '#default_value' => isset($values['event']['googleanalytics_event_label']) ? $values['event']['googleanalytics_event_label'] : '',
+      '#description' => t('Useful for categorizing events (e.g. nav buttons).'),
+      '#maxlength' => 255,
+    );
+    $form['event']['googleanalytics_event_value'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Value'),
+      '#default_value' => isset($values['event']['googleanalytics_event_value']) ? $values['event']['googleanalytics_event_value'] : '',
+      '#description' => t('Values must be non-negative. Useful to pass counts (e.g. 4 times)'),
+      '#maxlength' => 255,
+    );
+    $form['event']['googleanalytics_event_noninteraction'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Non-interaction event'),
+      '#default_value' => isset($values['event']['googleanalytics_event_noninteraction']) ? $values['event']['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;
+  }
+
+  /**
+   * Output a list of active contexts.
+   */
+  function execute(&$variables) {
+    $contexts = $this->get_contexts();
+    foreach ($contexts as $context) {
+      if (!empty($context->reactions[$this->plugin]['event'])) {
+        $variables['events'][] = array(
+          'hitType' => 'event',
+          'eventCategory' => $context->reactions[$this->plugin]['event']['googleanalytics_event_category'],
+          'eventAction' => $context->reactions[$this->plugin]['event']['googleanalytics_event_action'],
+          'eventLabel' => $context->reactions[$this->plugin]['event']['googleanalytics_event_label'],
+          'eventValue' => $context->reactions[$this->plugin]['event']['googleanalytics_event_value'],
+          'nonInteraction' => $context->reactions[$this->plugin]['event']['googleanalytics_event_noninteraction'],
+        );
+      }
+    }
+  }
+}
+
+/**
+ * Context reaction for Google Analytics code snippets.
+ */
+class GoogleAnalyticsReactionCodeSnippet extends context_reaction {
+  /**
+   * Editor form.
+   */
+  function editor_form($context) {
+    $form = $this->options_form($context);
+    return $form;
+  }
+
+  /**
+   * Submit handler for editor form.
+   */
+  function editor_form_submit($context, $values) {
+    return $values;
+  }
+
+  /**
+   * Allow admins to provide a section title, section subtitle and section class.
+   */
+  function options_form($context) {
+    $values = $this->fetch_from_context($context);
+
+    // Code injection is possible, protect the configuration.
+    // @fixme data get's lost in disabled state!
+    $access = !user_access('administer google analytics');
+
+    $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>.'),
+      '#disabled' => $access,
+    );
+    $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.'),
+      '#disabled' => $access,
+    );
+
+    return $form;
+  }
+
+  /**
+   * Output a list of active contexts.
+   */
+  function execute(&$variables) {
+    $contexts = $this->get_contexts();
+    foreach ($contexts as $context) {
+      if (!empty($context->reactions[$this->plugin]['codesnippet']['googleanalytics_codesnippet_before'])) {
+        $variables['before'][] = $context->reactions[$this->plugin]['codesnippet']['googleanalytics_codesnippet_before'];
+      }
+      if (!empty($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 1f34309..adc9f8f 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.
@@ -331,10 +337,41 @@
       $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) {
+          if ($googleanalytics_context_event['nonInteraction'] == FALSE) {
+            unset($googleanalytics_context_event['nonInteraction']);
+          }
+          $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)) {
@@ -345,7 +382,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;
     }
 
@@ -667,3 +709,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',
+      ),
+    ),
+  );
+}
