Browse Source

update 7.1

Signed-off-by: bachy <git@g-u-i.net>
bachy 12 years ago
parent
commit
4ef59776b0

+ 90 - 0
entityreference.diff.inc

@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Provide Diff module field callbacks for the Entity Reference module.
+ */
+
+/**
+ * Diff field callback for preloading entities.
+ */
+function entityreference_field_diff_view_prepare(&$old_items, &$new_items, $context) {
+  $field = $context['field'];
+
+  // Build an array of entities ID.
+  $entity_ids = array();
+  foreach (array_merge_recursive($old_items, $new_items) as $item) {
+    $entity_ids[] = $item['target_id'];
+  }
+
+  // Load those entities and loop through them to extract their labels.
+  $entities = entity_load($field['settings']['target_type'], $entity_ids);
+
+  foreach ($old_items as $delta => $info) {
+    $old_items[$delta]['entity'] = isset($entities[$info['target_id']]) ? $entities[$info['target_id']] : NULL;
+  }
+  foreach ($new_items as $delta => $info) {
+    $new_items[$delta]['entity'] = isset($entities[$info['target_id']]) ? $entities[$info['target_id']] : NULL;
+  }
+}
+
+/**
+ * Diff field callback for parsing entity field comparative values.
+ */
+function entityreference_field_diff_view($items, $context) {
+  $field = $context['field'];
+  $instance = $context['instance'];
+  $settings = $context['settings'];
+  $entity_type = $field['settings']['target_type'];
+
+  $diff_items = array();
+
+  // We populate as much as possible to allow the best flexability in any
+  // string overrides.
+  $t_args = array();
+  $t_args['!entity_type'] = $entity_type;
+
+  $entity_info = entity_get_info($entity_type);
+  $t_args['!entity_type_label'] = $entity_info['label'];
+
+  foreach ($items as $delta => $item) {
+    if (isset($item['entity'])) {
+      $output = array();
+
+      list($id,, $bundle) = entity_extract_ids($entity_type, $item['entity']);
+      $t_args['!id'] = $id;
+      $t_args['!bundle'] = $bundle;
+      $t_args['!diff_entity_label'] = entity_label($entity_type, $item['entity']);
+
+      $output['entity'] = t('!diff_entity_label', $t_args);
+      if ($settings['show_id']) {
+        $output['id'] = t('ID: !id', $t_args);
+      }
+      $diff_items[$delta] = implode('; ', $output);
+    }
+  }
+
+  return $diff_items;
+}
+
+/**
+ * Provide default field comparison options.
+ */
+function entityreference_field_diff_default_options($field_type) {
+  return array(
+    'show_id' => 0,
+  );
+}
+
+/**
+ * Provide a form for setting the field comparison options.
+ */
+function entityreference_field_diff_options_form($field_type, $settings) {
+  $options_form = array();
+  $options_form['show_id'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Show ID'),
+    '#default_value' => $settings['show_id'],
+  );
+  return $options_form;
+}

+ 4 - 3
entityreference.info

@@ -19,11 +19,12 @@ files[] = views/entityreference_plugin_row_fields.inc
 
 ; Tests.
 files[] = tests/entityreference.handlers.test
+files[] = tests/entityreference.taxonomy.test
 files[] = tests/entityreference.admin.test
 
-; Information added by drupal.org packaging script on 2012-09-25
-version = "7.x-1.0-rc5"
+; Information added by drupal.org packaging script on 2012-11-18
+version = "7.x-1.0"
 core = "7.x"
 project = "entityreference"
-datestamp = "1348565045"
+datestamp = "1353230808"
 

+ 4 - 1
entityreference.migrate.inc

@@ -9,7 +9,10 @@
  * Implement hook_migrate_api().
  */
 function entityreference_migrate_api() {
-  return array('api' => 2);
+  return array(
+    'api' => 2,
+    'field_handlers' => array('MigrateEntityReferenceFieldHandler'),
+  );
 }
 
 class MigrateEntityReferenceFieldHandler extends MigrateSimpleFieldHandler {

+ 110 - 12
entityreference.module

@@ -330,6 +330,49 @@ function entityreference_field_attach_delete($entity_type, $entity) {
   }
 }
 
+/**
+ * Implements hook_entity_insert().
+ */
+function entityreference_entity_insert($entity, $entity_type) {
+  entityreference_entity_crud($entity, $entity_type, 'entityPostInsert');
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function entityreference_entity_update($entity, $entity_type) {
+  entityreference_entity_crud($entity, $entity_type, 'entityPostUpdate');
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function entityreference_entity_delete($entity, $entity_type) {
+  entityreference_entity_crud($entity, $entity_type, 'entityPostDelete');
+}
+
+/**
+ * Invoke a behavior based on entity CRUD.
+ *
+ * @param $entity
+ *   The entity object.
+ * @param $entity_type
+ *   The entity type.
+ * @param $method_name
+ *   The method to invoke.
+ */
+function entityreference_entity_crud($entity, $entity_type, $method_name) {
+  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+  foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) {
+    $field = field_info_field($field_name);
+    if ($field['type'] == 'entityreference') {
+      foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
+        $handler->{$method_name}($entity_type, $entity, $field, $instance);
+      }
+    }
+  }
+}
+
 /**
  * Implements hook_field_settings_form().
  */
@@ -719,7 +762,21 @@ function entityreference_field_widget_settings_form($field, $instance) {
  * Implements hook_options_list().
  */
 function entityreference_options_list($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
-  return entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->getReferencableEntities();
+  if (!$options = entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->getReferencableEntities()) {
+    return array();
+  }
+
+  // Rebuild the array, by changing the bundle key into the bundle label.
+  $target_type = $field['settings']['target_type'];
+  $entity_info = entity_get_info($target_type);
+
+  $return = array();
+  foreach ($options as $bundle => $entity_ids) {
+    $bundle_label = check_plain($entity_info['bundles'][$bundle]['label']);
+    $return[$bundle_label] = $entity_ids;
+  }
+
+  return count($return) == 1 ? reset($return) : $return;
 }
 
 /**
@@ -830,6 +887,10 @@ function _entityreference_autocomplete_validate($element, &$form_state, $form) {
       // autocomplete but filled in a value manually.
       $field = field_info_field($element['#field_name']);
       $handler = entityreference_get_selection_handler($field);
+      $field_name = $element['#field_name'];
+      $field = field_info_field($field_name);
+      $instance = field_info_instance($element['#entity_type'], $field_name, $element['#bundle']);
+      $handler = entityreference_get_selection_handler($field, $instance);
       $value = $handler->validateAutocompleteInput($element['#value'], $element, $form_state, $form);
     }
   }
@@ -916,6 +977,31 @@ function entityreference_autocomplete_access_callback($type, $field_name, $entit
 function entityreference_autocomplete_callback($type, $field_name, $entity_type, $bundle_name, $entity_id = '', $string = '') {
   $field = field_info_field($field_name);
   $instance = field_info_instance($entity_type, $field_name, $bundle_name);
+
+  return entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id, $string);
+}
+
+/**
+ * Return JSON based on given field, instance and string.
+ *
+ * This function can be used by other modules that wish to pass a mocked
+ * definition of the field on instance.
+ *
+ * @param $type
+ *   The widget type (i.e. 'single' or 'tags').
+ * @param $field
+ *   The field array defintion.
+ * @param $instance
+ *   The instance array defintion.
+ * @param $entity_type
+ *   The entity type.
+ * @param $entity_id
+ *   Optional; The entity ID the entity-reference field is attached to.
+ *   Defaults to ''.
+ * @param $string
+ *   The label of the entity to query by.
+ */
+function entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id = '', $string = '') {
   $matches = array();
 
   $entity = NULL;
@@ -925,6 +1011,7 @@ function entityreference_autocomplete_callback($type, $field_name, $entity_type,
       return MENU_ACCESS_DENIED;
     }
   }
