better_statistics.module in Better Statistics 7
Same filename and directory in other branches
Drupal hook implementations for the Better Statistics module.
File
better_statistics.moduleView source
<?php
/**
* @file
* Drupal hook implementations for the Better Statistics module.
*/
/**
* Current and minimum Statistics API version. For internal use only!
*/
define('BETTER_STATISTICS_API_VERSION', 1.0);
define('BETTER_STATISTICS_API_MIN_VERSION', 1.0);
/**
* Accesslog mode-type constants.
*/
define('BETTER_STATISTICS_ACCESSLOG_DISABLED', 0);
define('BETTER_STATISTICS_ACCESSLOG_DEFAULT', 1);
define('BETTER_STATISTICS_ACCESSLOG_MIXED', 2);
define('BETTER_STATISTICS_ACCESSLOG_CLIENT', 3);
/**
* Content counter mode constants.
*/
define('BETTER_STATISTICS_ENTITY_VIEW_DISABLED', 0);
define('BETTER_STATISTICS_ENTITY_VIEW_DEFAULT', 1);
define('BETTER_STATISTICS_ENTITY_VIEW_CLIENT', 2);
/**
* Implements hook_menu().
*/
function better_statistics_menu() {
$items['statistics/ajax/%'] = array(
'title' => 'Statistics AJAX handler',
'page callback' => 'better_statistics_ajax_handler',
'file' => 'better_statistics.ajax.inc',
'page arguments' => array(
2,
),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_menu_alter().
*
* Redirects core's link to access log details page.
*/
function better_statistics_menu_alter(&$items) {
$items['admin/reports/access/%']['page callback'] = 'better_statistics_access_log';
$items['admin/reports/access/%']['module'] = 'better_statistics';
$items['admin/reports/access/%']['file'] = 'better_statistics.admin.inc';
}
/**
* Implements hook_schema_alter().
*
* Reflects the customizations chosen by the user in the schema.
*/
function better_statistics_schema_alter(&$schema) {
// Fetch the fields defined by this module.
$fields = variable_get('better_statistics_fields', better_statistics_get_default_fields());
// For each field defined, add it to the schema.
foreach ($fields as $field => $data) {
$schema['accesslog']['fields'][$field] = $data['schema'];
}
}
/**
* Implements hook_module_implements_alter().
*
* Ensures this module fires before Statistics' exit hook so that we don't
* double up on data. Note we're not removing Statistics' hook entirely because
* Statistics also handles node count data.
*/
function better_statistics_module_implements_alter(&$implementations, $hook) {
if ($hook == 'exit') {
// Ensure our hook fires before statistics.
$implementations['better_statistics'] = -1;
}
}
/**
* Implements hook_ctools_plugin_api_hook_name().
*
* Report to CTools that we use hook_statistics_api instead of
* hook_ctools_plugin_api().
*/
function better_statistics_ctools_plugin_api_hook_name() {
return 'statistics_api';
}
/**
* Implements hook_exit().
*
* Gathers additional data and inserts our data into accesslog.
*/
function better_statistics_exit() {
// If statistics access log is set to run, run our code.
$mode = variable_get('statistics_enable_access_log', BETTER_STATISTICS_ACCESSLOG_DISABLED);
$default_mode = $mode == BETTER_STATISTICS_ACCESSLOG_DEFAULT;
$mixed_mode = $mode == BETTER_STATISTICS_ACCESSLOG_MIXED;
$client_mode = $mode == BETTER_STATISTICS_ACCESSLOG_CLIENT;
if ($default_mode || $mixed_mode) {
// Force a recheck on cache status, just in case this function was called
// before the X-Drupal-Cache header was set.
$cache_status = better_statistics_served_from_cache(TRUE);
// @see statistics_exit()
drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
include_once DRUPAL_ROOT . '/includes/unicode.inc';
// Load all relevant module.statistics.inc files for hook invocations.
better_statistics_load_active_incs();
// Allow all modules to react before logging begins.
module_invoke_all('better_statistics_prelog');
// Log the request if the request is loggable.
if (better_statistics_request_is_loggable()) {
// Get all declared fields and their computed values.
$fields = better_statistics_get_fields_data();
// Allow modules to log statistics data.
module_invoke_all('better_statistics_log', $fields);
}
}
// Keep statistics from writing additional data to the accesslog.
global $conf;
$conf['statistics_enable_access_log'] = FALSE;
// If client-side or mixed-mode are set, disable server-side node counter.
if ($mixed_mode || $client_mode) {
$conf['statistics_count_content_views'] = FALSE;
}
}
/**
* Implements hook_preprocess_html().
*
* If accesslog mode is set to client-side only or mixed mode, we add JS when
* HTML output is being rendered so that client-side data collection can be used
* for access statistics and/or node hit statistics.
*/
function better_statistics_preprocess_html(&$vars) {
$default_js = '';
$bs = variable_get('better_statistics_bs_var', 'bs');
$bs_path = drupal_get_path('module', 'better_statistics');
// If the accesslog is set to client-side or mixed mode, do some processing.
$al_mode = variable_get('statistics_enable_access_log', BETTER_STATISTICS_ACCESSLOG_DISABLED);
if ($al_mode == BETTER_STATISTICS_ACCESSLOG_MIXED || $al_mode == BETTER_STATISTICS_ACCESSLOG_CLIENT) {
// Grab all declared JS from Better Statistics fields and add them here
// at the JS_DEFAULT group level in such a way that they are all
// aggregated together.
$fields = variable_get('better_statistics_fields', better_statistics_get_default_fields());
$options = array(
'scope' => 'header',
'group' => JS_DEFAULT,
'every_page' => FALSE,
);
foreach ($fields as $field) {
if (isset($field['js']['data'])) {
$data = $field['js']['data'];
unset($field['js']['data']);
drupal_add_js($data, array_merge($field['js'], $options));
}
}
// Add BS JS API accesslog method. Note that internal path should always
// be the same for a page even when cached, so we manually add it here.
$default_js .= $bs . "('set', 'path', '" . truncate_utf8($_GET['q'], 255) . "'); ";
$default_js .= $bs . "('accesslog'); ";
// Prevent access statistics from being collected in hook_exit() so that
// duplicate statistics are never gathered when in mixed mode.
global $conf;
$conf['statistics_enable_access_log'] = BETTER_STATISTICS_ACCESSLOG_DISABLED;
}
// If enabled, add code to JS API implementation for the node counter.
// @see statistics_exit()
$ev_mode = variable_get('statistics_count_content_views', BETTER_STATISTICS_ENTITY_VIEW_DISABLED);
if ($ev_mode == BETTER_STATISTICS_ENTITY_VIEW_CLIENT) {
// We are counting content views.
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == NULL) {
// Add a call to the method.
$default_js .= $bs . "('entity_view', 'node', " . arg(1) . "); ";
// Prevent the node counter from being triggered in hook_exit() so that
// duplicate statistics are never gathered when in mixed mode.
global $conf;
$conf['statistics_count_content_views'] = FALSE;
}
}
// If any BS JS API methods have been added to the request, add the API.
$api_added = better_statistics_add_js_api();
// Add a default implementation of the BS JS API.
if ($api_added && !empty($default_js)) {
$default_js = preg_replace('!\\s+!', ' ', $default_js);
drupal_add_js($default_js, array(
'type' => 'inline',
'group' => JS_THEME,
'weight' => 25,
));
}
}
/**
* Implements hook_views_api().
*/
function better_statistics_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'better_statistics') . '/views',
);
}
/**
* Implements hook_flush_caches().
*
* When all caches are flushed, detect any updates we need to make on active
* fields. This is implemented not to declare a cache table for flushing, but to
* react when all caches are flushed.
*/
function better_statistics_flush_caches() {
module_load_include('inc', 'better_statistics', 'better_statistics.admin');
_better_statistics_update_fields();
_better_statistics_update_methods();
return array();
}
/**
* Implements hook_form_FORM_ID_alter().
*
* @see _better_statistics_form_statistics_settings_form_alter()
*/
function better_statistics_form_statistics_settings_form_alter(&$form, &$form_state, $form_id) {
module_load_include('inc', 'better_statistics', 'better_statistics.admin');
_better_statistics_form_statistics_settings_form_alter($form, $form_state, $form_id);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* @see _better_statistics_form_system_performance_settings_form_alter()
*/
function better_statistics_form_system_performance_settings_alter(&$form, &$form_state, $form_id) {
module_load_include('inc', 'better_statistics', 'better_statistics.admin');
_better_statistics_form_system_performance_settings_alter($form, $form_state, $form_id);
}
/**
* Submit handler for the Better Statistics configuration form.
*
* @see _better_statistics_settings_form_submit()
*/
function better_statistics_settings_form_submit($form, &$form_state) {
module_load_include('inc', 'better_statistics', 'better_statistics.admin');
_better_statistics_settings_form_submit($form, $form_state);
}
/**
* Implements hook_statistics_api().
*/
function better_statistics_statistics_api() {
return array(
// In your module, do not use this constant!
'api' => BETTER_STATISTICS_API_VERSION,
'path' => drupal_get_path('module', 'better_statistics'),
);
}
/**
* Returns default fields in the exact same form expected of fields exposed via
* hook_better_statistics_fields().
*/
function better_statistics_get_default_fields() {
static $default_fields;
if (!isset($default_fields)) {
// In some contexts, drupal_get_schema_unproccessed may be unavailable, so
// here, we get the accesslog schema the same way it does.
require_once DRUPAL_ROOT . '/modules/statistics/statistics.install';
$schema = module_invoke('statistics', 'schema');
// Expose Drupal Core's accesslog fields to our own API.
$accesslog = $schema['accesslog'];
foreach ($accesslog['fields'] as $field => $schema) {
$default_fields[$field] = array(
'schema' => $schema,
'views_field' => FALSE,
'callback' => 'better_statistics_get_field_value',
'js' => array(
'data' => dirname(drupal_get_filename('module', 'better_statistics')) . '/js/fields/core.js',
'type' => 'file',
),
);
}
}
return $default_fields;
}
/**
* Returns data for all valid, declared fields in preparation for DB insertion.
*/
function better_statistics_get_fields_data() {
$fields = variable_get('better_statistics_fields', better_statistics_get_default_fields());
$field_data = array();
// Loop through all custom fields.
foreach ($fields as $field => $data) {
// Only insert data into this field if there's a callback to get data.
if (is_callable($data['callback'])) {
$field_data[$field] = $data['callback']($field);
}
}
// The AID field should be removed in order to auto-increment.
unset($field_data['aid']);
return $field_data;
}
/**
* Loads module.statistics.inc files for all active fields.
*/
function better_statistics_load_active_incs() {
// Prevent this function from loading it multiple times.
static $loaded;
if (empty($loaded)) {
$loaded = TRUE;
// Include this module's API file because of how core fields are implemented.
require_once dirname(__FILE__) . '/better_statistics.statistics.inc';
// Loop through each file stored in the active incs list and include it.
// These are stored in a variable rather than being assembled dynamically
// due to the nature of cached pages and their bootstrap status.
$incs = variable_get('better_statistics_active_incs', array());
foreach ($incs as $file) {
// If there's an inc file and it hasn't been loaded yet, load it.
if (file_exists($file)) {
require_once $file;
}
}
}
}
/**
* Determines the loggability of the current request.
*
* @return
* TRUE if the current page can be logged, FALSE otherwise.
*/
function better_statistics_request_is_loggable($allow_logging = NULL) {
$allow_logging_static =& drupal_static(__FUNCTION__, TRUE);
if (isset($allow_logging)) {
$allow_logging_static = $allow_logging;
}
return $allow_logging_static;
}
/**
* Determines whether or not the current request is being served from cache.
*
* Note that you may need to use === to determine the cache status of the page,
* because some requests will return NULL, which evaluates to FALSE.
*
* @param boolean $recheck
* (optional) If set to TRUE, cache status will be manually re-checked.
*
* @return
* A boolean TRUE if the page is being served from cache, FALSE if the page is
* cacheable, but not served from cache. NULL is returned in the case when the
* page was never cacheable to begin with.
*/
function better_statistics_served_from_cache($recheck = FALSE) {
static $cached;
if (!isset($cached) || $recheck) {
$headers = headers_list();
$hit = array_search('X-Drupal-Cache: HIT', $headers);
$miss = array_search('X-Drupal-Cache: MISS', $headers);
// If neither a hit nor miss header are present, the page wasn't cacheable
// to begin with. Return NULL.
if ($hit !== FALSE) {
$cached = TRUE;
}
elseif ($miss !== FALSE) {
$cached = FALSE;
}
else {
$cached = NULL;
}
}
return $cached;
}
/**
* Add the Better Statistics JS API file to the current request.
*
* @return
* TRUE if the BS JS API file was added successfully to the request, or FALSE
* if the API file was not added to the request.
*/
function better_statistics_add_js_api() {
static $js_added = FALSE;
$file =& drupal_static(__FUNCTION__, FALSE);
$methods = variable_get('better_statistics_methods', array());
// No need to run this if no methods have been added.
if (!empty($methods)) {
// If the file URI isn't yet known, attempt to build the file.
if (!$file) {
// Prepend the BS JS API init file to the methods array.
$bs_path = drupal_get_path('module', 'better_statistics');
array_unshift($methods, $bs_path . '/js/better_statistics.init.js');
// Append the BS JS API exec file to the methods array.
$methods[] = $bs_path . '/js/better_statistics.exec.js';
// Prepare all files for use in drupal_build_js_cache().
$js_files = array();
foreach ($methods as $file) {
$js_files[$file] = array(
'preprocess' => TRUE,
'data' => $file,
);
}
// Build the JS cache file.
$uri = drupal_build_js_cache($js_files);
// If the file was created successfully, generate the URL.
if ($uri) {
$file = file_create_url($uri);
}
}
// If the JS hasn't been added yet, add it to the request.
if (!$js_added) {
$bs = variable_get('better_statistics_bs_var', 'bs');
$js = preg_replace('!\\s+!', ' ', "(function(w,d,s,u,v,a,m) {w['BetterStatsObj'] = v;\n w[v] = w[v] ||\n function() {(w[v].q = w[v].q || []).push(arguments)};\n w[v]('set', 'i', 1 * new Date());\n w[v]('set', 'basePath', '" . base_path() . "');\n a=d.createElement(s),\n m=d.getElementsByTagName(s)[0];\n a.src=u;\n a.async=1;\n m.parentNode.insertBefore(a,m);})(window,document,'script','" . $file . "','" . $bs . "');");
drupal_add_js($js, array(
'type' => 'inline',
'scope' => 'header',
'group' => JS_LIBRARY,
'weight' => -20,
));
$js_added = TRUE;
}
}
return $js_added;
}
// Declare API compatibility on behalf of core modules:
if (!function_exists('node_statistics_api')) {
function node_statistics_api() {
$api = better_statistics_statistics_api();
$api['path'] = $api['path'] . '/modules';
return $api;
}
}
if (!function_exists('user_statistics_api')) {
function user_statistics_api() {
$api = better_statistics_statistics_api();
$api['path'] = $api['path'] . '/modules';
return $api;
}
}
if (!function_exists('taxonomy_statistics_api')) {
function taxonomy_statistics_api() {
$api = better_statistics_statistics_api();
$api['path'] = $api['path'] . '/modules';
return $api;
}
}
Functions
Constants
Name | Description |
---|---|
BETTER_STATISTICS_ACCESSLOG_CLIENT | |
BETTER_STATISTICS_ACCESSLOG_DEFAULT | |
BETTER_STATISTICS_ACCESSLOG_DISABLED | Accesslog mode-type constants. |
BETTER_STATISTICS_ACCESSLOG_MIXED | |
BETTER_STATISTICS_API_MIN_VERSION | |
BETTER_STATISTICS_API_VERSION | Current and minimum Statistics API version. For internal use only! |
BETTER_STATISTICS_ENTITY_VIEW_CLIENT | |
BETTER_STATISTICS_ENTITY_VIEW_DEFAULT | |
BETTER_STATISTICS_ENTITY_VIEW_DISABLED | Content counter mode constants. |