<?php
// $Id $

/*
 * Drupal Module: LoopFuse Statistics
 * Adds the required Javascript to the bottom of all your Drupal pages
 * to allow tracking by the LoopFuse statistics package.
 * This is almost an identical copy of the GoogleAnalytics module by Mike Carter <www.ixis.co.uk/contact>
 *
 * @author: Ethan Fremen <i@mindlace.net>
 */

define('LF_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');

function loopfusestatistics_help($path) {
  switch ($path) {
    case 'admin/settings/loopfusestatistics':
      return t('<a href="@lf_url">loopfuse</a> is a Marketing Automation package. This module provides services to integrate the Loopfuse website tracking component with Drupal.', array('@lf_url' => 'http://loopfuse.com/'));
  }
}

function loopfusestatistics_perm() {
  return array('opt-in or out of tracking', 'use PHP for tracking visibility');
}

function loopfusestatistics_menu($may_cache) {
  global $user;

	if (!$may_cache) {
	  $id = variable_get('loopfuse_account', '');

	  // 1. Check if the LF account number has a value.
	  // 2. Track page views based on visibility value.
	  // 3. Check if we should track the currently active user's role.
	  if (!empty($id) && _loopfusestatistics_visibility_pages() && _loopfusestatistics_visibility_user($user)) {
	    // Use the old version of LoopFuse Statistics?
	    $scope = variable_get('loopfusestatistics_js_scope', 'footer');

	    // Should a local cached copy of listen.js be used?
	    $js_file = 'listen.js';
	    $url =  loopfuse_get_base_url() . '/webrecorder/js/'. $js_file;

	    if (variable_get('loopfusestatistics_cache', 0) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC) && $source = _loopfusestatistics_cache($url)) {
	      drupal_add_js($source, 'module', $scope);
	    }
	    else {
	      $script = 'var lfJsProt = (("https:" == document.location.protocol) ? "https://" : "http://");';
	      $script .= 'document.write(unescape("%3Cscript src=\'" + lfJsProt + "' . loopfuse_get_hostname() . '/webrecorder/js/'. $js_file .'\' type=\'text/javascript\'%3E%3C/script%3E"));';
	      drupal_add_js($script, 'inline', $scope);
      
	    }

	    // Add link tracking.
	    $link_settings = array();
	    if ($trackOutgoing = variable_get('loopfusestatistics_trackoutgoing', 1)) {
	      $link_settings['trackOutgoing'] = $trackOutgoing;
	    }
	    if ($trackMailto = variable_get('loopfusestatistics_trackmailto', 1)) {
	      $link_settings['trackMailto'] = $trackMailto;
	    }
	    if (($trackDownload = variable_get('loopfusestatistics_trackfiles', 1)) && ($trackfiles_extensions = variable_get('loopfusestatistics_trackfiles_extensions', LF_TRACKFILES_EXTENSIONS))) {
	      $link_settings['trackDownload'] = $trackDownload;
	      $link_settings['trackDownloadExtensions'] = $trackfiles_extensions;
	    }
	    if (!empty($link_settings)) {
	      drupal_add_js(array('loopfusestatistics' => $link_settings), 'setting', 'header');
	     	drupal_add_js(drupal_get_path('module', 'loopfusestatistics') .'/loopfusestatistics.js', 'module', $scope);
	    }
	  }
	}
	
}