+
   $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
 
   if ($type == 'tags') {
@@ -946,15 +1033,17 @@ function entityreference_autocomplete_callback($type, $field_name, $entity_type,
     $entity_labels = $handler->getReferencableEntities($tag_last, $instance['widget']['settings']['match_operator'], 10);
 
     // Loop through the products and convert them into autocomplete output.
-    foreach ($entity_labels as $entity_id => $label) {
-      $key = "$label ($entity_id)";
-      // Strip things like starting/trailing white spaces, line breaks and tags.
-      $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key)))));
-      // Names containing commas or quotes must be wrapped in quotes.
-      if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
-        $key = '"' . str_replace('"', '""', $key) . '"';
+    foreach ($entity_labels as $values) {
+      foreach ($values as $entity_id => $label) {
+        $key = "$label ($entity_id)";
+        // Strip things like starting/trailing white spaces, line breaks and tags.
+        $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key)))));
+        // 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">' . $label . '</div>';
       }
-      $matches[$prefix . $key] = '<div class="reference-autocomplete">' . $label . '</div>';
     }
   }
 
@@ -1083,12 +1172,14 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $
     $rekey = FALSE;
 
     foreach ($items[$id] as $delta => $item) {
-      // Check whether the referenced entity could be loaded and that the user has access to it.
-      if (isset($target_entities[$item['target_id']]) && entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']])) {
+      // Check whether the referenced entity could be loaded.
+      if (isset($target_entities[$item['target_id']])) {
         // Replace the instance value with the term data.
         $items[$id][$delta]['entity'] = $target_entities[$item['target_id']];
+        // Check whether the user has access to the referenced entity.
+        $items[$id][$delta]['access'] = entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']]);
       }
-      // Otherwise, unset the instance value, since the entity does not exists or should not be accessible.
+      // Otherwise, unset the instance value, since the entity does not exist.
       else {
         unset($items[$id][$delta]);
         $rekey = TRUE;
@@ -1109,6 +1200,13 @@ function entityreference_field_formatter_view($entity_type, $entity, $field, $in
   $result = array();
   $settings = $display['settings'];
 
+  // Rebuild the items list to contain only those with access.
+  foreach ($items as $key => $item) {
+    if (empty($item['access'])) {
+      unset($items[$key]);
+    }
+  }
+
   switch ($display['type']) {
     case 'entityreference_label':
       $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);

+ 3 - 3
examples/entityreference_behavior_example/entityreference_behavior_example.info

@@ -4,9 +4,9 @@ core = 7.x
 package = Fields
 dependencies[] = entityreference
 
-; Information added by drupal.org packaging script on 2012-09-25
-version = "7.x-1.0-rc5"
+; Information added by drupal.org packaging script on 2012-11-18
+version = "7.x-1.0"
 core = "7.x"
 project = "entityreference"
-datestamp = "1348565045"
+datestamp = "1353230808"
 

+ 189 - 0
plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php

@@ -0,0 +1,189 @@
+<?php
+
+/**
+ * @file
+ * CTools plugin class for the taxonomy-index behavior.
+ */
+
+/**
+ * Extends an entityreference field to maintain its references to taxonomy terms
+ * in the {taxonomy_index} table.
+ *
+ * Note, unlike entityPostInsert() and entityPostUpdate(), entityDelete()
+ * is not needed as cleanup is performed by taxonomy module in
+ * taxonomy_delete_node_index().
+ */
+class EntityReferenceBehavior_TaxonomyIndex extends EntityReference_BehaviorHandler_Abstract {
+
+  /**
+   * Overrides EntityReference_BehaviorHandler_Abstract::access().
+   *
+   * Ensure that it is only enabled for ER instances on nodes targeting
+   * terms, and the core variable to maintain index is enabled.
+   */
+  public function access($field, $instance) {
+    if ($instance['entity_type'] != 'node' || $field['settings']['target_type'] != 'taxonomy_term') {
+      return;
+    }
+
+    if ($field['storage']['type'] !== 'field_sql_storage') {
+      // Field doesn't use SQL storage.
+      return;
+    }
+
+    return variable_get('taxonomy_maintain_index_table', TRUE);
+  }
+
+  /**
+   * Overrides EntityReference_BehaviorHandler_Abstract::entityPostInsert().
+   *
+   * Runs after hook_node_insert() used by taxonomy module.
+   */
+  public function entityPostInsert($entity_type, $entity, $field, $instance) {
+    if ($entity_type != 'node') {
+      return;
+    }
+
+    $this->buildNodeIndex($entity);
+  }
+
+  /**
+   * Overrides EntityReference_BehaviorHandler_Abstract::entityPostUpdate().
+   *
+   * Runs after hook_node_update() used by taxonomy module.
+   */
+  public function entityPostUpdate($entity_type, $entity, $field, $instance) {
+    if ($entity_type != 'node') {
+      return;
+    }
+
+    $this->buildNodeIndex($entity);
+  }
+
+  /**
+   * Builds and inserts taxonomy index entries for a given node.
+   *
+   * The index lists all terms that are related to a given node entity, and is
+   * therefore maintained at the entity level.
+   *
+   * @param $node
+   *   The node object.
+   *
+   * @see taxonomy_build_node_index()
+   */
+  protected function buildNodeIndex($node) {
+    // We maintain a denormalized table of term/node relationships, containing
+    // only data for current, published nodes.
+    $status = NULL;
+    if (variable_get('taxonomy_maintain_index_table', TRUE)) {
+      // If a node property is not set in the node object when node_save() is
+      // called, the old value from $node->original is used.
+      if (!empty($node->original)) {
+        $status = (int)(!empty($node->status) || (!isset($node->status) && !empty($node->original->status)));
+        $sticky = (int)(!empty($node->sticky) || (!isset($node->sticky) && !empty($node->original->sticky)));
+      }
+      else {
+        $status = (int)(!empty($node->status));
+        $sticky = (int)(!empty($node->sticky));
+      }
+    }
+    // We only maintain the taxonomy index for published nodes.
+    if ($status) {
+      // Collect a unique list of all the term IDs from all node fields.
+      $tid_all = array();
+      foreach (field_info_instances('node', $node->type) as $instance) {
+        $field_name = $instance['field_name'];
+        $field = field_info_field($field_name);
+        if (!empty($field['settings']['target_type']) && $field['settings']['target_type'] == 'taxonomy_term' && $field['storage']['type'] == 'field_sql_storage') {
+          // If a field value is not set in the node object when node_save() is
+          // called, the old value from $node->original is used.
+          if (isset($node->{$field_name})) {
+            $items = $node->{$field_name};
+          }
+          elseif (isset($node->original->{$field_name})) {
+            $items = $node->original->{$field_name};
+          }
+          else {
+            continue;
+          }
+          foreach (field_available_languages('node', $field) as $langcode) {
+            if (!empty($items[$langcode])) {
+              foreach ($items[$langcode] as $item) {
+                $tid_all[$item['target_id']] = $item['target_id'];
+              }
+            }
+          }
+        }
+
+        // Re-calculate the terms added in taxonomy_build_node_index() so
+        // we can optimize database queries.
+        $original_tid_all = array();
+        if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') {
+          // If a field value is not set in the node object when node_save() is
+          // called, the old value from $node->original is used.
+          if (isset($node->{$field_name})) {
+            $items = $node->{$field_name};
+          }
+          elseif (isset($node->original->{$field_name})) {
+            $items = $node->original->{$field_name};
+          }
+          else {
+            continue;
+          }
+          foreach (field_available_languages('node', $field) as $langcode) {
+            if (!empty($items[$langcode])) {
+              foreach ($items[$langcode] as $item) {
+                $original_tid_all[$item['tid']] = $item['tid'];
+              }
+            }
+          }
+        }
+      }
+
+      // Insert index entries for all the node's terms, that were not
+      // already inserted in taxonomy_build_node_index().
+      $tid_all = array_diff($tid_all, $original_tid_all);
+
+      // Insert index entries for all the node's terms.
+      if (!empty($tid_all)) {
+        $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created'));
+        foreach ($tid_all as $tid) {
+          $query->values(array(
+            'nid' => $node->nid,
+            'tid' => $tid,
+            'sticky' => $sticky,
+            'created' => $node->created,
+          ));
+        }
+        $query->execute();
+      }
+    }
+  }
+
+  /**
+   * Overrides EntityReference_BehaviorHandler_Abstract::settingsForm().
+   */
+  public function settingsForm($field, $instance) {
+    $form = array();
+    $target = $field['settings']['target_type'];
+    if ($target != 'taxonomy_term') {
+      $form['ti-on-terms'] = array(
+        '#markup' => t('This behavior can only be set when the target type is taxonomy_term, but the target of this field is %target.', array('%target' => $target)),
+      );
+    }
+
+    $entity_type = $instance['entity_type'];
+    if ($entity_type != 'node') {
+      $form['ti-on-nodes'] = array(
+        '#markup' => t('This behavior can only be set when the entity type is node, but the entity type of this instance is %type.', array('%type' => $entity_type)),
+      );
+    }
+
+    if (!variable_get('taxonomy_maintain_index_table', TRUE)) {
+      $form['ti-disabled'] = array(
+        '#markup' => t('This core variable "taxonomy_maintain_index_table" is disabled.'),
+      );
+    }
+    return $form;
+  }
+}

+ 27 - 0
plugins/behavior/abstract.inc

@@ -107,6 +107,27 @@ interface EntityReference_BehaviorHandler {
    */
   public function postDelete($entity_type, $entity, $field, $instance);
 
+  /**
+   * Act after inserting an entity.
+   *
+   * @see hook_entity_insert()
+   */
+  public function entityPostInsert($entity_type, $entity, $field, $instance);
+
+  /**
+   * Act after updating an entity.
+   *
+   * @see hook_entity_update()
+   */
+  public function entityPostUpdate($entity_type, $entity, $field, $instance);
+
+  /**
+   * Act after deleting an entity.
+   *
+   * @see hook_entity_delete()
+   */
+  public function entityPostDelete($entity_type, $entity, $field, $instance);
+
   /**
    * Generate a settings form for this handler.
    */
@@ -167,6 +188,12 @@ abstract class EntityReference_BehaviorHandler_Abstract implements EntityReferen
 
   public function postDelete($entity_type, $entity, $field, $instance) {}
 
+  public function entityPostInsert($entity_type, $entity, $field, $instance) {}
+
+  public function entityPostUpdate($entity_type, $entity, $field, $instance) {}
+
+  public function entityPostDelete($entity_type, $entity, $field, $instance) {}
+
   public function settingsForm($field, $instance) {}
 
   public function access($field, $instance) {

+ 16 - 0
plugins/behavior/taxonomy-index.inc

@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * @file
+ * CTools plugin declaration for taxonomy-index behavior.
+ */
+
+if (module_exists('taxonomy')) {
+  $plugin = array(
+    'title' => t('Taxonomy index'),
+    'description' => t('Include the term references created by instances of this field carried by node entities in the core {taxonomy_index} table. This will allow various modules to handle them like core term_reference fields.'),
+    'class' => 'EntityReferenceBehavior_TaxonomyIndex',
+    'behavior type' => 'instance',
+    'force enabled' => TRUE,
+  );
+}

+ 78 - 60
plugins/selection/EntityReference_SelectionHandler_Generic.class.php

@@ -169,7 +169,8 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select
     if (!empty($results[$entity_type])) {
       $entities = entity_load($entity_type, array_keys($results[$entity_type]));
       foreach ($entities as $entity_id => $entity) {
-        $options[$entity_id] = check_plain($this->getLabel($entity));
+        list(,, $bundle) = entity_extract_ids($entity_type, $entity);
+        $options[$bundle][$entity_id] = check_plain($this->getLabel($entity));
       }
     }
 
@@ -305,6 +306,42 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select
   public function getLabel($entity) {
     return entity_label($this->field['settings']['target_type'], $entity);
   }
+
+  /**
+   * Ensure a base table exists for the query.
+   *
+   * If we have a field-only query, we want to assure we have a base-table
+   * so we can later alter the query in entityFieldQueryAlter().
+   *
+   * @param $query
+   *   The Select query.
+   *
+   * @return
+   *   The alias of the base-table.
+   */
+  public function ensureBaseTable(SelectQueryInterface $query) {
+    $tables = $query->getTables();
+
+    // Check the current base table.
+    foreach ($tables as $table) {
+      if (empty($table['join'])) {
+        $alias = $table['alias'];
+        break;
+      }
+    }
+
+    if (strpos($alias, 'field_data_') !== 0) {
+      // The existing base-table is the correct one.
+      return $alias;
+    }
+
+    // Join the known base-table.
+    $target_type = $this->field['settings']['target_type'];
+    $entity_info = entity_get_info($target_type);
+    $id = $entity_info['entity keys']['id'];
+    // Return the alias of the table.
+    return $query->innerJoin($target_type, NULL, "$target_type.$id = $alias.entity_id");
+  }
 }
 
 /**
@@ -320,8 +357,8 @@ class EntityReference_SelectionHandler_Generic_node extends EntityReference_Sele
     // modules in use on the site. As long as one access control module is there,
     // it is supposed to handle this check.
     if (!user_access('bypass node access') && !count(module_implements('node_grants'))) {
-      $tables = $query->getTables();
-      $query->condition(key($tables) . '.status', NODE_PUBLISHED);
+      $base_table = $this->ensureBaseTable($query);
+      $query->condition("$base_table.status", NODE_PUBLISHED);
     }
   }
 }
@@ -332,27 +369,6 @@ class EntityReference_SelectionHandler_Generic_node extends EntityReference_Sele
  * This only exists to workaround core bugs.
  */
 class EntityReference_SelectionHandler_Generic_user extends EntityReference_SelectionHandler_Generic {
-  /**
-   * Implements EntityReferenceHandler::settingsForm().
-   */
-  public static function settingsForm($field, $instance) {
-    $settings = $field['settings']['handler_settings'];
-    $form = parent::settingsForm($field, $instance);
-    $form['referenceable_roles'] = array(
-      '#type' => 'checkboxes',
-      '#title' => t('User roles that can be referenced'),
-      '#default_value' => isset($settings['referenceable_roles']) ? array_filter($settings['referenceable_roles']) : array(),
-      '#options' => user_roles(TRUE),
-    );
-    $form['referenceable_status'] = array(
-      '#type' => 'checkboxes',
-      '#title' => t('User status that can be referenced'),
-      '#default_value' => isset($settings['referenceable_status']) ? array_filter($settings['referenceable_status']) : array('active' => 'active'),
-      '#options' => array('active' => t('Active'), 'blocked' => t('Blocked')),
-    );
-    return $form;
-  }
-
   public function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') {
     $query = parent::buildEntityFieldQuery($match, $match_operator);
 
@@ -361,33 +377,21 @@ class EntityReference_SelectionHandler_Generic_user extends EntityReference_Sele
       $query->propertyCondition('name', $match, $match_operator);
     }
 
-    $field = $this->field;
-    $settings = $field['settings']['handler_settings'];
-    $referenceable_roles = isset($settings['referenceable_roles']) ? array_filter($settings['referenceable_roles']) : array();
-    $referenceable_status = isset($settings['referenceable_status']) ? array_filter($settings['referenceable_status']) : array('active' => 'active');
-
-    // If this filter is not filled, use the users access permissions.
-    if (empty($referenceable_status)) {
-      // Adding the 'user_access' tag is sadly insufficient for users: core
-      // requires us to also know about the concept of 'blocked' and 'active'.
-      if (!user_access('administer users')) {
-        $query->propertyCondition('status', 1);
-      }
-    }
-    elseif (count($referenceable_status) == 1) {
-      $values = array('active' => 1, 'blocked' => 0);
-      $query->propertyCondition('status', $values[key($referenceable_status)]);
+    // Adding the 'user_access' tag is sadly insufficient for users: core
+    // requires us to also know about the concept of 'blocked' and
+    // 'active'.
+    if (!user_access('administer users')) {
+      $query->propertyCondition('status', 1);
     }
-
     return $query;
   }
 
   public function entityFieldQueryAlter(SelectQueryInterface $query) {
-    $conditions = &$query->conditions();
     if (user_access('administer users')) {
-      // If the user is administrator, we need to make sure to
+      // In addition, if the user is administrator, we need to make sure to
       // match the anonymous user, that doesn't actually have a name in the
       // database.
+      $conditions = &$query->conditions();
       foreach ($conditions as $key => $condition) {
         if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users.name') {
           // Remove the condition.
@@ -414,19 +418,6 @@ class EntityReference_SelectionHandler_Generic_user extends EntityReference_Sele
         }
       }
     }
-
-    $field = $this->field;
-    $settings = $field['settings']['handler_settings'];
-    $referenceable_roles = isset($settings['referenceable_roles']) ? array_filter($settings['referenceable_roles']) : array();
-    if (!$referenceable_roles || !empty($referenceable_roles[DRUPAL_AUTHENTICATED_RID])) {
-      // Return early if "authenticated user" choosen.
-      return;
-    }
-
-    if (!isset($referenceable_roles[DRUPAL_AUTHENTICATED_RID])) {
-      $query->join('users_roles', 'users_roles', 'users.uid = users_roles.uid');
-      $query->condition('users_roles.rid', array_keys($referenceable_roles), 'IN');
-    }
   }
 }
 
@@ -441,8 +432,8 @@ class EntityReference_SelectionHandler_Generic_comment extends EntityReference_S
     // requires us to also know about the concept of 'published' and
     // 'unpublished'.
     if (!user_access('administer comments')) {
-      $tables = $query->getTables();
-      $query->condition(key($tables) . '.status', COMMENT_PUBLISHED);
+      $base_table = $this->ensureBaseTable($query);
+      $query->condition("$base_table.status", COMMENT_PUBLISHED);
     }
 
     // The Comment module doesn't implement any proper comment access,
@@ -514,8 +505,7 @@ class EntityReference_SelectionHandler_Generic_taxonomy_term extends EntityRefer
     // The Taxonomy module doesn't implement any proper taxonomy term access,
     // and as a consequence doesn't make sure that taxonomy terms cannot be viewed
     // when the user doesn't have access to the vocabulary.
-    $tables = $query->getTables();
-    $base_table = key($tables);
+    $base_table = $this->ensureBaseTable($query);
     $vocabulary_alias = $query->innerJoin('taxonomy_vocabulary', 'n', '%alias.vid = ' . $base_table . '.vid');
     $query->addMetadata('base_table', $vocabulary_alias);
     // Pass the query to the taxonomy access control.
@@ -532,4 +522,32 @@ class EntityReference_SelectionHandler_Generic_taxonomy_term extends EntityRefer
       }
     }
   }
