You are here

uuid_link.module in UUID Link 7

Same filename and directory in other branches
  1. 6 uuid_link.module

Provides a filter and UI for adding links to entities that are not affected by changes in URL alias.

File

uuid_link.module
View source
<?php

/**
 * @file
 * Provides a filter and UI for adding links to entities that are not affected
 * by changes in URL alias.
 */

/**
 * Implements hook_permission().
 */
function uuid_link_permission() {
  return array(
    'access uuid link' => array(
      'title' => t('Access UUID Link'),
      'description' => t('Access UUID Link user interface.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function uuid_link_menu() {
  $items = array();
  $items['uuid-link/autocomplete'] = array(
    'page callback' => 'uuid_link_autocomplete',
    'access arguments' => array(
      'access uuid link',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_wysiwyg_plugin().
 */
function uuid_link_wysiwyg_plugin($editor, $version) {
  if ($editor == 'ckeditor') {
    return array(
      'uuid_link' => array(
        'path' => drupal_get_path('module', 'uuid_link') . '/plugins/link/',
        'load' => TRUE,
        'extensions' => array(
          'uuid_link' => t('Link to site content'),
        ),
      ),
    );
  }
}

/**
 * Implements hook_element_info_alter().
 */
function uuid_link_element_info_alter(&$type) {
  if (!empty($type['text_format'])) {
    $type['form']['#post_render'][] = 'uuid_link_form_post_render';
  }
}

/**
 * Implements hook_entity_info_alter().
 */
function uuid_link_entity_info_alter(&$entity_info) {

  // The username should be the label for users.
  $entity_info['user']['entity keys']['label'] = 'name';
}

/**
 * Post render callback. Load javascript files and set module settings.
 */
function uuid_link_form_post_render($content, $element) {
  static $added;
  if (empty($added) && ($js = drupal_add_js()) && isset($js['settings']['data'])) {
    $settings = call_user_func_array('array_merge_recursive', $js['settings']['data']);
    if (isset($settings['ckeditor']) || isset($settings['wysiwyg']['configs']['ckeditor'])) {

      // Pre-load all UUID titles.
      preg_match_all('/\\[uuid-link:(.*?)\\]/', $content, $matches);
      $tokens = array_unique($matches[1]);
      $uuids = array();
      foreach ($tokens as $token) {
        list($entity_type, $uuid) = explode(':', $token);
        $uuids[$entity_type][$uuid] = $uuid;
      }
      foreach (array_keys($uuids) as $entity_type) {
        $entities = entity_uuid_load($entity_type, $uuids[$entity_type]);
        foreach ($entities as $entity_id => $entity) {

          // If content is language specific show language code.
          if (!empty($entity->language) && $entity->language != LANGUAGE_NONE) {
            $uuids[$entity_type][$entity->uuid] = t('[@language] @label', array(
              '@language' => $entity->language,
              '@label' => entity_label($type, $entity),
            ));
          }
          else {
            $uuids[$entity_type][$entity->uuid] = check_plain(entity_label($entity_type, $entity));
          }
        }
      }
      $added = TRUE;
      drupal_add_js('misc/autocomplete.js');
      $settings = array(
        'autocomplete_path' => url('uuid-link/autocomplete'),
        'type_name' => uuid_link_get_link_type_name(),
        'uuids' => $uuids,
      );
      drupal_add_js(array(
        'uuid_link' => $settings,
      ), 'setting');
    }
  }
  return $content;
}

/**
 * Get a list of available link types. In this case, a list of entity types.
 */
function uuid_link_get_link_type_name() {
  $types = array();
  $entity_types = entity_get_info();
  foreach ($entity_types as $entity_type => $entity_type_info) {

    // Only include entities that have a label key set. And that supports UUID.
    if (!empty($entity_type_info['entity keys']['label']) && !empty($entity_type_info['entity keys']['uuid'])) {
      $types[$entity_type] = $entity_type_info['label'];
    }
  }
  return $types;
}

/**
 * Implements hook_token_info().
 */
function uuid_link_token_info() {
  $type = array(
    'name' => t('UUID Link'),
    'description' => t('UUID Link tokens.'),
    'needs-data' => 'uuid-link',
  );
  $entity_types = entity_get_info();
  $tokens = array();
  foreach ($entity_types as $entity_type => $entity_type_info) {
    $tokens[$entity_type] = array(
      'name' => t('UUID Link @entity', array(
        '@entity' => $entity_type_info['label'],
      )),
      'description' => t('Translates into an URL to the @entity entity matching the UUID.', array(
        '@entity' => $entity_type_info['label'],
      )),
      'dynamic' => TRUE,
    );
  }
  return array(
    'types' => array(
      'uuid-link' => $type,
    ),
    'tokens' => array(
      'uuid-link' => $tokens,
    ),
  );
}

/**
 * Implements hook_tokens().
 */
function uuid_link_tokens($type, $tokens, array $data = array(), array $options = array()) {
  if ($type != 'uuid-link') {
    return array();
  }
  static $seen_uuids = array();
  static $replacements = array();
  $entity_types = entity_get_info();
  foreach ($entity_types as $entity_type => $info) {
    if (empty($info['entity keys']['label']) || empty($info['entity keys']['uuid'])) {
      continue;
    }
    if ($uuid_link_tokens = token_find_with_prefix($tokens, $entity_type)) {
      foreach ($uuid_link_tokens as $uuid => $original) {

        // Prevent infinite loops and wasted lookups.
        if (isset($replacements[$original])) {
          continue;
        }
        if ($entity_type == 'node') {

          // We create a dummy node with only nid and language, as an attempt
          // to get a working URL for the node.
          $dummy_node = db_select('node', 'n')
            ->fields('n', array(
            'nid',
            'language',
          ))
            ->condition($info['entity keys']['uuid'], $uuid)
            ->execute()
            ->fetchObject();
          if (!empty($dummy_node)) {
            $replacements[$original] = _uuid_link_entity_url(node_uri($dummy_node), $dummy_node);
            continue;
          }
        }
        else {

          // Prevent infinite loops for non-nodes. This has the potential to
          // result in failed token replacements in the case of circular references.
          if (isset($seen_uuids[$uuid])) {
            continue;
          }
          $seen_uuids[$uuid] = TRUE;
          $entities = entity_uuid_load($entity_type, array(
            $uuid,
          ));
          $entity = array_shift($entities);

          // If the entity we just loaded itself has a [uuid-link] token for another entity which
          // in turn has a [uuid-link] token for the original entity, (ie. a circular reference)
          // then the value will already have been set in the static $replacements[$original], so we
          // don't need to load it.
          if (isset($replacements[$original])) {
            continue;
          }
        }
        if (!empty($entity)) {
          $replacements[$original] = uuid_link_entity_url($entity_type, $entity);
        }
        else {

          // Set link URL to this hash, so the link tag can be removed later
          // in the filter process function.
          $replacements[$original] = '#uuid-link-not-found';
          watchdog('uuid_link', 'This content contains a link to content that no longer exists. Please edit the page and remove or change this link to get rid of this notice.');
        }
      }
    }
  }

  // Return replacements only for the tokens it was asked for.
  return array_intersect_key($replacements, array_flip($tokens));
}

/**
 * Get URL to an entity.
 *
 * @param $entity_type
 *   Entity type.
 * @param $entity
 *   Entity object.
 */
function uuid_link_entity_url($entity_type, $entity) {
  switch ($entity_type) {
    default:
      $uri = entity_uri($entity_type, $entity);
      if (is_array($uri)) {
        return _uuid_link_entity_url($uri, $entity);
      }
    case 'file':
      return file_create_url($entity->uri);
  }
}

/**
 * Helper function for getting an language aware url.
 */
function _uuid_link_entity_url($uri, $entity) {
  $languages =& drupal_static(__FUNCTION__);
  if (empty($languages)) {
    $languages = language_list();
  }
  if (!isset($uri['options']['language']) && !empty($entity->language) && LANGUAGE_NONE !== $entity->language) {
    $uri['options']['language'] = $languages[$entity->language];
  }
  return url($uri['path'], empty($uri['options']) ? array() : $uri['options']);
}

/**
 * Implements hook_filter_info().
 */
function uuid_link_filter_info() {
  $filters = array();
  $filters['uuid_link_filter'] = array(
    'title' => t('UUID Link filter'),
    'description' => t('Converts links added through UUID Link into actual link to content.'),
    'process callback' => 'uuid_link_filter_process',
  );
  return $filters;
}

/**
 * Filter process callback.
 */
function uuid_link_filter_process($text, $filter, $format, $langcode, $cache, $cache_id) {

  // Replace uuid-link tokens tokens.
  $data = array(
    'uuid-link' => array(),
  );
  $text = token_replace($text, $data);

  // Remove tags for links to content that no longer exists. Check the existence
  // of the #uuid-link-not-found before doing this expensive job.
  if (strpos($text, '#uuid-link-not-found') !== FALSE) {
    $dom = filter_dom_load($text);
    $xpath = new DOMXPath($dom);
    foreach ($xpath
      ->query('//a[@href="#uuid-link-not-found"]') as $link) {

      // Move all link tag content to its parent node just before it.
      while ($link
        ->hasChildNodes()) {
        $child = $link
          ->removeChild($link->firstChild);
        $link->parentNode
          ->insertBefore($child, $link);
      }

      // Remove the link tag.
      $link->parentNode
        ->removeChild($link);
    }
    $text = filter_dom_serialize($dom);
  }
  return $text;
}

/**
 * Autocomplete callback for entities.
 */
function uuid_link_autocomplete($type, $string = '') {
  $matches = array();
  $entity_type = entity_get_info($type);
  if (!empty($string) && !empty($entity_type['entity keys']['label'])) {
    $label_key = $entity_type['entity keys']['label'];
    $query = new EntityFieldQuery();
    $query
      ->entityCondition('entity_type', $type)
      ->propertyCondition($label_key, $string, 'STARTS_WITH')
      ->propertyOrderBy($label_key)
      ->range(0, 15);

    // Only search for published nodes.
    if ($type == 'node') {
      $query
        ->propertyCondition('status', 1);
    }
    if ($result = $query
      ->execute()) {
      $entities = entity_load($type, array_keys($result[$type]));
      $uuids = entity_get_uuid_by_id($type, array_keys($entities));
      foreach ($entities as $entity_id => $entity) {
        $uuid = $uuids[$entity_id];

        // If content is language specific show language code.
        if (!empty($entity->language) && $entity->language != LANGUAGE_NONE) {
          $label = t('[@language] @label', array(
            '@language' => $entity->language,
            '@label' => entity_label($type, $entity),
          ));
        }
        else {
          $label = check_plain(entity_label($type, $entity));
        }
        $matches[t('@label (@uuid)', array(
          '@label' => $label,
          '@uuid' => $uuid,
        ))] = $label;
      }
    }
  }
  drupal_json_output($matches);
}

Functions

Namesort descending Description
uuid_link_autocomplete Autocomplete callback for entities.
uuid_link_element_info_alter Implements hook_element_info_alter().
uuid_link_entity_info_alter Implements hook_entity_info_alter().
uuid_link_entity_url Get URL to an entity.
uuid_link_filter_info Implements hook_filter_info().
uuid_link_filter_process Filter process callback.
uuid_link_form_post_render Post render callback. Load javascript files and set module settings.
uuid_link_get_link_type_name Get a list of available link types. In this case, a list of entity types.
uuid_link_menu Implements hook_menu().
uuid_link_permission Implements hook_permission().
uuid_link_tokens Implements hook_tokens().
uuid_link_token_info Implements hook_token_info().
uuid_link_wysiwyg_plugin Implements hook_wysiwyg_plugin().
_uuid_link_entity_url Helper function for getting an language aware url.