You are here

entity_autocomplete.module in Entity Autocomplete 7

Provides functionalities for entity fields autocompletion.

File

entity_autocomplete.module
View source
<?php

/**
 * @file
 * Provides functionalities for entity fields autocompletion.
 */

/**
 * Implements hook_permission().
 *
 */
function entity_autocomplete_permission() {
  return array(
    'use entity autocomplete' => array(
      'title' => t('Use Entity Autocomplete'),
      'description' => t('Access any entity autocomplete path. Warning: gives visibility to all entities.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function entity_autocomplete_menu() {
  $items = array();
  $items['entity-autocomplete/%'] = array(
    'title' => 'Entity Autocomplete',
    'page callback' => 'entity_autocomplete',
    'page arguments' => array(
      2,
      1,
      NULL,
    ),
    'access arguments' => array(
      'use entity autocomplete',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['entity-autocomplete/bundle/%/%entity_autocomplete_bundles'] = array(
    'title' => 'Entity Autocomplete',
    'page callback' => 'entity_autocomplete',
    'page arguments' => array(
      4,
      2,
      3,
    ),
    'access arguments' => array(
      'use entity autocomplete',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Returns a bundles list from bundles path part.
 */
function entity_autocomplete_bundles_load($bundles) {
  return empty($bundles) ? NULL : explode('+', $bundles);
}

/**
 * Autocomplete callback for all entity types.
 */
function entity_autocomplete($string, $entity_type, $bundles = NULL) {
  $info = entity_get_info($entity_type);
  $table = $info['base table'];
  $matches = array();
  if (drupal_strlen($string)) {
    $query = db_select($table);
    $query
      ->addField($table, $info['entity keys']['id'], 'id');
    $query
      ->addField($table, $info['entity keys']['label'], 'label');
    $query
      ->condition($info['entity keys']['label'], '%' . db_like($string) . '%', 'LIKE');
    if ($bundles) {
      $bundle_field = $info['entity keys']['bundle'];
      if ($entity_type == 'taxonomy_term') {
        $vids = array_map(create_function('$v', 'return $v->vid;'), taxonomy_vocabulary_get_names());
        $bundles = array_intersect_key($vids, array_flip($bundles));
        $bundle_field = 'vid';
      }
      $query
        ->condition($bundle_field, $bundles);
    }
    $result = $query
      ->execute();
    while ($entity = $result
      ->fetch()) {
      $label = entity_autocomplete_get_label($entity->label, $entity->id);
      $matches[$label] = $label;
    }
  }
  drupal_json_output($matches);
}

/**
 *  Build a label smaller than 128 chars.
 */
function entity_autocomplete_get_label($label, $id) {
  $label = check_plain($label);
  $ref_id = ' [id:' . $id . ']';
  $ref_id_length = strlen($ref_id);
  if (drupal_strlen($label) >= 128 - $ref_id_length) {
    $label = drupal_substr($label, 124 - $ref_id_length) . ' ...';
  }
  return strip_tags(decode_entities($label)) . $ref_id;
}

/**
 * Extract an ID from a string returned by one of the autocomplete callbacks.
 *
 * @param $value
 *   A string of the form "title [id:ID]" or an integer.
 * @return
 *   The extracted ID.
 */
function entity_autocomplete_get_id($value) {
  return is_numeric($value) ? $value : preg_replace('/.*\\[id:(.*)\\].*/', '${1}', $value);
}

/**
 * Implements hook_element_info().
 */
function entity_autocomplete_element_info() {
  return array(
    'entity_autocomplete' => array(
      '#input' => TRUE,
      '#size' => 60,
      '#maxlength' => 128,
      '#autocomplete_path' => FALSE,
      '#entity_type' => 'node',
      '#bundles' => array(),
      '#process' => array(
        'entity_autocomplete_element_process',
        'ajax_process_form',
      ),
      '#element_validate' => array(
        'entity_autocomplete_element_validate',
      ),
      '#value_callback' => 'entity_autocomplete_element_value',
      '#theme' => 'textfield',
      '#theme_wrappers' => array(
        'form_element',
      ),
    ),
  );
}

/**
 * Determines the value for an entity autocomplete form element.
 * 
 * @param $form
 *   The form element whose value is being populated.
 * @param $input
 *   The incoming input to populate the form element. If this is FALSE,
 *   the element's default value should be returned.
 *
 * @return
 *   The data that will appear in the $element_state['values'] collection
 *   for this element. Return nothing to use the default.
 */
function entity_autocomplete_element_value($element, $input = FALSE) {
  if (FALSE === $input) {
    return isset($element['#default_value']) ? $element['#default_value'] : '';
  }
  else {
    return isset($input) && strlen($input) ? entity_autocomplete_get_id($input) : '';
  }
}

/**
 * Form element processing handler for the #autocomplete_path property.
 */
function entity_autocomplete_element_process($element, $form_state) {
  if (user_access('use entity autocomplete')) {
    $parts = array(
      'entity-autocomplete',
      $element['#entity_type'],
    );
    if ($element['#bundles']) {
      array_splice($parts, 1, 0, 'bundle');
      $parts[] = implode('+', (array) $element['#bundles']);
    }
    $element['#autocomplete_path'] = implode('/', $parts);

    // Try to display the full entity label.
    if (!empty($element['#value']) && is_numeric($element['#value'])) {
      $element['#value'] = entity_autocomplete_element_label($element['#value'], $element['#entity_type']);
    }
  }

  // Function form_process_autocomplete() was added to core in v7.39.
  if (function_exists('form_process_autocomplete')) {
    $element = form_process_autocomplete($element);
  }

  // Keep compatible with AJAX.
  if (isset($element['#ajax']) && !isset($element['#ajax']['event'])) {

    // The ajax_pre_render_element() function does not recognize the
    // 'entity_autocomplete' element, so is does not add the expected event
    // that triggers the AJAX callback.
    $element['#ajax']['event'] = 'blur';
  }
  return $element;
}

/**
 * Retrieve entity label from its ID.
 */
function entity_autocomplete_element_label($entity_id, $entity_type) {
  $labels =& drupal_static(__FUNCTION__, array());
  if (!isset($labels[$entity_type][$entity_id])) {
    $labels[$entity_type][$entity_id] = $entity_id;
    if ($info = entity_get_info($entity_type)) {
      $table = $info['base table'];
      $query = db_select($table);
      $query
        ->addField($table, $info['entity keys']['label'], 'label');
      $query
        ->condition($info['entity keys']['id'], $entity_id);
      if ($row = $query
        ->execute()
        ->fetch()) {
        $labels[$entity_type][$entity_id] = entity_autocomplete_get_label($row->label, $entity_id);
      }
    }
  }
  return $labels[$entity_type][$entity_id];
}

/**
 * Validate an entity autocomplete element.
 */
function entity_autocomplete_element_validate($element, &$form_state, $form) {
  if (!empty($element['#value'])) {
    $info = entity_get_info($element['#entity_type']);
    $table = $info['base table'];
    $id_field = $info['entity keys']['id'];
    $query = db_select($table);
    $query
      ->addField($table, $id_field);
    $query
      ->condition($id_field, entity_autocomplete_get_id($element['#value']));
    if ($element['#bundles']) {
      $bundle_field = $info['entity keys']['bundle'];
      $bundles = (array) $element['#bundles'];

      // Handle taxonomy seperately as its bundles work differently.
      if ($element['#entity_type'] === 'taxonomy_term') {
        $vids = array();
        foreach (taxonomy_get_vocabularies() as $vid => $vocab) {
          $vids[$vocab->machine_name] = $vid;
        }
        $bundles = array_intersect_key($vids, array_flip($bundles));
        $bundle_field = 'vid';
      }
      $query
        ->condition($bundle_field, $bundles);
    }
    if (!$query
      ->execute()
      ->fetch()) {
      form_error($element, t('The specified entity %value does not match requirements.', array(
        '%value' => $element['#value'],
      )));
    }
  }
}

Functions

Namesort descending Description
entity_autocomplete Autocomplete callback for all entity types.
entity_autocomplete_bundles_load Returns a bundles list from bundles path part.
entity_autocomplete_element_info Implements hook_element_info().
entity_autocomplete_element_label Retrieve entity label from its ID.
entity_autocomplete_element_process Form element processing handler for the #autocomplete_path property.
entity_autocomplete_element_validate Validate an entity autocomplete element.
entity_autocomplete_element_value Determines the value for an entity autocomplete form element.
entity_autocomplete_get_id Extract an ID from a string returned by one of the autocomplete callbacks.
entity_autocomplete_get_label Build a label smaller than 128 chars.
entity_autocomplete_menu Implements hook_menu().
entity_autocomplete_permission Implements hook_permission().