+
+  /**
+   * Implements EntityReferenceHandler::getReferencableEntities().
+   */
+  public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
+    if ($match || $limit) {
+      return parent::getReferencableEntities($match , $match_operator, $limit);
+    }
+
+    $options = array();
+    $entity_type = $this->field['settings']['target_type'];
+
+    // We imitate core by calling taxonomy_get_tree().
+    $entity_info = entity_get_info('taxonomy_term');
+    $bundles = !empty($this->field['settings']['handler_settings']['target_bundles']) ? $this->field['settings']['handler_settings']['target_bundles'] : array_keys($entity_info['bundles']);
+
+    foreach ($bundles as $bundle) {
+      if ($vocabulary = taxonomy_vocabulary_machine_name_load($bundle)) {
+        if ($terms = taxonomy_get_tree($vocabulary->vid, 0)) {
+          foreach ($terms as $term) {
+            $options[$vocabulary->machine_name][$term->tid] = str_repeat('-', $term->depth) . check_plain($term->name);
+          }
+        }
+      }
+    }
+
+    return $options;
+  }
 }

+ 0 - 489
plugins/selection/EntityReference_SelectionHandler_Generic.class.php.orig

@@ -1,489 +0,0 @@
-<?php
-
-/**
- * A generic Entity handler.
- *
- * The generic base implementation has a variety of overrides to workaround
- * core's largely deficient entity handling.
- */
-class EntityReference_SelectionHandler_Generic implements EntityReference_SelectionHandler {
-
-  /**
-   * Implements EntityReferenceHandler::getInstance().
-   */
-  public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
-    $target_entity_type = $field['settings']['target_type'];
-
-    // Check if the entity type does exist and has a base table.
-    $entity_info = entity_get_info($target_entity_type);
-    if (empty($entity_info['base table'])) {
-      return EntityReference_SelectionHandler_Broken::getInstance($field, $instance);
-    }
-
-    if (class_exists($class_name = 'EntityReference_SelectionHandler_Generic_' . $target_entity_type)) {
-      return new $class_name($field, $instance, $entity_type, $entity);
-    }
-    else {
-      return new EntityReference_SelectionHandler_Generic($field, $instance, $entity_type, $entity);
-    }
-  }
-
-  protected function __construct($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
-    $this->field = $field;
-    $this->instance = $instance;
-    $this->entity_type = $entity_type;
-    $this->entity = $entity;
-  }
-
-  /**
-   * Implements EntityReferenceHandler::settingsForm().
-   */
-  public static function settingsForm($field, $instance) {
-    $entity_info = entity_get_info($field['settings']['target_type']);
-
-    // Merge-in default values.
-    $field['settings']['handler_settings'] += array(
-      'target_bundles' => array(),
-      'sort' => array(
-        'type' => 'none',
-      )
-    );
-
-    if (!empty($entity_info['entity keys']['bundle'])) {
-      $bundles = array();
-      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
-        $bundles[$bundle_name] = $bundle_info['label'];
-      }
-
-      $form['target_bundles'] = array(
-        '#type' => 'checkboxes',
-        '#title' => t('Target bundles'),
-        '#options' => $bundles,
-        '#default_value' => $field['settings']['handler_settings']['target_bundles'],
-        '#size' => 6,
-        '#multiple' => TRUE,
-        '#description' => t('The bundles of the entity type that can be referenced. Optional, leave empty for all bundles.'),
-        '#element_validate' => array('_entityreference_element_validate_filter'),
-      );
-    }
-    else {
-      $form['target_bundles'] = array(
-        '#type' => 'value',
-        '#value' => array(),
-      );
-    }
-
-    $form['sort']['type'] = array(
-      '#type' => 'select',
-      '#title' => t('Sort by'),
-      '#options' => array(
-        'none' => t("Don't sort"),
-        'property' => t('A property of the base table of the entity'),
-        'field' => t('A field attached to this entity'),
-      ),
-      '#ajax' => TRUE,
-      '#limit_validation_errors' => array(),
-      '#default_value' => $field['settings']['handler_settings']['sort']['type'],
-    );
-
-    $form['sort']['settings'] = array(
-      '#type' => 'container',
-      '#attributes' => array('class' => array('entityreference-settings')),
-      '#process' => array('_entityreference_form_process_merge_parent'),
-    );
-
-    if ($field['settings']['handler_settings']['sort']['type'] == 'property') {
-      // Merge-in default values.
-      $field['settings']['handler_settings']['sort'] += array(
-        'property' => NULL,
-      );
-
-      $form['sort']['settings']['property'] = array(
-        '#type' => 'select',
-        '#title' => t('Sort property'),
-        '#required' => TRUE,
-        '#options' => drupal_map_assoc($entity_info['schema_fields_sql']['base table']),
-        '#default_value' => $field['settings']['handler_settings']['sort']['property'],
-      );
-    }
-    elseif ($field['settings']['handler_settings']['sort']['type'] == 'field') {
-      // Merge-in default values.
-      $field['settings']['handler_settings']['sort'] += array(
-        'field' => NULL,
-      );
-
-      $fields = array();
-      foreach (field_info_instances($field['settings']['target_type']) as $bundle_name => $bundle_instances) {
-        foreach ($bundle_instances as $instance_name => $instance_info) {
-          $field_info = field_info_field($instance_name);
-          foreach ($field_info['columns'] as $column_name => $column_info) {
-            $fields[$instance_name . ':' . $column_name] = t('@label (column @column)', array('@label' => $instance_info['label'], '@column' => $column_name));
-          }
-        }
-      }
-
-      $form['sort']['settings']['field'] = array(
-        '#type' => 'select',
-        '#title' => t('Sort field'),
-        '#required' => TRUE,
-        '#options' => $fields,
-        '#default_value' => $field['settings']['handler_settings']['sort']['field'],
-      );
-    }
-
-    if ($field['settings']['handler_settings']['sort']['type'] != 'none') {
-      // Merge-in default values.
-      $field['settings']['handler_settings']['sort'] += array(
-        'direction' => 'ASC',
-      );
-
-      $form['sort']['settings']['direction'] = array(
-        '#type' => 'select',
-        '#title' => t('Sort direction'),
-        '#required' => TRUE,
-        '#options' => array(
-          'ASC' => t('Ascending'),
-          'DESC' => t('Descending'),
-        ),
-        '#default_value' => $field['settings']['handler_settings']['sort']['direction'],
-      );
-    }
-
-    return $form;
-  }
-
-  /**
-   * Implements EntityReferenceHandler::getReferencableEntities().
-   */
-  public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
-    $options = array();
-    $entity_type = $this->field['settings']['target_type'];
-
-    $query = $this->buildEntityFieldQuery($match, $match_operator);
-    if ($limit > 0) {
-      $query->range(0, $limit);
-    }
-
-    $results = $query->execute();
-
-    if (!empty($results[$entity_type])) {
-      $entities = entity_load($entity_type, array_keys($results[$entity_type]));
-      foreach ($entities as $entity_id => $entity) {
-        $options[$entity_id] = check_plain($this->getLabel($entity));
-      }
-    }
-
-    return $options;
-  }
-
-  /**
-   * Implements EntityReferenceHandler::countReferencableEntities().
-   */
-  public function countReferencableEntities($match = NULL, $match_operator = 'CONTAINS') {
-    $query = $this->buildEntityFieldQuery($match, $match_operator);
-    return $query
-      ->count()
-      ->execute();
-  }
-
-  /**
-   * Implements EntityReferenceHandler::validateReferencableEntities().
-   */
-  public function validateReferencableEntities(array $ids) {
-    if ($ids) {
-      $entity_type = $this->field['settings']['target_type'];
-      $query = $this->buildEntityFieldQuery();
-      $query->entityCondition('entity_id', $ids, 'IN');
-      $result = $query->execute();
-      if (!empty($result[$entity_type])) {
-        return array_keys($result[$entity_type]);
-      }
-    }
-
-    return array();
-  }
-
-  /**
-   * Implements EntityReferenceHandler::validateAutocompleteInput().
-   */
-  public function validateAutocompleteInput($input, &$element, &$form_state, $form) {
-      $entities = $this->getReferencableEntities($input, '=', 6);
-      if (empty($entities)) {
-        // Error if there are no entities available for a required field.
-        form_error($element, t('There are no entities matching "%value"', array('%value' => $input)));
-      }
-      elseif (count($entities) > 5) {
-        // Error if there are more than 5 matching entities.
-        form_error($element, t('Many entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)"', array(
-          '%value' => $input,
-          '@value' => $input,
-          '@id' => key($entities),
-        )));
-      }
-      elseif (count($entities) > 1) {
-        // More helpful error if there are only a few matching entities.
-        $multiples = array();
-        foreach ($entities as $id => $name) {
-          $multiples[] = $name . ' (' . $id . ')';
-        }
-        form_error($element, t('Multiple entities match this reference; "%multiple"', array('%multiple' => implode('", "', $multiples))));
-      }
-      else {
-        // Take the one and only matching entity.
-        return key($entities);
-      }
-  }
-
-  /**
-   * Build an EntityFieldQuery to get referencable entities.
-   */
-  protected function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') {
-    $query = new EntityFieldQuery();
-    $query->entityCondition('entity_type', $this->field['settings']['target_type']);
-    if (!empty($this->field['settings']['handler_settings']['target_bundles'])) {
-      $query->entityCondition('bundle', $this->field['settings']['handler_settings']['target_bundles'], 'IN');
-    }
-    if (isset($match)) {
-      $entity_info = entity_get_info($this->field['settings']['target_type']);
-      if (isset($entity_info['entity keys']['label'])) {
-        $query->propertyCondition($entity_info['entity keys']['label'], $match, $match_operator);
-      }
-    }
-
-    // Add a generic entity access tag to the query.
-    $query->addTag($this->field['settings']['target_type'] . '_access');
-    $query->addTag('entityreference');
-    $query->addMetaData('field', $this->field);
-    $query->addMetaData('entityreference_selection_handler', $this);
-
-    // Add the sort option.
-    if (!empty($this->field['settings']['handler_settings']['sort'])) {
-      $sort_settings = $this->field['settings']['handler_settings']['sort'];
-      if ($sort_settings['type'] == 'property') {
-        $query->propertyOrderBy($sort_settings['property'], $sort_settings['direction']);
-      }
-      elseif ($sort_settings['type'] == 'field') {
-        list($field, $column) = explode(':', $sort_settings['field'], 2);
-        $query->fieldOrderBy($field, $column, $sort_settings['direction']);
-      }
-    }
-
-    return $query;
-  }
-
-  /**
-   * Implements EntityReferenceHandler::entityFieldQueryAlter().
-   */
-  public function entityFieldQueryAlter(SelectQueryInterface $query) {
-
-  }
-
-  /**
-   * Helper method: pass a query to the alteration system again.
-   *
-   * This allow Entity Reference to add a tag to an existing query, to ask
-   * access control mechanisms to alter it again.
-   */
-  protected function reAlterQuery(SelectQueryInterface $query, $tag, $base_table) {
-    // Save the old tags and metadata.
-    // For some reason, those are public.
-    $old_tags = $query->alterTags;
-    $old_metadata = $query->alterMetaData;
-
-    $query->alterTags = array($tag => TRUE);
-    $query->alterMetaData['base_table'] = $base_table;
-    drupal_alter(array('query', 'query_' . $tag), $query);
-
-    // Restore the tags and metadata.
-    $query->alterTags = $old_tags;
-    $query->alterMetaData = $old_metadata;
-  }
-
-  /**
-   * Implements EntityReferenceHandler::getLabel().
-   */
-  public function getLabel($entity) {
-    return entity_label($this->field['settings']['target_type'], $entity);
-  }
-}
-
-/**
- * Override for the Node type.
- *
- * This only exists to workaround core bugs.
- */
-class EntityReference_SelectionHandler_Generic_node extends EntityReference_SelectionHandler_Generic {
-  public function entityFieldQueryAlter(SelectQueryInterface $query) {
-    // Adding the 'node_access' tag is sadly insufficient for nodes: core
-    // requires us to also know about the concept of 'published' and
-    // 'unpublished'. We need to do that as long as there are no access control
-    // modules in use on the site. As long as one access control module is there,
-    // it is supposed to handle this check.
-    if (!user_access('bypass node access') && !count(module_implements('node_grants'))) {
-      $tables = $query->getTables();
-      $query->condition(key($tables) . '.status', NODE_PUBLISHED);
-    }
-  }
-}
-
-/**
- * Override for the User type.
- *
- * This only exists to workaround core bugs.
- */
-class EntityReference_SelectionHandler_Generic_user extends EntityReference_SelectionHandler_Generic {
-  public function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') {
-    $query = parent::buildEntityFieldQuery($match, $match_operator);
-
-    // The user entity doesn't have a label column.
-    if (isset($match)) {
-      $query->propertyCondition('name', $match, $match_operator);
-    }
-
-    // Adding the 'user_access' tag is sadly insufficient for users: core
-    // requires us to also know about the concept of 'blocked' and
-    // 'active'.
-    if (!user_access('administer users')) {
-      $query->propertyCondition('status', 1);
-    }
-    return $query;
-  }
-
-  public function entityFieldQueryAlter(SelectQueryInterface $query) {
-    if (user_access('administer users')) {
-      // In addition, if the user is administrator, we need to make sure to
-      // match the anonymous user, that doesn't actually have a name in the
-      // database.
-      $conditions = &$query->conditions();
-      foreach ($conditions as $key => $condition) {
-        if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users.name') {
-          // Remove the condition.
-          unset($conditions[$key]);
-
-          // Re-add the condition and a condition on uid = 0 so that we end up
-          // with a query in the form:
-          //    WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0)
-          $or = db_or();
-          $or->condition($condition['field'], $condition['value'], $condition['operator']);
-          // Sadly, the Database layer doesn't allow us to build a condition
-          // in the form ':placeholder = :placeholder2', because the 'field'
-          // part of a condition is always escaped.
-          // As a (cheap) workaround, we separately build a condition with no
-          // field, and concatenate the field and the condition separately.
-          $value_part = db_and();
-          $value_part->condition('anonymous_name', $condition['value'], $condition['operator']);
-          $value_part->compile(Database::getConnection(), $query);
-          $or->condition(db_and()
-            ->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + array(':anonymous_name' => format_username(user_load(0))))
-            ->condition('users.uid', 0)
-          );
-          $query->condition($or);
-        }
-      }
-    }
-  }
-}
-
-/**
- * Override for the Comment type.
- *
- * This only exists to workaround core bugs.
- */
-class EntityReference_SelectionHandler_Generic_comment extends EntityReference_SelectionHandler_Generic {
-  public function entityFieldQueryAlter(SelectQueryInterface $query) {
-    // Adding the 'comment_access' tag is sadly insufficient for comments: core
-    // requires us to also know about the concept of 'published' and
-    // 'unpublished'.
-    if (!user_access('administer comments')) {
-      $tables = $query->getTables();
-      $query->condition(key($tables) . '.status', COMMENT_PUBLISHED);
-    }
-
-    // The Comment module doesn't implement any proper comment access,
-    // and as a consequence doesn't make sure that comments cannot be viewed
-    // when the user doesn't have access to the node.
-    $tables = $query->getTables();
-    $base_table = key($tables);
-    $node_alias = $query->innerJoin('node', 'n', '%alias.nid = ' . $base_table . '.nid');
-    // Pass the query to the node access control.
-    $this->reAlterQuery($query, 'node_access', $node_alias);
-
-    // Alas, the comment entity exposes a bundle, but doesn't have a bundle column
-    // in the database. We have to alter the query ourself to go fetch the
-    // bundle.
-    $conditions = &$query->conditions();
-    foreach ($conditions as $key => &$condition) {
-      if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'node_type') {
-        $condition['field'] = $node_alias . '.type';
-        foreach ($condition['value'] as &$value) {
-          if (substr($value, 0, 13) == 'comment_node_') {
-            $value = substr($value, 13);
-          }
-        }
-        break;
-      }
-    }
-
-    // Passing the query to node_query_node_access_alter() is sadly
-    // insufficient for nodes.
-    // @see EntityReferenceHandler_node::entityFieldQueryAlter()
-    if (!user_access('bypass node access') && !count(module_implements('node_grants'))) {
-      $query->condition($node_alias . '.status', 1);
-    }
-  }
-}
-
-/**
- * Override for the File type.
- *
- * This only exists to workaround core bugs.
- */
-class EntityReference_SelectionHandler_Generic_file extends EntityReference_SelectionHandler_Generic {
-  public function entityFieldQueryAlter(SelectQueryInterface $query) {
-    // Core forces us to know about 'permanent' vs. 'temporary' files.
-    $tables = $query->getTables();
-    $base_table = key($tables);
-    $query->condition('status', FILE_STATUS_PERMANENT);
-
-    // Access control to files is a very difficult business. For now, we are not
-    // going to give it a shot.
-    // @todo: fix this when core access control is less insane.
-    return $query;
-  }
-
-  public function getLabel($entity) {
-    // The file entity doesn't have a label. More over, the filename is
-    // sometimes empty, so use the basename in that case.
-    return $entity->filename !== '' ? $entity->filename : basename($entity->uri);
-  }
-}
-
-/**
- * Override for the Taxonomy term type.
- *
- * This only exists to workaround core bugs.
- */
-class EntityReference_SelectionHandler_Generic_taxonomy_term extends EntityReference_SelectionHandler_Generic {
-  public function entityFieldQueryAlter(SelectQueryInterface $query) {
-    // The Taxonomy module doesn't implement any proper taxonomy term access,
-    // and as a consequence doesn't make sure that taxonomy terms cannot be viewed
-    // when the user doesn't have access to the vocabulary.
-    $tables = $query->getTables();
-    $base_table = key($tables);
-    $vocabulary_alias = $query->innerJoin('taxonomy_vocabulary', 'n', '%alias.vid = ' . $base_table . '.vid');
-    $query->addMetadata('base_table', $vocabulary_alias);
-    // Pass the query to the taxonomy access control.
-    $this->reAlterQuery($query, 'taxonomy_vocabulary_access', $vocabulary_alias);
-
-    // Also, the taxonomy term entity exposes a bundle, but doesn't have a bundle
-    // column in the database. We have to alter the query ourself to go fetch
-    // the bundle.
-    $conditions = &$query->conditions();
-    foreach ($conditions as $key => &$condition) {
-      if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'vocabulary_machine_name') {
-        $condition['field'] = $vocabulary_alias . '.machine_name';
-        break;
-      }
-    }
-  }
-}

