[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/google_analytics/ -> googleanalytics.module (source)

   1  <?php
   2  
   3  /*
   4   * @file
   5   * Drupal Module: GoogleAnalytics
   6   * Adds the required Javascript to the bottom of all your Drupal pages
   7   * to allow tracking by the Google Analytics statistics package.
   8   *
   9   * @author: Alexander Hass <http://drupal.org/user/85918>
  10   */
  11  
  12  define('GOOGLEANALYTICS_TRACKFILES_EXTENSIONS', '7z|aac|arc|arj|asf|asx|avi|bin|csv|doc|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mp(2|3|4|e?g)|mov(ie)?|msi|msp|pdf|phps|png|ppt|qtm?|ra(m|r)?|sea|sit|tar|tgz|torrent|txt|wav|wma|wmv|wpd|xls|xml|z|zip');
  13  
  14  // Remove tracking from all administrative pages, see http://drupal.org/node/34970.
  15  define('GOOGLEANALYTICS_PAGES', "admin\nadmin/*\nbatch\nnode/add*\nnode/*/*\nuser/*/*");
  16  
  17  /**
  18   * Implementation of hook_help().
  19   */
  20  function googleanalytics_help($path, $arg) {
  21    switch ($path) {
  22      case 'admin/settings/googleanalytics':
  23        return t('<a href="@ga_url">Google Analytics</a> is a free (registration required) website traffic and marketing effectiveness service.', array('@ga_url' => 'http://www.google.com/analytics/'));
  24    }
  25  }
  26  
  27  /**
  28   * Implementation of hook_theme().
  29   */
  30  function googleanalytics_theme() {
  31    return array(
  32      'googleanalytics_admin_custom_var_table' => array(
  33        'arguments' => array('form' => NULL),
  34      ),
  35    );
  36  }
  37  
  38  /**
  39   * Implementation of hook_perm().
  40   */
  41  function googleanalytics_perm() {
  42    return array('administer google analytics', 'opt-in or out of tracking', 'use PHP for tracking visibility');
  43  }
  44  
  45  /**
  46   * Implementation of hook_menu().
  47   */
  48  function googleanalytics_menu() {
  49    $items['admin/settings/googleanalytics'] = array(
  50      'title' => 'Google Analytics',
  51      'description' => 'Configure the settings used to generate your Google Analytics tracking code.',
  52      'page callback' => 'drupal_get_form',
  53      'page arguments' => array('googleanalytics_admin_settings_form'),
  54      'access arguments' => array('administer google analytics'),
  55      'file' => 'googleanalytics.admin.inc',
  56      'type' => MENU_NORMAL_ITEM,
  57    );
  58  
  59    return $items;
  60  }
  61  
  62  /**
  63   * Implementation of hook_init().
  64   */
  65  function googleanalytics_init() {
  66    global $user;
  67  
  68    $id = variable_get('googleanalytics_account', '');
  69  
  70    // 1. Check if the GA account number has a value.
  71    // 2. Track page views based on visibility value.
  72    // 3. Check if we should track the currently active user's role.
  73    if (!empty($id) && _googleanalytics_visibility_pages() && _googleanalytics_visibility_user($user)) {
  74  
  75      // Custom tracking.
  76      if (variable_get('googleanalytics_trackadsense', FALSE)) {
  77        drupal_add_js('window.google_analytics_uacct = ' . drupal_to_js($id) . ';', 'inline', 'header');
  78      }
  79  
  80      // Add link tracking.
  81      $link_settings = array();
  82      if ($track_outgoing = variable_get('googleanalytics_trackoutgoing', 1)) {
  83        $link_settings['trackOutgoing'] = $track_outgoing;
  84      }
  85      if ($track_mailto = variable_get('googleanalytics_trackmailto', 1)) {
  86        $link_settings['trackMailto'] = $track_mailto;
  87      }
  88      if (($track_download = variable_get('googleanalytics_trackfiles', 1)) && ($trackfiles_extensions = variable_get('googleanalytics_trackfiles_extensions', GOOGLEANALYTICS_TRACKFILES_EXTENSIONS))) {
  89        $link_settings['trackDownload'] = $track_download;
  90        $link_settings['trackDownloadExtensions'] = $trackfiles_extensions;
  91      }
  92      if ($track_outbound_as_pageview = variable_get('googleanalytics_trackoutboundaspageview', 0)) {
  93        $link_settings['trackOutboundAsPageview'] = $track_outbound_as_pageview;
  94      }
  95      if (!empty($link_settings)) {
  96        drupal_add_js(array('googleanalytics' => $link_settings), 'setting', 'header');
  97        drupal_add_js(drupal_get_path('module', 'googleanalytics') .'/googleanalytics.js', 'module', 'header');
  98      }
  99    }
 100  }
 101  
 102  /**
 103   * Implementation of hook_footer() to insert JavaScript at the end of the page.
 104   */
 105  function googleanalytics_footer($main = 0) {
 106    global $user;
 107  
 108    $id = variable_get('googleanalytics_account', '');
 109  
 110    // 1. Check if the GA account number has a value.
 111    // 2. Track page views based on visibility value.
 112    // 3. Check if we should track the currently active user's role.
 113    if (!empty($id) && _googleanalytics_visibility_pages() && _googleanalytics_visibility_user($user)) {
 114  
 115      // Add User profile segmentation values.
 116      $profile_fields = variable_get('googleanalytics_segmentation', array());
 117      if (!empty($profile_fields)) {
 118  
 119        if (module_exists('profile')) {
 120          // Extend the $user object with profile data. Otherwise only 'User roles' can be tracked.
 121          profile_load_profile($user);
 122        }
 123  
 124        $fields = array();
 125        foreach ($profile_fields as $field => $title) {
 126          $fields[$field] = is_array($user->$field) ? implode(',', $user->$field) : $user->$field;
 127        }
 128  
 129        // Only show segmentation variable if there are specified fields.
 130        $segmentation = '';
 131        if (count($fields) > 0) {
 132          $segmentation = '_gaq.push(["_setVar", ' . drupal_to_js(implode(':', $fields)) . ']);';
 133        }
 134      }
 135  
 136      // Site search tracking support.
 137      $url_custom = '';
 138      if (module_exists('search') && variable_get('googleanalytics_site_search', FALSE) && arg(0) == 'search' && $keys = search_get_keys()) {
 139        $url_custom = '(window.googleanalytics_search_results) ? ' . drupal_to_js(url('search/'. arg(1), array('query' => 'search='. drupal_urlencode($keys)))) . ' : ' . drupal_to_js(url('search/'. arg(1), array('query' => 'search=no-results:'. drupal_urlencode($keys) .'&cat=no-results')));
 140      }
 141  
 142      // If this node is a translation of another node, pass the original
 143      // node instead.
 144      if (module_exists('translation') && variable_get('googleanalytics_translation_set', 0)) {
 145        // Check we have a node object, it supports translation, and its
 146        // translated node ID (tnid) doesn't match its own node ID.
 147        $node = menu_get_object();
 148        if ($node && translation_supported_type($node->type) && isset($node->tnid) && ($node->tnid != $node->nid)) {
 149          $source_node = node_load($node->tnid);
 150          $languages = language_list();
 151          $url_custom = drupal_to_js(url('node/'. $source_node->nid, array('language' => $languages[$source_node->language])));
 152        }
 153      }
 154  
 155      // Track access denied (403) and file not found (404) pages.
 156      $headers = drupal_get_headers();
 157      if (strstr($headers, '403 Forbidden')) {
 158        // See http://www.google.com/support/analytics/bin/answer.py?answer=86927
 159        $url_custom = '"/403.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
 160      }
 161      elseif (strstr($headers, '404 Not Found')) {
 162        $url_custom = '"/404.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
 163      }
 164  
 165      // Add any custom code snippets if specified.
 166      $codesnippet_before = variable_get('googleanalytics_codesnippet_before', '');
 167      $codesnippet_after = variable_get('googleanalytics_codesnippet_after', '');
 168  
 169      // Add custom variables.
 170      $googleanalytics_custom_vars = variable_get('googleanalytics_custom_var', array());
 171      $custom_var = '';
 172      for ($i = 1; $i < 6; $i++) {
 173        $custom_var_name = !empty($googleanalytics_custom_vars['slots'][$i]['name']) ? $googleanalytics_custom_vars['slots'][$i]['name'] : '';
 174        if (!empty($custom_var_name)) {
 175          $custom_var_value = !empty($googleanalytics_custom_vars['slots'][$i]['value']) ? $googleanalytics_custom_vars['slots'][$i]['value'] : '';
 176          $custom_var_scope = !empty($googleanalytics_custom_vars['slots'][$i]['scope']) ? $googleanalytics_custom_vars['slots'][$i]['scope'] : 3;
 177  
 178          if (module_exists('token')) {
 179            $node = menu_get_object();
 180            $custom_var_value = token_replace_multiple($custom_var_value, array('global' => NULL, 'user' => $user, 'node' => $node), '[', ']', array('clear' => TRUE));
 181          }
 182  
 183          // Suppress empty custom variables.
 184          if (!is_numeric($custom_var_value) && empty($custom_var_value)) {
 185            continue;
 186          }
 187  
 188          // The length of the string used for the 'name' and the length of the
 189          // string used for the 'value' must not exceed 64 bytes after url encoding.
 190          $name_length = drupal_strlen(rawurlencode($custom_var_name));
 191          $tmp_value = rawurlencode($custom_var_value);
 192          $value_length = drupal_strlen($tmp_value);
 193          if ($name_length + $value_length > 64) {
 194            // Trim value and remove fragments of url encoding.
 195            $tmp_value = rtrim(substr($tmp_value, 0, 63 - $name_length), '%0..9A..F');
 196            $custom_var_value = urldecode($tmp_value);
 197          }
 198  
 199          $custom_var_name = drupal_to_js($custom_var_name);
 200          $custom_var_value = drupal_to_js($custom_var_value);
 201          $custom_var .= "_gaq.push(['_setCustomVar', $i, $custom_var_name, $custom_var_value, $custom_var_scope]);";
 202        }
 203      }
 204  
 205      // Build tracker code.
 206      $script = 'var _gaq = _gaq || [];';
 207      $script .= '_gaq.push(["_setAccount", ' . drupal_to_js($id) . ']);';
 208      if (variable_get('googleanalytics_tracker_anonymizeip', 0)) {
 209        // FIXME: The Google API is currently broken and "_gat._anonymizeIp" is only
 210        // a workaround until "_anonymizeIp" has been implemented/fixed.
 211        $script .= '_gaq.push(["_gat._anonymizeIp"]);';
 212      }
 213  
 214      // Domain tracking type.
 215      global $cookie_domain;
 216      $domain_mode = variable_get('googleanalytics_domain_mode', 0);
 217  
 218      // Per RFC 2109, cookie domains must contain at least one dot other than the
 219      // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
 220      if ($domain_mode == 1 && count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
 221        $script .= '_gaq.push(["_setDomainName", ' . drupal_to_js($cookie_domain) . ']);';
 222      }
 223  
 224      if (!empty($segmentation)) {
 225        $script .= $segmentation;
 226      }
 227      if (!empty($custom_var)) {
 228        $script .= $custom_var;
 229      }
 230      if (!empty($codesnippet_before)) {
 231        $script .= $codesnippet_before;
 232      }
 233      if (empty($url_custom)) {
 234        $script .= '_gaq.push(["_trackPageview"]);';
 235      }
 236      else {
 237        $script .= '_gaq.push(["_trackPageview", ' . $url_custom . ']);';
 238      }
 239      if (!empty($codesnippet_after)) {
 240        $script .= $codesnippet_after;
 241      }
 242  
 243      $script .= '(function() {';
 244      $script .= 'var ga = document.createElement("script");';
 245      $script .= 'ga.type = "text/javascript";';
 246      $script .= 'ga.async = true;';
 247  
 248      // Should a local cached copy of ga.js be used?
 249      if (variable_get('googleanalytics_cache', 0) && $url = _googleanalytics_cache('http://www.google-analytics.com/ga.js')) {
 250        // A dummy query-string is added to filenames, to gain control over
 251        // browser-caching. The string changes on every update or full cache
 252        // flush, forcing browsers to load a new copy of the files, as the
 253        // URL changed.
 254        $query_string = '?'. substr(variable_get('css_js_query_string', '0'), 0, 1);
 255  
 256        $script .= 'ga.src = "' . $url . $query_string . '";';
 257      }
 258      else {
 259        $script .= 'ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";';
 260      }
 261      $script .= 'var s = document.getElementsByTagName("script")[0];';
 262      $script .= 's.parentNode.insertBefore(ga, s);';
 263      $script .= '})();';
 264  
 265      drupal_add_js($script, 'inline', 'footer');
 266    }
 267  }
 268  
 269  /**
 270   * Implementation of hook_user().
 271   *
 272   * Allow users to decide if tracking code will be added to pages or not.
 273   */
 274  function googleanalytics_user($type, $edit, &$account, $category = NULL) {
 275    switch ($type) {
 276      case 'form':
 277        if ($category == 'account' && user_access('opt-in or out of tracking') && ($custom = variable_get('googleanalytics_custom', 0)) != 0 && _googleanalytics_visibility_roles($account)) {
 278          $form['googleanalytics'] = array(
 279            '#type' => 'fieldset',
 280            '#title' => t('Google Analytics configuration'),
 281            '#weight' => 3,
 282            '#collapsible' => TRUE,
 283            '#tree' => TRUE
 284          );
 285  
 286          switch ($custom) {
 287            case 1:
 288              $description = t('Users are tracked by default, but you are able to opt out.');
 289              break;
 290  
 291            case 2:
 292              $description = t('Users are <em>not</em> tracked by default, but you are able to opt in.');
 293              break;
 294          }
 295  
 296          // Disable tracking for visitors who have opted out from tracking via DNT (Do-Not-Track) header.
 297          $disabled = FALSE;
 298          if (variable_get('googleanalytics_privacy_donottrack', 1) && !empty($_SERVER['HTTP_DNT'])) {
 299            $disabled = TRUE;
 300  
 301            // Override settings value.
 302            $account->googleanalytics['custom'] = FALSE;
 303  
 304            $description .= '<span class="admin-disabled">';
 305            $description .= ' ' . t('You have opted out from tracking via browser privacy settings.');
 306            $description .= '</span>';
 307          }
 308  
 309          $form['googleanalytics']['custom'] = array(
 310            '#type' => 'checkbox',
 311            '#title' => t('Enable user tracking'),
 312            '#description' => $description,
 313            '#default_value' => isset($account->googleanalytics['custom']) ? $account->googleanalytics['custom'] : ($custom == 1),
 314            '#disabled' => $disabled,
 315          );
 316  
 317          return $form;
 318        }
 319        break;
 320  
 321    }
 322  }
 323  
 324  /**
 325   * Implementation of hook_cron().
 326   */
 327  function googleanalytics_cron() {
 328    // Regenerate the tracking code file every day.
 329    if (time() - variable_get('googleanalytics_last_cache', 0) >= 86400 && variable_get('googleanalytics_cache', 0)) {
 330      _googleanalytics_cache('http://www.google-analytics.com/ga.js', TRUE);
 331      variable_set('googleanalytics_last_cache', time());
 332    }
 333  }
 334  
 335  /**
 336   * Implementation of hook_preprocess_search_results().
 337   *
 338   * Collects the number of search results. It need to be noted, that this
 339   * function is not executed if the search result is empty.
 340   */
 341  function googleanalytics_preprocess_search_results(&$variables) {
 342    // There is no search result $variable available that hold the number of items
 343    // found. But the pager item mumber can tell the number of search results.
 344    global $pager_total_items;
 345  
 346    drupal_add_js('window.googleanalytics_search_results = ' . intval($pager_total_items[0]) . ';', 'inline', 'header');
 347  }
 348  
 349  /**
 350   * Download/Synchronize/Cache tracking code file locally.
 351   *
 352   * @param $location
 353   *   The full URL to the external javascript file.
 354   * @param $sync_cached_file
 355   *   Synchronize tracking code and update if remote file have changed.
 356   * @return mixed
 357   *   The path to the local javascript file on success, boolean FALSE on failure.
 358   */
 359  function _googleanalytics_cache($location, $sync_cached_file = FALSE) {
 360  
 361    $path = file_create_path('googleanalytics');
 362    $file_destination = $path . '/' . basename($location);
 363  
 364    if (!file_exists($file_destination) || $sync_cached_file) {
 365      // Download the latest tracking code.
 366      $result = drupal_http_request($location);
 367  
 368      if ($result->code == 200) {
 369        if (file_exists($file_destination)) {
 370          // Synchronize tracking code and and replace local file if outdated.
 371          $data_hash_local = md5(file_get_contents($file_destination));
 372          $data_hash_remote = md5($result->data);
 373          // Check that the files directory is writable.
 374          if ($data_hash_local != $data_hash_remote && file_check_directory($path)) {
 375            // Save updated tracking code file to disk.
 376            file_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
 377            watchdog('googleanalytics', 'Locally cached tracking code file has been updated.', array(), WATCHDOG_INFO);
 378  
 379            // Change query-strings on css/js files to enforce reload for all users.
 380            _drupal_flush_css_js();
 381          }
 382        }
 383        else {
 384          // Check that the files directory is writable.
 385          if (file_check_directory($path, FILE_CREATE_DIRECTORY)) {
 386            // There is no need to flush JS here as core refreshes JS caches
 387            // automatically, if new files are added.
 388            file_save_data($result->data, $file_destination, FILE_EXISTS_REPLACE);
 389            watchdog('googleanalytics', 'Locally cached tracking code file has been saved.', array(), WATCHDOG_INFO);
 390  
 391            // Return the local JS file path.
 392            return base_path() . $file_destination;
 393          }
 394        }
 395      }
 396    }
 397    else {
 398      // Return the local JS file path.
 399      return base_path() . $file_destination;
 400    }
 401  }
 402  
 403  /**
 404   * Delete cached files and directory.
 405   */
 406  function googleanalytics_clear_js_cache() {
 407    $path = file_create_path('googleanalytics');
 408    if (file_check_directory($path)) {
 409      file_scan_directory($path, '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
 410      rmdir($path);
 411  
 412      // Change query-strings on css/js files to enforce reload for all users.
 413      _drupal_flush_css_js();
 414  
 415      watchdog('googleanalytics', 'Local cache has been purged.', array(), WATCHDOG_INFO);
 416    }
 417  }
 418  
 419  /**
 420   * Tracking visibility check for an user object.
 421   *
 422   * @param $account
 423   *   A user object containing an array of roles to check.
 424   * @return boolean
 425   *   A decision on if the current user is being tracked by Google Analytics.
 426   */
 427  function _googleanalytics_visibility_user($account) {
 428  
 429    $enabled = FALSE;
 430  
 431    // Is current user a member of a role that should be tracked?
 432    if (_googleanalytics_visibility_header($account) && _googleanalytics_visibility_roles($account)) {
 433  
 434      // Use the user's block visibility setting, if necessary.
 435      if (($custom = variable_get('googleanalytics_custom', 0)) != 0) {
 436        if ($account->uid && isset($account->googleanalytics['custom'])) {
 437          $enabled = $account->googleanalytics['custom'];
 438        }
 439        else {
 440          $enabled = ($custom == 1);
 441        }
 442      }
 443      else {
 444        $enabled = TRUE;
 445      }
 446  
 447    }
 448  
 449    return $enabled;
 450  }
 451  
 452  /**
 453   * Based on visibility setting this function returns TRUE if GA code should
 454   * be added for the current role and otherwise FALSE.
 455   */
 456  function _googleanalytics_visibility_roles($account) {
 457  
 458    $visibility = variable_get('googleanalytics_visibility_roles', 0);
 459    $enabled = $visibility;
 460    $roles = variable_get('googleanalytics_roles', array());
 461  
 462    if (array_sum($roles) > 0) {
 463      // One or more roles are selected.
 464      foreach (array_keys($account->roles) as $rid) {
 465        // Is the current user a member of one of these roles?
 466        if (isset($roles[$rid]) && $rid == $roles[$rid]) {
 467          // Current user is a member of a role that should be tracked/excluded from tracking.
 468          $enabled = !$visibility;
 469          break;
 470        }
 471      }
 472    }
 473    else {
 474      // No role is selected for tracking, therefore all roles should be tracked.
 475      $enabled = TRUE;
 476    }
 477  
 478    return $enabled;
 479  }
 480  
 481  /**
 482   * Based on visibility setting this function returns TRUE if GA code should
 483   * be added to the current page and otherwise FALSE.
 484   */
 485  function _googleanalytics_visibility_pages() {
 486    static $page_match;
 487  
 488    // Cache visibility setting in hook_init for hook_footer.
 489    if (!isset($page_match)) {
 490  
 491      $visibility = variable_get('googleanalytics_visibility', 0);
 492      $setting_pages = variable_get('googleanalytics_pages', GOOGLEANALYTICS_PAGES);
 493  
 494      // Match path if necessary.
 495      if (!empty($setting_pages)) {
 496        // Convert path to lowercase. This allows comparison of the same path
 497        // with different case. Ex: /Page, /page, /PAGE.
 498        $pages = drupal_strtolower($setting_pages);
 499        if ($visibility < 2) {
 500          // Convert the Drupal path to lowercase
 501          $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
 502          // Compare the lowercase internal and lowercase path alias (if any).
 503          $page_match = drupal_match_path($path, $pages);
 504          if ($path != $_GET['q']) {
 505            $page_match = $page_match || drupal_match_path($_GET['q'], $pages);
 506          }
 507          // When $visibility has a value of 0, the tracking code is displayed on
 508          // all pages except those listed in $pages. When set to 1, it
 509          // is displayed only on those pages listed in $pages.
 510          $page_match = !($visibility xor $page_match);
 511        }
 512        else {
 513          $page_match = drupal_eval($setting_pages);
 514        }
 515      }
 516      else {
 517        $page_match = TRUE;
 518      }
 519  
 520    }
 521    return $page_match;
 522  }
 523  
 524  /**
 525   * Based on headers send by clients this function returns TRUE if GA code should
 526   * be added to the current page and otherwise FALSE.
 527   */
 528  function _googleanalytics_visibility_header($account) {
 529  
 530    if (($account->uid || variable_get('cache', 0) == 0) && variable_get('googleanalytics_privacy_donottrack', 1) && !empty($_SERVER['HTTP_DNT'])) {
 531      // Disable tracking if caching is disabled or a visitors is logged in and
 532      // have opted out from tracking via DNT (Do-Not-Track) header.
 533      return FALSE;
 534    }
 535  
 536    return TRUE;
 537  }
 538  
 539  /**
 540   * Implementation of hook_token_values().
 541   */
 542  function googleanalytics_token_values($type, $object = NULL, $options = array()) {
 543    if ($type == 'user') {
 544      if (!empty($object->roles)) {
 545        $account = $object;
 546      }
 547      else {
 548        $account = user_load(array('uid' => $GLOBALS['user']->uid));
 549      }
 550  
 551      $tokens['user-role-names'] = implode(',', $account->roles);
 552      $tokens['user-role-ids'] = implode(',', array_keys($account->roles));
 553  
 554      return $tokens;
 555    }
 556  }
 557  
 558  /**
 559   * Implementation of hook_token_list().
 560   */
 561  function googleanalytics_token_list($type = 'all') {
 562    if ($type == 'user' || $type == 'all') {
 563      $tokens['user']['user-role-names'] = t('The role names the user account is a member of as comma separated list.');
 564      $tokens['user']['user-role-ids'] = t('The role ids the user account is a member of as comma separated list.');
 565      return $tokens;
 566    }
 567  }


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7