You are here

display_cache.module in Display Cache 7

Module file for Display Cache.

File

display_cache.module
View source
<?php

/**
 * @file
 * Module file for Display Cache.
 */
define('DISPLAY_CACHE_DISABLED', '0');
define('DISPLAY_CACHE_ENABLED', '1');
define('DISPLAY_CACHE_FIELDS', '2');

/**
 * Name of the display cache cache bin.
 *
 * @var string
 */
define('DISPLAY_CACHE_CACHE_BIN', 'cache_display_cache');

/**
 * Suffix to a view mode that triggers on-the-fly re-rendering of the view mode.
 *
 * @var string
 */
define('DISPLAY_CACHE_VIEW_MODE_CACHE_IGNORE_SUFFIX', ':display_cache_no_cache');

/**
 * Implements hook_form_FORM_ID_alter().
 */
function display_cache_form_field_ui_display_overview_form_alter(&$form, &$form_state) {
  if (user_access('administer display cache')) {
    form_load_include($form_state, 'inc', 'display_cache', 'display_cache.admin');
    display_cache_field_ui_form($form, $form_state);
  }
}

/**
 * Implements hook_flush_caches().
 */
function display_cache_flush_caches() {
  return array(
    DISPLAY_CACHE_CACHE_BIN,
  );
}

/**
 * Implements hook_menu().
 */
function display_cache_menu() {
  $menu_items = array();

  // Menu item for configurations.
  $menu_items['admin/config/development/display-cache'] = array(
    'title' => 'Display Cache',
    'description' => 'Form to submit cache flush for display cache.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'display_cache_flush_caches_form',
    ),
    'access arguments' => array(
      'administer display cache',
    ),
    'file' => 'display_cache.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  return $menu_items;
}

/**
 * Implements hook_admin_menu_cache_info().
 */
function display_cache_admin_menu_cache_info() {
  $links['display_cache'] = array(
    'title' => 'Display Caches',
    'callback' => '_display_cache_clear_cache',
  );
  return $links;
}

/**
 * Implements hook_permission().
 */
