German translation updated.
[project/google_analytics.git] / googleanalytics.module
CommitLineData
0efd556a 1<?php
430ff5f8 2// $Id$
8b5a6323 3
430ff5f8 4/*
e130812a 5 * @file
430ff5f8 6 * Drupal Module: GoogleAnalytics
d18f90cc 7 * Adds the required Javascript to the bottom of all your Drupal pages
0efd556a
MC
8 * to allow tracking by the Google Analytics statistics package.
9 *
834c2715 10 * @author: Mike Carter <www.ixis.co.uk/contact>
0efd556a
MC
11 */
12
939f0101 13define('GA_TRACKFILES_EXTENSIONS', '7z|aac|avi|csv|doc|exe|flv|gif|gz|jpe?g|js|mp(3|4|e?g)|mov|pdf|phps|png|ppt|rar|sit|tar|torrent|txt|wma|wmv|xls|xml|zip');
430ff5f8 14
0efd556a
MC
15function googleanalytics_help($section) {
16 switch ($section) {
430ff5f8 17 case 'admin/settings/googleanalytics':
c57de0d0 18 return t('<a href="@ga_url">Google Analytics</a> is a free statistics package based on the excellent Urchin system. This module provides services to better integrate Drupal with Google Analytics.', array('@ga_url' => 'http://www.google.com/analytics/'));
0efd556a
MC
19 }
20}
21
68102149 22function googleanalytics_perm() {
b7ad9417 23 return array('administer google analytics', 'opt-in or out of tracking', 'use PHP for tracking visibility');
68102149 24}
25
834c2715 26function googleanalytics_menu($maycache) {
609f4952 27 global $user;
834c2715 28 $items = array();
609f4952 29
834c2715
MC
30 if ($maycache) {
31 $items[] = array(
32 'path' => 'admin/settings/googleanalytics',
5215f4f7 33 'title' => t('Google Analytics'),
25751755 34 'description' => t('Configure the settings used to generate your Google Analytics tracking code.'),
834c2715 35 'callback' => 'drupal_get_form',
3322ccb0 36 'callback arguments' => 'googleanalytics_admin_settings_form',
68102149 37 'access' => user_access('administer google analytics'),
834c2715
MC
38 'type' => MENU_NORMAL_ITEM,
39 );
40 }
609f4952 41 else {
42 $id = variable_get('googleanalytics_account', '');
81f59da9 43
609f4952 44 // 1. Check if the GA account number has a value.
45 // 2. Track page views based on visibility value.
46 // 3. Check if we should track the currently active user's role.
47 if (!empty($id) && _googleanalytics_visibility_pages() && _googleanalytics_visibility_user($user)) {
48 // Use the old version of Google Analytics?
49 $legacy_version = variable_get('googleanalytics_legacy_version', FALSE);
50 $scope = variable_get('googleanalytics_js_scope', 'footer');
81f59da9 51
609f4952 52 // Should a local cached copy of urchin.js or ga.js be used?
53 $js_file = ($legacy_version) ? 'urchin.js' : 'ga.js';
54 $url = 'http://www.google-analytics.com/'. $js_file;
81f59da9 55
609f4952 56 if (variable_get('googleanalytics_cache', 0) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC) && $source = _googleanalytics_cache($url)) {
57 drupal_add_js($source, 'module', $scope);
58 }
59 else {
60 $script = 'var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");';
61 $script .= 'document.write(unescape("%3Cscript src=\'" + gaJsHost + "google-analytics.com/'. $js_file .'\' type=\'text/javascript\'%3E%3C/script%3E"));';
62 drupal_add_js($script, 'inline', $scope);
63 }
81f59da9 64
609f4952 65 // Add link tracking.
66 $link_settings = array();
a1201173 67 if ($track_outgoing = variable_get('googleanalytics_trackoutgoing', 1)) {
68 $link_settings['trackOutgoing'] = $track_outgoing;
609f4952 69 }
a1201173 70 if ($track_mailto = variable_get('googleanalytics_trackmailto', 1)) {
71 $link_settings['trackMailto'] = $track_mailto;
609f4952 72 }
a1201173 73 if (($track_download = variable_get('googleanalytics_trackfiles', 1)) && ($trackfiles_extensions = variable_get('googleanalytics_trackfiles_extensions', GA_TRACKFILES_EXTENSIONS))) {
74 $link_settings['trackDownload'] = $track_download;
609f4952 75 $link_settings['trackDownloadExtensions'] = $trackfiles_extensions;
76 }
77 if (!empty($link_settings)) {
78 // TODO: Why is this legacy_version added as data type string and not integer to settings?
79 $link_settings['LegacyVersion'] = $legacy_version ? 1 : 0;
80 drupal_add_js(array('googleanalytics' => $link_settings), 'setting', 'header');
81 drupal_add_js(drupal_get_path('module', 'googleanalytics') .'/googleanalytics.js', 'module', $scope);
82 }
83 }
84 }
85
834c2715
MC
86 return $items;
87}
88
a9eeae9c 89/**
609f4952 90 * Implementation of hook_footer() to insert Javascript at the end of the page
a9eeae9c
MC
91 */
92function googleanalytics_footer($main = 0) {
93 global $user;
94
95 $id = variable_get('googleanalytics_account', '');
96
6f2ffb4b 97 // 1. Check if the GA account number has a value.
98 // 2. Track page views based on visibility value.
99 // 3. Check if we should track the currently active user's role.
68cf69e0 100 if (!empty($id) && _googleanalytics_visibility_pages() && _googleanalytics_visibility_user($user)) {
abae6098 101
9de7dbf1 102 // Use the old version of Google Analytics?
44613035 103 $legacy_version = variable_get('googleanalytics_legacy_version', 1);
9de7dbf1 104
d2c2fa2c 105 // Add User profile segmentation values
ff2aacac 106 if (is_array($profile_fields = variable_get('googleanalytics_segmentation', '')) && ($user->uid > 0)) {
d2c2fa2c 107
7c7ff600 108 $p = module_invoke('profile', 'load_profile', $user);
d2c2fa2c
MC
109
110 $fields = array();
ff2aacac 111 foreach ($profile_fields as $field => $title) {
d2c2fa2c
MC
112 $value = $user->$field;
113
ff2aacac 114 if (is_array($value)) {
efd252ae 115 $value = implode(',', $value);
d2c2fa2c
MC
116 }
117
abf9c24d 118 $fields[$field] = $value;
d2c2fa2c 119 }
a280452a 120
bed62aa8 121 // Only show segmentation variable if there are specified fields.
122 $segmentation = '';
ff2aacac 123 if (count($fields) > 0) {
9de7dbf1 124 if ($legacy_version) {
abf9c24d 125 $segmentation = '__utmSetVar('. drupal_to_js(implode(':', $fields)) .');';
9de7dbf1 126 } else {
abf9c24d 127 $segmentation = 'pageTracker._setVar('. drupal_to_js(implode(':', $fields)) .');';
9de7dbf1 128 }
a280452a 129 }
9de7dbf1 130 }
5215f4f7 131
bed62aa8 132 // Site search tracking support.
133 $url_custom = '';
56d0ce21 134 if (module_exists('search') && variable_get('googleanalytics_site_search', FALSE) && arg(0) == 'search' && $keys = search_get_keys()) {
135 $url_custom = drupal_to_js(url('search/'. arg(1), 'search='. $keys));
bed62aa8 136 }
137
feca20a6 138 // Track access denied (403) and file not found (404) pages.
d94e8c78 139 if (function_exists('drupal_get_headers')) {
140 $headers = drupal_get_headers();
feca20a6 141 if (strstr($headers, 'HTTP/1.1 403 Forbidden')) {
d94e8c78 142 if ($legacy_version) {
143 // See http://www.google.com/support/analytics/bin/answer.py?answer=86928
feca20a6 144 $url_custom = '"/403.html?page=" + _udl.pathname + _udl.search';
d94e8c78 145 }
146 else {
147 // See http://www.google.com/support/analytics/bin/answer.py?answer=86927
feca20a6 148 $url_custom = '"/403.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
149 }
150 }
151 elseif (strstr($headers, 'HTTP/1.1 404 Not Found')) {
152 if ($legacy_version) {
153 $url_custom = '"/404.html?page=" + _udl.pathname + _udl.search';
154 }
155 else {
d94e8c78 156 $url_custom = '"/404.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
157 }
158 }
962363af 159 }
22fe8c34 160
722c7cfe 161 // Add any custom code snippets if specified
188db7f0 162 $codesnippet_before = variable_get('googleanalytics_codesnippet_before', '');
163 $codesnippet_after = variable_get('googleanalytics_codesnippet_after', '');
722c7cfe 164
9de7dbf1 165 // Should the legacy code be used?
722c7cfe 166 $script = '';
9de7dbf1 167 if ($legacy_version) {
abf9c24d 168 $script .= '_uacct = '. drupal_to_js($id) .';';
bed62aa8 169 if (!empty($segmentation)) {
170 $script .= $segmentation;
171 }
188db7f0 172 if (!empty($codesnippet_before)) {
173 $script .= $codesnippet_before;
bed62aa8 174 }
175 $script .= 'urchinTracker('. $url_custom .');';
188db7f0 176 if (!empty($codesnippet_after)) {
177 $script .= $codesnippet_after;
178 }
9de7dbf1 179 }
bed62aa8 180 else {
abf9c24d 181 $script .= 'var pageTracker = _gat._getTracker('. drupal_to_js($id) .');';
bed62aa8 182 if (!empty($segmentation)) {
183 $script .= $segmentation;
184 }
188db7f0 185 if (!empty($codesnippet_before)) {
186 $script .= $codesnippet_before;
bed62aa8 187 }
188 $script .= 'pageTracker._trackPageview('. $url_custom .');';
188db7f0 189 if (!empty($codesnippet_after)) {
190 $script .= $codesnippet_after;
191 }
bed62aa8 192 }
722c7cfe 193
194 drupal_add_js($script, 'inline', 'footer');
0efd556a
MC
195 }
196}
197
0efd556a 198/**
68cf69e0 199 * Implementation of hook_user().
200 *
201 * Allow users to decide if tracking code will be added to pages or not.
202 */
203function googleanalytics_user($type, $edit, &$account, $category = NULL) {
204 switch ($type) {
205 case 'form':
b7ad9417 206 if ($category == 'account' && user_access('opt-in or out of tracking') && ($custom = variable_get('googleanalytics_custom', 0)) != 0 && _googleanalytics_visibility_roles($account)) {
68cf69e0 207 $form['googleanalytics'] = array(
208 '#type' => 'fieldset',
209 '#title' => t('Google Analytics configuration'),
210 '#weight' => 3,
211 '#collapsible' => TRUE,
212 '#tree' => TRUE
213 );
214 $form['googleanalytics']['custom'] = array(
215 '#type' => 'checkbox',
216 '#title' => t('User tracking'),
217 '#default_value' => isset($account->googleanalytics['custom']) ? $account->googleanalytics['custom'] : ($custom == 1)
218 );
219
220 return $form;
221 }
222 break;
223
224 }
225}
226
227/**
834c2715 228 * Implementation of hook_admin_settings() for configuring the module
0efd556a 229 */
3322ccb0 230function googleanalytics_admin_settings_form() {
74888994 231 $form['account'] = array(
f241720d 232 '#type' => 'fieldset',
c57de0d0 233 '#title' => t('General settings'),
f241720d
MC
234 '#collapsible' => FALSE,
235 );
74888994
MC
236
237 $form['account']['googleanalytics_account'] = array(
f241720d 238 '#type' => 'textfield',
3322ccb0 239 '#title' => t('Google Analytics account number'),
ff2aacac 240 '#default_value' => variable_get('googleanalytics_account', 'UA-'),
f241720d
MC
241 '#size' => 15,
242 '#maxlength' => 20,
243 '#required' => TRUE,
ea999cc5 244 '#description' => t('The account number is unique to the websites domain. Click the <strong>Edit</strong> link in your Google Analytics account next to the appropriate profile on the <strong>Analytics Settings</strong> page, then select <strong>Check Status</strong> at the top-right of the table to find the account number (UA-xxxx-x) of your site. You can obtain a user account from the <a href="@url">Google Analytics</a> website.', array('@url' => 'http://www.google.com/analytics/')),
f241720d 245 );
74888994 246
68cf69e0 247 // Standard tracking configurations.
248 $form['user_vis_settings'] = array(
f241720d 249 '#type' => 'fieldset',
68cf69e0 250 '#title' => t('User specific tracking settings'),
f241720d 251 '#collapsible' => TRUE,
68cf69e0 252 );
253 $form['user_vis_settings']['googleanalytics_custom'] = array(
254 '#type' => 'radios',
255 '#title' => t('Custom tracking settings'),
256 '#options' => array(
b7ad9417 257 t('Users cannot control whether they are tracked or not.'),
258 t('Track users by default, but let individual users to opt out.'),
259 t('Do not track users by default, but let individual users to opt in.')
68cf69e0 260 ),
c66469e2 261 '#description' => t('Allow individual users to customize the visibility of tracking in their account settings. Only users with %permission permission are allowed to set their own preference.', array('%permission' => t('opt-in or out of tracking'))),
68cf69e0 262 '#default_value' => variable_get('googleanalytics_custom', 0),
2cc17535
MC
263 );
264
68cf69e0 265 // Render the role overview.
266 $form['role_vis_settings'] = array(
267 '#type' => 'fieldset',
268 '#title' => t('Role specific tracking settings'),
269 '#collapsible' => TRUE,
9de7dbf1 270 );
85e14433 271
8f12448e 272 $default_role_options = array();
273 $default_roles = variable_get('googleanalytics_roles', array());
274 foreach ($default_roles as $default_rid => $checked) {
275 if ($checked) {
276 $default_role_options[] = $default_rid;
277 }
278 }
19001973 279 $roles = _googleanalytics_user_roles();
68cf69e0 280 $role_options = array();
281 foreach ($roles as $rid => $name) {
282 $role_options[$rid] = $name;
2cc17535 283 }
68cf69e0 284 $form['role_vis_settings']['googleanalytics_roles'] = array(
285 '#type' => 'checkboxes',
286 '#title' => t('Add tracking for specific roles'),
8f12448e 287 '#default_value' => $default_role_options,
68cf69e0 288 '#options' => $role_options,
b7ad9417 289 '#description' => t('Add tracking only for the selected role(s). If none of the roles are selected, all users will be tracked. If a user has any of the roles checked, that user will be tracked.'),
68cf69e0 290 );
2cc17535 291
5699033a 292 // Page specific visibility configurations.
293 $form['page_vis_settings'] = array(
294 '#type' => 'fieldset',
295 '#title' => t('Page specific tracking settings'),
296 '#collapsible' => TRUE,
297 '#collapsed' => TRUE,
298 );
299
300 $access = user_access('use PHP for tracking visibility');
301 $visibility = variable_get('googleanalytics_visibility', 0);
302 $pages = variable_get('googleanalytics_pages', '');
303
304 if ($visibility == 2 && !$access) {
305 $form['page_vis_settings'] = array();
306 $form['page_vis_settings']['visibility'] = array('#type' => 'value', '#value' => 2);
307 $form['page_vis_settings']['pages'] = array('#type' => 'value', '#value' => $pages);
308 }
309 else {
c57de0d0 310 $options = array(t('Add to every page except the listed pages.'), t('Add to the listed pages only.'));
5699033a 311 $description = t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'));
312
313 if ($access) {
314 $options[] = t('Add if the following PHP code returns <code>TRUE</code> (PHP-mode, experts only).');
315 $description .= ' '. t('If the PHP-mode is chosen, enter PHP code between %php. Note that executing incorrect PHP-code can break your Drupal site.', array('%php' => '<?php ?>'));
316 }
317 $form['page_vis_settings']['googleanalytics_visibility'] = array(
318 '#type' => 'radios',
319 '#title' => t('Add tracking to specific pages'),
320 '#options' => $options,
321 '#default_value' => $visibility,
322 );
323 $form['page_vis_settings']['googleanalytics_pages'] = array(
324 '#type' => 'textarea',
325 '#title' => t('Pages'),
326 '#default_value' => $pages,
327 '#description' => $description,
a6b00651 328 '#wysiwyg' => FALSE,
5699033a 329 );
330 }
331
c57de0d0 332 $profile_enabled = module_exists('profile');
d2c2fa2c 333 $form['segmentation'] = array(
ff2aacac 334 '#type' => 'fieldset',
c57de0d0 335 '#title' => t('User segmentation settings'),
ff2aacac 336 '#collapsible' => TRUE,
d2c2fa2c
MC
337 );
338
c57de0d0 339 // Compile a list of fields to show.
340 $fields = array(
341 'uid' => t('User identifier'),
342 'name' => t('User name'),
343 'roles' => t('User roles')
344 );
345 if ($profile_enabled) {
d2c2fa2c
MC
346 $result = db_query('SELECT name, title, type, weight FROM {profile_fields} ORDER BY weight');
347 while ($record = db_fetch_object($result)) {
348 $fields[$record->name] = $record->title;
349 }
d2c2fa2c 350 }
5215f4f7 351
c57de0d0 352 $form['segmentation']['googleanalytics_segmentation'] = array(
353 '#type' => 'select',
b7ad9417 354 '#title' => t('Add segmentation information to tracking code'),
8e34de03 355 '#description' => t('Segment users based on different properties, additionally to the basic IP address based tracking provided by Google Analytics.') . (!$profile_enabled ? ' '. t('<a href="@module_list">Enable the profile module</a> to be able to use profile fields for more granular tracking.', array('@module_list' => url('admin/build/modules'))) : '') .' '. t('Selecting one or more values is supported. To select multiple items, hold down CTRL while selecting fields.'),
c57de0d0 356 '#default_value' => variable_get('googleanalytics_segmentation', ''),
357 '#options' => $fields,
358 '#size' => $profile_enabled ? 10 : 3,
359 '#multiple' => TRUE
360 );
361
6f2ffb4b 362 // Link specific configurations.
1b65d6f6 363 $form['linktracking'] = array(
364 '#type' => 'fieldset',
c57de0d0 365 '#title' => t('Link tracking settings'),
1b65d6f6 366 '#collapsible' => TRUE,
367 '#collapsed' => FALSE,
368 );
44613035 369 $form['linktracking']['googleanalytics_trackoutgoing'] = array(
370 '#type' => 'checkbox',
371 '#title' => t('Track outgoing links'),
372 '#default_value' => variable_get('googleanalytics_trackoutgoing', 1),
373 '#description' => t('Enables tracking of clicks on outgoing links.')
374 );
375 $form['linktracking']['googleanalytics_trackmailto'] = array(
376 '#type' => 'checkbox',
377 '#title' => t('Track mailto links'),
aaf3c126 378 '#default_value' => variable_get('googleanalytics_trackmailto', 1),
44613035 379 '#description' => t('Enables tracking of clicks on mailto links.')
380 );
1b65d6f6 381 $form['linktracking']['googleanalytics_trackfiles'] = array(
382 '#type' => 'checkbox',
44613035 383 '#title' => t('Track download links'),
384 '#default_value' => variable_get('googleanalytics_trackfiles', 1),
c57de0d0 385 '#description' => t('Enables tracking of clicks on links to files based on the file extensions list below.')
1b65d6f6 386 );
387 $form['linktracking']['googleanalytics_trackfiles_extensions'] = array(
f241720d 388 '#type' => 'textfield',
8fab9ad0 389 '#title' => t('File extensions to track'),
939f0101 390 '#default_value' => variable_get('googleanalytics_trackfiles_extensions', GA_TRACKFILES_EXTENSIONS),
c57de0d0 391 '#description' => t('A pipe separated list of file extensions that should be tracked when clicked with regular expression support. Example: !extensions', array('!extensions' => GA_TRACKFILES_EXTENSIONS))
f241720d
MC
392 );
393
6f2ffb4b 394 // Advanced feature configurations.
d2c2fa2c 395 $form['advanced'] = array(
ff2aacac 396 '#type' => 'fieldset',
c57de0d0 397 '#title' => t('Advanced settings'),
ff2aacac 398 '#collapsible' => TRUE,
399 '#collapsed' => TRUE,
c57de0d0 400 );
401
402 $form['advanced']['googleanalytics_legacy_version'] = array(
403 '#type' => 'radios',
404 '#title' => t('Google Analytics version used'),
405 '#default_value' => variable_get('googleanalytics_legacy_version', 0),
406 '#options' => array(
407 0 => t('Latest (ga.js) tracking code'),
408 1 => t('Legacy (urchin.js) tracking code')
409 ),
410 '#description' => t('<a href="@ga_js_url">On December 13, 2007, Google rolled out a new API</a> for its tracking code, and suggests all new sites to use this code. You should only use the older legacy code, if you have custom tracking code tied to that API. Otherwise it is suggested you use the latest API, as the legacy code will not receive feature updates and is not compatible with new features.', array('@ga_js_url' => 'http://analytics.blogspot.com/2007/12/announcing-new-graphing-tools-gajs.html')),
d2c2fa2c 411 );
962363af
MC
412
413 $form['advanced']['googleanalytics_cache'] = array(
414 '#type' => 'checkbox',
bed62aa8 415 '#title' => t('Cache tracking code file locally'),
c57de0d0 416 '#description' => t("If checked, the tracking code file is retrieved from Google Analytics and cached locally. It is updated daily from Google's servers to ensure updates to tracking code are reflected in the local copy. Do not activate this until after Google Analytics has confirmed your tracker!"),
962363af
MC
417 '#default_value' => variable_get('googleanalytics_cache', 0),
418 );
419 if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE) {
420 $form['advanced']['googleanalytics_cache']['#disabled'] = TRUE;
a1201173 421 $form['advanced']['googleanalytics_cache']['#description'] .= ' '. t('<a href="@url">Public file transfers</a> must be enabled to allow local caching.', array('@url' => url('admin/settings/file-system', drupal_get_destination())));
962363af
MC
422 }
423
bed62aa8 424 $site_search_dependencies = '<div class="admin-dependencies">';
65be5d04 425 $site_search_dependencies .= t('Depends on: !dependencies', array('!dependencies' => (module_exists('search') ? t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => 'Search')) : t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => 'Search')))));
bed62aa8 426 $site_search_dependencies .= '</div>';
427
428 // Google already have many translations, if not - they display a note to change the language.
27ce6487 429 global $locale;
bed62aa8 430 $form['advanced']['googleanalytics_site_search'] = array(
431 '#type' => 'checkbox',
8fab9ad0 432 '#title' => t('Track internal search'),
65be5d04 433 '#description' => t('If checked, internal search keywords are tracked. You must configure your Google account to use the internal query parameter <strong>search</strong>. For more information see <a href="@url">How do I set up Site Search for my profile</a>.', array('@url' => 'http://www.google.com/support/analytics/bin/answer.py?hl='. $locale .'&amp;answer=75817')) . $site_search_dependencies,
bed62aa8 434 '#default_value' => variable_get('googleanalytics_site_search', FALSE),
435 '#disabled' => (module_exists('search') ? FALSE : TRUE),
436 );
437
188db7f0 438 $form['advanced']['codesnippet'] = array(
439 '#type' => 'fieldset',
8fab9ad0 440 '#title' => t('Custom JavaScript code'),
188db7f0 441 '#collapsible' => TRUE,
442 '#collapsed' => TRUE,
65be5d04 443 '#description' => t('You can add custom Google Analytics <a href="@snippets">code snippets</a> here. These will be added to every page that Google Analytics appears on. Before you add custom code to the below textarea\'s you should read <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' => 'http://code.google.com/apis/analytics/docs/gaConceptsOverview.html', '@ga_js_api' => 'http://code.google.com/apis/analytics/docs/gaJSApi.html'))
188db7f0 444 );
445 $form['advanced']['codesnippet']['googleanalytics_codesnippet_before'] = array(
446 '#type' => 'textarea',
447 '#title' => t('Code snippet (before)'),
448 '#default_value' => variable_get('googleanalytics_codesnippet_before', ''),
449 '#rows' => 5,
a6b00651 450 '#wysiwyg' => FALSE,
188db7f0 451 '#description' => t('Code in this textarea will be added <strong>before</strong> urchinTracker() or pageTracker._trackPageview().')
452 );
453 $form['advanced']['codesnippet']['googleanalytics_codesnippet_after'] = array(
454 '#type' => 'textarea',
455 '#title' => t('Code snippet (after)'),
456 '#default_value' => variable_get('googleanalytics_codesnippet_after', ''),
d96499f1 457 '#rows' => 5,
a6b00651 458 '#wysiwyg' => FALSE,
5c4bf5fa 459 '#description' => t("Code in this textarea will be added <strong>after</strong> urchinTracker() or pageTracker._trackPageview(). This is useful if you'd like to track a site in two accounts.")
ff2aacac 460 );
d2c2fa2c 461
722c7cfe 462 $form['advanced']['googleanalytics_js_scope'] = array(
463 '#type' => 'select',
464 '#title' => t('JavaScript scope'),
465 '#description' => t("<strong>Warning:</strong> Google recommends adding the external JavaScript files to footer for performance reasons."),
466 '#options' => array(
467 'footer' => t('Footer'),
468 'header' => t('Header'),
469 ),
470 '#default_value' => variable_get('googleanalytics_js_scope', 'footer'),
471 );
472
834c2715 473 return system_settings_form($form);
570957cf
MC
474}
475
3322ccb0 476function googleanalytics_admin_settings_form_validate($form_id, $form_values) {
477 if (!preg_match('/^UA-\d{4,}-\d+$/', $form_values['googleanalytics_account'])) {
8e34de03 478 form_set_error('googleanalytics_account', t('A valid Google Analytics account number is case sensitive and formatted like UA-xxxxxx-x.'));
3322ccb0 479 }
8d69f61d 480
481 // This is for the Newbie's who cannot read a text area description.
188db7f0 482 if (stristr($form_values['googleanalytics_codesnippet_before'], 'http://www.google-analytics.com/urchin.js') || stristr($form_values['googleanalytics_codesnippet_before'], 'google-analytics.com/ga.js')) {
483 form_set_error('googleanalytics_codesnippet_before', t('Do not add the tracker code provided by Google into the javascript code snippets! This module already builds the tracker code based on your Google Analytics account number and settings.'));
484 }
485 if (stristr($form_values['googleanalytics_codesnippet_after'], 'http://www.google-analytics.com/urchin.js') || stristr($form_values['googleanalytics_codesnippet_after'], 'google-analytics.com/ga.js')) {
486 form_set_error('googleanalytics_codesnippet_after', t('Do not add the tracker code provided by Google into the javascript code snippets! This module already builds the tracker code based on your Google Analytics account number and settings.'));
487 }
488 if (preg_match('/(.*)<\/?script(.*)>(.*)/i', $form_values['googleanalytics_codesnippet_before'])) {
489 form_set_error('googleanalytics_codesnippet_before', t('Do not include the &lt;script&gt; tags in the javascript code snippets.'));
b5785ddb 490 }
188db7f0 491 if (preg_match('/(.*)<\/?script(.*)>(.*)/i', $form_values['googleanalytics_codesnippet_after'])) {
492 form_set_error('googleanalytics_codesnippet_after', t('Do not include the &lt;script&gt; tags in the javascript code snippets.'));
8d69f61d 493 }
3322ccb0 494}
495
6f2ffb4b 496function googleanalytics_admin_settings_form_submit($form_id, $form_values) {
8d69f61d 497 // Trim some text area values.
6f2ffb4b 498 $form_values['googleanalytics_pages'] = trim($form_values['googleanalytics_pages']);
188db7f0 499 $form_values['googleanalytics_codesnippet_before'] = trim($form_values['googleanalytics_codesnippet_before']);
500 $form_values['googleanalytics_codesnippet_after'] = trim($form_values['googleanalytics_codesnippet_after']);
6f2ffb4b 501 system_settings_form_submit($form_id, $form_values);
502}
503
570957cf
MC
504/**
505 * Implementation of hook_requirements().
506 */
507function googleanalytics_requirements($phase) {
508 $requirements = array();
509
510 if ($phase == 'runtime') {
511 // Raise warning if Google user account has not been set yet.
3322ccb0 512 if (!preg_match('/^UA-\d{4,}-\d+$/', variable_get('googleanalytics_account', 'UA-'))) {
570957cf
MC
513 $requirements['googleanalytics'] = array(
514 'title' => t('Google Analytics module'),
515 'description' => t('Google Analytics module has not been configured yet. Please configure its settings from the <a href="@url">Google Analytics settings page</a>.', array('@url' => url('admin/settings/googleanalytics'))),
516 'severity' => REQUIREMENT_ERROR,
517 'value' => t('Not configured'),
518 );
519 }
520 }
521
522 return $requirements;
962363af
MC
523}
524
962363af
MC
525/**
526 * Implementation of hook_cron().
527 */
528function googleanalytics_cron() {
df8370fd 529 // Regenerate the google analytics urchin.js or ga.js every day.
530 if (time() - variable_get('googleanalytics_last_cache', 0) >= 86400) {
bed62aa8 531 // Legacy google analytics version.
962363af 532 file_delete(file_directory_path() .'/googleanalytics/urchin.js');
bed62aa8 533
534 // New google analytics version.
535 file_delete(file_directory_path() .'/googleanalytics/ga.js');
df8370fd 536 variable_set('googleanalytics_last_cache', time());
962363af
MC
537 }
538}
539
962363af
MC
540/**
541 * Download and cache the urchin.js file locally.
542 * @param $location
543 * The full URL to the external javascript file.
544 * @return mixed
545 * The path to the local javascript file on success, boolean FALSE on failure.
546 */
ec17d4dc 547function _googleanalytics_cache($location) {
ff2aacac 548 $directory = file_directory_path() .'/googleanalytics';
962363af
MC
549 $file_destination = $directory .'/'. basename($location);
550 if (!file_exists($file_destination)) {
551 $result = drupal_http_request($location);
552 if ($result->code == 200) {
553 // Check that the files directory is writable
554 if (file_check_directory($directory, FILE_CREATE_DIRECTORY)) {
555 return file_save_data($result->data, $directory .'/'. basename($location), FILE_EXISTS_REPLACE);
556 }
557 }
558 }
559 else {
560 return $file_destination;
561 }
562}
bed62aa8 563
564/**
68cf69e0 565 * Tracking visibility check for an user object.
bed62aa8 566 *
567 * @param $account
68cf69e0 568 * A user object containing an array of roles to check.
bed62aa8 569 * @return boolean
68cf69e0 570 * A decision on if the current user is being tracked by Google Analytics.
bed62aa8 571 */
68cf69e0 572function _googleanalytics_visibility_user($account) {
573
574 $enabled = FALSE;
575
576 // Is current user a member of a role that should be tracked?
577 if (_googleanalytics_visibility_roles($account)) {
578
579 // Use the user's block visibility setting, if necessary.
580 if (($custom = variable_get('googleanalytics_custom', 0)) != 0) {
581 if ($account->uid && isset($account->googleanalytics['custom'])) {
582 $enabled = $account->googleanalytics['custom'];
583 }
584 else {
585 $enabled = ($custom == 1);
586 }
587 }
588 else {
589 $enabled = TRUE;
bed62aa8 590 }
68cf69e0 591
bed62aa8 592 }
593
68cf69e0 594 return $enabled;
595}
596
597/**
598 * Based on visibility setting this function returns TRUE if GA code should
599 * be added for the current role and otherwise FALSE.
600 */
601function _googleanalytics_visibility_roles($account) {
602
603 $enabled = FALSE;
604 $roles = variable_get('googleanalytics_roles', array());
605
606 if (array_sum($roles) > 0) {
607 // One or more roles are selected for tracking.
608 foreach (array_keys($account->roles) as $rid) {
609 // Is the current user a member of one role enabled for tracking?
610 if (isset($roles[$rid]) && $rid == $roles[$rid]) {
611 // Current user is a member of a role that should be tracked.
612 $enabled = TRUE;
613 break;
614 }
615 }
bed62aa8 616 }
68cf69e0 617 else {
a1201173 618 // No role is selected for tracking, therefor all roles should be tracked.
68cf69e0 619 $enabled = TRUE;
bed62aa8 620 }
621
68cf69e0 622 return $enabled;
bed62aa8 623}
19001973 624
625/**
6f2ffb4b 626 * Based on visibility setting this function returns TRUE if GA code should
627 * be added to the current page and otherwise FALSE.
628 */
0a2bd7f8 629function _googleanalytics_visibility_pages() {
609f4952 630 static $page_match;
631
632 // Cache visibility setting in hook_init for hook_footer.
633 if (!isset($page_match)) {
634
635 $visibility = variable_get('googleanalytics_visibility', 0);
636 $pages = variable_get('googleanalytics_pages', '');
637
638 // Match path if necessary.
639 if (!empty($pages)) {
640 if ($visibility < 2) {
641 $path = drupal_get_path_alias($_GET['q']);
642 // Compare with the internal and path alias (if any).
643 $page_match = _googleanalytics_match_path($path, $pages);
644 if ($path != $_GET['q']) {
645 $page_match = $page_match || _googleanalytics_match_path($_GET['q'], $pages);
646 }
647 // When $visibility has a value of 0, the block is displayed on
648 // all pages except those listed in $pages. When set to 1, it
649 // is displayed only on those pages listed in $pages.
650 $page_match = !($visibility xor $page_match);
651 }
652 else {
653 $page_match = drupal_eval($pages);
6f2ffb4b 654 }
6f2ffb4b 655 }
656 else {
609f4952 657 $page_match = TRUE;
6f2ffb4b 658 }
6f2ffb4b 659
609f4952 660 }
6f2ffb4b 661 return $page_match;
662}
663
664/**
665 * D6 backport of drupal_match_path().
666 */
667function _googleanalytics_match_path($path, $patterns) {
668 static $regexps;
669
670 if (!isset($regexps[$patterns])) {
671 $regexps[$patterns] = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. preg_quote(variable_get('site_frontpage', 'node'), '/') .'\2'), preg_quote($patterns, '/')) .')$/';
672 }
673 return preg_match($regexps[$patterns], $path);
674}
675
676/**
19001973 677 * D6 backport orders core standard roles on top and translate core roles.
678 */
679function _googleanalytics_user_roles() {
680 // System roles take the first two positions.
681 $roles = array(
682 DRUPAL_ANONYMOUS_RID => NULL,
683 DRUPAL_AUTHENTICATED_RID => NULL,
684 );
685
686 $result = db_query('SELECT * FROM {role} ORDER BY name');
687
688 while ($role = db_fetch_object($result)) {
689 switch ($role->rid) {
690 // We only translate the built in role names
691 case DRUPAL_ANONYMOUS_RID:
692 $roles[$role->rid] = t($role->name);
693 break;
694 case DRUPAL_AUTHENTICATED_RID:
695 $roles[$role->rid] = t($role->name);
696 break;
697 default:
698 $roles[$role->rid] = $role->name;
699 }
700 }
701
702 // Filter to remove unmatched system roles.
703 return array_filter($roles);
704}