You are here

browscap.module in Browscap 5

Replacement for PHP's get_browser() function

File

browscap.module
View source
<?php

/**
 * @file
 * Replacement for PHP's get_browser() function
 */

/**
 * ******************** Drupal Hooks ************************
 */

/**
 * Implementation of hook_menu().
 *
 * @return array
 */
function browscap_menu($may_cache) {
  $items = array();
  $access = user_access('access administration pages');
  if ($may_cache) {

    // LOG PAGES
    $items[] = array(
      'path' => 'admin/logs/browscap',
      'title' => t('Browscap'),
      'description' => t('Browser-specific site statistics.'),
      'callback' => 'browscap_top_useragents',
      'callback arguments' => array(
        'all',
      ),
      'access' => $access,
      'weight' => 5,
    );
    $items[] = array(
      'path' => 'admin/logs/browscap/useragents',
      'title' => t('All user agents'),
      'access' => $access,
      'weight' => 1,
      'type' => MENU_DEFAULT_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/logs/browscap/browsers',
      'callback' => 'browscap_top_useragents',
      'callback arguments' => array(
        'browsers',
      ),
      'title' => t('Browsers'),
      'access' => $access,
      'weight' => 2,
      'type' => MENU_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/logs/browscap/crawlers',
      'callback' => 'browscap_top_useragents',
      'callback arguments' => array(
        'crawlers',
      ),
      'title' => t('Crawlers'),
      'access' => $access,
      'weight' => 3,
      'type' => MENU_LOCAL_TASK,
    );

    // SETTINGS PAGE
    $items[] = array(
      'path' => 'admin/settings/browscap',
      'title' => t('Browscap'),
      'description' => t('Enable browscap site statistics.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'browscap_settings',
      ),
      'access' => user_access('administer site configuration'),
    );
    $items[] = array(
      'path' => 'admin/settings/browscap/refresh',
      'title' => t('Browscap Refresh'),
      'callback' => 'browscap_refresh',
      'access' => user_access('administer site configuration'),
      'type' => MENU_CALLBACK,
    );
  }
  else {
    if (arg(0) == 'admin' && arg(1) == 'logs' && arg(2) == 'browscap' && arg(3) == 'useragent' && arg(4)) {

      // INFORMATION ABOUT A USERAGENT
      $items[] = array(
        'path' => 'admin/logs/browscap/useragent',
        'callback' => 'browscap_useragent_properties',
        'title' => 'Useragent details',
        'access' => $access,
        'weight' => 5,
        'type' => MENU_LOCAL_TASK,
      );
    }
  }
  return $items;
}

/**
 * Implementation of hook_exit().
 *
 * Keep tabs on browsers that visit
 */
function browscap_exit() {

  // If monitoring is enabled, record the browser
  if (variable_get('browscap_monitor', FALSE)) {
    $browser = browscap_get_browser();
    $browserstring = substr(trim($browser['parent']), 0, 255);
    if ($browserstring == '' or $browserstring == 'Default Browser') {
      $browserstring = trim($_SERVER['HTTP_USER_AGENT']);
    }
    db_query("UPDATE {browscap_statistics} SET counter = counter + 1, is_crawler=%d " . "WHERE parent='%s'", $browser['crawler'], $browserstring);

    // If we affected 0 rows, this is the first time we've seen this browser
    if (!db_affected_rows()) {

      // We must create a new row to store counters for the new browser.
      db_query('INSERT INTO {browscap_statistics} (parent,counter,is_crawler) ' . "VALUES('%s', 1, %d)", $browserstring, $browser['crawler']);
    }
  }
}

/**
 * Implementation of hook_cron().
 */
function browscap_cron() {

  // Has it been a week since the last (attempt to) import?
  $last_imported = variable_get('browscap_imported', 0);
  if ($last_imported + 60 * 60 * 24 * 7 < time()) {
    _browscap_import();
    variable_set('browscap_imported', time());
  }
}

/**
 * ******************** Menu Callbacks ************************
 */

/**
 * Callback for settings form
 * Turn monitoring on or off
 *
 * @return array
 */
function browscap_settings() {
  $form['browscap_monitor'] = array(
    '#type' => 'checkbox',
    '#prefix' => t('<p>Browscap data current as of %fileversion. [<a href="!refresh">Refresh now</a>]</p>', array(
      '%fileversion' => variable_get('browscap_version', t('Never fetched')),
      '!refresh' => url('admin/settings/browscap/refresh'),
    )),
    '#title' => t('Monitor browsers'),
    '#default_value' => variable_get('browscap_monitor', FALSE),
    '#description' => t('Monitor all user agents visiting the site.'),
  );
  return system_settings_form($form);
}
function browscap_refresh() {
  _browscap_import(FALSE);
  drupal_goto('admin/settings/browscap');
}

/**
 * Menu callback; presents the user agents monitoring page.
 *
 * @param $view
 *   - "browsers": Only display "real" browsers
 *   - "crawlers": Only display search engine crawlers
 *   - "all": Display all user agents.
 */