function loopfusestatistics_form_alter($form_id, &$form) {
	switch ($form_id) {
		case 'loopfuse_admin_settings':
	  // Standard tracking configurations.
	  $statsform['user_vis_settings'] = array(
	    '#type' => 'fieldset',
	    '#title' => t('User specific tracking settings'),
	    '#collapsible' => TRUE,
	  );
	  $statsform['user_vis_settings']['loopfusestatistics_custom'] = array(
	    '#type' => 'radios',
	    '#title' => t('Custom tracking settings'),
	    '#options' => array(
	      t('Users cannot control whether they are tracked or not.'),
	      t('Track users by default, but let individual users to opt out.'),
	      t('Do not track users by default, but let individual users to opt in.')
	    ),
	    '#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'))),
	    '#default_value' => variable_get('loopfusestatistics_custom', 0),
	  );

	  // Render the role overview.
	  $statsform['role_vis_settings'] = array(
	    '#type' => 'fieldset',
	    '#title' => t('Role specific tracking settings'),
	    '#collapsible' => TRUE,
	  );

	  $roles = user_roles();
	  $role_options = array();
	  foreach ($roles as $rid => $name) {
	    $role_options[$rid] = $name;
	  }
	  $statsform['role_vis_settings']['loopfusestatistics_roles'] = array(
	    '#type' => 'checkboxes',
	    '#title' => t('Add tracking for specific roles'),
	    '#default_value' => variable_get('loopfusestatistics_roles', array()),
	    '#options' => $role_options,
	    '#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.'),
	  );

	  // Page specific visibility configurations.
	  $statsform['page_vis_settings'] = array(
	    '#type' => 'fieldset',
	    '#title' => t('Page specific tracking settings'),
	    '#collapsible' => TRUE,
	    '#collapsed' => TRUE,
	  );

	  $access = user_access('use PHP for tracking visibility');
	  $visibility = variable_get('loopfusestatistics_visibility', 0);
	  $pages = variable_get('loopfusestatistics_pages', '');

	  if ($visibility == 2 && !$access) {
	    $statsform['page_vis_settings'] = array();
	    $statsform['page_vis_settings']['visibility'] = array('#type' => 'value', '#value' => 2);
	    $statsform['page_vis_settings']['pages'] = array('#type' => 'value', '#value' => $pages);
	  }
	  else {
	    $options = array(t('Add to every page except the listed pages.'), t('Add to the listed pages only.'));
	    $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>'));

	    if ($access) {
	      $options[] = t('Add if the following PHP code returns <code>TRUE</code> (PHP-mode, experts only).');
	      $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 ?>'));
	    }
	    $statsform['page_vis_settings']['loopfusestatistics_visibility'] = array(
	      '#type' => 'radios',
	      '#title' => t('Add tracking to specific pages'),
	      '#options' => $options,
	      '#default_value' => $visibility,
	    );
	    $statsform['page_vis_settings']['loopfusestatistics_pages'] = array(
	      '#type' => 'textarea',
	      '#title' => t('Pages'),
	      '#default_value' => $pages,
	      '#description' => $description,
	    );
	  }

	  $profile_enabled = module_exists('profile');
	  $statsform['segmentation'] = array(
	    '#type' => 'fieldset',
	    '#title' => t('User segmentation settings'),
	    '#collapsible' => TRUE,
	  );

	  // Compile a list of fields to show.
	  $fields = array(
	    'uid' => t('User identifier'),
	    'name' => t('User name'),
	    'roles' => t('User roles')
	  );
	  if ($profile_enabled) {
	    $result = db_query('SELECT name, title, type, weight FROM {profile_fields} ORDER BY weight');
	    while ($record = db_fetch_object($result)) {
	      $fields[$record->name] = $record->title;
	    }
	  }

	  $statsform['segmentation']['loopfusestatistics_segmentation'] = array(
	    '#type' => 'select',
	    '#title' => t('Add segmentation information to tracking code'),
	    '#description' => t('Segment users based on different properties, additionally to the basic IP address based tracking provided by Loopfuse.') . (!$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.'),
	    '#default_value' => variable_get('loopfusestatistics_segmentation', ''),
	    '#options' => $fields,
	    '#size' => $profile_enabled ? 10 : 3,
	    '#multiple' => TRUE
	  );

	  // Link specific configurations.
	  $statsform['linktracking'] = array(
	    '#type' => 'fieldset',
	    '#title' => t('Link tracking settings'),
	    '#collapsible' => TRUE,
	    '#collapsed' => FALSE,
	  );
	  $statsform['linktracking']['loopfusestatistics_trackoutgoing'] = array(
	    '#type' => 'checkbox',
	    '#title' => t('Track outgoing links'),
	    '#default_value' => variable_get('loopfusestatistics_trackoutgoing', 1),
	    '#description' => t('Enables tracking of clicks on outgoing links.')
	  );
	  $statsform['linktracking']['loopfusestatistics_trackmailto'] = array(
	    '#type' => 'checkbox',
	    '#title' => t('Track mailto links'),
	    '#default_value' => variable_get('loopfusestatistics_trackmailto', 1),
	    '#description' => t('Enables tracking of clicks on mailto links.')
	  );
	  $statsform['linktracking']['loopfusestatistics_trackfiles'] = array(
	    '#type' => 'checkbox',
	    '#title' => t('Track download links'),
	    '#default_value' => variable_get('loopfusestatistics_trackfiles', 1),
	    '#description' => t('Enables tracking of clicks on links to files based on the file extensions list below.')
	  );
	  $statsform['linktracking']['loopfusestatistics_trackfiles_extensions'] = array(
	    '#type' => 'textfield',
	    '#title' => t('File extensions to track'),
	    '#default_value' => variable_get('loopfusestatistics_trackfiles_extensions', LF_TRACKFILES_EXTENSIONS),
	    '#description' => t('A pipe separated list of file extensions that should be tracked when clicked with regular expression support. Example: !extensions', array('!extensions' => LF_TRACKFILES_EXTENSIONS))
	  );

	  // Advanced feature configurations.
	  $statsform['advanced'] = array(
	    '#type' => 'fieldset',
	    '#title' => t('Advanced settings'),
	    '#collapsible' => TRUE,
	    '#collapsed' => TRUE,
	  );
	  $statsform['advanced']['loopfusestatistics_cache'] = array(
	    '#type' => 'checkbox',
	    '#title' => t('Cache tracking code file locally'),
	    '#description' => t("If checked, the tracking code file is retrieved from LoopFuse and cached locally. It is updated daily from loopfuse's servers to ensure updates to tracking code are reflected in the local copy. Do not activate this until after LoopFuse has confirmed your tracker!"),
	    '#default_value' => variable_get('loopfusestatistics_cache', 0),
	  );
	  if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PRIVATE) {
	    $statsform['advanced']['loopfusestatistics_cache']['#disabled'] = TRUE;
	    $statsform['advanced']['loopfusestatistics_cache']['#description'] .= ' '. t('<a href="!url">Public file transfers</a> must be enabled to allow local caching.', array('!url' => url('admin/settings/file-system', array('query' => drupal_get_destination()))));
	  }

	  $statsform['advanced']['loopfusestatistics_js_scope'] = array(
	    '#type' => 'select',
	    '#title' => t('JavaScript scope'),
	    '#description' => t("<strong>Warning:</strong> LoopFuse instructs you to add the external JavaScript files to the footer for performance and to avoid potential problems with Internet Explorer."),
	    '#options' => array(
	      'footer' => t('Footer'),
	      'header' => t('Header'),
	    ),
	    '#default_value' => variable_get('loopfusestatistics_js_scope', 'footer'),
	  );
	
	  // integrate into the form cleanly
	  $pos = array_search('account', array_keys($form)) + 1;
	  $form = array_merge(array_slice($form, 0, $pos), $statsform, array_slice($form, $pos));

		break;
	}
}


