authcache_p13n.module in Authenticated User Page Caching (Authcache) 7.2
Provides methods for serving personalized content fragments.
File
modules/authcache_p13n/authcache_p13n.moduleView source
<?php
/**
* @file
* Provides methods for serving personalized content fragments.
*/
/**
* Implements hook_menu().
*/
function authcache_p13n_menu() {
$items['admin/config/system/authcache/p13n'] = array(
'title' => 'Personalization',
'description' => 'List markup substitution configuration objects',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'authcache_p13n_admin_markup_configs',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'authcache_p13n.admin.inc',
'type' => MENU_LOCAL_TASK,
);
$items['admin/config/system/authcache/p13n/markup-substitution'] = array(
'title' => 'Markup Substitution',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/config/system/authcache/p13n/frontcontroller'] = array(
'title' => 'Frontcontroller',
'description' => 'Rebuild request router for frontcontroller serving personalized fragments',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'authcache_p13n_admin_routes',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'authcache_p13n.admin.inc',
'type' => MENU_LOCAL_TASK,
);
$items['admin/config/system/authcache/p13n/frontcontroller/route'] = array(
'title' => 'Route Definition',
'description' => 'Show route definition for a given route',
'page callback' => 'authcache_p13n_admin_route_page',
'access arguments' => array(
'administer site configuration',
),
'file' => 'authcache_p13n.admin.inc',
'type' => MENU_LOCAL_TASK,
);
return $items;
}
/**
* Implements hook_authcache_request_exclude().
*/
function authcache_p13n_authcache_request_exclude() {
if (authcache_p13n_is_authcache_p13n_request()) {
return t('Authcache personalization');
}
}
/**
* @defgroup authcache_p13n_markup Markup substitution
* @{
* Replace personalized markup on cacheable pages for authenticated users.
*
* Functions and hook implementations in this group help with replacing
* personalized markup on cacheable pages.
*/
/**
* Return information about fragments implemented by other modules.
*/
function authcache_p13n_fragment_info() {
$info =& drupal_static(__FUNCTION__);
if (!isset($info)) {
$info = module_invoke_all('authcache_p13n_fragment');
drupal_alter('authcache_p13n_fragment', $info);
}
return $info;
}
/**
* Return information about settings implemented by other modules.
*/
function authcache_p13n_setting_info() {
$info =& drupal_static(__FUNCTION__);
if (!isset($info)) {
$info = module_invoke_all('authcache_p13n_setting');
drupal_alter('authcache_p13n_setting', $info);
}
return $info;
}
/**
* Return information about fragment assemblies implemented by other modules.
*/
function authcache_p13n_assembly_info() {
$info =& drupal_static(__FUNCTION__);
if (!isset($info)) {
$info = module_invoke_all('authcache_p13n_assembly');
drupal_alter('authcache_p13n_assembly', $info);
}
return $info;
}
/**
* Implements hook_theme().
*/
function authcache_p13n_theme() {
return array(
'authcache_p13n_fragment' => array(
'variables' => array(
'fragment' => '',
'param' => '',
'callback' => NULL,
'clients' => NULL,
'fallback' => 'hide',
'original' => NULL,
'attributes' => array(),
),
),
'authcache_p13n_setting' => array(
'variables' => array(
'setting' => '',
'param' => '',
'callback' => NULL,
'clients' => NULL,
'fallback' => 'hide',
),
),
'authcache_p13n_assembly' => array(
'variables' => array(
'assembly' => '',
'param' => '',
'callback' => NULL,
'clients' => NULL,
'fallback' => 'hide',
),
),
'authcache_p13n_partial' => array(
'variables' => array(
'assembly' => '',
'partial' => '',
'param' => '',
'clients' => NULL,
'fallback' => 'hide',
'original' => NULL,
'attributes' => array(),
),
),
'authcache_p13n_config_client_order' => array(
'render element' => 'element',
),
);
}
/**
* Preprocess a placeholder for a personalized fragment.
*/
function template_preprocess_authcache_p13n_fragment(&$variables) {
$fragment = $variables['fragment'];
$param = $variables['param'];
$clients = $variables['clients'];
$client = authcache_p13n_client_get_preferred('fragment', $fragment, $clients);
if ($client && strlen($fragment)) {
$url = authcache_p13n_request_get_callback('frag/' . $fragment, $param);
}
if (!empty($url)) {
$variables['theme_hook_suggestions'][] = 'authcache_p13n_fragment__' . $client;
$variables['theme_hook_suggestions'][] = 'authcache_p13n_fragment__' . $client . '__' . preg_replace('/_{2,}/', '_', preg_replace('/[^0-9a-z]/i', '_', $fragment));
$variables['client'] = $client;
$variables['url'] = $url;
}
}
/**
* Theme a placeholder for a personalized fragment.
*/
function theme_authcache_p13n_fragment($variables) {
$fragment = $variables['fragment'];
$param = $variables['param'];
$clients = $variables['clients'];
$fallback = $variables['fallback'];
$original = $variables['original'];
$context = array(
'type' => 'fragment',
'id' => $fragment,
'param' => $param,
'clients' => $clients,
'original' => $original,
);
$fallback_markup = '<!-- Error: Failed to render fragment -->';
drupal_alter('authcache_p13n_client_fallback', $fallback_markup, $fallback, $context);
return $fallback_markup;
}
/**
* Preprocess a placeholder for a personalized setting.
*/
function template_preprocess_authcache_p13n_setting(&$variables) {
$setting = $variables['setting'];
$param = $variables['param'];
$clients = $variables['clients'];
$client = authcache_p13n_client_get_preferred('setting', $setting, $clients);
if ($client && strlen($setting)) {
_authcache_p13n_array_unique_recursive($param);
$url = authcache_p13n_request_get_callback('setting/' . $setting, $param);
}
if (!empty($url)) {
$variables['theme_hook_suggestions'][] = 'authcache_p13n_setting__' . $client;
$variables['theme_hook_suggestions'][] = 'authcache_p13n_setting__' . $client . '__' . preg_replace('/_{2,}/', '_', preg_replace('/[^0-9a-z]/i', '_', $setting));
$variables['client'] = $client;
$variables['url'] = $url;
}
}
/**
* Theme a placeholder for a personalized setting.
*/
function theme_authcache_p13n_setting($variables) {
$setting = $variables['setting'];
$param = $variables['param'];
$clients = $variables['clients'];
$fallback = $variables['fallback'];
$context = array(
'type' => 'setting',
'id' => $setting,
'param' => $param,
'clients' => $clients,
'original' => NULL,
);
$fallback_markup = '<!-- Error: Failed to render setting -->';
drupal_alter('authcache_p13n_client_fallback', $fallback_markup, $fallback, $context);
return $fallback_markup;
}
/**
* Preprocess a placeholder for a personalized assembly.
*/
function template_preprocess_authcache_p13n_assembly(&$variables) {
$assembly = $variables['assembly'];
$param = $variables['param'];
$clients = $variables['clients'];
$client = authcache_p13n_client_get_preferred('assembly', $assembly, $clients);
if ($client && strlen($assembly)) {
_authcache_p13n_array_unique_recursive($param);
$url = authcache_p13n_request_get_callback('asm/' . $assembly, $param);
}
if (!empty($url)) {
$variables['theme_hook_suggestions'][] = 'authcache_p13n_assembly__' . $client;
$variables['theme_hook_suggestions'][] = 'authcache_p13n_assembly__' . $client . '__' . preg_replace('/_{2,}/', '_', preg_replace('/[^0-9a-z]/i', '_', $assembly));
$variables['client'] = $client;
$variables['url'] = $url;
$variables['class'] = drupal_html_class('authcache-p13n-asm-' . $assembly);
}
}
/**
* Theme a placeholder for a personalized assembly.
*/
function theme_authcache_p13n_assembly($variables) {
$assembly = $variables['assembly'];
$param = $variables['param'];
$clients = $variables['clients'];
$fallback = $variables['fallback'];
$context = array(
'type' => 'assembly',
'id' => $assembly,
'param' => $param,
'clients' => $clients,
'original' => NULL,
);
$fallback_markup = '<!-- Error: Failed to render assembly -->';
drupal_alter('authcache_p13n_client_fallback', $fallback_markup, $fallback, $context);
return $fallback_markup;
}
/**
* Preprocess a placeholder for a part of an assembly.
*/
function template_preprocess_authcache_p13n_partial(&$variables) {
$partial = $variables['partial'];
$assembly = $variables['assembly'];
$param = $variables['param'];
$clients = $variables['clients'];
$client = authcache_p13n_client_get_preferred('assembly', $assembly, $clients);
if ($client && strlen($partial) && strlen($assembly)) {
$exists = authcache_p13n_request_exists('asm/' . $assembly);
}
if (!empty($exists)) {
$variables['theme_hook_suggestions'][] = 'authcache_p13n_partial__' . $client;
$variables['theme_hook_suggestions'][] = 'authcache_p13n_partial__' . $client . '__' . preg_replace('/_{2,}/', '_', preg_replace('/[^0-9a-z]/i', '_', $assembly));
$variables['client'] = $client;
$variables['class'] = drupal_html_class('authcache-p13n-asm-' . $assembly);
authcache_p13n_add_partial($assembly, $partial, $param);
}
}
/**
* Theme a placeholder for a part of an assembly.
*/
function theme_authcache_p13n_partial($variables) {
$partial = $variables['partial'];
$assembly = $variables['assembly'];
$param = $variables['param'];
$clients = $variables['clients'];
$fallback = $variables['fallback'];
$original = $variables['original'];
$context = array(
'type' => 'partial',
'id' => $partial,
'param' => $param,
'assembly' => $assembly,
'clients' => $clients,
'original' => $original,
);
$fallback_markup = '<!-- Error: Failed to render partial -->';
drupal_alter('authcache_p13n_client_fallback', $fallback_markup, $fallback, $context);
return $fallback_markup;
}
/**
* Attach fragment or setting to the target render element.
*/
function authcache_p13n_attach(&$target, $element) {
if (!empty($element['#setting'])) {
// Attach a setting.
$target['#attached']['authcache_p13n_add_setting'][] = array(
$element,
);
}
else {
// Attach post render callback and replacement element.
$target['#post_render'][] = 'authcache_p13n_element_post_render';
$target['#authcache_p13n_element'] = $element;
}
}
/**
* Post render callback for elements with personalized content.
*
* Either replace element with rendered content of '#authcache_p13n_element or
* perform fallback operation.
*/
function authcache_p13n_element_post_render($markup, $element) {
if (authcache_page_is_cacheable() && !empty($element['#authcache_p13n_element'])) {
if (!isset($element['#authcache_p13n_element']['#original'])) {
$element['#authcache_p13n_element']['#original'] = $markup;
}
$markup = render($element['#authcache_p13n_element']);
}
return $markup;
}
/**
* Add a deferred setting to the page.
*/
function authcache_p13n_add_setting($element = array()) {
$settings =& drupal_static(__FUNCTION__, array());
if ($element && !empty($element['#setting'])) {
$settings[] = array(
$element['#setting'] => $element,
);
}
return $settings;
}
/**
* Return deferred settings for this page.
*/
function authcache_p13n_get_settings() {
return drupal_array_merge_deep_array(authcache_p13n_add_setting());
}
/**
* Add partial fragment to page.
*/
function authcache_p13n_add_partial($assembly = NULL, $frag = NULL, $param = NULL) {
$partials =& drupal_static(__FUNCTION__, array());
if ($assembly && $frag) {
$partials[] = array(
$assembly => array(
$frag => array(
$param,
),
),
);
}
return $partials;
}
/**
* Return all assemblies for this page (including added partials).
*/
function authcache_p13n_get_assemblies() {
return drupal_array_merge_deep_array(authcache_p13n_add_partial());
}
/**
* Implements hook_preprocess_html().
*/
function authcache_p13n_preprocess_html(&$variables) {
$variables['page']['page_bottom']['#pre_render'][] = 'authcache_p13n_page_bottom_pre_render';
}
/**
* Pre-render callback for the page_bottom region.
*/
function authcache_p13n_page_bottom_pre_render($region) {
foreach (authcache_p13n_get_settings() as $name => $element) {
$region['authcache_setting'][$name] = array(
'#theme' => 'authcache_p13n_setting',
) + $element;
}
foreach (authcache_p13n_get_assemblies() as $name => $params) {
$region['authcache_assembly'][$name] = array(
'#theme' => 'authcache_p13n_assembly',
'#assembly' => $name,
'#param' => $params,
);
}
return $region;
}
/**
* Implements hook_form_alter().
*/
function authcache_p13n_form_alter(&$form, &$form_state, $form_id) {
if (authcache_p13n_is_authcache_p13n_request()) {
// When forms are rendered as a part of a personalization fragment remove
// the action-attribute from the form-element.
$form['#action'] = "";
}
}
/**
* Discover theme suggestions provided by client-modules.
*
* @param string $client
* The client name (e.g. authcache_ajax or authcache_esi).
*
* @return array
* The functions found, suitable for returning from hook_theme;
*
* @see drupal_find_theme_functions()
*/
function authcache_p13n_find_theme_functions($client) {
$implementations = array();
foreach (authcache_p13n_theme() as $hook => $info) {
$new_hook = $hook . '__' . $client;
$function = 'theme_' . $new_hook;
if (function_exists('theme_' . $new_hook)) {
$arg_name = isset($info['variables']) ? 'variables' : 'render element';
$implementations[$new_hook] = array(
'function' => $function,
$arg_name => $info[$arg_name],
'base hook' => $hook,
);
}
}
return $implementations;
}
/**
* Helper: remove duplicate values from arrays with numeric keys.
*
* @param any &$item
* A nested array structure.
* @param any $key
* Internal usage.
*/
function _authcache_p13n_array_unique_recursive(&$item, $key = NULL) {
if (!is_array($item)) {
return is_numeric($key);
}
if (count($item)) {
$has_only_integer_keys = TRUE;
foreach ($item as $key => &$value) {
if (!_authcache_p13n_array_unique_recursive($value, $key)) {
$has_only_integer_keys = FALSE;
}
}
if ($has_only_integer_keys) {
$item = array_unique($item);
}
}
}
/**
* @} End of "defgroup authcache_p13n_markup"
*/
/**
* @defgroup authcache_p13n_session Session cache control
* @{
* Invalidate browser cache when user session changes
*/
/**
* Invalidate user specific content.
*/
function authcache_p13n_session_invalidate($report_only = FALSE) {
$should_invalidate =& drupal_static(__FUNCTION__, FALSE);
if (empty($report_only)) {
$should_invalidate = TRUE;
}
return $should_invalidate;
}
/**
* Implements hook_exit().
*/
function authcache_p13n_exit($destination = NULL) {
// Invalidate session cache on post requests.
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
authcache_p13n_session_invalidate();
}
if (authcache_p13n_session_invalidate(TRUE)) {
module_invoke_all('authcache_p13n_session_invalidate');
}
// Log an error if this is called from within frontcontroller with a
// destination path (due to drupal_goto). Fragments should never redirect.
if (authcache_p13n_is_authcache_p13n_request()) {
if ($destination !== NULL) {
$bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$caller_frame = _authcache_p13n_get_goto_caller($bt, array(
__FUNCTION__,
));
if ($caller_frame) {
$caller_frame += array(
'class' => '',
'type' => '',
'function' => 'unknown',
'file' => 'unknown',
'line' => 0,
);
$origin = strtr('classtypefunction() called at [file:line]', $caller_frame);
}
else {
$origin = 'unknown';
}
watchdog('Authcache P13n Front Controller', 'A redirect was triggered from within a personalization request originating from @origin', array(
'@origin' => $origin,
), WATCHDOG_ERROR);
}
}
}
/**
* Utility function: return backtrace frame of the function calling drupal_goto.
*/
function _authcache_p13n_get_goto_caller($backtrace, $ignore_additional = array()) {
// Examine the stack.
$ignore_functions = array_merge($ignore_additional, array(
'call_user_func_array',
'module_invoke_all',
'drupal_exit',
'drupal_goto',
));
foreach ($backtrace as $frame) {
if (!in_array($frame['function'], $ignore_functions)) {
return $frame;
}
}
}
/**
* Implements hook_user_login().
*/
function authcache_p13n_user_login(&$edit, $account) {
authcache_p13n_session_invalidate();
}
/**
* Implements hook_user_logout().
*/
function authcache_p13n_user_logout($account) {
authcache_p13n_session_invalidate();
}
/**
* Implements hook_authcache_cookie().
*
* Remove cache-version cookie when session is empty.
*/
function authcache_p13n_authcache_cookie($account) {
global $user;
$cookie = array();
// See drupal_session_commit().
if (empty($user->uid) && empty($_SESSION)) {
// Make sure cookie is not set when there is no session.
$cookie['aucp13n']['present'] = FALSE;
}
elseif (authcache_p13n_session_invalidate(TRUE) || empty($_COOKIE['aucp13n'])) {
// Renew cookie if necessary.
$cookie['aucp13n']['present'] = TRUE;
$cookie['aucp13n']['value'] = base_convert(mt_rand(), 10, 36);
}
return $cookie;
}
/**
* @} End of "defgroup authcache_p13n_session"
*/
/**
* @defgroup authcache_p13n_client Client implementations
* @{
* Return information about client modules capable of rendering fragments
*/
/**
* Return information about fragment clients.
*
* @see hook_authcache_p13n_client()
*/
function authcache_p13n_client_info() {
$info =& drupal_static(__FUNCTION__);
if (!isset($info)) {
$info = module_invoke_all('authcache_p13n_client');
drupal_alter('authcache_p13n_client', $info);
}
return $info;
}
/**
* Return the preferred client for the given fragment/assembly/setting.
*/
function authcache_p13n_client_get_preferred($type, $id, $clients = NULL) {
$preferred_clients =& drupal_static(__FUNCTION__);
if (!isset($preferred_clients[$type][$id])) {
// Gather clients available during the current page request.
$available_clients = array_filter(authcache_p13n_client_info(), function ($client) {
return !empty($client['enabled']);
});
// Check for clients enabled in the given configuration.
if (!isset($clients)) {
$clients = $available_clients;
}
drupal_alter('authcache_p13n_client_order', $clients, $type, $id);
$enabled_clients = array_filter($clients, function ($client) {
return !isset($client['status']) || !empty($client['status']);
});
// Collect and sort configured clients.
$configured_clients = array_intersect_key($enabled_clients, $available_clients);
foreach (array_keys($configured_clients) as $name) {
$configured_clients[$name] += $available_clients[$name];
}
uasort($configured_clients, 'drupal_sort_weight');
$preferred_clients[$type][$id] = key($configured_clients) ?: FALSE;
}
// Return the first configured client.
return $preferred_clients[$type][$id];
}
/**
* Implements hook_authcache_p13n_client_fallback_alter().
*/
function authcache_p13n_authcache_p13n_client_fallback_alter(&$markup, $method, $context) {
switch ($method) {
case 'cancel':
authcache_cancel(t('No client for %type %id', array(
'%type' => $context['type'],
'%id' => $context['id'],
)));
if (isset($context['original'])) {
$markup = $context['original'];
}
break;
}
}
/**
* @} End of "defgroup authcache_p13n_client"
*/
/**
* @defgroup authcache_p13n_request Request handlers
* @{
* Maintain a registry of handler classes for personalization requests.
*/
/**
* Return true if the given route exists in the router.
*/
function authcache_p13n_request_exists($route_id) {
$routes =& drupal_static(__FUNCTION__);
if (!isset($routes)) {
$router = authcache_p13n_request_get_router();
$routes = drupal_map_assoc($router
->getRoutes());
}
return isset($routes[$route_id]);
}
/**
* Return uri for the request.
*
* @return string|null
* Either the URL for the given route_id plus arguments or null, if no such
* request is available.
*/
function authcache_p13n_request_get_callback($route_id, $arg) {
$router = authcache_p13n_request_get_router();
return $router
->generateURL($route_id, $arg);
}
/**
* Generate a list of requests resources.
*
* @see hook_authcache_p13n_request()
*/
function authcache_p13n_request_resources() {
$requests = module_invoke_all('authcache_p13n_request');
$preproc = authcache_p13n_resource_preprocessor();
foreach ($requests as $key => $resources) {
$requests[$key] = $preproc
->preprocess($resources);
}
drupal_alter('authcache_p13n_request', $requests);
foreach ($requests as $key => $resources) {
$requests[$key] = $preproc
->preprocess($resources);
}
return $requests;
}
/**
* Invoke hook_authcache_p13n_resource_preprocessors().
*/
function authcache_p13n_resource_preprocessor() {
$preprocs = module_invoke_all('authcache_p13n_resource_preprocessors');
drupal_alter('authcache_p13n_resource_preprocessors', $preprocs);
$preproc = new AuthcacheP13nObjectResourcePreprocessor($preprocs);
return $preproc;
}
/**
* Implements hook_authcache_p13n_resource_preprocessors().
*/
function authcache_p13n_authcache_p13n_resource_preprocessors() {
return array(
'partial' => '_authcache_p13n_resource_preproc_partial',
'setting' => '_authcache_p13n_resource_preproc_setting',
) + AuthcacheP13nObjectResourcePreprocessor::defaultPreprocessors();
}
/**
* Invoke hook_authcache_p13n_resource_processors().
*/
function authcache_p13n_resource_processors() {
$processors = module_invoke_all('authcache_p13n_resource_processors');
drupal_alter('authcache_p13n_resource_processors', $processors);
return $processors;
}
/**
* Implements hook_authcache_p13n_resource_processors().
*/
function authcache_p13n_authcache_p13n_resource_processors() {
return array(
'as_object' => '_authcache_p13n_resource_proc_as_object',
) + AuthcacheP13nObjectFactory::defaultProcessors();
}
/**
* Derive collection-id and add #member_of and #key entries if necessary.
*/
function _authcache_p13n_resource_preproc_partial($resource, $priority, $rname, $enqueue) {
// Find partial-id.
if (!is_array($resource) || !isset($resource['#partial'])) {
return;
}
$partial_id = $resource['#partial'];
unset($resource['#partial']);
// Derive collection.
if (isset($resource['#member_of'])) {
$collection_id = $resource['#member_of'];
}
else {
$collection_id = 'default partial ' . $partial_id . ' ' . drupal_random_key();
}
// Create a set of default resources: One collection entry and one renderer,
// validator, loader and access checker. Use negative priority in order to
// avoid overriding resources defined elsewhere.
$enqueue(array(
$collection_id => array(
'#collection' => $collection_id,
'#member_of' => 'partials',
'#key' => $partial_id,
'#processors' => array(
'renderer' => 'require_instance(AuthcacheP13nFragmentInterface)',
'validator' => 'accept_instance(AuthcacheP13nFragmentValidatorInterface)',
'loader' => 'accept_instance(AuthcacheP13nFragmentLoaderInterface)',
'access' => 'accept_instance(AuthcacheP13nFragmentAccessInterface)',
),
),
$collection_id . ' renderer' => $resource + array(
'#member_of' => $collection_id,
'#key' => 'renderer',
),
$collection_id . ' validator' => $resource + array(
'#member_of' => $collection_id,
'#key' => 'validator',
),
$collection_id . ' loader' => $resource + array(
'#member_of' => $collection_id,
'#key' => 'loader',
),
$collection_id . ' access' => $resource + array(
'#member_of' => $collection_id,
'#key' => 'access',
),
), $priority - 1);
return $resource + array(
'#member_of' => $collection_id,
'#key' => 'renderer',
);
}
/**
* Derive collection-id and add #member_of and #key entries if necessary.
*/
function _authcache_p13n_resource_preproc_setting($resource, $priority, $rname, $enqueue) {
// Find setting-id.
if (!is_array($resource) || !isset($resource['#setting'])) {
return;
}
$setting_id = $resource['#setting'];
unset($resource['#setting']);
// Derive collection.
if (isset($resource['#member_of'])) {
$collection_id = $resource['#member_of'];
}
else {
$collection_id = 'default setting ' . $setting_id . ' ' . drupal_random_key();
}
// Create a set of default resources: One collection entry and one renderer,
// validator, loader and access checker. Use negative priority in order to
// avoid overriding resources defined elsewhere.
$enqueue(array(
$collection_id => array(
'#collection' => $collection_id,
'#member_of' => 'settings',
'#key' => $setting_id,
'#processors' => array(
'renderer' => 'require_instance(AuthcacheP13nSettingInterface)',
'validator' => 'accept_instance(AuthcacheP13nSettingValidatorInterface)',
'access' => 'accept_instance(AuthcacheP13nSettingAccessInterface)',
),
),
$collection_id . ' renderer' => $resource + array(
'#member_of' => $collection_id,
'#key' => 'renderer',
),
$collection_id . ' target' => array(
'#value' => $setting_id,
'#member_of' => $collection_id,
'#key' => 'target',
),
$collection_id . ' validator' => $resource + array(
'#member_of' => $collection_id,
'#key' => 'validator',
),
$collection_id . ' access' => $resource + array(
'#member_of' => $collection_id,
'#key' => 'access',
),
), $priority - 1);
// If a target key is specified, add a target resource with the same priority.
if (isset($resource['#target'])) {
$enqueue(array(
$collection_id . ' target' => array(
'#value' => $resource['#target'],
'#member_of' => $collection_id,
'#key' => 'target',
),
), $priority);
}
return $resource + array(
'#member_of' => $collection_id,
'#key' => 'renderer',
);
}
/**
* Cast a resource into a stdObject.
*/
function _authcache_p13n_resource_proc_as_object($subject, $arg, $rname, $factory) {
return (object) $subject;
}
/**
* Implements hook_authcache_p13n_base_request().
*/
function authcache_p13n_authcache_p13n_base_request() {
$frontcontroller_path = variable_get('authcache_p13n_frontcontroller_path', drupal_get_path('module', 'authcache_p13n') . '/frontcontroller/authcache.php');
// Note that fragment, setting, assembly request need to provide 'content
// builder' and 'content encoder' resources.
return array(
// Overridable resources.
'cache maxage' => 600,
'cache granularity' => AuthcacheP13nCacheGranularity::PER_USER,
'bootstrap phase' => NULL,
'admin type' => t('Unknown'),
'admin group' => t('Other'),
'admin name' => t('Unknown'),
'admin description' => '',
'admin path' => NULL,
'admin entry object' => '@admin entry[as_object]',
'conf override' => array(),
// Normally not overridden.
'cache granularity object' => array(
'#class' => 'AuthcacheP13nCacheGranularity',
'#arguments' => array(
'@cache granularity',
),
),
'cache control header' => array(
'#class' => 'AuthcacheP13nAddCacheControlHeaderFilter',
'#arguments' => array(
'@services[require_instance(AuthcacheP13nCoreServiceInterface)]',
'@cache maxage',
'@cache granularity object[require_instance(AuthcacheP13nCacheGranularity)]',
),
'#member_of' => 'request filters',
),
'request validator' => '@content builder[accept_instance(AuthcacheP13nRequestValidatorInterface)]',
'request filters' => array(
'#collection' => 'request filters',
'#processor' => 'require_instance(AuthcacheP13nFilterInterface)',
),
'response filters' => array(
'#collection' => 'response filters',
'#processor' => 'require_instance(AuthcacheP13nFilterInterface)',
),
'filters' => array(
'request' => '@request filters',
'response' => '@response filters',
),
'conf override context provider' => array(
'#class' => 'AuthcacheP13nConfOverrideContextProvider',
'#arguments' => array(
'@conf override',
),
'#weight' => -150,
'#member_of' => 'context providers',
'#key' => 'conf override',
),
'bootstrap context provider' => array(
'#class' => 'AuthcacheP13nBootstrapContextProvider',
'#arguments' => array(
'@services[require_instance(AuthcacheP13nCoreServiceInterface)]',
'@bootstrap phase',
),
'#weight' => -50,
'#member_of' => 'context providers',
'#key' => 'bootstrap phase',
),
'context providers' => array(
'#collection' => 'context providers',
'#processor' => 'require_instance(AuthcacheP13nContextProviderInterface)',
),
'handler' => array(
'#class' => 'AuthcacheP13nDefaultRequestHandler',
'#arguments' => array(
'@services[require_instance(AuthcacheP13nCoreServiceInterface)]',
'@request validator[accept_instance(AuthcacheP13nRequestValidatorInterface)]',
'@content builder[require_instance(AuthcacheP13nContentBuilderInterface)]',
'@content encoder[require_instance(AuthcacheP13nContentEncoderInterface)]',
'@filters',
'@context providers',
),
),
'frontcontroller' => array(
'#value' => $frontcontroller_path,
),
'url generator' => array(
'#class' => 'AuthcacheP13nDefaultRequestUrlGenerator',
'#arguments' => array(
'@frontcontroller',
'@cache granularity object[require_instance(AuthcacheP13nCacheGranularity)]',
),
),
'services' => array(
'#class' => 'AuthcacheP13nDefaultCoreService',
),
'admin entry' => array(
'type' => '@admin type',
'group' => '@admin group',
'name' => '@admin name',
'description' => '@admin description',
'clients' => NULL,
'cacheMaxage' => '@cache maxage',
'cacheGranularity' => '@cache granularity object[require_instance(AuthcacheP13nCacheGranularity)]',
'adminPath' => '@admin path',
),
);
}
/**
* Implements hook_authcache_p13n_request().
*/
function authcache_p13n_authcache_p13n_request() {
$requests = array();
$request_base = module_invoke_all('authcache_p13n_base_request');
drupal_alter('authcache_p13n_base_request', $request_base);
$fragments = authcache_p13n_fragment_info();
$fragment_defaults = array(
// Overridable resources.
'fragment' => NULL,
'fragment validator' => '@fragment[accept_instance(AuthcacheP13nFragmentValidatorInterface)]',
'fragment loader' => '@fragment[accept_instance(AuthcacheP13nFragmentLoaderInterface)]',
'fragment access' => '@fragment[accept_instance(AuthcacheP13nFragmentAccessInterface)]',
// Normally not overridden.
'admin type' => t('Fragment'),
'content builder' => array(
'#class' => 'AuthcacheP13nFragmentBuilder',
'#arguments' => array(
'@fragment',
'@fragment validator',
'@fragment loader',
'@fragment access',
),
),
'content encoder' => array(
'#class' => 'AuthcacheP13nHTMLContent',
),
) + $request_base;
foreach ($fragments as $key => $config) {
$requests['frag/' . $key] = $config + $fragment_defaults;
}
$settings = authcache_p13n_setting_info();
$setting_defaults = array(
// Normally not overridden.
'settings' => array(
'#collection' => 'settings',
),
'admin type' => t('Setting'),
'content builder' => array(
'#class' => 'AuthcacheP13nSettingBuilder',
'#arguments' => array(
'@settings',
),
),
'content encoder' => array(
'#class' => 'AuthcacheP13nJSONContent',
),
) + $request_base;
foreach ($settings as $key => $config) {
$requests['setting/' . $key] = $config + $setting_defaults;
}
$assemblies = authcache_p13n_assembly_info();
$assembly_defaults = array(
// Normally not overridden.
'partials' => array(
'#collection' => 'partials',
),
'admin type' => t('Assembly'),
'content builder' => array(
'#class' => 'AuthcacheP13nFragmentAssemblyBuilder',
'#arguments' => array(
'@partials',
),
),
'content encoder' => array(
'#class' => 'AuthcacheP13nJSONContent',
),
) + $request_base;
foreach ($assemblies as $key => $config) {
$requests['asm/' . $key] = $config + $assembly_defaults;
}
return $requests;
}
/**
* Rebuild the router of personalization request handlers.
*/
function authcache_p13n_request_router_rebuild() {
$router = authcache_p13n_request_get_router();
$router
->rebuild();
drupal_static_reset('authcache_p13n_request_exists');
}
/**
* Return the router class for requests.
*/
function authcache_p13n_request_get_router() {
$router =& drupal_static(__FUNCTION__);
if (!isset($router)) {
$routerclass = variable_get('authcache_p13n_router', 'AuthcacheP13nDefaultRequestRouter');
$router = new $routerclass();
}
return $router;
}
/**
* Implements hook_modules_enabled().
*/
function authcache_p13n_modules_enabled($modules) {
authcache_p13n_request_router_rebuild();
}
/**
* Implements hook_modules_disabled().
*/
function authcache_p13n_modules_disabled($modules) {
authcache_p13n_request_router_rebuild();
}
/**
* Implements hook_flush_caches().
*/
function authcache_p13n_flush_caches() {
return array(
'cache_authcache_p13n',
);
}
/**
* Return TRUE if the P13n front controller was used for this request.
*/
function authcache_p13n_is_authcache_p13n_request() {
return defined('AUTHCACHE_P13N_ROOT');
}
/**
* @} End of "defgroup authcache_p13n_request"
*/
/**
* @defgroup authcache_p13n_debug Debug widget
* @{
* Provide additional status information through the debug widget.
*/
/**
* Implements hook_authcache_debug_exclude().
*/
function authcache_p13n_authcache_debug_exclude() {
if (authcache_p13n_is_authcache_p13n_request()) {
return TRUE;
}
}
/**
* Implements hook_authcache_debug_info().
*/
function authcache_p13n_authcache_debug_info() {
$debug_info = array();
$client_info = authcache_p13n_client_info();
$all_clients = array_map(function ($client) {
return $client['title'];
}, $client_info);
$enabled_clients = array_map(function ($client) {
return $client['title'];
}, array_filter($client_info, function ($client) {
return !empty($client['enabled']);
}));
if (empty($all_clients)) {
authcache_debug_log(t('P13n'), t('No client module enabled, markup substitution will not work.'));
$debug_info['P13n Clients'] = t('No client module enabled');
}
else {
$debug_info['P13n Clients'] = implode(', ', $all_clients);
if (empty($enabled_clients)) {
authcache_debug_log(t('P13n'), t('None of the enabled client modules is active on this request, markup substitution will not work.'));
$debug_info['Active P13n Clients'] = t('No active client module');
}
else {
$debug_info['Active P13n Clients'] = implode(', ', $enabled_clients);
}
}
return $debug_info;
}
/**
* @} End of "defgroup authcache_p13n_debug"
*/
/**
* @defgroup authcache_p13n_config Personalized request configuration widget
* @{
* Reusable form API element for markup substitution / request settings
*/
/**
* Implements hook_element_info().
*/
function authcache_p13n_element_info() {
$types['authcache_p13n_config'] = array(
'#input' => TRUE,
'#process' => array(
'authcache_p13n_process_config',
'form_process_container',
),
'#theme_wrappers' => array(
'container',
),
);
return $types;
}
/**
* Return default value for config widget.
*/
function authcache_p13n_config_defaults() {
return array(
'status' => FALSE,
'lifespan' => 3600,
'lifespan_custom' => NULL,
'peruser' => 1,
'perpage' => 0,
'fallback' => 'cancel',
'clients' => authcache_p13n_client_info(),
);
}
/**
* Form API process callback for config element.
*/
function authcache_p13n_process_config($element, &$form_state) {
// Prepare #default_value
$defaults = authcache_p13n_config_defaults();
if (empty($element['#default_value'])) {
$element['#default_value'] = $defaults;
}
else {
$element['#default_value'] = $element['#default_value'] + $defaults;
}
$element['#tree'] = TRUE;
$element['#attached']['js'][] = drupal_get_path('module', 'authcache_p13n') . '/authcache_p13n.admin.js';
$element['#attached']['css'][] = drupal_get_path('module', 'authcache_p13n') . '/authcache_p13n.admin.css';
$enabled_id = drupal_html_id($element['#id'] . '-status');
$element['status'] = array(
'#type' => 'checkbox',
'#title' => t('Authcache'),
'#description' => t('Use ESI or Ajax to deliver this content.'),
'#default_value' => $element['#default_value']['status'],
'#id' => $enabled_id,
);
$container_id = drupal_html_id($element['#id'] . '-container');
$element['settings'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array(
'authcache-p13n-config-settings-wrapper',
),
),
'#parents' => $element['#parents'],
'#id' => $container_id,
'#states' => array(
'visible' => array(
'#' . $enabled_id => array(
'checked' => TRUE,
),
),
),
);
$element['settings']['lifespan'] = array(
'#type' => 'authcache_duration_select',
'#title' => t('Cache lifetime'),
'#description' => t('The maximum time an external cache or the browser can keep a copy of this content.'),
'#durations' => array(
0,
60,
300,
1800,
3600,
21600,
518400,
),
'#default_value' => $element['#default_value']['lifespan'],
'#zero_duration' => t('Never cache'),
);
$element['settings']['peruser'] = array(
'#type' => 'checkbox',
'#title' => t('Per user'),
'#description' => t('Select when content is different depending on user.'),
'#default_value' => $element['#default_value']['peruser'],
);
$element['settings']['perpage'] = array(
'#type' => 'checkbox',
'#title' => t('Per page'),
'#description' => t('Select when content is different depending on the URL of the page.'),
'#default_value' => $element['#default_value']['perpage'],
);
// Clients.
$parents_for_clients = array_merge($element['#parents'], array(
'clients',
));
$client_info = authcache_p13n_client_info();
$clients = $element['#default_value']['clients'];
$element['settings']['#clients'] = $clients;
// Status.
$client_status_id = drupal_html_id($element['#id'] . '-clients-status-wrapper');
$element['settings']['clients']['status'] = array(
'#type' => 'item',
'#title' => t('Enabled clients'),
'#prefix' => '<div ' . drupal_attributes(array(
'id' => $client_status_id,
'class' => 'clients-status-wrapper',
)) . '>',
'#suffix' => '</div>',
'#description' => t('Select the method(s) used to substitute personalized markup.'),
);
$element['settings']['clients']['status']['status-warning'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array(
'authcache-p13n-clients-warning',
'messages',
'warning',
),
),
'message' => array(
'#markup' => count($client_info) > 0 ? t('Please select at least one client, otherwise markup substitution will not work.') : t('No client module enabled, markup substitution will not work.'),
),
);
$enabled_clients = 0;
foreach ($client_info as $name => $client) {
$status = !isset($clients[$name]['status']) || !empty($clients[$name]['status']);
$element['settings']['clients']['status'][$name] = array(
'#type' => 'checkbox',
'#title' => $client['title'],
'#default_value' => $status,
'#parents' => array_merge($parents_for_clients, array(
$name,
'status',
)),
);
if ($status) {
$enabled_clients++;
}
}
if ($enabled_clients) {
$element['settings']['clients']['status']['status-warning']['#attributes']['class'][] = 'element-hidden';
}
// Client order (tabledrag).
$client_order_wrapper_id = drupal_html_id($element['#id'] . '-clients-order-wrapper');
$client_order_tabledrag_id = drupal_html_id($element['#id'] . '-clients-order-table');
$element['settings']['clients']['order'] = array(
'#type' => 'item',
'#title' => t('Client order'),
'#theme' => 'authcache_p13n_config_client_order',
'#id' => $client_order_wrapper_id,
'#tabledrag_id' => $client_order_tabledrag_id,
'#description' => t('Set client precedence by moving preferred client modules to the top. If more than one client module is capable of handling markup substitution the one ranking the highest is used.'),
);
foreach ($client_info as $name => $client) {
$weight = empty($clients[$name]['weight']) ? 0 : $clients[$name]['weight'];
$element['settings']['clients']['order'][$name]['client'] = array(
'#markup' => $client['title'],
);
$element['settings']['clients']['order'][$name]['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight for @title', array(
'@title' => $client['title'],
)),
'#title_display' => 'invisible',
'#delta' => 50,
'#default_value' => $weight,
'#parents' => array_merge($parents_for_clients, array(
$name,
'weight',
)),
);
$element['settings']['clients']['order'][$name]['#weight'] = $weight;
}
$element['settings']['fallback'] = array(
'#type' => 'radios',
'#title' => t('Fallback'),
'#description' => t('What to do when no client is available (neither ESI nor Ajax).'),
'#options' => array(
'cancel' => t('Cancel caching'),
'hide' => t('Hide content'),
),
'#default_value' => $element['#default_value']['fallback'],
);
return $element;
}
/**
* Returns HTML for a client order form.
*
* @param array $variables
* An associative array containing:
* - element: A render element representing the form.
*
* @ingroup themeable
*/
function theme_authcache_p13n_config_client_order($variables) {
$element = $variables['element'];
// Client order (tabledrag).
$rows = array();
foreach (element_children($element, TRUE) as $name) {
$element[$name]['weight']['#attributes']['class'][] = 'clients-order-weight';
$rows[] = array(
'data' => array(
drupal_render($element[$name]['client']),
drupal_render($element[$name]['weight']),
),
'class' => array(
'draggable',
),
);
}
$id = empty($element['#tabledrag_id']) ? 'clients-order' : $element['#tabledrag_id'];
unset($element['#tabledrag_id']);
$output = drupal_render_children($element);
$output .= theme('table', array(
'rows' => $rows,
'attributes' => array(
'id' => $id,
'class' => 'clients-order-table',
),
));
drupal_add_tabledrag($id, 'order', 'sibling', 'clients-order-weight', NULL, NULL, TRUE);
return $output;
}
/**
* Utility: Return the number of seconds the request should be cached.
*
* @param array $config
* The value produced by the authcache_p13n_config widget.
*
* @return int
* Number of seconds the request should be cached
*/
function authcache_p13n_config_cache_maxage($config) {
return !empty($config['lifespan']) ? $config['lifespan'] : 0;
}
/**
* Utility: Return cache granularity flags.
*
* @param array $config
* The value produced by the authcache_p13n_config widget.
*
* @return int
* A combination of cache granularity flags.
*/
function authcache_p13n_config_cache_granularity($config) {
$result = 0;
if (!empty($config['peruser'])) {
$result |= AuthcacheP13nCacheGranularity::PER_USER;
}
if (!empty($config['perpage'])) {
$result |= AuthcacheP13nCacheGranularity::PER_PAGE;
}
return $result;
}
/**
* @} End of "defgroup authcache_p13n_config"
*/
Functions
Name | Description |
---|---|
authcache_p13n_add_partial | Add partial fragment to page. |
authcache_p13n_add_setting | Add a deferred setting to the page. |
authcache_p13n_assembly_info | Return information about fragment assemblies implemented by other modules. |
authcache_p13n_attach | Attach fragment or setting to the target render element. |
authcache_p13n_authcache_cookie | Implements hook_authcache_cookie(). |
authcache_p13n_authcache_debug_exclude | Implements hook_authcache_debug_exclude(). |
authcache_p13n_authcache_debug_info | Implements hook_authcache_debug_info(). |
authcache_p13n_authcache_p13n_base_request | Implements hook_authcache_p13n_base_request(). |
authcache_p13n_authcache_p13n_client_fallback_alter | Implements hook_authcache_p13n_client_fallback_alter(). |
authcache_p13n_authcache_p13n_request | Implements hook_authcache_p13n_request(). |
authcache_p13n_authcache_p13n_resource_preprocessors | Implements hook_authcache_p13n_resource_preprocessors(). |
authcache_p13n_authcache_p13n_resource_processors | Implements hook_authcache_p13n_resource_processors(). |
authcache_p13n_authcache_request_exclude | Implements hook_authcache_request_exclude(). |
authcache_p13n_client_get_preferred | Return the preferred client for the given fragment/assembly/setting. |
authcache_p13n_client_info | Return information about fragment clients. |
authcache_p13n_config_cache_granularity | Utility: Return cache granularity flags. |
authcache_p13n_config_cache_maxage | Utility: Return the number of seconds the request should be cached. |
authcache_p13n_config_defaults | Return default value for config widget. |
authcache_p13n_element_info | Implements hook_element_info(). |
authcache_p13n_element_post_render | Post render callback for elements with personalized content. |
authcache_p13n_exit | Implements hook_exit(). |
authcache_p13n_find_theme_functions | Discover theme suggestions provided by client-modules. |
authcache_p13n_flush_caches | Implements hook_flush_caches(). |
authcache_p13n_form_alter | Implements hook_form_alter(). |
authcache_p13n_fragment_info | Return information about fragments implemented by other modules. |
authcache_p13n_get_assemblies | Return all assemblies for this page (including added partials). |
authcache_p13n_get_settings | Return deferred settings for this page. |
authcache_p13n_is_authcache_p13n_request | Return TRUE if the P13n front controller was used for this request. |
authcache_p13n_menu | Implements hook_menu(). |
authcache_p13n_modules_disabled | Implements hook_modules_disabled(). |
authcache_p13n_modules_enabled | Implements hook_modules_enabled(). |
authcache_p13n_page_bottom_pre_render | Pre-render callback for the page_bottom region. |
authcache_p13n_preprocess_html | Implements hook_preprocess_html(). |
authcache_p13n_process_config | Form API process callback for config element. |
authcache_p13n_request_exists | Return true if the given route exists in the router. |
authcache_p13n_request_get_callback | Return uri for the request. |
authcache_p13n_request_get_router | Return the router class for requests. |
authcache_p13n_request_resources | Generate a list of requests resources. |
authcache_p13n_request_router_rebuild | Rebuild the router of personalization request handlers. |
authcache_p13n_resource_preprocessor | Invoke hook_authcache_p13n_resource_preprocessors(). |
authcache_p13n_resource_processors | Invoke hook_authcache_p13n_resource_processors(). |
authcache_p13n_session_invalidate | Invalidate user specific content. |
authcache_p13n_setting_info | Return information about settings implemented by other modules. |
authcache_p13n_theme | Implements hook_theme(). |
authcache_p13n_user_login | Implements hook_user_login(). |
authcache_p13n_user_logout | Implements hook_user_logout(). |
template_preprocess_authcache_p13n_assembly | Preprocess a placeholder for a personalized assembly. |
template_preprocess_authcache_p13n_fragment | Preprocess a placeholder for a personalized fragment. |
template_preprocess_authcache_p13n_partial | Preprocess a placeholder for a part of an assembly. |
template_preprocess_authcache_p13n_setting | Preprocess a placeholder for a personalized setting. |
theme_authcache_p13n_assembly | Theme a placeholder for a personalized assembly. |
theme_authcache_p13n_config_client_order | Returns HTML for a client order form. |
theme_authcache_p13n_fragment | Theme a placeholder for a personalized fragment. |
theme_authcache_p13n_partial | Theme a placeholder for a part of an assembly. |
theme_authcache_p13n_setting | Theme a placeholder for a personalized setting. |
_authcache_p13n_array_unique_recursive | Helper: remove duplicate values from arrays with numeric keys. |
_authcache_p13n_get_goto_caller | Utility function: return backtrace frame of the function calling drupal_goto. |
_authcache_p13n_resource_preproc_partial | Derive collection-id and add #member_of and #key entries if necessary. |
_authcache_p13n_resource_preproc_setting | Derive collection-id and add #member_of and #key entries if necessary. |
_authcache_p13n_resource_proc_as_object | Cast a resource into a stdObject. |