function display_cache_permission() {
  return array(
    'administer display cache' => array(
      'title' => t('Administer Display Cache'),
      'description' => t('Permission to administer display cache settings and clear display cache caches from the ui.'),
    ),
  );
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * FORM ID: node_type
 */
function display_cache_form_node_type_form_alter(&$form, &$form_state) {
  if (user_access('administer display cache')) {
    form_load_include($form_state, 'inc', 'display_cache', 'display_cache.admin');
    display_cache_node_type_form($form, $form_state);
  }
}

/**
 * Implements hook_comment_publish().
 */
function display_cache_comment_publish($comment) {
  display_cache_clear_comment_host_entity_cache($comment);
}

/**
 * Implements hook_comment_update().
 *
 * This basically implements hook_comment_unpublished() which is not implemented
 * by the comment module.
 */
function display_cache_comment_update($comment) {
  if ($comment->original && $comment->original->status != $comment->status && $comment->status == COMMENT_NOT_PUBLISHED) {
    display_cache_clear_comment_host_entity_cache($comment);
  }
}

/**
 * Implements hook_comment_delete().
 */
function display_cache_comment_delete($comment) {
  if ($comment->status == COMMENT_PUBLISHED) {
    display_cache_clear_comment_host_entity_cache($comment);
  }
}

/**
 * Implemenets hook_node_type_delete().
 */
function display_cache_node_type_delete($info) {
  variable_del('display_cache_comment_publication_clears_host_entity_cache_' . $info->type);
  variable_del('display_cache_comment_publication_clears_host_entity_cache_node_' . $info->type);
}

/**
 * Clears the given comments' host entity if needed.
 *
 * @param Object $comment
 *   The comment.
 */
function display_cache_clear_comment_host_entity_cache($comment) {
  if ($comment->nid) {
    $node = node_load($comment->nid);
    $should_clear_host_entity_cache_globally = variable_get('display_cache_comment_publication_clears_host_entity_cache_' . $node->type, FALSE);
    $view_modes_to_clear = display_cache_get_comment_publication_clears_host_entity_cache_settings('node', $node->type);
    if ($should_clear_host_entity_cache_globally) {
      display_cache_flush_cache('node', $node);
    }
    elseif (!empty($view_modes_to_clear) && array_sum($view_modes_to_clear) > 0) {
      foreach ($view_modes_to_clear as $view_mode => $clear) {
        if ($clear) {
          display_cache_flush_cache('node', $node, $view_mode);
        }
      }
    }
  }
}

/**
 * Flushes the display cache of a given element.
 *
 * If one argument is missing, all following arguments will be replaced with a
 * wildcard.
 *
 * @param string $entity_type
 *   Type like 'node' or 'comment'.
 *
 * @param int|object $entity
 *   (optional) Entity object or entity id.
 *   Leave empty to flush all entities of given type.
 *
 * @param string $view_mode
 *   (optional) View mode like 'full' or 'teaser'.
 *   Leave empty to flush all view modes of given entity.
 *
 * @param string $element
 *   (optional) Element like 'entity', 'field' or 'render_array'.
 */
function display_cache_flush_cache($entity_type, $entity = NULL, $view_mode = NULL, $element = NULL) {
  if (is_object($entity)) {
    $entity = entity_id($entity_type, $entity);
  }
  $keys = display_cache_get_cache_keys($entity_type, $entity, $view_mode, $element);
  $cache_id = implode(':', $keys) . ':';
  cache_clear_all($cache_id, DISPLAY_CACHE_CACHE_BIN, TRUE);
}

/**
 * Implements hook_entity_delete().
 */
function display_cache_entity_delete($entity, $entity_type) {
  display_cache_flush_cache($entity_type, $entity);
}

/**
 * Implements hook_entity_update().
 */
function display_cache_entity_update($entity, $entity_type) {
  display_cache_flush_cache($entity_type, $entity);
}

/**
 * Returns display cache settings for given entity type, bundle and view mode.
 *
 * @param string $entity_type
 *   The entity type like 'node' or 'comment'.
 *
 * @param string $bundle
 *   Bundle within the entity type like 'page' or 'article' for nodes.
 *
 * @param string $view_mode
 *   View mode like 'full' or 'teaser'.
 *
 * @return array
 *   Array with settings.
 */
function display_cache_get_settings($entity_type, $bundle, $view_mode) {
  $settings = variable_get('display_cache_' . $entity_type . '_' . $bundle . '_' . $view_mode, FALSE);

  // Get default settings.
  if (!$settings && $view_mode !== 'default') {
    $settings = display_cache_get_settings($entity_type, $bundle, 'default');
    if (!$settings) {
      return array(
        'default' => array(
          'use' => DISPLAY_CACHE_DISABLED,
          'page_granularity' => DISPLAY_CACHE_DISABLED,
          'user_granularity' => DISPLAY_CACHE_DISABLED,
          'granularity' => DISPLAY_CACHE_DISABLED,
        ),
      );
    }
  }
  return $settings;
}

/**
 * Collects the comment publication host entity cache clearing settings.
 *
 * @param string $entity_type
 *   This is almost all the type 'node'. It is just here for completeness.
 * @param string $bundle
 *   The bundle to check.
 * @param string $view_mode
 *   If given, the settings obtained will be searched for this specific view
 *   mode.
 *
 * @return mixed(array|bool)
 *   Returns a setting array or a bool if $view_mode has been given.
 */
function display_cache_get_comment_publication_clears_host_entity_cache_settings($entity_type, $bundle, $view_mode = NULL) {
  $variable_name = display_cache_get_comment_publication_clears_host_entity_cache_setting_name($entity_type, $bundle);
  $result = variable_get($variable_name, array());
  if (!is_null($view_mode)) {
    $result = isset($result[$view_mode]) ? $result[$view_mode] : FALSE;
  }
  return $result;
}

/**
 * Implements hook_entity_view_alter().
 */
function display_cache_entity_view_alter(&$build, $entity_type) {

  // Disable display_cache by global setting.
  if (variable_get('display_cache_disable', FALSE) === TRUE) {
    return;
  }
  $view_mode = $build['#view_mode'];
  $bundle = isset($build['#bundle']) ? $build['#bundle'] : NULL;
  $settings = display_cache_get_settings($entity_type, $bundle, $view_mode);
  $display_settings = $settings['default'];

  // Cache if enabled.
  if ($display_settings['use'] === DISPLAY_CACHE_ENABLED) {
    if (!empty($build['#' . $entity_type])) {
      $entity_id = entity_id($entity_type, $build['#' . $entity_type]);
    }
    elseif (!empty($build['#entity'])) {
      $entity_id = entity_id($entity_type, $build['#entity']);
    }
    elseif ($entity_type === 'taxonomy_term') {
      $entity_id = entity_id($entity_type, $build['#term']);
    }
    elseif ($entity_type === 'user') {
      $entity_id = entity_id($entity_type, $build['#account']);
    }
    else {

      // Create watchdog entry for unsupported entities.
      watchdog('display cache', 'The entity type %entity_type seems to be unsupported until now. Please report this to the issue cue of Display Cache so we can fix this.', array(
        '%entity_type' => $entity_type,
      ), WATCHDOG_WARNING);
      return;
    }
    $keys = display_cache_get_cache_keys($entity_type, $entity_id, $view_mode, 'entity');
    $build['#cache'] = array(
      'keys' => $keys,
      'bin' => DISPLAY_CACHE_CACHE_BIN,
      'granularity' => $display_settings['granularity'],
    );
  }
}

/**
 * Implements hook_field_attach_view_alter().
 */
function display_cache_field_attach_view_alter(&$output, $context) {

  // Disable display_cache by global setting.
  if (variable_get('display_cache_disable', FALSE) === TRUE) {
    return;
  }
  $entity_type = $context['entity_type'];
  $entity_id = entity_id($entity_type, $context['entity']);
  $view_mode = $context['view_mode'];

  // Get bundle.
  if (isset($output['#bundle'])) {
    $bundle = $output['#bundle'];
  }
  else {
    $first_field = reset($output);
    $bundle = $first_field['#bundle'];
  }
  $settings = display_cache_get_settings($entity_type, $bundle, $view_mode);

  // Cache if enabled.
  if (!empty($settings['default']['use']) && $settings['default']['use'] == DISPLAY_CACHE_FIELDS) {
    foreach (element_children($output) as $field_name) {
      if (array_key_exists($field_name, $settings) && $settings[$field_name]['override'] != DISPLAY_CACHE_DISABLED) {
        $element =& $output[$field_name];
        $keys = display_cache_get_cache_keys($entity_type, $entity_id, $view_mode, $field_name);
        $element['#cache'] = array(
          'keys' => $keys,
          'bin' => DISPLAY_CACHE_CACHE_BIN,
          'granularity' => $settings[$field_name]['granularity'],
        );
      }
    }
  }
}

/**
 * Implements hook_entity_info_alter().
 */
function display_cache_entity_info_alter(&$entity_info) {
  foreach ($entity_info as &$entity) {

    // Replace view callback with own callback display_cache_view_entity().
    if (isset($entity['view callback'])) {
      $entity['entity view callback'] = $entity['view callback'];
    }
    $entity['view callback'] = 'display_cache_view_entity';
  }
}

/**
 * Alternative entity view callback.
 *
 * This callback replacing all view callbacks provided by the Entity module. The
 * original callback will be called within this callback. In this way it is
 * possible to return the cached html before a renderable array is build.
 *
 * @see entity_view()
 * @see display_cache_entity_info_alter()
 */
function display_cache_view_entity($entities, $view_mode, $langcode, $entity_type) {
  $info = entity_get_info($entity_type);
  $return = array();
  $ignore_cache = strpos($view_mode, DISPLAY_CACHE_VIEW_MODE_CACHE_IGNORE_SUFFIX) !== FALSE;
  if ($ignore_cache) {
    $view_mode = str_replace(':display_cache_no_cache', '', $view_mode);
  }
  foreach ($entities as $entity_id => $entity) {
    $render_array = array();

    // Disable display_cache by global setting.
    if (!$ignore_cache && variable_get('display_cache_disable', FALSE) === FALSE) {
      if (!empty($info['entity keys']['bundle'])) {
        $bundle_key = $info['entity keys']['bundle'];
      }
      elseif (!empty($entity_info['bundle keys']['bundle'])) {
        $bundle_key = $info['bundle keys']['bundle'];
      }

      // This entity may not have a bundle key, fallback to the entity type.
      if (isset($bundle_key) && !empty($entity->{$bundle_key})) {
        $bundle = $entity->{$bundle_key};
      }
      else {
        $bundle = $entity_type;
      }
      $render_array = display_cache_get_cached_display($entity_type, $bundle, $entity_id, $view_mode, 'entity');
    }

    // No cached result was found. Render entity.
    if (empty($render_array)) {

      // Get renderable array.
      if (isset($info['entity view callback'])) {
        $entity = entity_key_array_by_property(array(
          $entity,
        ), $info['entity keys']['id']);
        $render_array = $info['entity view callback']($entity, $view_mode, $langcode, $entity_type);
        $render_array = $render_array[$entity_type][$entity_id];
      }
      elseif (in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) {
        $render_array = entity_get_controller($entity_type)
          ->view(array(
          $entity,
        ), $view_mode, $langcode);
        $render_array = $render_array[$entity_type][$entity_id];
      }
    }
    $return[$entity_type][$entity_id] = $render_array;
  }
  return $return;
}

/**
 * Check for cached display.
 *
 * @param string $entity_type
 *   The entity type.
 * @param string $bundle
 *   The bundle.
 * @param int $entity_id
 *   The entity id.
 * @param string $view_mode
 *   The view mode.
 * @param string $element
 *   Element which is cached like 'entity', 'field' or 'render_array'.
 *
 * @return array
 *   A render array containing only a #markup index.
 */
function display_cache_get_cached_display($entity_type, $bundle = NULL, $entity_id = NULL, $view_mode = NULL, $element = 'entity') {

  // Initiate result.
  $result = array();

  // Get settings.
  $settings = display_cache_get_settings($entity_type, $bundle, $view_mode);
  if ($settings['default']['use'] == DISPLAY_CACHE_ENABLED) {

    // Get rendered HTML from cache.
    $keys = display_cache_get_cache_keys($entity_type, $entity_id, $view_mode, $element);
    $cid_parts = array(
      '#cache' => array(
        'keys' => $keys,
        'granularity' => $settings['default']['granularity'],
      ),
    );
    $cid = drupal_render_cid_create($cid_parts);
    $data = cache_get($cid, DISPLAY_CACHE_CACHE_BIN);
    if (!empty($data->data)) {
      $result = $data->data;
    }
  }
  return $result;
}

/**
 * Implements hook_module_implements_alter().
 *
 * Move the hook implementation of hook_entity_info_alter() to the bottom to be
 * able to alter the view callback added by Entity API during this hook.
 *
 * @see entity_entity_info_alter()
 * @see display_cache_install()
 */
function display_cache_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'entity_info_alter') {

    // Move our hook implementation to the bottom.
    $group = $implementations['display_cache'];
    unset($implementations['display_cache']);
    $implementations['display_cache'] = $group;
  }
}