+ 11 - 1
plugins/selection/EntityReference_SelectionHandler_Views.class.php

@@ -110,7 +110,17 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
       // Get the results.
       $result = $this->view->execute_display($display_name, $args);
     }
-    return $result;
+
+    $return = array();
+    if ($result) {
+      $target_type = $this->field['settings']['target_type'];
+      $entities = entity_load($target_type, array_keys($result));
+      foreach($entities as $entity) {
+        list($id,, $bundle) = entity_extract_ids($target_type, $entity);
+        $return[$bundle][$id] = $result[$id];
+      }
+    }
+    return $return;
   }
 
   /**

+ 175 - 26
tests/entityreference.handlers.test

@@ -27,10 +27,18 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
     foreach ($tests as $test) {
       foreach ($test['arguments'] as $arguments) {
         $result = call_user_func_array(array($handler, 'getReferencableEntities'), $arguments);
-        $this->assertEqual($result, $test['result'], t('Valid result set returned by @handler.', array('@handler' => $handler_name)));
+        $this->assertEqual($result, $test['result'], format_string('Valid result set returned by @handler.', array('@handler' => $handler_name)));
 
         $result = call_user_func_array(array($handler, 'countReferencableEntities'), $arguments);
-        $this->assertEqual($result, count($test['result']), t('Valid count returned by @handler.', array('@handler' => $handler_name)));
+        if (!empty($test['result'])) {
+          $bundle = key($test['result']);
+          $count = count($test['result'][$bundle]);
+        }
+        else {
+          $count = 0;
+        }
+
+        $this->assertEqual($result, $count, format_string('Valid count returned by @handler.', array('@handler' => $handler_name)));
       }
     }
   }
@@ -93,8 +101,10 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array(NULL, 'CONTAINS'),
         ),
         'result' => array(
-          $nodes['published1']->nid => $node_labels['published1'],
-          $nodes['published2']->nid => $node_labels['published2'],
+          'article' => array(
+            $nodes['published1']->nid => $node_labels['published1'],
+            $nodes['published2']->nid => $node_labels['published2'],
+          ),
         ),
       ),
       array(
@@ -103,7 +113,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array('Published1', 'CONTAINS'),
         ),
         'result' => array(
-          $nodes['published1']->nid => $node_labels['published1'],
+          'article' => array(
+            $nodes['published1']->nid => $node_labels['published1'],
+          ),
         ),
       ),
       array(
@@ -112,7 +124,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array('Published2', 'CONTAINS'),
         ),
         'result' => array(
-          $nodes['published2']->nid => $node_labels['published2'],
+          'article' => array(
+            $nodes['published2']->nid => $node_labels['published2'],
+          ),
         ),
       ),
       array(
@@ -139,9 +153,11 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array(NULL, 'CONTAINS'),
         ),
         'result' => array(
-          $nodes['published1']->nid => $node_labels['published1'],
-          $nodes['published2']->nid => $node_labels['published2'],
-          $nodes['unpublished']->nid => $node_labels['unpublished'],
+          'article' => array(
+            $nodes['published1']->nid => $node_labels['published1'],
+            $nodes['published2']->nid => $node_labels['published2'],
+            $nodes['unpublished']->nid => $node_labels['unpublished'],
+          ),
         ),
       ),
       array(
@@ -149,7 +165,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array('Node unpublished', 'CONTAINS'),
         ),
         'result' => array(
-          $nodes['unpublished']->nid => $node_labels['unpublished'],
+          'article' => array(
+            $nodes['unpublished']->nid => $node_labels['unpublished'],
+          ),
         ),
       ),
     );
@@ -215,8 +233,10 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array(NULL, 'CONTAINS'),
         ),
         'result' => array(
-          $users['admin']->uid => $user_labels['admin'],
-          $users['non_admin']->uid => $user_labels['non_admin'],
+          'user' => array(
+            $users['admin']->uid => $user_labels['admin'],
+            $users['non_admin']->uid => $user_labels['non_admin'],
+          ),
         ),
       ),
       array(
@@ -225,7 +245,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array('NON_ADMIN', 'CONTAINS'),
         ),
         'result' => array(
-          $users['non_admin']->uid => $user_labels['non_admin'],
+          'user' => array(
+            $users['non_admin']->uid => $user_labels['non_admin'],
+          ),
         ),
       ),
       array(
@@ -250,10 +272,12 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array(NULL, 'CONTAINS'),
         ),
         'result' => array(
-          $users['anonymous']->uid => $user_labels['anonymous'],
-          $users['admin']->uid => $user_labels['admin'],
-          $users['non_admin']->uid => $user_labels['non_admin'],
-          $users['blocked']->uid => $user_labels['blocked'],
+          'user' => array(
+            $users['anonymous']->uid => $user_labels['anonymous'],
+            $users['admin']->uid => $user_labels['admin'],
+            $users['non_admin']->uid => $user_labels['non_admin'],
+            $users['blocked']->uid => $user_labels['blocked'],
+          ),
         ),
       ),
       array(
@@ -261,7 +285,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array('blocked', 'CONTAINS'),
         ),
         'result' => array(
-          $users['blocked']->uid => $user_labels['blocked'],
+          'user' => array(
+            $users['blocked']->uid => $user_labels['blocked'],
+          ),
         ),
       ),
       array(
@@ -270,7 +296,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array('anonymous', 'CONTAINS'),
         ),
         'result' => array(
-          $users['anonymous']->uid => $user_labels['anonymous'],
+          'user' => array(
+            $users['anonymous']->uid => $user_labels['anonymous'],
+          ),
         ),
       ),
     );
@@ -364,7 +392,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array(NULL, 'CONTAINS'),
         ),
         'result' => array(
-          $comments['published_published']->cid => $comment_labels['published_published'],
+          'comment_node_article' => array(
+            $comments['published_published']->cid => $comment_labels['published_published'],
+          ),
         ),
       ),
       array(
@@ -372,7 +402,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array('Published', 'CONTAINS'),
         ),
         'result' => array(
-          $comments['published_published']->cid => $comment_labels['published_published'],
+          'comment_node_article' => array(
+            $comments['published_published']->cid => $comment_labels['published_published'],
+          ),
         ),
       ),
       array(
@@ -399,8 +431,10 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array(NULL, 'CONTAINS'),
         ),
         'result' => array(
-          $comments['published_published']->cid => $comment_labels['published_published'],
-          $comments['published_unpublished']->cid => $comment_labels['published_unpublished'],
+          'comment_node_article' => array(
+            $comments['published_published']->cid => $comment_labels['published_published'],
+            $comments['published_unpublished']->cid => $comment_labels['published_unpublished'],
+          ),
         ),
       ),
     );
@@ -415,12 +449,127 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
           array(NULL, 'CONTAINS'),
         ),
         'result' => array(
-          $comments['published_published']->cid => $comment_labels['published_published'],
-          $comments['published_unpublished']->cid => $comment_labels['published_unpublished'],
-          $comments['unpublished_published']->cid => $comment_labels['unpublished_published'],
+          'comment_node_article' => array(
+            $comments['published_published']->cid => $comment_labels['published_published'],
+            $comments['published_unpublished']->cid => $comment_labels['published_unpublished'],
+            $comments['unpublished_published']->cid => $comment_labels['unpublished_published'],
+          ),
         ),
       ),
     );
     $this->assertReferencable($field, $referencable_tests, 'Comment handler (comment + node admin)');
   }
+
+  /**
+   * Assert sorting by field works for non-admins.
+   *
+   * Since we are sorting on a field, we need to make sure the base-table
+   * is added, and access-control is behaving as expected.
+   */
+  public function testSortByField() {
+    // Add text field to entity, to sort by.
+    $field_info = array(
+      'field_name' => 'field_text',
+      'type' => 'text',
+      'entity_types' => array('node'),
+    );
+    field_create_field($field_info);
+
+    $instance = array(
+      'label' => 'Text Field',
+      'field_name' => 'field_text',
+      'entity_type' => 'node',
+      'bundle' => 'article',
+      'settings' => array(),
+      'required' => FALSE,
+    );
+    field_create_instance($instance);
+
+
+    // Build a fake field instance.
+    $field = array(
+      'translatable' => FALSE,
+      'entity_types' => array(),
+      'settings' => array(
+        'handler' => 'base',
+        'target_type' => 'node',
+        'handler_settings' => array(
+          'target_bundles' => array(),
+          // Add sorting.
+          'sort' => array(
+            'type' => 'field',
+            'field' => 'field_text:value',
+            'direction' => 'DESC',
+          ),
+        ),
+      ),
+      'field_name' => 'test_field',
+      'type' => 'entityreference',
+      'cardinality' => '1',
+    );
+
+    // Build a set of test data.
+    $nodes = array(
+      'published1' => (object) array(
+        'type' => 'article',
+        'status' => 1,
+        'title' => 'Node published1 (<&>)',
+        'uid' => 1,
+        'field_text' => array(
+          LANGUAGE_NONE => array(
+            array(
+              'value' => 1,
+            ),
+          ),
+        ),
+      ),
+      'published2' => (object) array(
+        'type' => 'article',
+        'status' => 1,
+        'title' => 'Node published2 (<&>)',
+        'uid' => 1,
+        'field_text' => array(
+          LANGUAGE_NONE => array(
+            array(
+              'value' => 2,
+            ),
+          ),
+        ),
+      ),
+      'unpublished' => (object) array(
+        'type' => 'article',
+        'status' => 0,
+        'title' => 'Node unpublished (<&>)',
+        'uid' => 1,
+        'field_text' => array(
+          LANGUAGE_NONE => array(
+            array(
+              'value' => 3,
+            ),
+          ),
+        ),
+      ),
+    );
+
+    $node_labels = array();
+    foreach ($nodes as $key => $node) {
+      node_save($node);
+      $node_labels[$key] = check_plain($node->title);
+    }
+
+    // Test as a non-admin.
+    $normal_user = $this->drupalCreateUser(array('access content'));
+    $GLOBALS['user'] = $normal_user;
+
+    $handler = entityreference_get_selection_handler($field);
+
+    // Not only assert the result, but make sure the keys are sorted as
+    // expected.
+    $result = $handler->getReferencableEntities();
+    $expected_result = array(
+      $nodes['published2']->nid => $node_labels['published2'],
+      $nodes['published1']->nid => $node_labels['published1'],
+    );
+    $this->assertIdentical($result['article'], $expected_result, 'Query sorted by field returned expected values for non-admin.');
+  }
 }

