authcache.module in Authenticated User Page Caching (Authcache) 6
Same filename and directory in other branches
Authenticated User Page Caching (and anonymous users, too!)
This file is limited to Drupal specific functions & hooks only.
See also
authcache.helpers.inc for cache helper functions
authcache.admin.inc for admin page functionality
File
authcache.moduleView source
<?php
/**
* @file
* Authenticated User Page Caching (and anonymous users, too!)
*
* This file is limited to Drupal specific functions & hooks only.
*
* @see authcache.helpers.inc for cache helper functions
* @see authcache.admin.inc for admin page functionality
*/
// Global variables
$is_page_authcache = FALSE;
$authcache_debug_info = NULL;
// Store reason for disabling page caching
// Default caching rules (Never cache these pages)
define('AUTHCACHE_NOCACHE_DEFAULT', 'admin*
user*
node/add/*
node/*/edit
node/*/track
tracker*
comment/edit*
civicrm*
cart*
filefield/*');
// Default non-HTML caching rules (don't append JS to page content)
define('AUTHCACHE_NONHTML_DEFAULT', 'robots.txt');
// Functions specifically for caching
require dirname(__FILE__) . '/authcache.helpers.inc';
/**
* Implements hook_menu().
*/
function authcache_menu() {
$items['admin/settings/performance/drupal'] = array(
'title' => t('Drupal core'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
'page callback' => 'drupal_goto',
// For admin_menu.module
'page arguments' => array(
'admin/settings/performance',
),
'access arguments' => array(
'administer site configuration',
),
);
$items['admin/settings/performance/authcache/config'] = array(
'title' => 'Configuration',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/settings/performance/authcache'] = array(
'title' => 'Authcache',
'description' => "Configure Authcache.",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'authcache_admin_config',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'authcache.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 10,
);
$items['admin/settings/performance/authcache/pagecaching'] = array(
'title' => t('Page caching settings'),
'description' => "Configure page cache settings.",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'authcache_admin_pagecaching',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'authcache.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 20,
);
$items['admin/settings/performance/authcache/blocks'] = array(
'title' => t('Blocks'),
'description' => "View Authcache blocks.",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'authcache_admin_blocks',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'authcache.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 30,
);
/* @todo
$items['admin/settings/performance/authcache/advanced'] = array(
'title' => 'Advanced Rulesets',
'description' => "Configure advanced caching rulesets.",
'page callback' => 'drupal_get_form',
'page arguments' => array('authcache_admin_advanced'),
'access arguments' => array('administer site configuration'),
'file' => 'authcache.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 30,
);
$items['admin/settings/performance/authcache/lookup'] = array(
'title' => t('View Cached'),
'page callback' => 'authcache_admin_lookup',
'access arguments' => array('administer site configuration'),
'file' => 'authcache.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 30,
);
*/
$items['authcache/ahah'] = array(
'title' => 'Javascript Ahah Callback',
'page callback' => 'authcache_ahah',
'access arguments' => array(
'administer site configuration',
),
'file' => 'authcache.admin.inc',
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_init().
*/
function authcache_init() {
global $user, $is_page_authcache;
// Pressflow compatibility (since Pressflow doesn't set this cookie)
if (($sysblock = system_block('list')) && $sysblock[0]['info'] == t('Powered by Pressflow')) {
drupal_add_js("document.cookie = 'has_js=1; path=/';", 'inline', 'header');
}
// Can this page request be cached?
if (_authcache_is_cacheable()) {
global $conf;
$is_page_authcache = TRUE;
// Don't allow format_date() to use the user's local timezone
$conf['configurable_timezones'] = FALSE;
// Authcache core JS
drupal_add_js(drupal_get_path('module', 'authcache') . '/authcache.js', 'module', 'header');
// Add JS for debug mode?
if (variable_get('authcache_debug_all', FALSE) || $user->uid && ($debug_users = variable_get('authcache_debug_users', array()))) {
drupal_add_js(drupal_get_path('module', 'authcache') . '/authcache.debug.js', 'module', 'header');
// Also see authcache_authcache_info() for user debug settings
}
// Initialize Authcache after all other JS is loaded
drupal_add_js('jQuery(function() { Authcache.init(); });', 'inline', 'header');
// Force Ajax to use POST method instead of GET
if (variable_get('authcache_post', FALSE)) {
drupal_add_js(array(
'Authcache' => array(
'post' => 1,
),
), 'setting', 'header');
}
// Forcibly disable Drupal's built-in SQL caching
// (No need to cache page twice for anonymous users)
if (!$user->uid && variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
variable_set('cache', CACHE_DISABLED);
}
// Status messages prevent pages from being cached.
if (variable_get('authcache_debug_page', FALSE)) {
drupal_set_message(t('Caching disabled by') . ' ' . l('Authcache Config.', 'admin/settings/performance/authcache/config'));
}
register_shutdown_function('_authcache_shutdown_save_page');
ob_start();
}
// Remove debug cookies
if (isset($_COOKIE['authcache_debug']) && !variable_get('authcache_debug_all', FALSE)) {
if (!$user->uid || !in_array($user->name, variable_get('authcache_debug_users', array()))) {
setcookie('authcache_debug', "", time() - 84000);
// Delete JS cookie
setcookie('authcache_debug', "", time() - 84000, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
setcookie('nocache', 1, 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
setcookie('nocache_temp', 1, 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
}
}
/**
* Implements hook_user().
*/
function authcache_user($op, &$edit, &$account, $category = NULL) {
global $is_page_authcache;
// Cookie expiration
$expires = ini_get('session.cookie_lifetime');
$expires = !empty($expires) && is_numeric($expires) ? time() + (int) $expires : 0;
$no_cache = isset($account->uid) && ($account->uid == 1 || !isset($_COOKIE['has_js']));
switch ($op) {
case 'load':
break;
case 'update':
break;
case 'insert':
break;
case 'register':
break;
case 'login':
if ($no_cache) {
setcookie('nocache', 1, $expires, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
else {
setcookie('authcache', _authcache_key($account), $expires, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
// Authcache debugging
if (in_array($account->name, variable_get('authcache_debug_users', array()))) {
setcookie('authcache_debug', 1, $expires, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
// Required to differentiate from anonymous users
setcookie('drupal_user', $account->name, $expires, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
setcookie('drupal_uid', $account->uid, $expires, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
break;
case 'logout':
// Note: include same cookie deletion in ajax/authcache.module
setcookie('drupal_user', "", time() - 86400, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
setcookie('drupal_uid', "", time() - 86400, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
setcookie('authcache', "", time() - 86400, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
if (isset($_COOKIE['nocache'])) {
setcookie('nocache', "", time() - 86400, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
if (isset($_COOKIE['authcache_debug'])) {
setcookie('authcache_debug', "", time() - 86400, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
foreach ($_COOKIE as $key => $value) {
if (substr($key, 0, 3) == 'nid') {
setcookie($key, "", time() - 86400, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
}
break;
default:
break;
}
}
/**
* Implements hook_nodeapi().
*/
function authcache_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
global $user, $is_page_authcache;
if ($is_page_authcache) {
switch ($op) {
case 'load':
// Don't cache poll results
if ($node->type == 'poll') {
if (user_access('vote on polls') && $node->active) {
$node->allowvotes = TRUE;
}
}
break;
}
}
}
/**
* Implements hook_form_alter(),
*/
function authcache_form_alter(&$form, $form_state, $form_id) {
global $user, $is_page_authcache, $is_ajax_authcache;
// Forms for logged-in users
if ($user->uid && $is_page_authcache) {
// Remove user-specific form token
if (isset($form['form_token'])) {
if (isset($form['form_token']['#default_value'])) {
$form['form_token']['#default_value'] = '';
}
if (isset($form['form_token']['#value'])) {
$form['form_token']['#value'] = '';
}
}
// Token will be generated via ajax_authcache.php, but correct id is needed
$form['form_token_id'] = array(
'#type' => 'hidden',
'#value' => isset($form['#token']) ? $form['#token'] : $form_id,
);
// Views exposed form (Views uses custom form rendering functions)
if (isset($form['#theme']) && is_array($form['#theme']) && in_array('views_exposed_form', $form['#theme'])) {
unset($form['#token']);
// Prevents validation error
unset($form['form_token_id']);
}
// Modify specific forms
switch ($form_id) {
// Don't cache name on comment form
case 'comment_form':
$form['_author']['#value'] = '<span class="authcache-user"></span>';
break;
// Remove default values on contact form (hook_authcache_ajax will retrieve defaults)
case 'contact_mail_page':
$form['name']['#default_value'] = $form['mail']['#default_value'] = '';
$form['form_token_id']['#value'] = 'contact_mail_page';
// Don't use user's email
break;
// /user/%uid/contact URL shouldn't even be cached
case 'contact_mail_user':
break;
}
}
// Anonymous & authenticatd cacheable forms
if ($is_page_authcache) {
// Forms won't be cached on cached pages, so no need for build ids
unset($form['form_build_id']);
unset($form['#build_id']);
}
// Forms being rendered during Ajax phase
if ($is_ajax_authcache) {
$form['#action'] = "";
}
if ($is_page_authcache || $is_ajax_authcache) {
switch ($form_id) {
// poll vote/results form may be ajax; must keep track of submit for cache invalidation
case 'poll_view_voting':
$form['vote']['#submit'][] = 'authcache_form_submit';
break;
case 'poll_cancel_form':
$form['submit']['#submit'][] = 'authcache_form_submit';
break;
}
}
// Alter all forms
switch ($form_id) {
// Alter Drupal's "Performance" admin form
case 'system_performance_settings':
$form['page_cache']['#description'] .= ' <strong>' . t('If Authcache is enabled for the "anonymous user" role, Drupal\'s built-in page caching will be automatically disabled since all page caching is done through Authcache API instead of Drupal core.') . '</strong>';
if (_authcache_is_account_cacheable(drupal_anonymous_user())) {
$form['page_cache']['cache']['#options'] = array(
0 => t('Disabled') . ' ' . t('by') . ' Authcache',
);
}
break;
case 'user_profile_form':
// Don't allow user local timezone
if (_authcache_is_account_cacheable()) {
unset($form['timezone']);
}
break;
case 'block_add_block_form':
case 'block_admin_configure':
$authcache_block = variable_get('authcache_block', array());
$block_id = "{$form['module']['#value']}-{$form['delta']['#value']}";
$form['block_settings']['#weight'] = -10;
$form['authcache_settings'] = array(
'#type' => 'fieldset',
'#title' => t('Authcache Ajax'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#weight' => -5,
);
$form['authcache_settings']['authcache'] = array(
'#type' => 'checkbox',
'#title' => t('Load block with Ajax on cached Authcache pages'),
'#description' => t('This is useful for dynamic or user-centric content, however it places additional load on the server.'),
'#default_value' => isset($authcache_block[$block_id]),
);
$form['authcache_settings']['authcache_lifetime'] = array(
'#type' => 'textfield',
'#title' => t('Minimum cache lifetime'),
'#description' => t('Enter the number of seconds to locally cache the block in the user\'s browser. This improves performance and prevents jumpiness.'),
'#field_suffix' => t('seconds'),
'#size' => 8,
'#default_value' => isset($authcache_block[$block_id]) ? $authcache_block[$block_id] : 0,
);
$form['#submit'][] = 'authcache_block_submit';
break;
}
}
/**
* Generic form submit handler.
*
* Set nid cookie for cache invalidation (e.g., poll node)
*/
function authcache_form_submit(&$form, &$form_state) {
$nid = FALSE;
if (isset($form['#node']) && $form['#node']->type == 'poll') {
$nid = $form['#node']->nid;
}
if (isset($form['#nid'])) {
$nid = $form['#nid'];
}
if ($nid) {
setcookie('nid' . $nid, time(), 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
}
/**
* Block submit handler.
*/
function authcache_block_submit(&$form, &$form_state) {
$module = $form_state['values']['module'];
$delta = $form_state['values']['delta'];
// Adding block
if ($module == 'block' && !$delta) {
$delta = db_result(db_query("SELECT bid FROM {boxes} WHERE info = '%s' ORDER BY bid DESC", $form_state['values']['info']));
}
$block_id = "{$module}-{$delta}";
$authcache_block = variable_get('authcache_block', array());
if (!$form_state['values']['authcache']) {
unset($authcache_block[$block_id]);
}
else {
$lifetime = trim($form_state['values']['authcache_lifetime']);
$authcache_block[$block_id] = is_numeric($lifetime) ? $lifetime : 0;
}
variable_set('authcache_block', $authcache_block);
}
/**
* Implements hook_link_alter().
*/
function authcache_link_alter(&$links, $node) {
global $is_page_authcache;
if ($is_page_authcache) {
// Remove number of new/unread comments for user
unset($links['comment_new_comments']);
}
}
/**
* Implements hook_exit().
*
* Called on drupal_goto() redirect.
* Make sure status messages show up, if applicable.
*/
function authcache_exit($destination = NULL) {
global $is_page_authcache;
if ($destination !== NULL) {
$is_page_authcache = FALSE;
// Cookie will inform Authcache not to display cached page on next request
if (drupal_set_message()) {
setcookie('nocache', 1, 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
setcookie('nocache_temp', 1, 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure') == '1');
}
}
}
/**
* Implements hook_theme_registry_alter().
*/
function authcache_theme_registry_alter(&$theme_registry) {
// Best way to modify $links variable in comment view??
// (Since the comment.module preprocess function flatens $links array)
$theme_registry['comment_view']['function'] = 'theme_authcache_comment_view';
}
//
// Preprocess functions
//
/**
* Process all template variables
*/
function authcache_preprocess(&$variables, $hook) {
global $is_page_authcache;
// Define variables for templates files
$variables['is_page_authcache'] = $is_page_authcache;
if ($variables['user']->uid) {
$variables['user_name'] = $is_page_authcache ? '<span class="authcache-user"></span>' : $variables['user']->name;
$variables['user_link'] = $is_page_authcache ? '<a href="" class="authcache-user-link">!username</a>' : l($variables['user']->name, "user", array(
'alias' => TRUE,
));
}
}
/**
* Process page template variables
*/
function authcache_preprocess_page(&$variables) {
global $is_page_authcache;
if ($variables['messages']) {
// Status message(s) found, do not cache page
$is_page_authcache = $variables['is_page_authcache'] = FALSE;
global $authcache_debug_info;
$authcache_debug_info = 'Status message displayed.';
}
// Required if tabs need to be generated dynamically
if ($is_page_authcache && $variables['logged_in']) {
$variables['tabs'] = '<span id="authcache-tabs">' . $variables['tabs'] . '</span>';
}
}
/**
* Process block template variables
*/
function authcache_preprocess_block(&$variables) {
if ($variables['is_page_authcache']) {
global $user;
$block = $variables['block'];
// User navigation block; use cookie for displaying username.
if ($block->module == 'user' && $user->uid && $user->name == $block->subject) {
$variables['block']->subject = '<span class="authcache-user"></span>';
}
// Authcache Blocks
$authcache_block = variable_get('authcache_block', array());
$block_id = "{$block->module}-{$block->delta}";
if (isset($authcache_block[$block_id])) {
$data = array();
// Max-age (local caching)
if ($authcache_block[$block_id]) {
$data[] = 'data-block-cache="' . $authcache_block[$block_id] . '"';
}
$data[] = 'data-block-cid="' . _block_get_cache_id($block) . '"';
$variables['block']->subject = '<span id="authcache-block-subj-' . $block_id . '">' . $variables['block']->subject . '</span>';
$variables['block']->content = '<div id="authcache-block-' . $block_id . '" class="authcache-block" ' . implode(' ', $data) . '></div>';
}
}
}
/**
* Process comment template variables
*
* @see comment.module
* Remove "new"
*/
function authcache_preprocess_comment_folded(&$variables) {
if ($variables['is_page_authcache']) {
$variables['new'] = '';
}
}
/**
* Process comment template variables
*
* @see comment.module
* Replace "new" marker with empty span containing timestamp info
* Add "edit" uid span for JS phase
*/
function authcache_preprocess_comment(&$variables) {
// Will use Ajax to determine whether to display "new" marker for user
if ($variables['is_page_authcache']) {
$variables['new'] = '<span class="authcache-comment-new" data-timestamp="' . $variables['comment']->timestamp . '"></span>';
// These comments are still editable
if ($variables['user']->uid && comment_num_replies($variables['comment']->cid) == 0) {
$variables['links'] .= '<span class="authcache-comment-edit" data-comment-uid="' . $variables['comment']->uid . '" data-comment-id="' . $variables['comment']->cid . '"></span>';
}
}
}
/**
* Process forum template variables
*
* @see forum.module
* Remove "new" marker
*/
function authcache_preprocess_forum_list(&$variables) {
// Will use Ajax to determine whether to display "new" marker for user
if ($variables['is_page_authcache']) {
foreach ($variables['forums'] as $id => $forum) {
if ($variables['user']->uid) {
if ($forum->num_topics) {
$forum->num_topics .= '<span class="authcache-topic-new" data-forum-id="' . $id . '"></span>';
}
}
$variables['forums'][$id]->new_text = '';
$variables['forums'][$id]->new_url = '';
}
}
}
/**
* Process forum template variables
*
* @see forum.module
* Remove "new" marker
*/
function authcache_preprocess_forum_topic_list(&$variables) {
if ($variables['is_page_authcache']) {
if (!empty($variables['topics'])) {
foreach ($variables['topics'] as $id => $topic) {
$nid = $variables['topics'][$id]->nid;
// Replace "new" icons. If you are using custom icons, make sure
// the filenames have the same format as Drupal core
$icon = str_replace('hot-new', 'hot', $variables['topics'][$id]->icon);
$icon = str_replace('new', 'default', $variables['topics'][$id]->icon);
$variables['topics'][$id]->icon = '<span class="authcache-topic-icon" data-nid="' . $nid . '">' . $icon . '</span>';
$variables['topics'][$id]->title .= '<span class="authcache-topic-info" data-timestamp="' . $variables['topics'][$id]->timestamp . '" data-nid="' . $nid . '"></span>';
// "New" reply count will be handle via Ajax
if ($topic->num_comments) {
$variables['topics'][$id]->num_comments .= '<span class="authcache-topic-replies" data-nid="' . $nid . '"></span>';
$variables['topics'][$id]->new_text = '';
$variables['topics'][$id]->new_url = '';
}
}
}
}
}
/**
* Save poll node id
*
* @see poll.module
*/
function authcache_preprocess_poll_vote(&$variables) {
if ($variables['is_page_authcache']) {
$variables['choice'] .= '<span class="authcache-poll" data-nid="' . $variables['form']['#node']->nid . '"></span>';
}
}
//
// Theme overrides
//
/**
* Overridden to handle $links
* @see comment.module
*/
function theme_authcache_comment_view($comment, $node, $links = array(), $visible = TRUE) {
global $user, $is_page_authcache;
if ($user->uid && $is_page_authcache && isset($links['comment_edit'])) {
unset($links['comment_edit']);
}
return theme_comment_view($comment, $node, $links, $visible);
}
//
// Authcache hooks
//
/**
* Implements hook_authcache_ajax().
*
* Modifies footer JSON for Ajax call.
*/
function authcache_authcache_ajax() {
global $user;
$authcache_ajax = array();
$node = arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == '' ? node_load(arg(1)) : FALSE;
// @see node.module
if ($user->uid && $node) {
$authcache_ajax['node_history'] = arg(1);
}
// @see contact.module
if ($user->uid && arg(0) == 'contact') {
$authcache_ajax['contact'] = 1;
}
// @see statistics.module
if (module_exists('statistics')) {
if ($node && variable_get('statistics_count_content_views', 0)) {
$authcache_ajax['statistics'] = 1;
}
if (variable_get('statistics_enable_access_log', 0) && module_invoke('throttle', 'status') == 0) {
$authcache_ajax['statistics'] = 1;
}
}
return $authcache_ajax;
}
/**
* Implements hook_authcache_info().
*
* Modifies footer JSON for JavaScript info.
*/
function authcache_authcache_info() {
global $user;
$authcache_info = array();
$node = arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == '' ? node_load(arg(1)) : FALSE;
if ($node) {
if ($user->uid) {
$authcache_info['node_author'] = $node->name;
}
// Comment functionality for users
if ($user->uid && $node->comment_count) {
$authcache_info['t']['new'] = t('new');
// "new" marker
$authcache_info['t']['edit'] = t('edit');
// "edit" marker
$authcache_info['comment_usertime'] = node_last_viewed($node->nid);
// For first page request and to inform JS phase that comments exist
}
}
// Debug mode by user only
if (!variable_get('authcache_debug_all', FALSE) && $user->uid && ($debug_users = variable_get('authcache_debug_users', array()))) {
$authcache_info['debug_users'] = $debug_users;
}
return $authcache_info;
}
Functions
Constants
Name | Description |
---|---|
AUTHCACHE_NOCACHE_DEFAULT | |
AUTHCACHE_NONHTML_DEFAULT |