You are here

akamai.module in Akamai 6.2

Integration with the Akamai CDN Cache Control Web Service.

Akamai is a registered trademark of Akamai Technologies, Inc.

File

akamai.module
View source
<?php

/**
 * @file
 * Integration with the Akamai CDN Cache Control Web Service.
 *
 * Akamai is a registered trademark of Akamai Technologies, Inc.
 */
define("AKAMAI_EMAIL_DISABLE", '#disable#');

/**
 * Implements hook_perm().
 */
function akamai_perm() {
  return array(
    'purge akamai cache',
    'administer akamai',
  );
}

/**
 * Implements hook_menu().
 */
function akamai_menu() {
  $items = array();
  $items['admin/settings/akamai'] = array(
    'title' => 'Akamai Cache Control',
    'description' => 'Akamai Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'akamai_settings',
    ),
    'file' => 'akamai.admin.inc',
    'access arguments' => array(
      'administer akamai',
    ),
  );
  $items['admin/settings/akamai/settings'] = array(
    'title' => 'Settings',
    'weight' => -10,
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/settings/akamai/refresh'] = array(
    'title' => 'Refresh Tool',
    'description' => 'Helper to flush Akamai cache from Drupal',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'akamai_cache_control',
    ),
    'file' => 'akamai.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array(
      'purge akamai cache',
    ),
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function akamai_theme() {
  return array(
    'akamai_page_cache_control_block' => array(
      'arguments' => array(
        'cache_control_form' => NULL,
      ),
      'template' => 'akamai-page-cache-control-block',
    ),
  );
}

/**
 * Implements hook_nodeapi().
 *
 * When nodes are modified, clear URL from the Akamai
 * cache. Clear base node/% url as well as aliases.
 */
function akamai_nodeapi(&$node, $op) {
  switch ($op) {
    case 'update':
    case 'delete':
      $url = base_path() . "node/{$node->nid}";
      akamai_clear_url($url, array(
        'email' => AKAMAI_EMAIL_DISABLE,
      ), $node);
      break;
  }
}

/**
 * Implements hook_block().
 */
function akamai_block($op = 'list', $delta = 0, $edit = array()) {
  if ($op == 'list') {
    $blocks[0] = array(
      'info' => t('Akamai Cache Control'),
      'region' => 'footer',
      'weight' => 0,
      'cache' => BLOCK_NO_CACHE,
    );
    return $blocks;
  }
  elseif ($op == 'view') {
    $cache_control_form = drupal_get_form('akamai_page_cache_control_form');
    $block = array(
      'subject' => t('Akamai Cache Control'),
      'content' => $content = theme('akamai_page_cache_control_block', $cache_control_form),
    );
    return $block;
  }
}

/**
 * Form for purging the current URL from the Akamai cache.
 */
function akamai_page_cache_control_form() {
  $form = array();
  $nid = arg(1);
  if (arg(0) == 'node' && is_numeric($nid) && arg(2) == NULL) {
    $path = arg(0) . '/' . $nid;
    $form['#node'] = node_load($nid);
  }
  else {
    $path = check_plain($_GET['q']);
    $form['#node'] = NULL;
  }
  $path_label = $path;
  if ($path == variable_get('site_frontpage', 'node')) {
    $path_label = t("[frontpage]");
  }
  $form['path'] = array(
    '#type' => 'hidden',
    '#value' => $path,
  );
  $form['message'] = array(
    '#type' => 'item',
    '#title' => t('Refresh URL'),
    '#markup' => $path_label,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Refresh Akamai Cache'),
  );
  return $form;
}

/**
 * Submit handler for akamai_page_cache_control.
 *
 * Purge the 'path' variable from the Akamai cache.
 */