/**
 * Implementation of hook_footer() to insert Javascript at the end of the page.
 */
function loopfusestatistics_footer($main = 0) {
  global $user;

  $id = variable_get('loopfuse_account', '');

  if (!empty($id) && _loopfusestatistics_visibility_pages() && _loopfusestatistics_visibility_user($user)) {


    // Add User profile segmentation values.
    if (is_array($profile_fields = variable_get('loopfusestatistics_segmentation', '')) && ($user->uid > 0)) {

      $p = module_invoke('profile', 'load_profile', $user);

      $fields = array();
      foreach ($profile_fields as $field => $title) {
        $value = $user->$field;

        if (is_array($value)) {
          $value = implode(',', $value);
        }

        $fields[$field] = $value;
      }

      // Only show segmentation variable if there are specified fields.
      $segmentation = '';
      if (count($fields) > 0) {
        if ($legacy_version) {
          $segmentation = '__utmSetVar('. drupal_to_js(implode(':', $fields)) .');';
        }
        else {
          $segmentation = 'pageTracker._setVar('. drupal_to_js(implode(':', $fields)) .');';
        }
      }
    }

/*
    // Site search tracking support.
    $url_custom = '';
    if (module_exists('search') && variable_get('loopfusestatistics_site_search', FALSE) && arg(0) == 'search') {
      $keys = search_get_keys();
      $url_custom = drupal_to_js(url('search/'. arg(1), array('query' => 'search='. trim($keys))));
    }
*/

    // Track access denied (403) and file not found (404) pages.
    // This code is retained from the google docs.
    if (function_exists('drupal_get_headers')) {
      $headers = drupal_get_headers();
      if (strstr($headers, 'HTTP/1.1 403 Forbidden')) {
          // See http://www.google.com/support/analytics/bin/answer.py?answer=86927
          $url_custom = '"/403.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
      }
      elseif (strstr($headers, 'HTTP/1.1 404 Not Found')) {
          $url_custom = '"/404.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer';
      }
    }
  
    $script .= '_lf_cid = '.drupal_to_js($id).'; _lf_remora();';
    drupal_add_js($script, 'inline', 'footer');
  }
}