+ 115 - 0
tests/entityreference.taxonomy.test

@@ -0,0 +1,115 @@
+<?php
+
+/**
+ * Test for Entity Reference taxonomy integration.
+ */
+class EntityReferenceTaxonomyTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity Reference Taxonomy',
+      'description' => 'Tests nodes with reference to terms as indexed.',
+      'group' => 'Entity Reference',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp('entityreference', 'taxonomy');
+
+    // Create an entity reference field.
+    $field = array(
+      'entity_types' => array('node'),
+      'settings' => array(
+        'handler' => 'base',
+        'target_type' => 'taxonomy_term',
+        'handler_settings' => array(
+          'target_bundles' => array(),
+        ),
+      ),
+      'field_name' => 'field_entityreference_term',
+      'type' => 'entityreference',
+    );
+    $field = field_create_field($field);
+    $instance = array(
+      'field_name' => 'field_entityreference_term',
+      'bundle' => 'article',
+      'entity_type' => 'node',
+    );
+
+    // Enable the taxonomy-index behavior.
+    $instance['settings']['behaviors']['taxonomy-index']['status'] = TRUE;
+    field_create_instance($instance);
+
+    // Create a term reference field.
+    $field = array(
+      'translatable' => FALSE,
+      'entity_types' => array('node'),
+      'settings' => array(
+        'allowed_values' => array(
+          array(
+            'vocabulary' => 'terms',
+            'parent' => 0,
+          ),
+        ),
+      ),
+      'field_name' => 'field_taxonomy_term',
+      'type' => 'taxonomy_term_reference',
+    );
+    $field = field_create_field($field);
+    $instance = array(
+      'field_name' => 'field_taxonomy_term',
+      'bundle' => 'article',
+      'entity_type' => 'node',
+    );
+    field_create_instance($instance);
+
+    // Create a terms vocobulary.
+    $vocabulary = new stdClass();
+    $vocabulary->name = 'Terms';
+    $vocabulary->machine_name = 'terms';
+    taxonomy_vocabulary_save($vocabulary);
+
+    // Create term.
+    for ($i = 1; $i <= 2; $i++) {
+      $term = new stdClass();
+      $term->name = "term $i";
+      $term->vid = 1;
+      taxonomy_term_save($term);
+    }
+  }
+
+  /**
+   * Test referencing a term using entity reference field.
+   */
+  public function testNodeIndex() {
+    // Asert node insert with reference to term.
+    $settings = array();
+    $settings['type'] = 'article';
+    $settings['field_entityreference_term'][LANGUAGE_NONE][0]['target_id'] = 1;
+    $node = $this->drupalCreateNode($settings);
+
+    $this->assertEqual(taxonomy_select_nodes(1), array($node->nid));
+
+    // Asert node update with reference to term.
+    node_save($node);
+    $this->assertEqual(taxonomy_select_nodes(1), array($node->nid));
+
+    // Assert node update with reference to term and taxonomy reference to
+    // another term.
+    $wrapper = entity_metadata_wrapper('node', $node);
+    $wrapper->field_taxonomy_term->set(2);
+    $wrapper->save();
+
+    $this->assertEqual(taxonomy_select_nodes(1), array($node->nid));
+    $this->assertEqual(taxonomy_select_nodes(2), array($node->nid));
+
+    // Assert node update with reference to term and taxonomy reference to
+    // same term.
+    $wrapper->field_taxonomy_term->set(1);
+    $wrapper->save();
+    $this->assertEqual(taxonomy_select_nodes(1), array($node->nid));
+
+    $wrapper->delete();
+    $this->assertFalse(taxonomy_select_nodes(1));
+  }
+
+}

+ 1 - 6
views/entityreference_plugin_display.inc

@@ -76,12 +76,7 @@ class entityreference_plugin_display extends views_plugin_display {
       foreach ($style_options['search_fields'] as $field_alias) {
         if (!empty($field_alias)) {
           // Get the table and field names for the checked field
-          if (empty($this->view->field[$field_alias]->field_info)) 
-            $field = $this->view->query->fields[$this->view->field[$field_alias]->field_alias];
-          else {
-            $this->view->query->add_field($this->view->field[$field_alias]->options['table'], $this->view->field[$field_alias]->real_field, $this->view->field[$field_alias]->options['field'], array());
-            $field = $this->view->query->fields[$this->view->field[$field_alias]->options['field']];
-          }
+          $field = $this->view->query->fields[$this->view->field[$field_alias]->field_alias];
           // Add an OR condition for the field
           $conditions->condition($field['table'] . '.' . $field['field'], $value, 'LIKE');
         }