function browscap_top_useragents($view = 'all') {
  if ($view == 'all') {
    $result = db_query('SELECT SUM(counter) FROM {browscap_statistics}');
    $total = db_result($result);
    if (!$total) {
      $total = 1;
    }
    $query = "SELECT parent,counter,(100*counter)/{$total} as percent,is_crawler FROM {browscap_statistics}";
    $query_cnt = 'SELECT COUNT(parent) FROM {browscap_statistics}';
    $title = t('Top user agents');
    $header = array(
      array(
        'data' => t('User agent'),
        'field' => 'parent',
      ),
      array(
        'data' => t('Count'),
        'field' => 'counter',
        'sort' => 'desc',
      ),
      array(
        'data' => t('Percent'),
        'field' => 'percent',
      ),
      array(
        'data' => t('Crawler?'),
        'field' => 'is_crawler',
      ),
    );
  }
  elseif ($view == 'browsers') {
    $result = db_query('SELECT SUM(counter) FROM {browscap_statistics} WHERE is_crawler=0');
    $total = db_result($result);
    if (!$total) {
      $total = 1;
    }
    $query = "SELECT parent,counter,(100*counter)/{$total} as percent FROM {browscap_statistics} WHERE is_crawler=0";
    $query_cnt = 'SELECT COUNT(parent) FROM {browscap_statistics} WHERE is_crawler=0';
    $title = t('Top browsers');
    $header = array(
      array(
        'data' => t('Browser'),
        'field' => 'parent',
      ),
      array(
        'data' => t('Count'),
        'field' => 'counter',
        'sort' => 'desc',
      ),
      array(
        'data' => t('Percent'),
        'field' => 'percent',
      ),
    );
  }
  else {
    $result = db_query('SELECT SUM(counter) FROM {browscap_statistics} WHERE is_crawler=1');
    $total = db_result($result);
    if (!$total) {
      $total = 1;
    }
    $query = "SELECT parent,counter,(100*counter)/{$total} as percent FROM {browscap_statistics} WHERE is_crawler=1";
    $query_cnt = 'SELECT COUNT(parent) FROM {browscap_statistics} WHERE is_crawler=1';
    $title = t('Top crawlers');
    $header = array(
      array(
        'data' => t('Crawler'),
        'field' => 'parent',
      ),
      array(
        'data' => t('Count'),
        'field' => 'counter',
        'sort' => 'desc',
      ),
      array(
        'data' => t('Percent'),
        'field' => 'percent',
      ),
    );
  }
  drupal_set_title($title);
  $query .= tablesort_sql($header);
  $result = pager_query($query, 50, 0, $query_cnt);
  while ($useragent = db_fetch_object($result)) {
    if (db_result(db_query_range('SELECT useragent FROM {browscap} WHERE useragent = "%s"', $useragent->parent, 0, 1))) {
      $parent = l($useragent->parent, 'admin/logs/browscap/useragent/' . urlencode($useragent->parent));
    }
    else {
      $parent = check_plain($useragent->parent);
    }
    if ($view == 'all') {
      if ($useragent->is_crawler) {
        $is_crawler = t('Yes');
      }
      else {
        $is_crawler = t('No');
      }
      $rows[] = array(
        $parent,
        $useragent->counter,
        $useragent->percent,
        $is_crawler,
      );
    }
    else {
      $rows[] = array(
        $parent,
        $useragent->counter,
        $useragent->percent,
      );
    }
  }
  if ($pager = theme('pager', NULL, 50, 0)) {
    $rows[] = array(
      array(
        'data' => $pager,
        'colspan' => 2,
      ),
    );
  }
  $output .= theme('table', $header, $rows);
  print theme('page', $output, $title);
}
function browscap_get_browser($useragent = NULL) {
  if (!$useragent) {
    $useragent = $_SERVER['HTTP_USER_AGENT'];
  }

  // Cache the results
  $cacheid = $useragent;
  $cache = cache_get($cacheid, 'cache_browscap');
  if (!empty($cache) and $cache->created > time() - 60 * 60 * 24) {

    // Found a fresh entry in the cache
    $browserinfo = unserialize($cache->data);
  }
  else {

    // Note the 'backwards' use of LIKE - the useragent column contains
    // the wildcarded pattern to match against our full-length string
    // The ORDER BY chooses the most-specific matching pattern
    $browserinfo = db_fetch_object(db_query_range("SELECT * from {browscap} WHERE '%s' LIKE useragent ORDER BY LENGTH(useragent) DESC", $useragent, 0, 1));

    // A couple of fieldnames not in our database, provided for
    // compatibility with PHP's get_browser()

    //$browserinfo->tables = $browserinfo->htmltables;
    cache_set($cacheid, 'cache_browscap', serialize($browserinfo));
  }
  $info = unserialize($browserinfo->data);
  $info['useragent'] = $useragent;
  $info['browser_name_pattern'] = strtr($browserinfo->useragent, '%_', '*?');
  return $info;
}