/**
 * Implementation of hook_user().
 *
 * Allow users to decide if tracking code will be added to pages or not.
 */
function loopfusestatistics_user($type, $edit, &$account, $category = NULL) {
  switch ($type) {
    case 'form':
      if ($category == 'account' && user_access('opt-in or out of tracking') && ($custom = variable_get('loopfusestatistics_custom', 0)) != 0 && _loopfusestatistics_visibility_roles($account)) {
        $form['loopfusestatistics'] = array(
          '#type' => 'fieldset',
          '#title' => t('LoopFuse Statistics configuration'),
          '#weight' => 3,
          '#collapsible' => TRUE,
          '#tree' => TRUE
        );
        $form['loopfusestatistics']['custom'] = array(
          '#type' => 'checkbox',
          '#title' => t('User tracking'),
          '#default_value' => isset($account->loopfusestatistics['custom']) ? $account->loopfusestatistics['custom'] : ($custom == 1)
        );

        return $form;
      }
      break;

  }
}

/**
 * Implementation of hook_requirements().
 */
function loopfusestatistics_requirements($phase) {
  $requirements = array();

  if ($phase == 'runtime') {
    // Raise warning if LoopFuse user account has not been set yet.
    if (!preg_match('/^LF_\S{8,}$/', variable_get('loopfuse_account', 'LF_'))) {
      $requirements['loopfusestatistics'] = array(
        'title' => t('LoopFuse Statistics module'),
        'description' => t('LoopFuse Statistics module has not been configured yet. Please configure its settings from the <a href="@url">LoopFuse Statistics settings page</a>.', array('@url' => url('admin/settings/loopfusestatistics'))),
        'severity' => REQUIREMENT_ERROR,
        'value' => t('Not configured'),
      );
    }
  }

  return $requirements;
}

/**
 * Implementation of hook_cron().
 */
function loopfusestatistics_cron() {
  // Regenerate the listen.js every day.
  if (time() - variable_get('loopfusestatistics_last_cache', 0) >= 86400) {

    file_delete(file_directory_path() .'/loopfusestatistics/listen.js');

    // Clear aggregated JS files.
    if (variable_get('preprocess_js', 0)) {
      drupal_clear_js_cache();
    }

    variable_set('loopfusestatistics_last_cache', time());
  }
}

