You are here

term_reference_filter_by_views.module in Taxonomy Term Reference Filter by Views 7.2

Same filename and directory in other branches
  1. 7 term_reference_filter_by_views.module

File

term_reference_filter_by_views.module
View source
<?php

/**
 * Implements hook_menu().
 */
function term_reference_filter_by_views_menu() {
  $items['term_reference_filter_by_views/autocomplete/%/%/%'] = array(
    'title' => 'Autocomplete taxonomy',
    'page callback' => 'term_reference_filter_by_views_taxonomy_autocomplete',
    'page arguments' => array(
      2,
      3,
      4,
    ),
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_widget_field_form().
 * Alters the Taxonomy Term Reference Widgets.
 */
function term_reference_filter_by_views_field_widget_form_alter(&$element, &$form_state, $context) {

  // Add a css class to widget form elements for all fields of type mytype.
  if ($context['field']['type'] == 'taxonomy_term_reference') {
    if (!empty($context['instance']['view']['view_name'])) {
      if (isset($element['#type']) && ($element['#type'] == 'select' || $element['#type'] == 'radios' || $element['#type'] == 'checkboxes')) {
        $allowed = _get_referencable_terms_from_view($context['field'], $context['instance'], NULL, 0, NULL, FALSE);
        if (!empty($allowed)) {
          $options = array();
          if (isset($element['#options']['_none'])) {
            $options = array(
              '_none' => $element['#options']['_none'],
            );
          }
          else {
            $options = array(
              '_none' => theme('options_none', array(
                'instance' => $context['instance'],
                'option' => $element['#properties']['empty_option'],
              )),
            );
          }
          $options += array_intersect_key($allowed, $element['#options']);
          $element['#options'] = $options;
        }
      }
      elseif (isset($element['#autocomplete_path']) || isset($element['tid']['#autocomplete_path'])) {
        if (isset($element['#field_name'])) {
          $element['#instance'] = $context['instance'];
          $element['#autocomplete_path'] = 'term_reference_filter_by_views/autocomplete/' . $element['#field_name'] . '/' . $context['instance']['entity_type'] . '/' . $context['instance']['bundle'] . '/';
          if (!empty($context['instance']['view']['size'])) {
            $element['#size'] = $context['instance']['view']['size'];
          }
        }
        elseif (isset($element['tid']['#field_name'])) {
          $element['tid']['#instance'] = $context['instance'];
          if (!empty($element['tid']['#default_value'])) {
            $view_term = _get_referencable_terms_from_view($context['field'], $context['instance'], NULL, 0, array(
              $element['tid']['#default_value'],
            ));
            $element['tid']['#default_value'] = current($view_term);
          }
          if (!empty($context['instance']['view']['size'])) {
            $element['tid']['#size'] = $context['instance']['view']['size'];
          }
          $element['tid']['#element_validate'] = array(
            '_term_reference_autocomplete_validate',
          );
          $element['tid']['#autocomplete_path'] = 'term_reference_filter_by_views/autocomplete/' . $element['tid']['#field_name'] . '/' . $context['instance']['entity_type'] . '/' . $context['instance']['bundle'] . '/';
        }
      }
      elseif (isset($element['#autocomplete_deluxe_path']) && module_exists('autocomplete_deluxe')) {
        if (!empty($element['#default_value'])) {
          foreach ($context['items'] as $item) {
            $tids[$item['tid']] = $item['tid'];
          }
          $referencable_terms = _get_referencable_terms_from_view($context['field'], $context['instance'], NULL, 0, $tids);
          $element['#default_value'] = implode(',', $referencable_terms);
        }
        if (!empty($context['instance']['view']['size'])) {
          $element['#size'] = $context['instance']['view']['size'];
        }
        $element['#instance'] = $context['instance'];
        $element['#element_validate'] = array(
          '_term_reference_autocomplete_validate',
        );
        $element['#autocomplete_deluxe_path'] = url('term_reference_filter_by_views/autocomplete/' . $element['#field_name'] . '/' . $context['instance']['entity_type'] . '/' . $context['instance']['bundle'] . '/', array(
          'absolute' => TRUE,
        ));
      }
    }

    // Be sure not to overwrite existing attributes.
  }
}
function _term_reference_autocomplete_validate($element, &$form_state, $form) {

  // If a value was entered into the autocomplete...
  $value = '';
  if (!empty($element['#value'])) {
    if (isset($element['#multiple']) && $element['value_field']['#type'] == 'textfield') {
      $terms_data = drupal_explode_tags($element['#value']);
    }
    else {
      $terms_data = array(
        $element['#value'],
      );
    }
    foreach ($terms_data as $term_data) {

      // Take "label (term id)', match the id from parenthesis.
      if (preg_match("/.+\\(\\s*id\\s*:(\\d+)\\)/", $term_data, $matches)) {
        if ($element['#instance']['widget']['type'] == 'references_dialog_term_reference') {
          $value = $matches[1];
        }
        else {
          $value[] = (array) taxonomy_term_load($matches[1]);
        }
      }
      else {
        $value[] = NULL;
      }
    }
  }

  // Update the value of this element so the field can validate the product IDs.
  form_set_value($element, $value, $form_state);
}
function term_reference_filter_by_views_form_field_ui_field_edit_form_alter(&$form) {
  if ($form['#field']['type'] == 'taxonomy_term_reference') {
    $field = $form['#field'];
    $view_settings = empty($form['#instance']['view']) ? '' : $form['#instance']['view'];
    $displays = views_get_applicable_views('term_reference display');

    // Filter views that list the terms we want, and group the separate
    // displays by view.
    $entity_info = entity_get_info('taxonomy_term');
    $options = array();
    foreach ($displays as $data) {
      list($view, $display_id) = $data;
      if ($view->base_table == $entity_info['base table']) {
        $options[$view->name . ':' . $display_id] = $view->name . ' - ' . $view->display[$display_id]->display_title;
      }
    }
    if ($options) {

      // The value of the 'view_and_display' select below will need to be split
      // into 'view_name' and 'view_display' in the final submitted values, so
      // we massage the data at validate time on the wrapping element (not
      // ideal).
      $options = array(
        '' => '<' . t('none') . '>',
      ) + $options;
      $default = !empty($view_settings['view_name']) ? $view_settings['view_name'] . ':' . $view_settings['display_name'] : '';
      $form['instance']['view'] = array(
        '#type' => 'fieldset',
        '#title' => t('Filter by Views settings'),
        '#element_validate' => array(
          'term_reference_view_settings_validate',
        ),
      );
      $form['instance']['view']['view_and_display'] = array(
        '#type' => 'select',
        '#title' => t('View used to select the entities'),
        '#options' => $options,
        '#default_value' => $default,
        '#description' => '<p>' . t('Choose the view and display that select the entities that can be referenced.<br />Only views with a display of type "Term Reference" are eligible.') . '</p>',
      );
      $default = !empty($view_settings['args']) ? implode(', ', $view_settings['args']) : '';
      $form['instance']['view']['args'] = array(
        '#type' => 'textfield',
        '#title' => t('View arguments'),
        '#default_value' => $default,
        '#required' => FALSE,
        '#description' => t('Provide a comma separated list of arguments to pass to the view.'),
      );
      $form['instance']['view']['match_operator'] = array(
        '#type' => 'select',
        '#title' => t('Autocomplete matching'),
        '#default_value' => isset($view_settings['match_operator']) ? $view_settings['match_operator'] : 'CONTAINS',
        '#options' => array(
          'STARTS_WITH' => t('Starts with'),
          'CONTAINS' => t('Contains'),
        ),
        '#description' => t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of terms.'),
      );
      $form['instance']['view']['size'] = array(
        '#type' => 'textfield',
        '#title' => t('Size of textfield'),
        '#default_value' => isset($view_settings['size']) ? $view_settings['size'] : NULL,
        '#element_validate' => array(
          '_element_validate_integer_positive',
        ),
      );
      if (!empty($view_settings['view_name']) && isset($form['instance']['widget']['settings']['autocomplete_match'])) {
        $form['instance']['widget']['settings']['autocomplete_match']['#access'] = FALSE;
      }
    }
    else {
      $form['instance']['view']['no_view_help'] = array(
        '#markup' => '<p>' . t('No eligible views were found. <a href="@create">Create a view</a> with an <em>Term Reference</em> display, or add such a display to an <a href="@existing">existing view</a>.', array(
          '@create' => url('admin/structure/views/add'),
          '@existing' => url('admin/structure/views'),
        )) . '</p>',
      );
    }
  }
}
function term_reference_view_settings_validate($element, &$form_state, $form) {

  // Split view name and display name from the 'view_and_display' value.
  if (!empty($element['view_and_display']['#value'])) {
    list($view, $display) = explode(':', $element['view_and_display']['#value']);
  }
  else {
    $view = '';
    $display = '';
  }

  // Explode the 'args' string into an actual array. Beware, explode() turns an
  // empty string into an array with one empty string. We'll need an empty array
  // instead.
  $args_string = trim($element['args']['#value']);
  if ($args_string === '') {
    $args = array();
  }
  else {

    // array_map is called to trim whitespaces from the arguments.
    $args = array_map('trim', explode(',', $args_string));
  }
  $value = array(
    'view_name' => $view,
    'display_name' => $display,
    'args' => $args,
    'match_operator' => $element['match_operator']['#value'],
    'size' => $element['size']['#value'],
  );
  form_set_value($element, $value, $form_state);
}

/**
 * Autocomplete callback for widgets that has autocomplete.
 */
function term_reference_filter_by_views_taxonomy_autocomplete($field_name, $entity_type, $bundle_name, $tid = '', $string = '') {
  $field = field_info_field($field_name);
  $instance = field_info_instance($entity_type, $field_name, $bundle_name);
  $matches = array();
  $type = 'tags';
  if (!$field || !$instance || $field['type'] != 'taxonomy_term_reference' || !field_access('edit', $field, $entity_type)) {
    return MENU_ACCESS_DENIED;
  }
  $term = NULL;
  if ($tid !== 'NULL' && is_numeric($tid)) {
    $term = entity_load_single('taxonomy_term', $tid);
    if (!$term || !entity_access('view', 'taxonomy_term', $term)) {
      return MENU_ACCESS_DENIED;
    }
  }

  //$handler = entityreference_get_selection_handler($field, $instance, 'taxonomy_term_reference', $term);
  if ($type == 'tags') {

    // The user enters a comma-separated list of tags. We only autocomplete the last tag.
    $tags_typed = drupal_explode_tags($string);
    $tag_last = drupal_strtolower(array_pop($tags_typed));
    if (!empty($tag_last)) {
      $prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
    }
  }
  else {

    // The user enters a single tag.
    $prefix = '';
    $tag_last = $string;
  }
  if (!empty($tag_last)) {

    // Get an array of matching entities.
    $term_labels = _get_referencable_terms_from_view($field, $instance, $tag_last, 15, NULL, FALSE);

    // Loop through the products and convert them into autocomplete output.
    foreach ($term_labels as $tid => $label) {
      $key = "{$label} (id:{$tid})";

      // Names containing commas or quotes must be wrapped in quotes.
      if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
        $key = '"' . str_replace('"', '""', $key) . '"';
      }

      //$matches[$prefix . $key] = '<div class="reference-autocomplete">' . $key . '</div>';
      $matches[$prefix . $key] = $key;
    }
  }
  drupal_json_output($matches);
}

/**
 * Get the TermsImplements hook_views_plugins_alter().
 */
function _get_referencable_terms_from_view($field, $instance, $match = NULL, $limit = 0, $ids = NULL, $width_tid = TRUE) {
  $terms = array();
  $view_terms = _get_view_results_for_term_reference_field($field, $instance, $match, $limit, $ids);
  foreach ($view_terms as $tid => $row) {

    // Strip things like starting/trailing white spaces, line breaks and tags.
    $terms[$tid] = preg_replace('/\\s\\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($row)))));
    if ($width_tid) {
      $terms[$tid] .= " (id:{$tid})";
    }
  }
  return $terms;
}

/**
 * Get the TermsImplements hook_views_plugins_alter().
 */
function _get_view_results_for_term_reference_field($field, $instance, $match = NULL, $limit = 0, $ids = NULL) {
  $display_name = $instance['view']['display_name'];
  $args = $instance['view']['args'];
  $result = array();
  $view_name = $instance['view']['view_name'];
  $display_name = $instance['view']['display_name'];
  $args = $instance['view']['args'];
  $entity_type = 'taxonomy_term';

  // Check that the view is valid and the display still exists.
  $view = views_get_view($view_name);
  if (!$view || !isset($view->display[$display_name]) || !$view
    ->access($display_name)) {
    watchdog('term_reference_filter_by_views', 'The view %view_name is no longer eligible for the %field_name field.', array(
      '%view_name' => $view_name,
      '%field_name' => $instance['label'],
    ), WATCHDOG_WARNING);
    return $result;
  }
  $view
    ->set_display($display_name);

  // Make sure the query is not cached.
  $view->is_cacheable = FALSE;

  // Pass options to the display handler to make them available later.
  $term_reference_options = array(
    'match' => $match,
    'match_operator' => isset($instance['view']['match_operator']) ? $instance['view']['match_operator'] : 'CONTAINS',
    'limit' => $limit,
    'ids' => $ids,
  );
  $view->display_handler
    ->set_option('term_reference_options', $term_reference_options);

  // Get the results.
  $result = $view
    ->execute_display($display_name, $args);
  return $result;
}

/**
 * Implements hook_views_api().
 */
function term_reference_filter_by_views_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'term_reference_filter_by_views') . '/views',
  );
}

/**
 * Implements hook_views_plugins_alter().
 */
function term_reference_filter_by_views_views_plugins_alter(&$plugins) {

  // Change the handler for references dialog.
  if (isset($plugins['display']['references_dialog'])) {
    $plugins['display']['references_dialog']['handler'] = 'term_reference_references_dialog_plugin_display';
  }
}