// A numeric interpretation of browscap.csv's TRUE/FALSE/default fields
function _browscap_boolean($value) {
  switch ($value) {
    case 'TRUE':
    case 'true':
      return 1;
    case 'FALSE':
    case 'false':
    case 'default':
    default:
      return 0;
  }
}

/**
 * If there's a new version of browscap.csv, fetch it and update the
 * database.
 */
function _browscap_import($cron = TRUE) {

  // Politely check the version for updates before fetching the file
  $versionpage = drupal_http_request('http://browsers.garykeith.com/versions/version-number.asp');
  if ($versionpage->error) {
    watchdog('browscap', t("Couldn't check version: ") . $versionpage->error);
    if (!$cron) {
      drupal_set_message(t("Couldn't check version: ") . $versionpage->error, 'error');
    }
    return;
  }
  $browscapversion = trim($versionpage->data);
  $oldversion = variable_get('browscap_version', 'Never fetched');
  if ($browscapversion == $oldversion) {

    // No update, nothing to do here
    watchdog('browscap', t('No new version of browscap to import'));
    if (!$cron) {
      drupal_set_message(t('No new version of browscap to import'));
    }
    return;
  }

  // Fetch the new version, and dump it in the temp directory
  $server = $_SERVER['SERVER_NAME'];
  $path = variable_get('file_directory_temp', '/tmp');
  $browscapfile = "{$path}/browscap_{$server}.ini";
  $browscap = drupal_http_request('http://browsers.garykeith.com/stream.asp?PHP_BrowsCapINI');
  if ($browscap->error or !trim($browscap->data)) {
    watchdog('browscap', t("Couldn't retrieve updated browscap: ") . $browscap->error);
    if (!$cron) {
      drupal_set_message(t("Couldn't retrieve updated browscap: ") . $browscap->error);
    }
    return;
  }
  $browscapfp = fopen($browscapfile, "w");
  fwrite($browscapfp, $browscap->data);
  fclose($browscapfp);
  $a = parse_ini_file($browscapfile, TRUE);
  if ($a) {

    // the first entry in the array is the version info
    $version = array_shift($a);
    foreach ($a as $key => $vals) {
      $e = $vals;

      // some recursive magic!
      $last_parent = array();
      while ($vals['Parent'] && $vals['Parent'] !== $last_parent) {
        $vals = $a[$vals['Parent']];
        $e = array_merge((array) $vals, (array) $e);
        $last_parent = $vals;
      }
      $useragent = strtr($key, '*?', '%_');
      $e = array_change_key_case($e);
      db_query("REPLACE INTO {browscap} (useragent, data) VALUES('%s','%s')", $useragent, serialize($e));
    }
    cache_clear_all('*', 'cache_browscap', TRUE);
    variable_set('browscap_version', $browscapversion);
    watchdog('browscap', t("New version of browscap imported: ") . $browscapversion);
    if (!$cron) {
      drupal_set_message(t("New version of browscap imported: ") . $browscapversion);
    }
  }
}

/*
 * Undo a recorded browser visit by request
 *
 * This function serves the statistics_filter module, enabling it
 * to ignore visits from specified roles.
 */
function browscap_unmonitor() {

  // No point if statistics aren't enabled
  if (!module_exists('statistics')) {
    return;
  }

  // If monitoring is enabled, unrecord the browser
  if (variable_get('browscap_monitor', FALSE)) {
    $browser = browscap_get_browser();
    $browserstring = trim($browser->parent);
    if ($browserstring == '' or $browserstring == 'Default Browser') {
      $browserstring = trim($_SERVER['HTTP_USER_AGENT']);
    }
    db_query("UPDATE {browscap_statistics} SET counter = counter - 1, is_crawler=%d " . "WHERE parent='%s'", $browser->crawler, $browserstring);
  }
}

/**
 * Page callback to show details about known useragents.
 *
 * @param string $useragent a useragent, taken from the url.
 * @return string an HTMl blob representing the data about this useragent.
 */
function browscap_useragent_properties($useragent = NULL) {
  drupal_set_title(check_plain(arg(4)));
  if ($useragent == NULL) {
    drupal_not_found();
    return;
  }
  $row = db_fetch_object(db_query('SELECT * FROM {browscap} WHERE useragent = "%s"', $useragent));
  if (!$row) {
    drupal_not_found();
    return;
  }
  $data = unserialize($row->data);
  $headers = array(
    t('property'),
    t('value'),
  );
  foreach ($data as $key => $val) {
    $rows[] = array(
      check_plain($key),
      check_plain($val),
    );
  }
  $output = theme('table', $headers, $rows);
  return $output;
}

Functions

Namesort descending Description
browscap_cron Implementation of hook_cron().
browscap_exit Implementation of hook_exit().
browscap_get_browser
browscap_menu Implementation of hook_menu().
browscap_refresh
browscap_settings Callback for settings form Turn monitoring on or off
browscap_top_useragents Menu callback; presents the user agents monitoring page.
browscap_unmonitor
browscap_useragent_properties Page callback to show details about known useragents.
_browscap_boolean
_browscap_import If there's a new version of browscap.csv, fetch it and update the database.