/**
 * Download and cache the listen.js file locally.
 * @param $location
 *   The full URL to the external javascript file.
 * @return mixed
 *   The path to the local javascript file on success, boolean FALSE on failure.
 */
function _loopfusestatistics_cache($location) {
  $directory = file_directory_path() .'/loopfusestatistics';
  $file_destination = $directory .'/'. basename($location);
  if (!file_exists($file_destination)) {
    $result = drupal_http_request($location);
    if ($result->code == 200) {
      // Check that the files directory is writable
      if (file_check_directory($directory, FILE_CREATE_DIRECTORY)) {
        return file_save_data($result->data, $directory .'/'. basename($location), FILE_EXISTS_REPLACE);
      }
    }
  }
  else {
    return $file_destination;
  }
}

/**
 * Tracking visibility check for an user object.
 *
 * @param $account
 *   A user object containing an array of roles to check.
 * @return boolean
 *   A decision on if the current user is being tracked by LoopFuse Statistics.
 */
function _loopfusestatistics_visibility_user($account) {

  $enabled = FALSE;

  // Is current user a member of a role that should be tracked?
  if (_loopfusestatistics_visibility_roles($account)) {

    // Use the user's block visibility setting, if necessary.
    if (($custom = variable_get('loopfusestatistics_custom', 0)) != 0) {
      if ($account->uid && isset($account->loopfusestatistics['custom'])) {
        $enabled = $account->loopfusestatistics['custom'];
      }
      else {
        $enabled = ($custom == 1);
      }
    }
    else {
      $enabled = TRUE;
    }

  }

  return $enabled;
}

/**
 * Based on visibility setting this function returns TRUE if LF code should
 * be added for the current role and otherwise FALSE.
 */
function _loopfusestatistics_visibility_roles($account) {

  $enabled = FALSE;
  $roles = variable_get('loopfusestatistics_roles', array());

  if (array_sum($roles) > 0) {
    // One or more roles are selected for tracking.
    foreach (array_keys($account->roles) as $rid) {
      // Is the current user a member of one role enabled for tracking?
      if (isset($roles[$rid]) && $rid == $roles[$rid]) {
        // Current user is a member of a role that should be tracked.
        $enabled = TRUE;
        break;
      }
    }
  }
  else {
    // No role is selected for tracking, therefore all roles should be tracked. 
    $enabled = TRUE;
  }

  return $enabled;
}

/**
 * Based on visibility setting this function returns TRUE if LF code should
 * be added to the current page and otherwise FALSE.
 */
function _loopfusestatistics_visibility_pages() {
  static $page_match;

  // Cache visibility setting in hook_init for hook_footer.
  if (!isset($page_match)) {

    $visibility = variable_get('loopfusestatistics_visibility', 0);
    $pages = variable_get('loopfusestatistics_pages', '');

    // Match path if necessary.
    if (!empty($pages)) {
      if ($visibility < 2) {
        $path = drupal_get_path_alias($_GET['q']);
        // Compare with the internal and path alias (if any).
        $page_match = _loopfuse_match_page_path($path, $pages);
        if ($path != $_GET['q']) {
          $page_match = $page_match || _loopfuse_match_page_path($_GET['q'], $pages);
        }
        // When $visibility has a value of 0, the block is displayed on
        // all pages except those listed in $pages. When set to 1, it
        // is displayed only on those pages listed in $pages.
        $page_match = !($visibility xor $page_match);
      }
      else {
        $page_match = drupal_eval($pages);
      }
    }
    else {
      $page_match = TRUE;
    }

  }
  return $page_match;
}

function _loopfuse_match_page_path($path, $patterns) {
  static $regexps;

  if (!isset($regexps[$patterns])) {
    $regexps[$patterns] = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1'. preg_quote(variable_get('site_frontpage', 'node'), '/') .'\2'), preg_quote($patterns, '/')) .')$/';
  }
  return preg_match($regexps[$patterns], $path);
}