function akamai_page_cache_control_form_submit($form, &$form_state) {
  $values = $form_state['values'];
  $path = $values['path'];
  $did_clear = akamai_clear_url($path, array(), $form['#node']);
  if ($did_clear) {
    $message = t("Akamai Cache Request has been made successfully, please allow 10 minutes for changes to take effect.");
    drupal_set_message($message);
  }
  else {
    drupal_set_message(t("There was a problem clearing the cache for this page.  Check the !watchdog messages for more information.", array(
      '!watchdog' => l(t("watchdog"), "admin/reports/dblog"),
    ), 'error'));
  }
}

/**
 * Clear a URL from Akamai.  Clears node/id and any url aliases.
 *
 * @param array $paths_in
 *   A array of paths or single path to clear
 *
 * @param array $params
 *   An array of params for the API call
 *
 * @return bool
 *   TRUE if it was successfully cleared, FALSE otherwise.
 */
function akamai_clear_url($paths_in, $params = array(), $node = NULL) {
  if (!is_array($paths_in)) {
    $paths_in = array(
      $paths_in,
    );
  }

  // Get the system path and all aliases to this url.
  $paths = array();
  foreach ($paths_in as $path) {
    $paths = array_merge($paths, _akamai_get_all_paths($path, $node));
  }

  // It is possible to have no paths at this point if other modules have altered
  // the paths.
  if (empty($paths)) {
    watchdog('akamai', 'No resultant paths to clear for %paths', array(
      '%paths' => implode(', ', $paths_in),
    ), WATCHDOG_NOTICE);
    return FALSE;
  }
  $paths = array_unique($paths);

  // Setup the notification email if an email address is not provided.
  if (!array_key_exists('email', $params) || empty($params['email'])) {
    $params['email'] = akamai_get_notification_email();
  }
  try {
    $akamai = akamai_get_class($params);
    $response = $akamai
      ->clear_url($paths);
    $response->client = $akamai;
    return $response;
  } catch (\Exception $e) {
    return FALSE;
  }
}

/**
 * Return all system and alias paths for the provided URL.
 *
 * @param string $url
 *   The URL to find all aliases
 *
 * @param int $node
 *   The Node for the URL being cleared if one exists, NULL otherwise
 *
 * @return array
 *   An array of all paths aliased to the provided URL.
 */
function _akamai_get_all_paths($url, $node = NULL) {
  global $language;

  // If it is not a node path, get the system path.
  if (strpos($url, 'node') !== 0) {
    $src = drupal_lookup_path('source', $url);
  }
  if (!isset($src) || !$src) {
    $src = $url;
  }
  $src = preg_replace("/^\\//", "", $src);
  $paths[] = $src;
  $result = db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND language IN('%s', '')", $src, $language->language);
  while ($alias = db_result($result)) {
    $paths[] = $alias;
  }

  // Allow other modules to add/modify paths to be cleared.
  drupal_alter('akamai_paths', $paths, $node);
  return $paths;
}

/**
 * Get the email address for notification.
 */
function akamai_get_notification_email() {
  $notification = variable_get('akamai_email', '');
  if (empty($notification)) {
    global $user;
    $notification = $user->mail;
  }
  return $notification;
}

/**
 * Implements hook_context_http_header_def().
 *
 * These are the form items that display for the http header context reaction.
 *
 * @source: context_http_header module
 */