/**
 * Returns array of given keys for caching.
 *
 * @param string $entity_type
 *   Entity type like 'node'.
 *
 * @param int $entity_id
 *   Entity ID.
 *
 * @param string $view_mode
 *   View mode like 'full' or 'teaser'.
 *
 * @param null $element
 *   Element which is cached like 'entity', 'field' or 'render_array'.
 *
 * @return array
 *   Array with keys to build the cache id.
 */
function display_cache_get_cache_keys($entity_type, $entity_id = NULL, $view_mode = NULL, $element = NULL) {
  $keys = array(
    'entity_type' => $entity_type,
  );
  if (!empty($entity_id)) {
    $keys['entity_id'] = $entity_id;
    if (!empty($view_mode)) {
      $keys['view_mode'] = $view_mode;
      if (!empty($element)) {
        $keys['element'] = $element;
      }
    }
  }
  drupal_alter('display_cache_cache_keys', $keys);
  return $keys;
}

/**
 * Clears cache of display_cache module.
 */
function _display_cache_clear_cache() {
  cache_clear_all('*', DISPLAY_CACHE_CACHE_BIN, TRUE);
}

/**
 * Returns setting name for comment publication host entity clearing.
 */
function display_cache_get_comment_publication_clears_host_entity_cache_setting_name($entity_type, $bundle) {
  $variable_name_parts = array(
    'display_cache_comment_publication_clears_host_entity_cache',
    $entity_type,
    $bundle,
  );
  return implode('_', $variable_name_parts);
}

