You are here

statistics_advanced.module in Statistics Advanced 6

Same filename and directory in other branches
  1. 5 statistics_advanced.module

Adds advanced settings and features for the core Statistics module.

File

statistics_advanced.module
View source
<?php

/**
 * @file
 * Adds advanced settings and features for the core Statistics module.
 */

/**
 * Implementation of hook_perm().
 */
function statistics_advanced_perm() {
  return array(
    'exclude visits from the access log',
    'exclude visits from counters',
  );
}

/**
 * Implementation of hook_menu().
 */
function statistics_advanced_menu() {
  $items['admin/reports/settings/default'] = array(
    'title' => 'Settings',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file path' => drupal_get_path('module', 'statistics'),
  );
  $items['admin/reports/settings/advanced'] = array(
    'title' => 'Advanced settings',
    'description' => 'Configure advanced statistics settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'statistics_advanced_settings_form',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'weight' => 10,
    'type' => MENU_LOCAL_TASK,
    'file' => 'statistics_advanced.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_link_alter().
 *
 * Removes the node views counter for certain node types.
 */
function statistics_advanced_link_alter(&$links, $node) {

  // @todo Remove when http://drupal.org/node/321295 is fixed.
  if (_statistics_advanced_check_link_alter($links, $node)) {
    return;
  }
  if (isset($links['statistics_counter'])) {
    $counter_node_types = statistics_advanced_var('counter_node_types');
    if (isset($counter_node_types[$node->type]) && !$counter_node_types[$node->type]) {
      unset($links['statistics_counter']);
    }
  }
}

/**
 * Check that the correct parameters are passed to hook_link_alter().
 *
 * A lot of modules that were convered from Drupal 5 to 6 did not switch the
 * parameters around and cause errors.
 *
 * @todo Remove when http://drupal.org/node/321295 is fixed.
 */
function _statistics_advanced_check_link_alter($links, $node) {
  if (!is_array($links) || !is_object($node)) {
    $message = 'Invalid parameters for hook_link_alter(). Please report this message to <a href="http://drupal.org/node/321295">http://drupal.org/node/321295</a>.';
    $backtrace = array_slice(debug_backtrace(), 2, 2);
    foreach ($backtrace as $index => $caller) {
      $message .= '<br />' . strtr('line @line in @file (@function)', array(
        '@file' => $caller['file'],
        '@line' => $caller['line'],
        '@function' => $caller['function'],
      ));
    }
    drupal_set_message($message, 'error', FALSE);
    watchdog('statistics_adv', $message, array(), WATCHDOG_ERROR);
    return TRUE;
  }
}

/**
 * Implementation of hook_boot().
 *
 * Checks to see if this is a unique content view by checking the 'accesslog'
 * table for anonymous views and the 'history' table for authenticated user
 * views.
 */
function statistics_advanced_boot() {
  global $user;

  // Load path handling.
  drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);
  if (_statistics_advanced_is_node_visit() && statistics_advanced_var('ignore_repeat_views')) {

    // Save the current user's uid as we may find from the accesslog table that
    // the user has logged out but has a record in the history table.
    $uid = $user->uid;
    if (!$uid && variable_get('statistics_enable_access_log', 0)) {
      $accesslog = db_fetch_object(db_query_range("SELECT uid, timestamp FROM {accesslog} WHERE (path = '%s' OR uid > 0) AND sid = '%s' ORDER BY timestamp DESC", array(
        ':path' => $_GET['q'],
        ':sid' => session_id(),
      ), 0, 1));
      if ($accesslog) {
        if ($accesslog->uid) {
          $uid = $accesslog->uid;
        }
        else {
          _statistics_advanced_ignore('nodecounter', $accesslog->timestamp);
        }
      }
    }
    if ($uid) {
      $historylog = db_result(db_query("SELECT timestamp FROM {history} WHERE uid = %d AND nid = %d", array(
        ':uid' => $uid,
        ':nid' => arg(1),
      )));
      if ($historylog) {

        // Repeat user visit (same user id and node id in history table)
        _statistics_advanced_ignore('nodecounter', $historylog);
      }
    }
  }
}
function _statistics_advanced_is_403_or_404() {
  global $base_root;
  $headers = '';
  if (function_exists('headers_list')) {
    $headers = implode("\n", headers_list());
  }
  elseif (function_exists('drupal_set_header')) {
    $headers = drupal_set_header();
  }
  elseif (variable_get('cache', CACHE_DISABLED) == CACHE_NORMAL) {
    $headers = db_result(db_query("SELECT headers FROM {cache_page} WHERE cid = '%s'", array(
      ':cid' => $base_root . request_uri(),
    )));
  }
  return preg_match('/404 Not Found|403 Forbidden/i', $headers);
}

/**
 * Determine if this page request is a node visit (e.g. node/x).
 */
function _statistics_advanced_is_node_visit() {
  static $is_node_visit;
  if (!isset($is_node_visit)) {
    $is_node_visit = arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == '' && variable_get('statistics_count_content_views', 0);
  }
  return $is_node_visit;
}

/**
 * Implementation of hook_exit().
 *
 * Delete unwanted records.
 */
function statistics_advanced_exit() {
  global $user;
  if (_statistics_advanced_is_node_visit()) {
    if (_statistics_advanced_is_403_or_404()) {

      // Ignore 404 and 403 node visits.
      _statistics_advanced_ignore('nodecounter', TRUE);
    }
    elseif (statistics_advanced_user_access('exclude visits from counters')) {
      _statistics_advanced_ignore('nodecounter', TRUE);
    }

    //else {

    //  // If the node has been previously read by the user and has changed since
    //  // the last read, count it as a new read.
    //  $changed = db_result(db_query("SELECT changed FROM {node} WHERE nid = %d", array(':nid' => arg(1))));
    //  $last_read = _statistics_advanced_ignore('nodecounter');
    //  if ($last_read && $changed > $last_read) {
    //    _statistics_advanced_ignore('nodecounter', FALSE);
    //  }

    //}
  }

  // Check if user's access log entry should be ignored based on user role.
  if (statistics_advanced_user_access('exclude visits from the access log')) {
    _statistics_advanced_ignore('accesslog', TRUE);
  }

  // Check if the user's browser is a crawler.
  if (!$user->uid && statistics_advanced_var('ignore_crawlers') && module_exists('browscap') && function_exists('browscap_get_browser')) {
    drupal_load('module', 'browscap');
    $browser = browscap_get_browser();
    if (!empty($browser['crawler'])) {
      _statistics_advanced_ignore('accesslog', TRUE);
      if (_statistics_advanced_is_node_visit()) {
        _statistics_advanced_ignore('nodecounter', TRUE);
      }
      if (variable_get('browscap_monitor', 0) && function_exists('browscap_unmonitor')) {
        browscap_unmonitor();
      }
    }
  }
  if (_statistics_advanced_ignore('nodecounter')) {
    db_query("UPDATE {node_counter} SET daycount = daycount - 1, totalcount = totalcount - 1 WHERE nid = %d AND totalcount > 0 AND daycount > 0", array(
      ':nid' => arg(1),
    ));
  }
  if (_statistics_advanced_ignore('accesslog')) {
    db_query("DELETE FROM {accesslog} WHERE (sid = '%s' AND uid = 0) OR (uid = %d AND uid > 0)", array(
      ':sid' => session_id(),
      ':uid' => $user->uid,
    ));
  }
}

/**
 * A copy of user.module's user_access() function so we do not need to load
 * the entire user.module during cached visits.
 */
function statistics_advanced_user_access($string, $account = NULL) {

  // If user.module is loaded, use that function call since it most likely has
  // the permissions already cached.
  if (function_exists('user_access')) {
    return user_access($string, $account);
  }
  static $perm = array();
  if (!isset($account)) {
    global $user;
    $account = $user;
  }

  // User #1 has all privileges:
  if ($account->uid == 1) {
    return TRUE;
  }

  // To reduce the number of SQL queries, we cache the user's permissions
  // in a static variable.
  if (!isset($perm[$account->uid])) {
    $result = db_query("SELECT p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid IN (" . db_placeholders($account->roles) . ")", array_keys($account->roles));
    $perms = array();
    while ($row = db_fetch_object($result)) {
      $perms += array_flip(explode(', ', $row->perm));
    }
    $perm[$account->uid] = $perms;
  }
  return isset($perm[$account->uid][$string]);
}

/**
 * Internal function to track which records to ignore.
 */
function _statistics_advanced_ignore($type, $value = NULL) {
  static $ignores = array();
  if (isset($value)) {
    $ignores[$type] = $value;
  }
  else {
    return isset($ignores[$type]) ? $ignores[$type] : FALSE;
  }
}

/**
 * Internal default variables for statistics_advanced_var().
 */
function statistics_advanced_variables() {
  return array(
    'statistics_advanced_ignore_crawlers' => 0,
    'statistics_advanced_ignore_repeat_views' => 1,
    'statistics_advanced_counter_node_types' => array(),
    // Deleted variables set to NULL so they can still be removed if found
    // during uninstall.
    'statistics_advanced_ignore_user_roles' => NULL,
    'statistics_advanced_ignore_roles' => NULL,
  );
}

/**
 * Internal implementation of variable_get().
 */
function statistics_advanced_var($name, $default = NULL) {
  static $defaults = NULL;
  if (!isset($defaults)) {
    $defaults = statistics_advanced_variables();
  }
  $name = 'statistics_advanced_' . $name;
  if (!isset($defaults[$name])) {
    trigger_error(t('Default variable for %variable not found.', array(
      '%variable' => $name,
    )));
  }
  return variable_get($name, isset($default) || !isset($defaults[$name]) ? $default : $defaults[$name]);
}

Functions

Namesort descending Description
statistics_advanced_boot Implementation of hook_boot().
statistics_advanced_exit Implementation of hook_exit().
statistics_advanced_link_alter Implementation of hook_link_alter().
statistics_advanced_menu Implementation of hook_menu().
statistics_advanced_perm Implementation of hook_perm().
statistics_advanced_user_access A copy of user.module's user_access() function so we do not need to load the entire user.module during cached visits.
statistics_advanced_var Internal implementation of variable_get().
statistics_advanced_variables Internal default variables for statistics_advanced_var().
_statistics_advanced_check_link_alter Check that the correct parameters are passed to hook_link_alter().
_statistics_advanced_ignore Internal function to track which records to ignore.
_statistics_advanced_is_403_or_404
_statistics_advanced_is_node_visit Determine if this page request is a node visit (e.g. node/x).