function akamai_context_http_header_def() {
  return array(
    'akamai_ttl' => array(
      '#title' => 'TTL',
      '#type' => 'textfield',
      '#description' => t("Specifies a Time-To-Live (TTL) for the object in the edge server’s cache. This is themaximum amount of time the content will be served before the edge server will issue an If Modified Since request back to the content provider to check whether the object content has changed. The content is an integer, followed by a unit specifier Current unit specifiers are: 's' (seconds), 'm' (minutes), 'h' (hours), 'd' (days)."),
    ),
    'akamai_nostore' => array(
      '#title' => 'No Store',
      '#type' => 'radios',
      '#description' => t("Specifies that the object is not to be cached. The edge server will retrieve the object from the origin server upon every request."),
      '#options' => array(
        FALSE => t('Not Specified'),
        'on' => t('On'),
        'off' => t('Off'),
      ),
    ),
    'akamai_bypass_cache' => array(
      '#title' => 'Bypass Cache',
      '#type' => 'radios',
      '#description' => t("When enabled, bypass-cache causes the request to be passed without caching. It's similar to no-store, except that it doesn't remove the cache entry if one already exists. This is useful if the object returned is an alternate for the content normally delivered"),
      '#options' => array(
        FALSE => t('Not Specified'),
        'on' => t('On'),
        'off' => t('Off'),
      ),
    ),
    'akamai_dynamic_content_assembly' => array(
      '#title' => 'Dynamic Content Assembly (dca)',
      '#type' => 'radios',
      '#description' => t('Nominates the request for Dynamic Content Assembly. Valid argument is a type of processing. This header is used to nominate the object for ESI processing. At this time, "esi" and "akamaizer" specify the available processing types. These headers tell the edge server to process the objects in the ESI processor. The other header dca=noop, tells the edge server not to process the associated content.'),
      '#options' => array(
        FALSE => t('Not Specified'),
        'noop' => t('noop'),
        'esi' => t('esi'),
        'akamaizer' => t('akamaizer'),
      ),
    ),
  );
}

/**
 * Implements hook_http_header_build().
 *
 * Build the akamai edge control headers.
 *
 * @source: context_http_header module
 */
function akamai_context_http_header_build($http_header_items) {
  $header_builds = array();
  if ($value = $http_header_items['akamai_ttl']) {
    $header_builds["Edge-Control"][] = "Cache-maxage=" . check_plain($value);
  }
  if ($value = $http_header_items['akamai_nostore']) {
    if ($value == 'on') {
      $header_builds["Edge-Control"][] = "No-store";
    }
    elseif ($value == 'off') {
      $header_builds["Edge-Control"][] = "!No-store";
    }
  }
  if ($value = $http_header_items['akamai_bypass_cache']) {
    if ($value == 'on') {
      $header_builds["Edge-Control"][] = "bypass-cache";
    }
    elseif ($value == 'off') {
      $header_builds["Edge-Control"][] = "!bypass-cache";
    }
  }
  if ($value = $http_header_items['akamai_dynamic_content_assembly']) {
    if ($value == 'noop') {
      $header_builds["Edge-Control"][] = "dca=noop";
    }
    if ($value == 'esi') {
      $header_builds["Edge-Control"][] = "dca=esi";
    }
    if ($value == 'akamaizer') {
      $header_builds["Edge-Control"][] = "dca=akamaizer";
    }
  }
  return $header_builds;
}

/**
 * Returns the Akamai Cache Control class.
 *
 * This is abstracted to provide the capability of a no-op or recording class
 * for testing purposes.
 */
function akamai_get_class($params = array()) {
  module_load_include('inc', 'akamai', 'akamai.class');
  $class = variable_get('akamai_service_class', 'AkamaiCacheControlClient');
  $akamai = new $class($params);
  return $akamai;
}

Functions

Namesort descending Description
akamai_block Implements hook_block().
akamai_clear_url Clear a URL from Akamai. Clears node/id and any url aliases.
akamai_context_http_header_build Implements hook_http_header_build().
akamai_context_http_header_def Implements hook_context_http_header_def().
akamai_get_class Returns the Akamai Cache Control class.
akamai_get_notification_email Get the email address for notification.
akamai_menu Implements hook_menu().
akamai_nodeapi Implements hook_nodeapi().
akamai_page_cache_control_form Form for purging the current URL from the Akamai cache.
akamai_page_cache_control_form_submit Submit handler for akamai_page_cache_control.
akamai_perm Implements hook_perm().
akamai_theme Implements hook_theme().
_akamai_get_all_paths Return all system and alias paths for the provided URL.

Constants

Namesort descending Description
AKAMAI_EMAIL_DISABLE @file Integration with the Akamai CDN Cache Control Web Service.