diff --git README.txt README.txt index 550a621..b953c06 100644 --- README.txt +++ README.txt @@ -27,6 +27,16 @@ browser types for visitors to your site. 5. The report itself is visible at Administer > Reports > Browscap and several other tabs in that area. +Upgrades +-------- + +Upgrading from 6.x to 7.x: + +* The upgrade path is untested so far. +* To view browscap reports in the 6.x module required the + "access administration pages" permission. The 7.x module requires the + more appropriate "access site reports" permission. + API --- diff --git browscap.info browscap.info index 3233728..cd3858c 100644 --- browscap.info +++ browscap.info @@ -1,3 +1,7 @@ name = Browscap description = "Provides statistics on browsers and a replacement for PHPs get_browser() function." -core = 6.x +core = 7.x +files[] = browscap.module +files[] = browscap.install +files[] = includes/admin.inc +configure = admin/reports/browscap/settings \ No newline at end of file diff --git browscap.install browscap.install index 1e4bfa1..a28d43d 100644 --- browscap.install +++ browscap.install @@ -1,13 +1,6 @@ 'int', 'size' => 'small', 'not null' => TRUE, 'default' => 0)); - } return $ret; } diff --git browscap.module browscap.module index 02b023c..e6e4a46 100644 --- browscap.module +++ browscap.module @@ -21,47 +21,50 @@ function browscap_menu() { 'description' => 'Browser-specific site statistics.', 'page callback' => 'browscap_top_useragents', 'page arguments' => array('all'), - 'access arguments' => array('access administration pages'), - 'weight' => 5); + 'access arguments' => array('access site reports'), + 'weight' => 5, + ); $items['admin/reports/browscap/useragents'] = array( 'title' => 'All user agents', - 'access arguments' => array('access administration pages'), + 'access arguments' => array('access site reports'), 'weight' => 1, - 'type' => MENU_DEFAULT_LOCAL_TASK + 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/reports/browscap/browsers'] = array( 'title' => 'Browsers', 'page callback' => 'browscap_top_useragents', 'page arguments' => array('browsers'), - 'access arguments' => array('access administration pages'), + 'access arguments' => array('access site reports'), 'weight' => 2, - 'type' => MENU_LOCAL_TASK + 'type' => MENU_LOCAL_TASK, ); $items['admin/reports/browscap/crawlers'] = array( 'title' => 'Crawlers', 'page callback' => 'browscap_top_useragents', 'page arguments' => array('crawlers'), - 'access arguments' => array('access administration pages'), + 'access arguments' => array('access site reports'), 'weight' => 3, - 'type' => MENU_LOCAL_TASK + 'type' => MENU_LOCAL_TASK, ); // SETTINGS PAGE - $items['admin/settings/browscap'] = array( - 'title' => 'Browscap', + $items['admin/reports/browscap/settings'] = array( + 'title' => 'Settings', 'description' => 'Enable browscap site statistics.', 'page callback' => 'drupal_get_form', 'page arguments' => array('browscap_settings'), 'access arguments' => array('administer site configuration'), + 'weight' => 100, + 'type' => MENU_LOCAL_TASK, ); $items['admin/reports/browscap/useragent/%browscap_useragent'] = array( - 'title' => 'Useragent details', + 'title' => 'Useragent details', 'page callback' => 'browscap_useragent_properties', 'page arguments' => array(4), - 'access arguments' => array('access administration pages'), + 'access arguments' => array('access site reports'), 'weight' => 5, - 'type' => MENU_LOCAL_TASK + 'type' => MENU_LOCAL_TASK, ); return $items; } @@ -75,18 +78,23 @@ function browscap_exit() { // If monitoring is enabled, record the browser if (variable_get('browscap_monitor', FALSE)) { if ($browser = browscap_get_browser()) { - $browserstring = substr(trim($browser['parent']), 0, 255); - if ($browserstring == '' or $browserstring == 'Default Browser') { + $browserstring = empty($browser['parent']) + ? 'Default Browser' + : substr(trim($browser['parent']), 0, 255); + if ($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']); + if (empty($browser['crawler'])) { + $browser['crawler'] = 0; } + db_merge('browscap_statistics') + ->key(array('parent' => $browserstring)) + ->fields(array( + 'counter' => 1, + 'is_crawler' => $browser['crawler'] ? 1 : 0, + )) + ->expression('counter', 'counter + 1') + ->execute(); } } } @@ -97,9 +105,9 @@ function browscap_exit() { 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()) { + if (($last_imported + 60*60*24*7) < REQUEST_TIME) { _browscap_import(); - variable_set('browscap_imported', time()); + variable_set('browscap_imported', REQUEST_TIME); } } @@ -114,6 +122,7 @@ function browscap_cron() { * @return array */ function browscap_settings() { + $version = variable_get('browscap_version', 0); $form['browscap_data_status'] = array( '#markup' => '

' . t('Current browscap data version: %fileversion.', array('%fileversion' => $version ? $version : t('Never fetched'))) . '

', @@ -138,7 +147,7 @@ function browscap_settings() { '#type' => 'submit', '#value' => t('Refresh browscap data'), ); - return system_settings_form($form); + return $form; } /** @@ -166,85 +175,87 @@ function browscap_settings_submit($form, &$form_state) { * - "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') - ); - } + $header = array( + 0 => array('data' => t('User agent'), 'field' => 'parent'), + 1 => array('data' => t('Count'), 'field' => 'counter', 'sort' => 'desc'), + 2 => array('data' => t('Percent'), 'field' => 'counter'), + 3 => array('data' => t('Crawler?'), 'field' => 'is_crawler') + ); + $query_total = db_select('browscap_statistics', 'bs'); + $query_total->addExpression('SUM(bs.counter)'); + + $query = db_select('browscap_statistics', 'bs') + ->fields('bs', array('parent', 'counter', 'is_crawler')) + ->extend('PagerDefault') + ->extend('TableSort') + ->limit(50) + ->orderByHeader($header); + + switch ($view) { + case 'browsers': + $title = t('Top browsers'); + $header[0]['data'] = t('Browser'); + unset($header[3]); + $query->condition('is_crawler', 0); + $query_total->condition('is_crawler', 0); + break; + + case 'crawlers': + $title = t('Top crawlers'); + $header[0]['data'] = t('Crawler'); + unset($header[3]); + $query->condition('is_crawler', 1); + $query_total->condition('is_crawler', 1); + break; + + default: + $title = t('Top user agents'); + break; + } drupal_set_title($title); - $query .= tablesort_sql($header); + $total = $query_total + ->execute() + ->fetchField(); + if (!$total) { + $total = 1; + } - $result = pager_query($query, 50, 0, $query_cnt); + $result = $query->execute(); $rows = array(); + foreach ($result as $useragent) { + $exists = (bool) db_query_range('SELECT 1 FROM {browscap} WHERE useragent = :useragent', 0, 1, + array(':useragent' => $useragent->parent)) + ->fetchField(); - 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/reports/browscap/useragent/'. urlencode($useragent->parent)); + if ($exists) { + $parent = l($useragent->parent, 'admin/reports/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); + $tablerow = array(); + $tablerow[] = $parent; + $tablerow[] = $useragent->counter; + $tablerow[] = round(100 * $useragent->counter / $total, 4); + if (!in_array($view, array('browsers', 'crawlers'))) { + $tablerow[] = $useragent->is_crawler ? t('Yes') : t('No'); } - else { - $rows[] = array($parent, $useragent->counter, $useragent->percent); - } - } - if ($pager = theme('pager', NULL, 50, 0)) { - $rows[] = array(array('data' => $pager, 'colspan' => 2)); + $rows[] = $tablerow; } - $output = ''; - if (empty($rows)) { - $output .= t('It appears that your site has not recorded any visits. If you want to record the visitors to your site you can enable "Monitor browsers" on the Browscap settings screen.', array('!settings_uri' => url('admin/settings/browscap'))); - } - $output .= theme('table', $header, $rows); + $build['browscap_statistics_table'] = array( + '#theme' => 'table', + '#header' => $header, + '#rows' => $rows, + '#empty' => t('It appears that your site has not recorded any visits. If you want to record the visitors to your site you can enable "Monitor browsers" on the Browscap settings screen.', array('!settings_uri' => url('admin/reports/browscap/settings'))), + ); + $build['browscap_statistics_pager'] = array( + '#theme' => 'pager', + ); - print theme('page', $output, $title); + return $build; } /** @@ -262,7 +273,7 @@ function browscap_get_browser($useragent = NULL) { // Cache the results $cacheid = $useragent; $cache = cache_get($cacheid, 'cache_browscap'); - if ((!empty($cache)) and ($cache->created > time() - 60*60*24)) { + if ((!empty($cache)) and ($cache->created > REQUEST_TIME - 60*60*24)) { // Found a fresh entry in the cache $browserinfo = $cache->data; } @@ -270,9 +281,10 @@ function browscap_get_browser($useragent = NULL) { // 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)); + $browserinfo = db_query( + "SELECT * FROM {browscap} WHERE ':useragent' LIKE useragent ORDER BY LENGTH(useragent) DESC", + array(':useragent' => $useragent)) + ->fetchObject(); // A couple of fieldnames not in our database, provided for // compatibility with PHP's get_browser() //$browserinfo->tables = $browserinfo->htmltables; @@ -287,7 +299,7 @@ function browscap_get_browser($useragent = NULL) { } /** - * Determine whether the current visitor + * Determine whether the current visitor is a bot. * * @param string $useragent * Optional user agent string. @@ -319,14 +331,14 @@ 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 (isset($versionpage->error)) { - watchdog('browscap', 'Couldn\'t check version: '. $versionpage->error); + watchdog('browscap', 'Couldn\'t check version: %error', array('%error' => $versionpage->error), WATCHDOG_ERROR); if (!$cron) { - drupal_set_message(t('Couldn\'t check version: ') . $versionpage->error, 'error'); + drupal_set_message(t('Couldn\'t check version: %error', array('%error' => $versionpage->error)), 'error'); } return; } $browscapversion = trim($versionpage->data); - $oldversion = variable_get('browscap_version', 'Never fetched'); + $oldversion = variable_get('browscap_version', 0); if ($browscapversion == $oldversion) { // No update, nothing to do here watchdog('browscap', 'No new version of browscap to import'); @@ -341,11 +353,12 @@ function _browscap_import($cron = TRUE) { $path = variable_get('file_directory_temp', '/tmp'); $browscapfile = "$path/browscap_$server.ini"; + // @todo This probably ought to be in a settings variable. $browscap = drupal_http_request('http://browsers.garykeith.com/stream.asp?PHP_BrowsCapINI'); if (isset($browscap->error) || empty($browscap)) { - watchdog('browscap', t("Couldn't retrieve updated browscap: ") . $browscap->error); + watchdog('browscap', "Couldn't retrieve updated browscap: %error", array('%error' => $browscap->error), WATCHDOG_ERROR); if (!$cron) { - drupal_set_message(t("Couldn't retrieve updated browscap: ") . $browscap->error); + drupal_set_message(t("Couldn't retrieve updated browscap: %error", array('%error' => $browscap->error)), 'error'); } return; } @@ -380,19 +393,26 @@ function _browscap_import($cron = TRUE) { } $useragent = strtr($key, '*?', '%_'); $e = array_change_key_case($e); - db_query("DELETE FROM {browscap} WHERE useragent = '%s'", $useragent); - db_query("INSERT INTO {browscap} (useragent, data) VALUES ('%s', %b)", $useragent, serialize($e)); + db_delete('browscap') + ->condition('useragent', $useragent) + ->execute(); + db_insert('browscap') + ->fields(array( + 'useragent' => $useragent, + 'data' => serialize($e) + )) + ->execute(); } cache_clear_all('*', 'cache_browscap', TRUE); variable_set('browscap_version', $browscapversion); - watchdog('browscap', 'New version of browscap imported: '. $browscapversion); + watchdog('browscap', 'New version of browscap imported: %version', array('%version' => $browscapversion)); if (!$cron) { - drupal_set_message(t('New version of browscap imported: ') . $browscapversion); + drupal_set_message(t('New version of browscap imported: %version', array('%version' => $browscapversion))); } } } - + /* * Undo a recorded browser visit by request * @@ -429,7 +449,11 @@ function browscap_useragent_load($useragent = NULL) { if (empty($useragent)) { return FALSE; } - $row = db_fetch_object(db_query("SELECT * FROM {browscap} WHERE useragent = '%s'", $useragent)); + $row = db_select('browscap', 'b') + ->fields('b') + ->condition('useragent', $useragent) + ->execute() + ->fetchObject(); if (!$row) { return FALSE; } @@ -441,14 +465,25 @@ function browscap_useragent_load($useragent = NULL) { * * @param $useragent * The useragent object, loaded from the database. - * @return string an HTMl blob representing the data about this useragent. + * @return array + * the data about this useragent. */ function browscap_useragent_properties($useragent = NULL) { - drupal_set_title(check_plain($useragent['browser'] .' '. $useragent['version'])); - $headers = array(t('Property'), t('Value')); + $useragent = (array)$useragent; + $header = array( + t('Property'), + t('Value'), + ); + $rows = array(); foreach ($useragent as $key => $val) { $rows[] = array(check_plain($key), check_plain($val)); } - $output = theme('table', $headers, $rows); - return $output; + $build = array( + '#theme' => 'table', + '#header' => $header, + '#rows' => $rows, + '#attributes' => array('id' => 'browscap-useragent'), + '#empty' => t('No useragent properties available.'), + ); + return $build; }