Functions

Namesort descending Description
display_cache_admin_menu_cache_info Implements hook_admin_menu_cache_info().
display_cache_clear_comment_host_entity_cache Clears the given comments' host entity if needed.
display_cache_comment_delete Implements hook_comment_delete().
display_cache_comment_publish Implements hook_comment_publish().
display_cache_comment_update Implements hook_comment_update().
display_cache_entity_delete Implements hook_entity_delete().
display_cache_entity_info_alter Implements hook_entity_info_alter().
display_cache_entity_update Implements hook_entity_update().
display_cache_entity_view_alter Implements hook_entity_view_alter().
display_cache_field_attach_view_alter Implements hook_field_attach_view_alter().
display_cache_flush_cache Flushes the display cache of a given element.
display_cache_flush_caches Implements hook_flush_caches().
display_cache_form_field_ui_display_overview_form_alter Implements hook_form_FORM_ID_alter().
display_cache_form_node_type_form_alter Implements hook_form_FORM_ID_alter().
display_cache_get_cached_display Check for cached display.
display_cache_get_cache_keys Returns array of given keys for caching.
display_cache_get_comment_publication_clears_host_entity_cache_settings Collects the comment publication host entity cache clearing settings.
display_cache_get_comment_publication_clears_host_entity_cache_setting_name Returns setting name for comment publication host entity clearing.
display_cache_get_settings Returns display cache settings for given entity type, bundle and view mode.
display_cache_menu Implements hook_menu().
display_cache_module_implements_alter Implements hook_module_implements_alter().
display_cache_node_type_delete Implemenets hook_node_type_delete().
display_cache_permission Implements hook_permission().
display_cache_view_entity Alternative entity view callback.
_display_cache_clear_cache Clears cache of display_cache module.

Constants

Namesort descending Description
DISPLAY_CACHE_CACHE_BIN Name of the display cache cache bin.
DISPLAY_CACHE_DISABLED @file Module file for Display Cache.
DISPLAY_CACHE_ENABLED
DISPLAY_CACHE_FIELDS
DISPLAY_CACHE_VIEW_MODE_CACHE_IGNORE_SUFFIX Suffix to a view mode that triggers on-the-fly re-rendering of the view mode.