Browse Source

merged entityreference submodule

Bachir Soussi Chiadmi 9 years ago
parent
commit
aceb299147
37 changed files with 5394 additions and 0 deletions
  1. 93 0
      sites/all/modules/contrib/fields/entityreference/1354482-er-user-roles-19.patch
  2. 339 0
      sites/all/modules/contrib/fields/entityreference/LICENSE.txt
  3. 16 0
      sites/all/modules/contrib/fields/entityreference/README.txt
  4. 4 0
      sites/all/modules/contrib/fields/entityreference/entityreference.admin.css
  5. 26 0
      sites/all/modules/contrib/fields/entityreference/entityreference.devel_generate.inc
  6. 90 0
      sites/all/modules/contrib/fields/entityreference/entityreference.diff.inc
  7. 121 0
      sites/all/modules/contrib/fields/entityreference/entityreference.feeds.inc
  8. 30 0
      sites/all/modules/contrib/fields/entityreference/entityreference.info
  9. 164 0
      sites/all/modules/contrib/fields/entityreference/entityreference.install
  10. 27 0
      sites/all/modules/contrib/fields/entityreference/entityreference.migrate.inc
  11. 1270 0
      sites/all/modules/contrib/fields/entityreference/entityreference.module
  12. 12 0
      sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info
  13. 15 0
      sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.module
  14. 31 0
      sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/plugins/behavior/EntityReferenceFieldBehaviorExample.class.php
  15. 31 0
      sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/plugins/behavior/EntityReferenceInstanceBehaviorExample.class.php
  16. 8 0
      sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/plugins/behavior/test_field_behavior.inc
  17. 8 0
      sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/plugins/behavior/test_instance_behavior.inc
  18. 5 0
      sites/all/modules/contrib/fields/entityreference/patch url.txt
  19. 18 0
      sites/all/modules/contrib/fields/entityreference/patch-entityreference-7.x.patch
  20. 189 0
      sites/all/modules/contrib/fields/entityreference/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php
  21. 18 0
      sites/all/modules/contrib/fields/entityreference/plugins/behavior/EntityReferenceBehavior_ViewsFilterSelect.class.php
  22. 214 0
      sites/all/modules/contrib/fields/entityreference/plugins/behavior/abstract.inc
  23. 16 0
      sites/all/modules/contrib/fields/entityreference/plugins/behavior/taxonomy-index.inc
  24. 10 0
      sites/all/modules/contrib/fields/entityreference/plugins/behavior/views-select-list.inc
  25. 599 0
      sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php
  26. 553 0
      sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php.orig
  27. 193 0
      sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php
  28. 113 0
      sites/all/modules/contrib/fields/entityreference/plugins/selection/abstract.inc
  29. 7 0
      sites/all/modules/contrib/fields/entityreference/plugins/selection/base.inc
  30. 9 0
      sites/all/modules/contrib/fields/entityreference/plugins/selection/views.inc
  31. 114 0
      sites/all/modules/contrib/fields/entityreference/tests/entityreference.admin.test
  32. 575 0
      sites/all/modules/contrib/fields/entityreference/tests/entityreference.handlers.test
  33. 115 0
      sites/all/modules/contrib/fields/entityreference/tests/entityreference.taxonomy.test
  34. 136 0
      sites/all/modules/contrib/fields/entityreference/views/entityreference.views.inc
  35. 123 0
      sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_display.inc
  36. 36 0
      sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_row_fields.inc
  37. 66 0
      sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_style.inc

+ 93 - 0
sites/all/modules/contrib/fields/entityreference/1354482-er-user-roles-19.patch

@@ -0,0 +1,93 @@
+diff --git a/plugins/selection/EntityReference_SelectionHandler_Generic.class.php b/plugins/selection/EntityReference_SelectionHandler_Generic.class.php
+index 7db4045..f3ff067 100644
+--- a/plugins/selection/EntityReference_SelectionHandler_Generic.class.php
++++ b/plugins/selection/EntityReference_SelectionHandler_Generic.class.php
+@@ -307,6 +307,27 @@ 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);
+ 
+@@ -315,21 +336,33 @@ class EntityReference_SelectionHandler_Generic_user extends EntityReference_Sele
+       $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);
++    $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)]);
+     }
++
+     return $query;
+   }
+ 
+   public function entityFieldQueryAlter(SelectQueryInterface $query) {
++    $conditions = &$query->conditions();
+     if (user_access('administer users')) {
+-      // In addition, if the user is administrator, we need to make sure to
++      // 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 ($condition['field'] == 'users.name') {
+           // Remove the condition.
+@@ -356,6 +389,19 @@ 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');
++    }
+   }
+ }
+ 

+ 339 - 0
sites/all/modules/contrib/fields/entityreference/LICENSE.txt

@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 16 - 0
sites/all/modules/contrib/fields/entityreference/README.txt

@@ -0,0 +1,16 @@
+DESCRIPTION
+===========
+Provides a field type that can reference arbitrary entities.
+
+SITE BUILDERS
+=============
+Note that when using a select widget, Entity reference loads all the
+entities in that list in order to get the entity's label. If there are
+too many loaded entities that site might reach its memory limit and crash
+(also known as WSOD). In such a case you are advised to change the widget
+to "autocomplete". If you get a WSOD when trying to edit the field
+settings, you can reach the widget settings directly by navigation to
+
+  admin/structure/types/manage/[ENTITY-TYPE]/fields/[FIELD-NAME]/widget-type
+
+Replace ENTITY-TYPE and FIELD_NAME with the correct values.

+ 4 - 0
sites/all/modules/contrib/fields/entityreference/entityreference.admin.css

@@ -0,0 +1,4 @@
+
+.entityreference-settings {
+  margin-left: 1.5em;
+}

+ 26 - 0
sites/all/modules/contrib/fields/entityreference/entityreference.devel_generate.inc

@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Support for processing entity reference fields in devel generate.
+ */
+
+function entityreference_devel_generate($object, $field, $instance, $bundle) {
+  if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) {
+    return devel_generate_multiple('_entityreference_devel_generate', $object, $field, $instance, $bundle);
+  }
+  else {
+    return _entityreference_devel_generate($object, $field, $instance, $bundle);
+  }
+}
+
+function _entityreference_devel_generate($object, $field, $instance, $bundle) {
+  $object_field = array();
+  // Get all the entity that are referencable here.
+  $referencable_entity = entityreference_get_selection_handler($field, $instance)->getReferencableEntities();
+  if (is_array($referencable_entity) && !empty($referencable_entity)) {
+    // Get a random key.
+    $object_field['target_id'] = array_rand($referencable_entity);
+  }
+  return $object_field;
+}

+ 90 - 0
sites/all/modules/contrib/fields/entityreference/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;
+}

+ 121 - 0
sites/all/modules/contrib/fields/entityreference/entityreference.feeds.inc

@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * @file
+ * Feeds mapping implementation for the Entity reference module
+ */
+
+/**
+ * Implements hook_feeds_processor_targets_alter().
+ *
+ * @see FeedsNodeProcessor::getMappingTargets().
+ */
+function entityreference_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) {
+
+  foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) {
+    $info = field_info_field($name);
+    if ($info['type'] == 'entityreference') {
+      $targets[$name] = array(
+        'name'        => check_plain($instance['label']),
+        'callback'    => 'entityreference_feeds_set_target',
+        'description' => t('The field instance @label of @id', array(
+          '@label' => $instance['label'],
+          '@id'    => $name,
+        )),
+      );
+    }
+  }
+}
+
+/**
+ * Entity reference callback for mapping.
+ *
+ * When the callback is invoked, $target contains the name of the field the
+ * user has decided to map to and $value contains the value of the feed item
+ * element the user has picked as a source.
+ *
+ * @param $source
+ *   A FeedsSource object.
+ * @param $entity
+ *   The entity to map to.
+ * @param $target
+ *   The target key on $entity to map to.
+ * @param $value
+ *   The value to map. MUST be an array.
+ * @param $mapping
+ *   Array of mapping settings for current value.
+ * @param $input_format
+ *   TRUE if an input format should be applied.
+ */
+function entityreference_feeds_set_target($source, $entity, $target, $value, $mapping, $input_format = FALSE) {
+
+  // Don't do anything if we weren't given any data.
+  if (empty($value)) {
+    return;
+  }
+
+  // Assume that the passed in value could really be any number of values.
+  if (is_array($value)) {
+    $values = $value;
+  }
+  else {
+    $values = array($value);
+  }
+
+  // Get some useful field information.
+  $info = field_info_field($target);
+
+  // Set the language of the field depending on the mapping.
+  $language = isset($mapping['language']) ? $mapping['language'] : LANGUAGE_NONE;
+
+  // Iterate over all values.
+  $iterator = 0;
+  $field = isset($entity->$target) ? $entity->$target : array();
+  foreach ($values as $value) {
+
+    // Only process if this value was set for this instance.
+    if ($value) {
+
+      // Fetch the entity ID resulting from the mapping table look-up.
+      $entity_id = db_query(
+        'SELECT entity_id FROM {feeds_item} WHERE guid = :guid',
+        array(':guid' => $value)
+      )->fetchField();
+
+      /*
+       * Only add a reference to an existing entity ID if there exists a
+       * mapping between it and the provided GUID.  In cases where no such
+       * mapping exists (yet), don't do anything here.  There may be a mapping
+       * defined later in the CSV file.  If so, and the user re-runs the import
+       * (as a second pass), we can add this reference then.  (The "Update
+       * existing nodes" option must be selected during the second pass.)
+       */
+      if ($entity_id) {
+
+        // Assign the target ID.
+        $field[$language][$iterator]['target_id']   = $entity_id;
+      }
+      else /* there is no $entity_id, no mapping */ {
+
+        /*
+         * Feeds stores a hash of every line imported from CSVs in order to
+         * make the import process more efficient by ignoring lines it's
+         * already seen.  We need to short-circuit this process in this case
+         * because users may want to re-import the same line as an update later
+         * when (and if) a map to a reference exists.  So in order to provide
+         * this opportunity later, we need to destroy the hash.
+         */
+        unset($entity->feeds_item->hash);
+      }
+    }
+
+    // Break out of the loop if this field is single-valued.
+    if ($info['cardinality'] == 1) {
+      break;
+    }
+    $iterator++;
+  }
+
+  // Add the field to the entity definition.
+  $entity->{$target} = $field;
+}

+ 30 - 0
sites/all/modules/contrib/fields/entityreference/entityreference.info

@@ -0,0 +1,30 @@
+name = Entity Reference
+description = Provides a field that can reference other entities.
+core = 7.x
+package = Fields
+dependencies[] = entity
+dependencies[] = ctools
+
+; Migrate handler.
+files[] = entityreference.migrate.inc
+
+; Our plugins interfaces and abstract implementations.
+files[] = plugins/selection/abstract.inc
+files[] = plugins/selection/views.inc
+files[] = plugins/behavior/abstract.inc
+
+files[] = views/entityreference_plugin_display.inc
+files[] = views/entityreference_plugin_style.inc
+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-11-18
+version = "7.x-1.0"
+core = "7.x"
+project = "entityreference"
+datestamp = "1353230808"
+

+ 164 - 0
sites/all/modules/contrib/fields/entityreference/entityreference.install

@@ -0,0 +1,164 @@
+<?php
+
+/**
+ * Implements hook_uninstall().
+ */
+function entityreference_uninstall() {
+  variable_del('entityreference:base-tables');
+}
+
+/**
+ * Implements hook_field_schema().
+ */
+function entityreference_field_schema($field) {
+  if ($field['type'] == 'entityreference') {
+    // Load the base table configuration from the cache.
+    $base_tables = variable_get('entityreference:base-tables', array());
+
+    $schema = array(
+      'columns' => array(
+        'target_id' => array(
+          'description' => 'The id of the target entity.',
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+        ),
+      ),
+      'indexes' => array(
+        'target_id' => array('target_id'),
+      ),
+      'foreign keys' => array(),
+    );
+
+    // Create a foreign key to the target entity type base type, if available.
+    $entity_type = $field['settings']['target_type'];
+    if (isset($base_tables[$entity_type])) {
+      list($base_table, $id_column) = $base_tables[$entity_type];
+      $schema['foreign keys'][$base_table] = array(
+        'table' => $base_table,
+        'columns' => array('target_id' => $id_column),
+      );
+    }
+
+    // Invoke the behaviors to allow them to change the schema.
+    foreach (entityreference_get_behavior_handlers($field) as $handler) {
+      $handler->schema_alter($schema, $field);
+    }
+
+    return $schema;
+  }
+}
+
+/**
+ * Update the field configuration to the new plugin structure.
+ */
+function entityreference_update_7000() {
+  // Enable ctools.
+  if (!module_enable(array('ctools'))) {
+    throw new DrupalUpdateException('This version of Entity Reference requires ctools, but it could not be enabled.');
+  }
+
+  // Get the list of fields of type 'entityreference'.
+  $fields = array();
+  foreach (field_info_fields() as $field_name => $field) {
+    // Update the field configuration.
+    if ($field['type'] == 'entityreference') {
+      $settings = &$field['settings'];
+      if (!isset($settings['handler'])) {
+        $settings['handler'] = 'base';
+        $settings['handler_settings']['target_bundles'] = $settings['target_bundles'];
+        unset($settings['target_bundles']);
+        field_update_field($field);
+      }
+    }
+
+    // Update the instance configurations.
+    foreach ($field['bundles'] as $entity_type => $bundles) {
+      foreach ($bundles as $bundle) {
+        $instance = field_info_instance($entity_type, $field_name, $bundle);
+        $save = FALSE;
+        if ($instance['widget']['type'] == 'entityreference_autocomplete') {
+          $instance['widget']['type'] = 'entityreference_autocomplete_tags';
+          $save = TRUE;
+        }
+        // When the autocomplete path is the default value, remove it from
+        // the configuration.
+        if (isset($instance['widget']['settings']['path']) && $instance['widget']['settings']['path'] == 'entityreference/autocomplete') {
+          unset($instance['widget']['settings']['path']);
+          $save = TRUE;
+        }
+        if ($save) {
+          field_update_instance($instance);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Drop "target_type" from the field schema.
+ */
+function entityreference_update_7001() {
+  if (!module_exists('field_sql_storage')) {
+    return;
+  }
+  foreach (field_info_fields() as $field_name => $field) {
+    if ($field['type'] != 'entityreference') {
+      // Not an entity reference field.
+      continue;
+    }
+
+    // Update the field settings.
+    $field = field_info_field($field_name);
+    unset($field['indexes']['target_entity']);
+    $field['indexes']['target_id'] = array('target_id');
+    field_update_field($field);
+
+    if ($field['storage']['type'] !== 'field_sql_storage') {
+      // Field doesn't use SQL storage, we cannot modify the schema.
+      continue;
+    }
+    $table_name = _field_sql_storage_tablename($field);
+    $revision_name = _field_sql_storage_revision_tablename($field);
+
+    db_drop_index($table_name, $field_name . '_target_entity');
+    db_drop_index($table_name, $field_name . '_target_id');
+    db_drop_field($table_name, $field_name . '_target_type');
+    db_add_index($table_name, $field_name . '_target_id', array($field_name . '_target_id'));
+
+    db_drop_index($revision_name, $field_name . '_target_entity');
+    db_drop_index($revision_name, $field_name . '_target_id');
+    db_drop_field($revision_name, $field_name . '_target_type');
+    db_add_index($revision_name, $field_name . '_target_id', array($field_name . '_target_id'));
+  }
+}
+
+/**
+ * Make the target_id column NOT NULL.
+ */
+function entityreference_update_7002() {
+  if (!module_exists('field_sql_storage')) {
+    return;
+  }
+  foreach (field_info_fields() as $field_name => $field) {
+    if ($field['type'] != 'entityreference') {
+      // Not an entity reference field.
+      continue;
+    }
+
+    if ($field['storage']['type'] !== 'field_sql_storage') {
+      // Field doesn't use SQL storage, we cannot modify the schema.
+      continue;
+    }
+
+    $table_name = _field_sql_storage_tablename($field);
+    $revision_name = _field_sql_storage_revision_tablename($field);
+
+    db_change_field($table_name, $field_name . '_target_id', $field_name . '_target_id', array(
+      'description' => 'The id of the target entity.',
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+    ));
+  }
+}

+ 27 - 0
sites/all/modules/contrib/fields/entityreference/entityreference.migrate.inc

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Support for processing entity reference fields in Migrate.
+ */
+
+/**
+ * Implement hook_migrate_api().
+ */
+function entityreference_migrate_api() {
+  return array(
+    'api' => 2,
+    'field_handlers' => array('MigrateEntityReferenceFieldHandler'),
+  );
+}
+
+class MigrateEntityReferenceFieldHandler extends MigrateSimpleFieldHandler {
+  public function __construct() {
+    parent::__construct(array(
+      'value_key' => 'target_id',
+      'skip_empty' => TRUE,
+    ));
+
+    $this->registerTypes(array('entityreference'));
+  }
+}

+ 1270 - 0
sites/all/modules/contrib/fields/entityreference/entityreference.module

@@ -0,0 +1,1270 @@
+<?php
+
+/**
+ * Implements hook_ctools_plugin_directory().
+ */
+function entityreference_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'entityreference') {
+    return 'plugins/' . $plugin;
+  }
+}
+
+/**
+ * Implements hook_init().
+ */
+function entityreference_init() {
+  // Include feeds.module integration.
+  if (module_exists('feeds')) {
+    module_load_include('inc', 'entityreference', 'entityreference.feeds');
+  }
+}
+
+/**
+ * Implements hook_ctools_plugin_type().
+ */
+function entityreference_ctools_plugin_type() {
+  $plugins['selection'] = array(
+    'classes' => array('class'),
+  );
+  $plugins['behavior'] = array(
+    'classes' => array('class'),
+    'process' => 'entityreference_behavior_plugin_process',
+  );
+  return $plugins;
+}
+
+/**
+ * CTools callback; Process the behavoir plugins.
+ */
+function entityreference_behavior_plugin_process(&$plugin, $info) {
+  $plugin += array(
+    'description' => '',
+    'behavior type' => 'field',
+    'access callback' => FALSE,
+    'force enabled' => FALSE,
+  );
+}
+
+/**
+ * Implements hook_field_info().
+ */
+function entityreference_field_info() {
+  $field_info['entityreference'] = array(
+    'label' => t('Entity Reference'),
+    'description' => t('This field reference another entity.'),
+    'settings' => array(
+      // Default to the core target entity type node.
+      'target_type' => 'node',
+      // The handler for this field.
+      'handler' => 'base',
+      // The handler settings.
+      'handler_settings' => array(),
+    ),
+    'instance_settings' => array(),
+    'default_widget' => 'entityreference_autocomplete',
+    'default_formatter' => 'entityreference_label',
+    'property_callbacks' => array('entityreference_field_property_callback'),
+  );
+  return $field_info;
+}
+
+/**
+ * Implements hook_flush_caches().
+ */
+function entityreference_flush_caches() {
+  // Because of the intricacies of the info hooks, we are forced to keep a
+  // separate list of the base tables of each entities, so that we can use
+  // it in entityreference_field_schema() without calling entity_get_info().
+  // See http://drupal.org/node/1416558 for details.
+  $base_tables = array();
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    if (!empty($entity_info['base table']) && !empty($entity_info['entity keys']['id'])) {
+      $base_tables[$entity_type] = array($entity_info['base table'], $entity_info['entity keys']['id']);
+    }
+  }
+  // We are using a variable because cache is going to be cleared right after
+  // hook_flush_caches() is finished.
+  variable_set('entityreference:base-tables', $base_tables);
+}
+
+/**
+ * Implements hook_menu().
+ */
+function entityreference_menu() {
+  $items = array();
+
+  $items['entityreference/autocomplete/single/%/%/%'] = array(
+    'title' => 'Entity Reference Autocomplete',
+    'page callback' => 'entityreference_autocomplete_callback',
+    'page arguments' => array(2, 3, 4, 5),
+    'access callback' => 'entityreference_autocomplete_access_callback',
+    'access arguments' => array(2, 3, 4, 5),
+    'type' => MENU_CALLBACK,
+  );
+  $items['entityreference/autocomplete/tags/%/%/%'] = array(
+    'title' => 'Entity Reference Autocomplete',
+    'page callback' => 'entityreference_autocomplete_callback',
+    'page arguments' => array(2, 3, 4, 5),
+    'access callback' => 'entityreference_autocomplete_access_callback',
+    'access arguments' => array(2, 3, 4, 5),
+    'type' => MENU_CALLBACK,
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function entityreference_field_is_empty($item, $field) {
+  $empty = !isset($item['target_id']) || !is_numeric($item['target_id']);
+
+  // Invoke the behaviors to allow them to override the empty status.
+  foreach (entityreference_get_behavior_handlers($field) as $handler) {
+    $handler->is_empty_alter($empty, $item, $field);
+  }
+  return $empty;
+}
+
+/**
+ * Get the behavior handlers for a given entityreference field.
+ */
+function entityreference_get_behavior_handlers($field, $instance = NULL) {
+  $object_cache = drupal_static(__FUNCTION__);
+  $identifier = $field['field_name'];
+  if (!empty($instance)) {
+    $identifier .= ':' . $instance['entity_type'] . ':' . $instance['bundle'];
+  }
+
+  if (!isset($object_cache[$identifier])) {
+    $object_cache[$identifier] = array();
+
+    // Merge in defaults.
+    $field['settings'] += array('behaviors' => array());
+
+    $object_cache[$field['field_name']] = array();
+    $behaviors = !empty($field['settings']['handler_settings']['behaviors']) ? $field['settings']['handler_settings']['behaviors'] : array();
+    if (!empty($instance['settings']['behaviors'])) {
+      $behaviors = array_merge($behaviors, $instance['settings']['behaviors']);
+    }
+    foreach ($behaviors as $behavior => $settings) {
+      if (empty($settings['status'])) {
+        // Behavior is not enabled.
+        continue;
+      }
+
+      $object_cache[$identifier][] = _entityreference_get_behavior_handler($behavior);
+    }
+  }
+
+  return $object_cache[$identifier];
+}
+
+/**
+ * Get the behavior handler for a given entityreference field and instance.
+ *
+ * @param $handler
+ *   The behavior handler name.
+ */
+function _entityreference_get_behavior_handler($behavior) {
+  $object_cache = drupal_static(__FUNCTION__);
+
+  if (!isset($object_cache[$behavior])) {
+    ctools_include('plugins');
+    $class = ctools_plugin_load_class('entityreference', 'behavior', $behavior, 'class');
+
+    $class = class_exists($class) ? $class : 'EntityReference_BehaviorHandler_Broken';
+    $object_cache[$behavior] = new $class($behavior);
+  }
+
+  return $object_cache[$behavior];
+}
+
+/**
+ * Get the selection handler for a given entityreference field.
+ */
+function entityreference_get_selection_handler($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
+  ctools_include('plugins');
+  $handler = $field['settings']['handler'];
+  $class = ctools_plugin_load_class('entityreference', 'selection', $handler, 'class');
+
+  if (class_exists($class)) {
+    return call_user_func(array($class, 'getInstance'), $field, $instance, $entity_type, $entity);
+  }
+  else {
+    return EntityReference_SelectionHandler_Broken::getInstance($field, $instance, $entity_type, $entity);
+  }
+}
+
+/**
+ * Implements hook_field_load().
+ */
+function entityreference_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) {
+  // Invoke the behaviors.
+  foreach (entityreference_get_behavior_handlers($field) as $handler) {
+    $handler->load($entity_type, $entities, $field, $instances, $langcode, $items);
+  }
+}
+
+/**
+ * Implements hook_field_validate().
+ */
+function entityreference_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+  $ids = array();
+  foreach ($items as $delta => $item) {
+    if (!entityreference_field_is_empty($item, $field) && $item['target_id'] !== NULL) {
+      $ids[$item['target_id']] = $delta;
+    }
+  }
+
+  if ($ids) {
+    $valid_ids = entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->validateReferencableEntities(array_keys($ids));
+
+    $invalid_entities = array_diff_key($ids, array_flip($valid_ids));
+    if ($invalid_entities) {
+      foreach ($invalid_entities as $id => $delta) {
+        $errors[$field['field_name']][$langcode][$delta][] = array(
+          'error' => 'entityreference_invalid_entity',
+          'message' => t('The referenced entity (@type: @id) is invalid.', array('@type' => $field['settings']['target_type'], '@id' => $id)),
+        );
+      }
+    }
+  }
+
+  // Invoke the behaviors.
+  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
+    $handler->validate($entity_type, $entity, $field, $instance, $langcode, $items, $errors);
+  }
+}
+
+/**
+ * Implements hook_field_presave().
+ *
+ * Adds the target type to the field data structure when saving.
+ */
+function entityreference_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  // Invoke the behaviors.
+  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
+    $handler->presave($entity_type, $entity, $field, $instance, $langcode, $items);
+  }
+}
+
+/**
+ * Implements hook_field_insert().
+ */
+function entityreference_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  // Invoke the behaviors.
+  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
+    $handler->insert($entity_type, $entity, $field, $instance, $langcode, $items);
+  }
+}
+
+/**
+ * Implements hook_field_attach_insert().
+ *
+ * Emulates a post-insert hook.
+ */
+function entityreference_field_attach_insert($entity_type, $entity) {
+  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->postInsert($entity_type, $entity, $field, $instance);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_update().
+ */
+function entityreference_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  // Invoke the behaviors.
+  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
+    $handler->update($entity_type, $entity, $field, $instance, $langcode, $items);
+  }
+}
+
+/**
+ * Implements hook_field_attach_update().
+ *
+ * Emulates a post-update hook.
+ */
+function entityreference_field_attach_update($entity_type, $entity) {
+  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->postUpdate($entity_type, $entity, $field, $instance);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_delete().
+ */
+function entityreference_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  // Invoke the behaviors.
+  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
+    $handler->delete($entity_type, $entity, $field, $instance, $langcode, $items);
+  }
+}
+
+/**
+ * Implements hook_field_attach_delete().
+ *
+ * Emulates a post-delete hook.
+ */
+function entityreference_field_attach_delete($entity_type, $entity) {
+  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->postDelete($entity_type, $entity, $field, $instance);
+      }
+    }
+  }
+}
+
+/**
+ * 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().
+ */
+function entityreference_field_settings_form($field, $instance, $has_data) {
+  // The field settings infrastructure is not AJAX enabled by default,
+  // because it doesn't pass over the $form_state.
+  // Build the whole form into a #process in which we actually have access
+  // to the form state.
+  $form = array(
+    '#type' => 'container',
+    '#attached' => array(
+      'css' => array(drupal_get_path('module', 'entityreference') . '/entityreference.admin.css'),
+    ),
+    '#process' => array(
+      '_entityreference_field_settings_process',
+      '_entityreference_field_settings_ajax_process',
+    ),
+    '#element_validate' => array('_entityreference_field_settings_validate'),
+    '#field' => $field,
+    '#instance' => $instance,
+    '#has_data' => $has_data,
+  );
+  return $form;
+}
+
+function _entityreference_field_settings_process($form, $form_state) {
+  $field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field'];
+  $instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance'];
+  $has_data = $form['#has_data'];
+
+  $settings = $field['settings'];
+  $settings += array('handler' => 'base');
+
+  // Select the target entity type.
+  $entity_type_options = array();
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    $entity_type_options[$entity_type] = $entity_info['label'];
+  }
+
+  $form['target_type'] = array(
+    '#type' => 'select',
+    '#title' => t('Target type'),
+    '#options' => $entity_type_options,
+    '#default_value' => $field['settings']['target_type'],
+    '#required' => TRUE,
+    '#description' => t('The entity type that can be referenced through this field.'),
+    '#disabled' => $has_data,
+    '#size' => 1,
+    '#ajax' => TRUE,
+    '#limit_validation_errors' => array(),
+  );
+
+  ctools_include('plugins');
+  $handlers = ctools_get_plugins('entityreference', 'selection');
+  uasort($handlers, 'ctools_plugin_sort');
+  $handlers_options = array();
+  foreach ($handlers as $handler => $handler_info) {
+    $handlers_options[$handler] = check_plain($handler_info['title']);
+  }
+
+  $form['handler'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Entity selection'),
+    '#tree' => TRUE,
+    '#process' => array('_entityreference_form_process_merge_parent'),
+  );
+
+  $form['handler']['handler'] = array(
+    '#type' => 'select',
+    '#title' => t('Mode'),
+    '#options' => $handlers_options,
+    '#default_value' => $settings['handler'],
+    '#required' => TRUE,
+    '#ajax' => TRUE,
+    '#limit_validation_errors' => array(),
+  );
+  $form['handler_submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Change handler'),
+    '#limit_validation_errors' => array(),
+    '#attributes' => array(
+      'class' => array('js-hide'),
+    ),
+    '#submit' => array('entityreference_settings_ajax_submit'),
+  );
+
+  $form['handler']['handler_settings'] = array(
+    '#type' => 'container',
+    '#attributes' => array('class' => array('entityreference-settings')),
+  );
+
+  $handler = entityreference_get_selection_handler($field, $instance);
+  $form['handler']['handler_settings'] += $handler->settingsForm($field, $instance);
+
+  _entityreference_get_behavior_elements($form, $field, $instance, 'field');
+  if (!empty($form['behaviors'])) {
+    $form['behaviors'] += array(
+      '#type' => 'fieldset',
+      '#title' => t('Additional behaviors'),
+      '#parents' => array_merge($form['#parents'], array('handler_settings', 'behaviors')),
+    );
+  }
+
+  return $form;
+}
+
+function _entityreference_field_settings_ajax_process($form, $form_state) {
+  _entityreference_field_settings_ajax_process_element($form, $form);
+  return $form;
+}
+
+function _entityreference_field_settings_ajax_process_element(&$element, $main_form) {
+  if (isset($element['#ajax']) && $element['#ajax'] === TRUE) {
+    $element['#ajax'] = array(
+      'callback' => 'entityreference_settings_ajax',
+      'wrapper' => $main_form['#id'],
+      'element' => $main_form['#array_parents'],
+    );
+  }
+
+  foreach (element_children($element) as $key) {
+    _entityreference_field_settings_ajax_process_element($element[$key], $main_form);
+  }
+}
+
+function _entityreference_form_process_merge_parent($element) {
+  $parents = $element['#parents'];
+  array_pop($parents);
+  $element['#parents'] = $parents;
+  return $element;
+}
+
+function _entityreference_element_validate_filter(&$element, &$form_state) {
+  $element['#value'] = array_filter($element['#value']);
+  form_set_value($element, $element['#value'], $form_state);
+}
+
+function _entityreference_field_settings_validate($form, &$form_state) {
+  // Store the new values in the form state.
+  $field = $form['#field'];
+  if (isset($form_state['values']['field'])) {
+    $field['settings'] = $form_state['values']['field']['settings'];
+  }
+  $form_state['entityreference']['field'] = $field;
+
+  unset($form_state['values']['field']['settings']['handler_submit']);
+}
+
+/**
+ * Implements hook_field_instance_settings_form().
+ */
+function entityreference_field_instance_settings_form($field, $instance) {
+  $form['settings'] = array(
+    '#type' => 'container',
+    '#attached' => array(
+      'css' => array(drupal_get_path('module', 'entityreference') . '/entityreference.admin.css'),
+    ),
+    '#weight' => 10,
+    '#tree' => TRUE,
+    '#process' => array(
+      '_entityreference_form_process_merge_parent',
+      '_entityreference_field_instance_settings_form',
+      '_entityreference_field_settings_ajax_process',
+    ),
+    '#element_validate' => array('_entityreference_field_instance_settings_validate'),
+    '#field' => $field,
+    '#instance' => $instance,
+  );
+
+  return $form;
+}
+
+function _entityreference_field_instance_settings_form($form, $form_state) {
+  $field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field'];
+  $instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance'];
+
+  _entityreference_get_behavior_elements($form, $field, $instance, 'instance');
+  if (!empty($form['behaviors'])) {
+    $form['behaviors'] += array(
+      '#type' => 'fieldset',
+      '#title' => t('Additional behaviors'),
+      '#process' => array(
+        '_entityreference_field_settings_ajax_process',
+      ),
+    );
+  }
+  return $form;
+}
+
+function _entityreference_field_instance_settings_validate($form, &$form_state) {
+  // Store the new values in the form state.
+  $instance = $form['#instance'];
+  if (isset($form_state['values']['instance'])) {
+    $instance = drupal_array_merge_deep($instance, $form_state['values']['instance']);
+  }
+  $form_state['entityreference']['instance'] = $instance;
+}
+
+/**
+ * Get the field or instance elements for the field configuration.
+ */
+function _entityreference_get_behavior_elements(&$element, $field, $instance, $level) {
+  // Add the accessible behavior handlers.
+  $behavior_plugins = entityreference_get_accessible_behavior_plugins($field, $instance);
+
+  if ($behavior_plugins[$level]) {
+    $element['behaviors'] = array();
+
+    foreach ($behavior_plugins[$level] as $name => $plugin) {
+      if ($level == 'field') {
+        $settings = !empty($field['settings']['handler_settings']['behaviors'][$name]) ? $field['settings']['handler_settings']['behaviors'][$name] : array();
+      }
+      else {
+        $settings = !empty($instance['settings']['behaviors'][$name]) ? $instance['settings']['behaviors'][$name] : array();
+      }
+      $settings += array('status' => $plugin['force enabled']);
+
+      // Render the checkbox.
+      $element['behaviors'][$name] = array(
+        '#tree' => TRUE,
+      );
+      $element['behaviors'][$name]['status'] = array(
+        '#type' => 'checkbox',
+        '#title' => check_plain($plugin['title']),
+        '#description' => $plugin['description'],
+        '#default_value' => $settings['status'],
+        '#disabled' => $plugin['force enabled'],
+        '#ajax' => TRUE,
+      );
+
+      if ($settings['status']) {
+        $handler = _entityreference_get_behavior_handler($name);
+        if ($behavior_elements = $handler->settingsForm($field, $instance)) {
+          foreach ($behavior_elements as $key => &$behavior_element) {
+            $behavior_element += array(
+              '#default_value' => !empty($settings[$key]) ? $settings[$key] : NULL,
+            );
+          }
+
+          // Get the behavior settings.
+          $behavior_elements += array(
+            '#type' => 'container',
+            '#process' => array('_entityreference_form_process_merge_parent'),
+            '#attributes' => array(
+              'class' => array('entityreference-settings'),
+            ),
+          );
+          $element['behaviors'][$name]['settings'] = $behavior_elements;
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Get all accessible behavior plugins.
+ */
+function entityreference_get_accessible_behavior_plugins($field, $instance) {
+  ctools_include('plugins');
+  $plugins = array('field' => array(), 'instance' => array());
+  foreach (ctools_get_plugins('entityreference', 'behavior') as $name => $plugin) {
+    $handler = _entityreference_get_behavior_handler($name);
+    $level = $plugin['behavior type'];
+    if ($handler->access($field, $instance)) {
+      $plugins[$level][$name] = $plugin;
+    }
+  }
+  return $plugins;
+}
+
+/**
+ * Ajax callback for the handler settings form.
+ *
+ * @see entityreference_field_settings_form()
+ */
+function entityreference_settings_ajax($form, $form_state) {
+  $trigger = $form_state['triggering_element'];
+  return drupal_array_get_nested_value($form, $trigger['#ajax']['element']);
+}
+
+/**
+ * Submit handler for the non-JS case.
+ *
+ * @see entityreference_field_settings_form()
+ */
+function entityreference_settings_ajax_submit($form, &$form_state) {
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Property callback for the Entity Metadata framework.
+ */
+function entityreference_field_property_callback(&$info, $entity_type, $field, $instance, $field_type) {
+  // Set the property type based on the targe type.
+  $field_type['property_type'] = $field['settings']['target_type'];
+
+  // Then apply the default.
+  entity_metadata_field_default_property_callback($info, $entity_type, $field, $instance, $field_type);
+
+  // Invoke the behaviors to allow them to change the properties.
+  foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) {
+    $handler->property_info_alter($info, $entity_type, $field, $instance, $field_type);
+  }
+}
+
+/**
+ * Implements hook_field_widget_info().
+ */
+function entityreference_field_widget_info() {
+  $widgets['entityreference_autocomplete'] = array(
+    'label' => t('Autocomplete'),
+    'description' => t('An autocomplete text field.'),
+    'field types' => array('entityreference'),
+    'settings' => array(
+      'match_operator' => 'CONTAINS',
+      'size' => 60,
+      // We don't have a default here, because it's not the same between
+      // the two widgets, and the Field API doesn't update default
+      // settings when the widget changes.
+      'path' => '',
+    ),
+  );
+
+  $widgets['entityreference_autocomplete_tags'] = array(
+    'label' => t('Autocomplete (Tags style)'),
+    'description' => t('An autocomplete text field.'),
+    'field types' => array('entityreference'),
+    'settings' => array(
+      'match_operator' => 'CONTAINS',
+      'size' => 60,
+      // We don't have a default here, because it's not the same between
+      // the two widgets, and the Field API doesn't update default
+      // settings when the widget changes.
+      'path' => '',
+    ),
+    'behaviors' => array(
+      'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+    ),
+  );
+
+  return $widgets;
+}
+
+/**
+ * Implements hook_field_widget_info_alter().
+ */
+function entityreference_field_widget_info_alter(&$info) {
+  if (module_exists('options')) {
+    $info['options_select']['field types'][] = 'entityreference';
+    $info['options_buttons']['field types'][] = 'entityreference';
+  }
+}
+
+/**
+ * Implements hook_field_widget_settings_form().
+ */
+function entityreference_field_widget_settings_form($field, $instance) {
+  $widget = $instance['widget'];
+  $settings = $widget['settings'] + field_info_widget_settings($widget['type']);
+
+  $form = array();
+
+  if ($widget['type'] == 'entityreference_autocomplete' || $widget['type'] == 'entityreference_autocomplete_tags') {
+    $form['match_operator'] = array(
+      '#type' => 'select',
+      '#title' => t('Autocomplete matching'),
+      '#default_value' => $settings['match_operator'],
+      '#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 nodes.'),
+    );
+    $form['size'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Size of textfield'),
+      '#default_value' => $settings['size'],
+      '#element_validate' => array('_element_validate_integer_positive'),
+      '#required' => TRUE,
+    );
+  }
+
+  return $form;
+}
+
+/**
+ * Implements hook_options_list().
+ */
+function entityreference_options_list($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
+  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;
+}
+
+/**
+ * Implements hook_query_TAG_alter().
+ */
+function entityreference_query_entityreference_alter(QueryAlterableInterface $query) {
+  $handler = $query->getMetadata('entityreference_selection_handler');
+  $handler->entityFieldQueryAlter($query);
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function entityreference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+  $entity_type = $instance['entity_type'];
+  $entity = isset($element['#entity']) ? $element['#entity'] : NULL;
+  $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
+
+  if ($instance['widget']['type'] == 'entityreference_autocomplete' || $instance['widget']['type'] == 'entityreference_autocomplete_tags') {
+
+    if ($instance['widget']['type'] == 'entityreference_autocomplete') {
+      // We let the Field API handles multiple values for us, only take
+      // care of the one matching our delta.
+      if (isset($items[$delta])) {
+        $items = array($items[$delta]);
+      }
+      else {
+        $items = array();
+      }
+    }
+
+    $entity_ids = array();
+    $entity_labels = array();
+
+    // Build an array of entities ID.
+    foreach ($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 ($entities as $entity_id => $entity_item) {
+      $label = $handler->getLabel($entity_item);
+      $key = "$label ($entity_id)";
+      // Labels containing commas or quotes must be wrapped in quotes.
+      if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
+        $key = '"' . str_replace('"', '""', $key) . '"';
+      }
+      $entity_labels[] = $key;
+    }
+
+    // Prepare the autocomplete path.
+    if (!empty($instance['widget']['settings']['path'])) {
+      $autocomplete_path = $instance['widget']['settings']['path'];
+    }
+    else {
+      $autocomplete_path = $instance['widget']['type'] == 'entityreference_autocomplete' ? 'entityreference/autocomplete/single' : 'entityreference/autocomplete/tags';
+    }
+
+    $autocomplete_path .= '/' . $field['field_name'] . '/' . $instance['entity_type'] . '/' . $instance['bundle'] . '/';
+    // Use <NULL> as a placeholder in the URL when we don't have an entity.
+    // Most webservers collapse two consecutive slashes.
+    $id = 'NULL';
+    if ($entity) {
+      list($eid) = entity_extract_ids($entity_type, $entity);
+      if ($eid) {
+        $id = $eid;
+      }
+    }
+    $autocomplete_path .= $id;
+
+    if ($instance['widget']['type'] == 'entityreference_autocomplete') {
+      $element += array(
+        '#type' => 'textfield',
+        '#maxlength' => 1024,
+        '#default_value' => implode(', ', $entity_labels),
+        '#autocomplete_path' => $autocomplete_path,
+        '#size' => $instance['widget']['settings']['size'],
+        '#element_validate' => array('_entityreference_autocomplete_validate'),
+      );
+      return array('target_id' => $element);
+    }
+    else {
+      $element += array(
+        '#type' => 'textfield',
+        '#maxlength' => 1024,
+        '#default_value' => implode(', ', $entity_labels),
+        '#autocomplete_path' => $autocomplete_path,
+        '#size' => $instance['widget']['settings']['size'],
+        '#element_validate' => array('_entityreference_autocomplete_tags_validate'),
+      );
+      return $element;
+    }
+  }
+}
+
+function _entityreference_autocomplete_validate($element, &$form_state, $form) {
+  // If a value was entered into the autocomplete...
+  $value = '';
+  if (!empty($element['#value'])) {
+    // Take "label (entity id)', match the id from parenthesis.
+    if (preg_match("/.+\((\d+)\)/", $element['#value'], $matches)) {
+      $value = $matches[1];
+    }
+    else {
+      // Try to get a match from the input string when the user didn't use the
+      // 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);
+    }
+  }
+  // Update the value of this element so the field can validate the product IDs.
+  form_set_value($element, $value, $form_state);
+}
+
+function _entityreference_autocomplete_tags_validate($element, &$form_state, $form) {
+  $value = array();
+  // If a value was entered into the autocomplete...
+  if (!empty($element['#value'])) {
+    $entities = drupal_explode_tags($element['#value']);
+    $value = array();
+    foreach ($entities as $entity) {
+      // Take "label (entity id)', match the id from parenthesis.
+      if (preg_match("/.+\((\d+)\)/", $entity, $matches)) {
+        $value[] = array(
+          'target_id' => $matches[1],
+        );
+      }
+      else {
+        // Try to get a match from the input string when the user didn't use the
+        // autocomplete but filled in a value manually.
+        $field = field_info_field($element['#field_name']);
+        $handler = entityreference_get_selection_handler($field);
+        $value[] = array(
+          'target_id' => $handler->validateAutocompleteInput($entity, $element, $form_state, $form),
+        );
+      }
+    }
+  }
+  // Update the value of this element so the field can validate the product IDs.
+  form_set_value($element, $value, $form_state);
+}
+
+/**
+ * Implements hook_field_widget_error().
+ */
+function entityreference_field_widget_error($element, $error) {
+  form_error($element, $error['message']);
+}
+
+/**
+ * Menu Access callback for the autocomplete widget.
+ *
+ * @param $type
+ *   The widget type (i.e. 'single' or 'tags').
+ * @param $field_name
+ *   The name of the entity-reference field.
+ * @param $entity_type
+ *   The entity type.
+ * @param $bundle_name
+ *   The bundle name.
+ * @return
+ *   True if user can access this menu item.
+ */
+function entityreference_autocomplete_access_callback($type, $field_name, $entity_type, $bundle_name) {
+  $field = field_info_field($field_name);
+  $instance = field_info_instance($entity_type, $field_name, $bundle_name);
+
+  if (!$field || !$instance || $field['type'] != 'entityreference' || !field_access('edit', $field, $entity_type)) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Menu callback: autocomplete the label of an entity.
+ *
+ * @param $type
+ *   The widget type (i.e. 'single' or 'tags').
+ * @param $field_name
+ *   The name of the entity-reference field.
+ * @param $entity_type
+ *   The entity type.
+ * @param $bundle_name
+ *   The bundle name.
+ * @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($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;
+  if ($entity_id !== 'NULL') {
+    $entity = entity_load_single($entity_type, $entity_id);
+    if (!$entity || !entity_access('view', $entity_type, $entity)) {
+      return MENU_ACCESS_DENIED;
+    }
+  }
+
+  $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
+
+  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) ? implode(', ', $tags_typed) . ', ' : '';
+    }
+  }
+  else {
+    // The user enters a single tag.
+    $prefix = '';
+    $tag_last = $string;
+  }
+
+  if (isset($tag_last)) {
+    // Get an array of matching entities.
+    $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 $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>';
+      }
+    }
+  }
+
+  drupal_json_output($matches);
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function entityreference_field_formatter_info() {
+  return array(
+    'entityreference_label' => array(
+      'label' => t('Label'),
+      'description' => t('Display the label of the referenced entities.'),
+      'field types' => array('entityreference'),
+      'settings' => array(
+        'link' => FALSE,
+      ),
+    ),
+    'entityreference_entity_id' => array(
+      'label' => t('Entity id'),
+      'description' => t('Display the id of the referenced entities.'),
+      'field types' => array('entityreference'),
+    ),
+    'entityreference_entity_view' => array(
+      'label' => t('Rendered entity'),
+      'description' => t('Display the referenced entities rendered by entity_view().'),
+      'field types' => array('entityreference'),
+      'settings' => array(
+        'view_mode' => '',
+        'links' => TRUE,
+      ),
+    ),
+  );
+}
+
+/**
+ * Implements hook_field_formatter_settings_form().
+ */
+function entityreference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
+  $display = $instance['display'][$view_mode];
+  $settings = $display['settings'];
+
+  if ($display['type'] == 'entityreference_label') {
+    $element['link'] = array(
+      '#title' => t('Link label to the referenced entity'),
+      '#type' => 'checkbox',
+      '#default_value' => $settings['link'],
+    );
+  }
+
+  if ($display['type'] == 'entityreference_entity_view') {
+    $entity_info = entity_get_info($field['settings']['target_type']);
+    $options = array();
+    if (!empty($entity_info['view modes'])) {
+      foreach ($entity_info['view modes'] as $view_mode => $view_mode_settings) {
+        $options[$view_mode] = $view_mode_settings['label'];
+      }
+    }
+
+    if (count($options) > 1) {
+      $element['view_mode'] = array(
+        '#type' => 'select',
+        '#options' => $options,
+        '#title' => t('View mode'),
+        '#default_value' => $settings['view_mode'],
+      );
+    }
+
+    $element['links'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Show links'),
+      '#default_value' => $settings['links'],
+    );
+  }
+
+  return $element;
+}
+
+/**
+ * Implements hook_field_formatter_settings_summary().
+ */
+function entityreference_field_formatter_settings_summary($field, $instance, $view_mode) {
+  $display = $instance['display'][$view_mode];
+  $settings = $display['settings'];
+
+  $summary = array();
+
+  if ($display['type'] == 'entityreference_label') {
+    $summary[] = $settings['link'] ? t('Link to the referenced entity') : t('No link');
+  }
+
+  if ($display['type'] == 'entityreference_entity_view') {
+    $entity_info = entity_get_info($field['settings']['target_type']);
+    $summary[] = t('Rendered as @mode', array('@mode' => isset($entity_info['view modes'][$settings['view_mode']]['label']) ? $entity_info['view modes'][$settings['view_mode']]['label'] : $settings['view_mode']));
+    $summary[] = !empty($settings['links']) ? t('Display links') : t('Do not display links');
+  }
+
+  return implode('<br />', $summary);
+}
+
+/**
+ * Implements hook_field_formatter_prepare_view().
+ */
+function entityreference_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
+  $target_ids = array();
+
+  // Collect every possible entity attached to any of the entities.
+  foreach ($entities as $id => $entity) {
+    foreach ($items[$id] as $delta => $item) {
+      if (isset($item['target_id'])) {
+        $target_ids[] = $item['target_id'];
+      }
+    }
+  }
+
+  if ($target_ids) {
+    $target_entities = entity_load($field['settings']['target_type'], $target_ids);
+  }
+  else {
+    $target_entities = array();
+  }
+
+  // Iterate through the fieldable entities again to attach the loaded data.
+  foreach ($entities as $id => $entity) {
+    $rekey = FALSE;
+
+    foreach ($items[$id] as $delta => $item) {
+      // 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 exist.
+      else {
+        unset($items[$id][$delta]);
+        $rekey = TRUE;
+      }
+    }
+
+    if ($rekey) {
+      // Rekey the items array.
+      $items[$id] = array_values($items[$id]);
+    }
+  }
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function entityreference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+  $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);
+
+      foreach ($items as $delta => $item) {
+        $label = $handler->getLabel($item['entity']);
+        // If the link is to be displayed and the entity has a uri, display a link.
+        // Note the assignment ($url = ) here is intended to be an assignment.
+        if ($display['settings']['link'] && ($uri = entity_uri($field['settings']['target_type'], $item['entity']))) {
+          $result[$delta] = array('#markup' => l($label, $uri['path'], $uri['options']));
+        }
+        else {
+          $result[$delta] = array('#markup' => check_plain($label));
+        }
+      }
+      break;
+
+    case 'entityreference_entity_id':
+      foreach ($items as $delta => $item) {
+        $result[$delta] = array('#markup' => check_plain($item['target_id']));
+      }
+      break;
+
+    case 'entityreference_entity_view':
+      foreach ($items as $delta => $item) {
+        // Protect ourselves from recursive rendering.
+        static $depth = 0;
+        $depth++;
+        if ($depth > 20) {
+          throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $entity_type, '@entity_id' => $item['target_id'])));
+        }
+
+        $entity = clone $item['entity'];
+        unset($entity->content);
+        $result[$delta] = entity_view($field['settings']['target_type'], array($item['target_id'] => $entity), $settings['view_mode'], $langcode, FALSE);
+
+        if (empty($settings['links']) && isset($result[$delta][$field['settings']['target_type']][$item['target_id']]['links'])) {
+          $result[$delta][$field['settings']['target_type']][$item['target_id']]['links']['#access'] = FALSE;
+        }
+        $depth = 0;
+      }
+      break;
+  }
+
+  return $result;
+}
+
+/**
+ * Exception thrown when the entity view renderer goes into a potentially infinite loop.
+ */
+class EntityReferenceRecursiveRenderingException extends Exception {}
+
+/**
+ * Implements hook_views_api().
+ */
+function entityreference_views_api() {
+  return array(
+    'api' => 3,
+    'path' => drupal_get_path('module', 'entityreference') . '/views',
+  );
+}

+ 12 - 0
sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.info

@@ -0,0 +1,12 @@
+name = Entity Reference Behavior Example
+description = Provides some example code for implementing Entity Reference behaviors.
+core = 7.x
+package = Fields
+dependencies[] = entityreference
+
+; Information added by drupal.org packaging script on 2012-11-18
+version = "7.x-1.0"
+core = "7.x"
+project = "entityreference"
+datestamp = "1353230808"
+

+ 15 - 0
sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/entityreference_behavior_example.module

@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * @file
+ * Example module to demonstrate Entity reference behavior handlers.
+ */
+
+/**
+ * Implements hook_ctools_plugin_directory().
+ */
+function entityreference_behavior_example_ctools_plugin_directory($module, $plugin) {
+  if ($module == 'entityreference') {
+    return 'plugins/' . $plugin;
+  }
+}

+ 31 - 0
sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/plugins/behavior/EntityReferenceFieldBehaviorExample.class.php

@@ -0,0 +1,31 @@
+<?php
+
+class EntityReferenceFieldBehaviorExample extends EntityReference_BehaviorHandler_Abstract {
+
+  public function load($entity_type, $entities, $field, $instances, $langcode, &$items) {
+    drupal_set_message(t('Do something on load!'));
+  }
+
+  public function insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
+    drupal_set_message(t('Do something on insert!'));
+  }
+
+  public function update($entity_type, $entity, $field, $instance, $langcode, &$items) {
+    drupal_set_message(t('Do something on update!'));
+  }
+
+  public function delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
+    drupal_set_message(t('Do something on delete!'));
+  }
+
+  /**
+   * Generate a settings form for this handler.
+   */
+  public function settingsForm($field, $instance) {
+    $form['test_field'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Field behavior setting'),
+    );
+    return $form;
+  }
+}

+ 31 - 0
sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/plugins/behavior/EntityReferenceInstanceBehaviorExample.class.php

@@ -0,0 +1,31 @@
+<?php
+
+class EntityReferenceInstanceBehaviorExample extends EntityReference_BehaviorHandler_Abstract {
+
+  public function load($entity_type, $entities, $field, $instances, $langcode, &$items) {
+    drupal_set_message(t('Do something on load, on the instance level!'));
+  }
+
+  public function insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
+    drupal_set_message(t('Do something on insert, on the instance level!'));
+  }
+
+  public function update($entity_type, $entity, $field, $instance, $langcode, &$items) {
+    drupal_set_message(t('Do something on update, on the instance level!'));
+  }
+
+  public function delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
+    drupal_set_message(t('Do something on delete, on the instance level!'));
+  }
+
+  /**
+   * Generate a settings form for this handler.
+   */
+  public function settingsForm($field, $instance) {
+    $form['test_instance'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Instance behavior setting'),
+    );
+    return $form;
+  }
+}

+ 8 - 0
sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/plugins/behavior/test_field_behavior.inc

@@ -0,0 +1,8 @@
+<?php
+
+$plugin = array(
+  'title' => t('Test behavior'),
+  'class' => 'EntityReferenceFieldBehaviorExample',
+  'weight' => 10,
+  'behavior type' => 'field',
+);

+ 8 - 0
sites/all/modules/contrib/fields/entityreference/examples/entityreference_behavior_example/plugins/behavior/test_instance_behavior.inc

@@ -0,0 +1,8 @@
+<?php
+
+$plugin = array(
+  'title' => t('Test instance behavior'),
+  'class' => 'EntityReferenceInstanceBehaviorExample',
+  'weight' => 10,
+  'behavior type' => 'instance',
+);

+ 5 - 0
sites/all/modules/contrib/fields/entityreference/patch url.txt

@@ -0,0 +1,5 @@
+1354482-er-user-roles-19.patch
+http://drupal.org/node/1354482
+
+patch-entityreference-7.x.patch
+http://drupal.org/node/1691612

+ 18 - 0
sites/all/modules/contrib/fields/entityreference/patch-entityreference-7.x.patch

@@ -0,0 +1,18 @@
+diff --git a/views/entityreference_plugin_display.inc b/views/entityreference_plugin_display.inc
+index 1fc6450..b9a956d 100644
+--- a/views/entityreference_plugin_display.inc
++++ b/views/entityreference_plugin_display.inc
+@@ -76,7 +76,12 @@
+       foreach ($style_options['search_fields'] as $field_alias) {
+         if (!empty($field_alias)) {
+           // Get the table and field names for the checked field
+-          $field = $this->view->query->fields[$this->view->field[$field_alias]->field_alias];
++          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']];
++          }
+           // Add an OR condition for the field
+           $conditions->condition($field['table'] . '.' . $field['field'], $value, 'LIKE');
+         }

+ 189 - 0
sites/all/modules/contrib/fields/entityreference/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;
+  }
+}

+ 18 - 0
sites/all/modules/contrib/fields/entityreference/plugins/behavior/EntityReferenceBehavior_ViewsFilterSelect.class.php

@@ -0,0 +1,18 @@
+<?php
+
+class EntityReferenceBehavior_ViewsFilterSelect extends EntityReference_BehaviorHandler_Abstract {
+
+  public function views_data_alter(&$data, $field) {
+    $entity_info = entity_get_info($field['settings']['target_type']);
+    $field_name = $field['field_name'] . '_target_id';
+    foreach ($data as $table_name => &$table_data) {
+      if (isset($table_data[$field_name])) {
+        // Set the entity id filter to use the in_operator handler with our
+        // own callback to return the values.
+        $table_data[$field_name]['filter']['handler'] = 'views_handler_filter_in_operator';
+        $table_data[$field_name]['filter']['options callback'] = 'entityreference_views_handler_options_list';
+        $table_data[$field_name]['filter']['options arguments'] = array($field['field_name']);
+      }
+    }
+  }
+}

+ 214 - 0
sites/all/modules/contrib/fields/entityreference/plugins/behavior/abstract.inc

@@ -0,0 +1,214 @@
+<?php
+
+/**
+ * Additional behaviors for a Entity Reference field.
+ *
+ * Implementations that wish to provide an implementation of this should
+ * register it using CTools' plugin system.
+ */
+interface EntityReference_BehaviorHandler {
+
+  /**
+   * Constructor for the behavior.
+   *
+   * @param $behavior
+   *   The name of the behavior plugin.
+   */
+  public function __construct($behavior);
+
+  /**
+   * Alter the field schema.
+   *
+   * @see hook_field_schema()
+   */
+  public function schema_alter(&$schema, $field);
+
+  /**
+   * Alter the properties information of a field instance.
+   *
+   * @see entity_hook_field_info()
+   */
+  public function property_info_alter(&$info, $entity_type, $field, $instance, $field_type);
+
+  /**
+   * Alter the views data of a field.
+   *
+   * @see entityreference_field_views_data()
+   */
+  public function views_data_alter(&$data, $field);
+
+  /**
+   * Act on loading entity reference fields of entities.
+   *
+   * @see hook_field_load()
+   */
+  public function load($entity_type, $entities, $field, $instances, $langcode, &$items);
+
+  /**
+   * Alter the empty status of a field item.
+   *
+   * @see hook_field_is_empty()
+   */
+  public function is_empty_alter(&$empty, $item, $field);
+
+  /**
+   * Act on validating an entity reference field.
+   *
+   * @see hook_field_validate()
+   */
+  public function validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors);
+
+  /**
+   * Act on presaving an entity reference field.
+   *
+   * @see hook_field_presave()
+   */
+  public function presave($entity_type, $entity, $field, $instance, $langcode, &$items);
+
+  /**
+   * Act before inserting an entity reference field.
+   *
+   * @see hook_field_insert()
+   */
+  public function insert($entity_type, $entity, $field, $instance, $langcode, &$items);
+
+  /**
+   * Act after inserting an entity reference field.
+   *
+   * @see hook_field_attach_insert()
+   */
+  public function postInsert($entity_type, $entity, $field, $instance);
+
+  /**
+   * Act before updating an entity reference field.
+   *
+   * @see hook_field_update()
+   */
+  public function update($entity_type, $entity, $field, $instance, $langcode, &$items);
+
+  /**
+   * Act after updating an entity reference field.
+   *
+   * @see hook_field_attach_update()
+   */
+  public function postUpdate($entity_type, $entity, $field, $instance);
+
+  /**
+   * Act before deleting an entity with an entity reference field.
+   *
+   * @see hook_field_delete()
+   */
+  public function delete($entity_type, $entity, $field, $instance, $langcode, &$items);
+
+  /**
+   * Act after deleting an entity with an entity reference field.
+   *
+   * @see hook_field_attach_delete()
+   */
+  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.
+   */
+  public function settingsForm($field, $instance);
+
+  /**
+   * Determine if handler should appear.
+   */
+  public function access($field, $instance);
+}
+
+/**
+ * An abstract implementation of EntityReference_BehaviorHandler.
+ */
+abstract class EntityReference_BehaviorHandler_Abstract implements EntityReference_BehaviorHandler {
+
+  /**
+   * The name of the behavior plugin.
+   */
+  protected $behavior;
+
+  /**
+   * The plugin definition.
+   */
+  protected $plugin;
+
+  public function __construct($behavior) {
+    $this->behavior = $behavior;
+
+    ctools_include('plugins');
+    $plugin = ctools_get_plugins('entityreference', 'behavior', $behavior);
+    $this->plugin = $plugin;
+  }
+
+  public function schema_alter(&$schema, $field) {}
+
+  public function property_info_alter(&$info, $entity_type, $field, $instance, $field_type) {}
+
+  public function views_data_alter(&$data, $field) {}
+
+  public function load($entity_type, $entities, $field, $instances, $langcode, &$items) {}
+
+  public function is_empty_alter(&$empty, $item, $field) {}
+
+  public function validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {}
+
+  public function presave($entity_type, $entity, $field, $instance, $langcode, &$items) {}
+
+  public function insert($entity_type, $entity, $field, $instance, $langcode, &$items) {}
+
+  public function postInsert($entity_type, $entity, $field, $instance) {}
+
+  public function update($entity_type, $entity, $field, $instance, $langcode, &$items) {}
+
+  public function postUpdate($entity_type, $entity, $field, $instance) {}
+
+  public function delete($entity_type, $entity, $field, $instance, $langcode, &$items) {}
+
+  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) {
+    return TRUE;
+  }
+}
+
+/**
+ * A broken implementation of EntityReference_BehaviorHandler.
+ */
+class EntityReference_BehaviorHandler_Broken extends EntityReference_BehaviorHandler_Abstract {
+  public function settingsForm($field, $instance) {
+    $form['behavior_handler'] = array(
+      '#markup' => t('The selected behavior handler is broken.'),
+    );
+    return $form;
+  }
+}

+ 16 - 0
sites/all/modules/contrib/fields/entityreference/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,
+  );
+}

+ 10 - 0
sites/all/modules/contrib/fields/entityreference/plugins/behavior/views-select-list.inc

@@ -0,0 +1,10 @@
+<?php
+
+if (module_exists('views')) {
+  $plugin = array(
+    'title' => t('Render Views filters as select list'),
+    'description' => t('Provides a select list for Views filters on this field. This should not be used when there are over 100 entities, as it might cause an out of memory error.'),
+    'class' => 'EntityReferenceBehavior_ViewsFilterSelect',
+    'behavior type' => 'field',
+  );
+}

+ 599 - 0
sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php

@@ -0,0 +1,599 @@
+<?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) {
+        list(,, $bundle) = entity_extract_ids($entity_type, $entity);
+        $options[$bundle][$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);
+  }
+
+  /**
+   * 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");
+  }
+}
+
+/**
+ * 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'))) {
+      $base_table = $this->ensureBaseTable($query);
+      $query->condition("$base_table.status", NODE_PUBLISHED);
+    }
+  }
+}
+
+/**
+ * Override for the User type.
+ *
+ * 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);
+
+    // The user entity doesn't have a label column.
+    if (isset($match)) {
+      $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)]);
+    }
+
+    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
+      // match the anonymous user, that doesn't actually have a name in the
+      // database.
+      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);
+        }
+      }
+    }
+
+    $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');
+    }
+  }
+}
+
+/**
+ * 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')) {
+      $base_table = $this->ensureBaseTable($query);
+      $query->condition("$base_table.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.
+    $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.
+    $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;
+      }
+    }
+  }
+
+  /**
+   * 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;
+  }
+}

+ 553 - 0
sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Generic.class.php.orig

@@ -0,0 +1,553 @@
+<?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) {
+        list(,, $bundle) = entity_extract_ids($entity_type, $entity);
+        $options[$bundle][$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);
+  }
+
+  /**
+   * 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");
+  }
+}
+
+/**
+ * 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'))) {
+      $base_table = $this->ensureBaseTable($query);
+      $query->condition("$base_table.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')) {
+      $base_table = $this->ensureBaseTable($query);
+      $query->condition("$base_table.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.
+    $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.
+    $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;
+      }
+    }
+  }
+
+  /**
+   * 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;
+  }
+}

+ 193 - 0
sites/all/modules/contrib/fields/entityreference/plugins/selection/EntityReference_SelectionHandler_Views.class.php

@@ -0,0 +1,193 @@
+<?php
+
+/**
+ * Entity handler for Views.
+ */
+class EntityReference_SelectionHandler_Views implements EntityReference_SelectionHandler {
+
+  /**
+   * Implements EntityReferenceHandler::getInstance().
+   */
+  public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
+    return new EntityReference_SelectionHandler_Views($field, $instance);
+  }
+
+  protected function __construct($field, $instance) {
+    $this->field = $field;
+    $this->instance = $instance;
+  }
+
+  /**
+   * Implements EntityReferenceHandler::settingsForm().
+   */
+  public static function settingsForm($field, $instance) {
+    $view_settings = empty($field['settings']['handler_settings']['view']) ? '' : $field['settings']['handler_settings']['view'];
+    $displays = views_get_applicable_views('entityreference display');
+    // Filter views that list the entity type we want, and group the separate
+    // displays by view.
+    $entity_info = entity_get_info($field['settings']['target_type']);
+    $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;
+      }
+    }
+
+    // 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).
+    $form['view']['#element_validate'] = array('entityreference_view_settings_validate');
+
+    if ($options) {
+      $default = !empty($view_settings['view_name']) ? $view_settings['view_name'] . ':' . $view_settings['display_name'] : NULL;
+      $form['view']['view_and_display'] = array(
+        '#type' => 'select',
+        '#title' => t('View used to select the entities'),
+        '#required' => TRUE,
+        '#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 "Entity Reference" are eligible.') . '</p>',
+      );
+
+      $default = !empty($view_settings['args']) ? implode(', ', $view_settings['args']) : '';
+      $form['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.'),
+      );
+    }
+    else {
+      $form['view']['no_view_help'] = array(
+        '#markup' => '<p>' . t('No eligible views were found. <a href="@create">Create a view</a> with an <em>Entity 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>',
+      );
+    }
+    return $form;
+  }
+
+  protected function initializeView($match = NULL, $match_operator = 'CONTAINS', $limit = 0, $ids = NULL) {
+    $view_name = $this->field['settings']['handler_settings']['view']['view_name'];
+    $display_name = $this->field['settings']['handler_settings']['view']['display_name'];
+    $args = $this->field['settings']['handler_settings']['view']['args'];
+    $entity_type = $this->field['settings']['target_type'];
+
+    // Check that the view is valid and the display still exists.
+    $this->view = views_get_view($view_name);
+    if (!$this->view || !isset($this->view->display[$display_name]) || !$this->view->access($display_name)) {
+      watchdog('entityreference', 'The view %view_name is no longer eligible for the %field_name field.', array('%view_name' => $view_name, '%field_name' => $this->instance['label']), WATCHDOG_WARNING);
+      return FALSE;
+    }
+    $this->view->set_display($display_name);
+
+    // Make sure the query is not cached.
+    $this->view->is_cacheable = FALSE;
+
+    // Pass options to the display handler to make them available later.
+    $entityreference_options = array(
+      'match' => $match,
+      'match_operator' => $match_operator,
+      'limit' => $limit,
+      'ids' => $ids,
+    );
+    $this->view->display_handler->set_option('entityreference_options', $entityreference_options);
+    return TRUE;
+  }
+
+  /**
+   * Implements EntityReferenceHandler::getReferencableEntities().
+   */
+  public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
+    $display_name = $this->field['settings']['handler_settings']['view']['display_name'];
+    $args = $this->field['settings']['handler_settings']['view']['args'];
+    $result = array();
+    if ($this->initializeView($match, $match_operator, $limit)) {
+      // Get the results.
+      $result = $this->view->execute_display($display_name, $args);
+    }
+
+    $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;
+  }
+
+  /**
+   * Implements EntityReferenceHandler::countReferencableEntities().
+   */
+  function countReferencableEntities($match = NULL, $match_operator = 'CONTAINS') {
+    $this->getReferencableEntities($match, $match_operator);
+    return $this->view->total_items;
+  }
+
+  function validateReferencableEntities(array $ids) {
+    $display_name = $this->field['settings']['handler_settings']['view']['display_name'];
+    $args = $this->field['settings']['handler_settings']['view']['args'];
+    $result = array();
+    if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) {
+      // Get the results.
+      $entities = $this->view->execute_display($display_name, $args);
+      $result = array_keys($entities);
+    }
+    return $result;
+  }
+
+  /**
+   * Implements EntityReferenceHandler::validateAutocompleteInput().
+   */
+  public function validateAutocompleteInput($input, &$element, &$form_state, $form) {
+    return NULL;
+  }
+
+  /**
+   * Implements EntityReferenceHandler::getLabel().
+   */
+  public function getLabel($entity) {
+    return entity_label($this->field['settings']['target_type'], $entity);
+  }
+
+  /**
+   * Implements EntityReferenceHandler::entityFieldQueryAlter().
+   */
+  public function entityFieldQueryAlter(SelectQueryInterface $query) {
+
+  }
+
+}
+
+function entityreference_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 {
+    form_error($element, t('The views entity selection mode requires a view.'));
+    return;
+  }
+
+  // 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);
+  form_set_value($element, $value, $form_state);
+}

+ 113 - 0
sites/all/modules/contrib/fields/entityreference/plugins/selection/abstract.inc

@@ -0,0 +1,113 @@
+<?php
+
+/**
+ * @file
+ * Abstraction of the selection logic of an entity reference field.
+ *
+ * Implementations that wish to provide an implementation of this should
+ * register it using CTools' plugin system.
+ */
+interface EntityReference_SelectionHandler {
+  /**
+   * Factory function: create a new instance of this handler for a given field.
+   *
+   * @param $field
+   *   A field datastructure.
+   * @return EntityReferenceHandler
+   */
+  public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL);
+
+  /**
+   * Return a list of referencable entities.
+   *
+   * @return
+   *   An array of referencable entities, which keys are entity ids and
+   *   values (safe HTML) labels to be displayed to the user.
+   */
+  public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0);
+
+  /**
+   * Count entities that are referencable by a given field.
+   */
+  public function countReferencableEntities($match = NULL, $match_operator = 'CONTAINS');
+
+  /**
+   * Validate that entities can be referenced by this field.
+   *
+   * @return
+   *   An array of entity ids that are valid.
+   */
+  public function validateReferencableEntities(array $ids);
+
+  /**
+   * Validate Input from autocomplete widget that has no Id.
+   *
+   * @see _entityreference_autocomplete_validate()
+   *
+   * @param $input
+   * 	 Single string from autocomplete widget.
+   * @param $element
+   *   The form element to set a form error.
+   * @return
+   *   Value of a matching entity id, or NULL if none.
+   */
+  public function validateAutocompleteInput($input, &$element, &$form_state, $form);
+
+  /**
+   * Give the handler a chance to alter the SelectQuery generated by EntityFieldQuery.
+   */
+  public function entityFieldQueryAlter(SelectQueryInterface $query);
+
+  /**
+   * Return the label of a given entity.
+   */
+  public function getLabel($entity);
+
+  /**
+   * Generate a settings form for this handler.
+   */
+  public static function settingsForm($field, $instance);
+}
+
+/**
+ * A null implementation of EntityReference_SelectionHandler.
+ */
+class EntityReference_SelectionHandler_Broken implements EntityReference_SelectionHandler {
+  public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
+    return new EntityReference_SelectionHandler_Broken($field, $instance, $entity_type, $entity);
+  }
+
+  protected function __construct($field, $instance) {
+    $this->field = $field;
+    $this->instance = $instance;
+  }
+
+  public static function settingsForm($field, $instance) {
+    $form['selection_handler'] = array(
+      '#markup' => t('The selected selection handler is broken.'),
+    );
+    return $form;
+  }
+
+  public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
+    return array();
+  }
+
+  public function countReferencableEntities($match = NULL, $match_operator = 'CONTAINS') {
+    return 0;
+  }
+
+  public function validateReferencableEntities(array $ids) {
+    return array();
+  }
+
+  public function validateAutocompleteInput($input, &$element, &$form_state, $form) {
+    return NULL;
+  }
+
+  public function entityFieldQueryAlter(SelectQueryInterface $query) {}
+
+  public function getLabel($entity) {
+    return '';
+  }
+}

+ 7 - 0
sites/all/modules/contrib/fields/entityreference/plugins/selection/base.inc

@@ -0,0 +1,7 @@
+<?php
+
+$plugin = array(
+  'title' => t('Simple (with optional filter by bundle)'),
+  'class' => 'EntityReference_SelectionHandler_Generic',
+  'weight' => -100,
+);

+ 9 - 0
sites/all/modules/contrib/fields/entityreference/plugins/selection/views.inc

@@ -0,0 +1,9 @@
+<?php
+
+if (module_exists('views')) {
+  $plugin = array(
+    'title' => t('Views: Filter by an entity reference view'),
+    'class' => 'EntityReference_SelectionHandler_Views',
+    'weight' => 0,
+  );
+}

+ 114 - 0
sites/all/modules/contrib/fields/entityreference/tests/entityreference.admin.test

@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * @file
+ * Contains EntityReferenceHandlersTestCase
+ */
+
+/**
+ * Test for Entity Reference admin UI.
+ */
+class EntityReferenceAdminTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity Reference UI',
+      'description' => 'Tests for the administrative UI.',
+      'group' => 'Entity Reference',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp(array('field_ui', 'entity', 'ctools', 'entityreference'));
+
+    // Create test user.
+    $this->admin_user = $this->drupalCreateUser(array('access content', 'administer content types'));
+    $this->drupalLogin($this->admin_user);
+
+    // Create content type, with underscores.
+    $type_name = strtolower($this->randomName(8)) . '_test';
+    $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
+    $this->type = $type->type;
+    // Store a valid URL name, with hyphens instead of underscores.
+    $this->hyphen_type = str_replace('_', '-', $this->type);
+  }
+
+  protected function assertFieldSelectOptions($name, $expected_options) {
+    $xpath = $this->buildXPathQuery('//select[@name=:name]', array(':name' => $name));
+    $fields = $this->xpath($xpath);
+    if ($fields) {
+      $field = $fields[0];
+      $options = $this->getAllOptionsList($field);
+      return $this->assertIdentical($options, $expected_options);
+    }
+    else {
+      return $this->fail(t('Unable to find field @name', array('@name' => $name)));
+    }
+  }
+
+  /**
+   * Extract all the options of a select element.
+   */
+  protected function getAllOptionsList($element) {
+    $options = array();
+    // Add all options items.
+    foreach ($element->option as $option) {
+      $options[] = (string) $option['value'];
+    }
+    // TODO: support optgroup.
+    return $options;
+  }
+
+  public function testFieldAdminHandler() {
+    $bundle_path = 'admin/structure/types/manage/' . $this->hyphen_type;
+
+    // First step: 'Add new field' on the 'Manage fields' page.
+    $this->drupalPost($bundle_path . '/fields', array(
+      'fields[_add_new_field][label]' => 'Test label',
+      'fields[_add_new_field][field_name]' => 'test',
+      'fields[_add_new_field][type]' => 'entityreference',
+      'fields[_add_new_field][widget_type]' => 'entityreference_autocomplete',
+    ), t('Save'));
+
+    // Node should be selected by default.
+    $this->assertFieldByName('field[settings][target_type]', 'node');
+    // The base handler should be selected by default.
+    $this->assertFieldByName('field[settings][handler]', 'base');
+
+    // The base handler settings should be diplayed.
+    $entity_type = 'node';
+    $entity_info = entity_get_info($entity_type);
+    foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+      $this->assertFieldByName('field[settings][handler_settings][target_bundles][' . $bundle_name . ']');
+    }
+
+    // Test the sort settings.
+    $options = array('none', 'property', 'field');
+    $this->assertFieldSelectOptions('field[settings][handler_settings][sort][type]', $options);
+    // Option 0: no sort.
+    $this->assertFieldByName('field[settings][handler_settings][sort][type]', 'none');
+    $this->assertNoFieldByName('field[settings][handler_settings][sort][property]');
+    $this->assertNoFieldByName('field[settings][handler_settings][sort][field]');
+    $this->assertNoFieldByName('field[settings][handler_settings][sort][direction]');
+    // Option 1: sort by property.
+    $this->drupalPostAJAX(NULL, array('field[settings][handler_settings][sort][type]' => 'property'), 'field[settings][handler_settings][sort][type]');
+    $this->assertFieldByName('field[settings][handler_settings][sort][property]', '');
+    $this->assertNoFieldByName('field[settings][handler_settings][sort][field]');
+    $this->assertFieldByName('field[settings][handler_settings][sort][direction]', 'ASC');
+    // Option 2: sort by field.
+    $this->drupalPostAJAX(NULL, array('field[settings][handler_settings][sort][type]' => 'field'), 'field[settings][handler_settings][sort][type]');
+    $this->assertNoFieldByName('field[settings][handler_settings][sort][property]');
+    $this->assertFieldByName('field[settings][handler_settings][sort][field]', '');
+    $this->assertFieldByName('field[settings][handler_settings][sort][direction]', 'ASC');
+    // Set back to no sort.
+    $this->drupalPostAJAX(NULL, array('field[settings][handler_settings][sort][type]' => 'none'), 'field[settings][handler_settings][sort][type]');
+
+    // Second step: 'Instance settings' form.
+    $this->drupalPost(NULL, array(), t('Save field settings'));
+
+    // Third step: confirm.
+    $this->drupalPost(NULL, array(), t('Save settings'));
+
+    // Check that the field appears in the overview form.
+    $this->assertFieldByXPath('//table[@id="field-overview"]//td[1]', 'Test label', t('Field was created and appears in the overview page.'));
+  }
+}

+ 575 - 0
sites/all/modules/contrib/fields/entityreference/tests/entityreference.handlers.test

@@ -0,0 +1,575 @@
+<?php
+
+/**
+ * @file
+ * Contains EntityReferenceHandlersTestCase
+ */
+
+/**
+ * Test for Entity Reference handlers.
+ */
+class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity Reference Handlers',
+      'description' => 'Tests for the base handlers provided by Entity Reference.',
+      'group' => 'Entity Reference',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp('entityreference');
+  }
+
+  protected function assertReferencable($field, $tests, $handler_name) {
+    $handler = entityreference_get_selection_handler($field);
+
+    foreach ($tests as $test) {
+      foreach ($test['arguments'] as $arguments) {
+        $result = call_user_func_array(array($handler, 'getReferencableEntities'), $arguments);
+        $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);
+        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)));
+      }
+    }
+  }
+
+  /**
+   * Test the node-specific overrides of the entity handler.
+   */
+  public function testNodeHandler() {
+    // 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(),
+        ),
+      ),
+      'field_name' => 'test_field',
+      'type' => 'entityreference',
+      'cardinality' => '1',
+    );
+
+    // Build a set of test data.
+    // Titles contain HTML-special characters to test escaping.
+    $nodes = array(
+      'published1' => (object) array(
+        'type' => 'article',
+        'status' => 1,
+        'title' => 'Node published1 (<&>)',
+        'uid' => 1,
+      ),
+      'published2' => (object) array(
+        'type' => 'article',
+        'status' => 1,
+        'title' => 'Node published2 (<&>)',
+        'uid' => 1,
+      ),
+      'unpublished' => (object) array(
+        'type' => 'article',
+        'status' => 0,
+        'title' => 'Node unpublished (<&>)',
+        'uid' => 1,
+      ),
+    );
+
+    $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;
+    $referencable_tests = array(
+      array(
+        'arguments' => array(
+          array(NULL, 'CONTAINS'),
+        ),
+        'result' => array(
+          'article' => array(
+            $nodes['published1']->nid => $node_labels['published1'],
+            $nodes['published2']->nid => $node_labels['published2'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('published1', 'CONTAINS'),
+          array('Published1', 'CONTAINS'),
+        ),
+        'result' => array(
+          'article' => array(
+            $nodes['published1']->nid => $node_labels['published1'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('published2', 'CONTAINS'),
+          array('Published2', 'CONTAINS'),
+        ),
+        'result' => array(
+          'article' => array(
+            $nodes['published2']->nid => $node_labels['published2'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('invalid node', 'CONTAINS'),
+        ),
+        'result' => array(),
+      ),
+      array(
+        'arguments' => array(
+          array('Node unpublished', 'CONTAINS'),
+        ),
+        'result' => array(),
+      ),
+    );
+    $this->assertReferencable($field, $referencable_tests, 'Node handler');
+
+    // Test as an admin.
+    $admin_user = $this->drupalCreateUser(array('access content', 'bypass node access'));
+    $GLOBALS['user'] = $admin_user;
+    $referencable_tests = array(
+      array(
+        'arguments' => array(
+          array(NULL, 'CONTAINS'),
+        ),
+        'result' => array(
+          'article' => array(
+            $nodes['published1']->nid => $node_labels['published1'],
+            $nodes['published2']->nid => $node_labels['published2'],
+            $nodes['unpublished']->nid => $node_labels['unpublished'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('Node unpublished', 'CONTAINS'),
+        ),
+        'result' => array(
+          'article' => array(
+            $nodes['unpublished']->nid => $node_labels['unpublished'],
+          ),
+        ),
+      ),
+    );
+    $this->assertReferencable($field, $referencable_tests, 'Node handler (admin)');
+  }
+
+  /**
+   * Test the user-specific overrides of the entity handler.
+   */
+  public function testUserHandler() {
+    // Build a fake field instance.
+    $field = array(
+      'translatable' => FALSE,
+      'entity_types' => array(),
+      'settings' => array(
+        'handler' => 'base',
+        'target_type' => 'user',
+        'handler_settings' => array(
+          'target_bundles' => array(),
+        ),
+      ),
+      'field_name' => 'test_field',
+      'type' => 'entityreference',
+      'cardinality' => '1',
+    );
+
+    // Build a set of test data.
+    $users = array(
+      'anonymous' => user_load(0),
+      'admin' => user_load(1),
+      'non_admin' => (object) array(
+        'name' => 'non_admin <&>',
+        'mail' => 'non_admin@example.com',
+        'roles' => array(),
+        'pass' => user_password(),
+        'status' => 1,
+      ),
+      'blocked' => (object) array(
+        'name' => 'blocked <&>',
+        'mail' => 'blocked@example.com',
+        'roles' => array(),
+        'pass' => user_password(),
+        'status' => 0,
+      ),
+    );
+
+    // The label of the anonymous user is variable_get('anonymous').
+    $users['anonymous']->name = variable_get('anonymous', t('Anonymous'));
+
+    $user_labels = array();
+    foreach ($users as $key => $user) {
+      if (!isset($user->uid)) {
+        $users[$key] = $user = user_save(drupal_anonymous_user(), (array) $user);
+      }
+      $user_labels[$key] = check_plain($user->name);
+    }
+
+    // Test as a non-admin.
+    $GLOBALS['user'] = $users['non_admin'];
+    $referencable_tests = array(
+      array(
+        'arguments' => array(
+          array(NULL, 'CONTAINS'),
+        ),
+        'result' => array(
+          'user' => array(
+            $users['admin']->uid => $user_labels['admin'],
+            $users['non_admin']->uid => $user_labels['non_admin'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('non_admin', 'CONTAINS'),
+          array('NON_ADMIN', 'CONTAINS'),
+        ),
+        'result' => array(
+          'user' => array(
+            $users['non_admin']->uid => $user_labels['non_admin'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('invalid user', 'CONTAINS'),
+        ),
+        'result' => array(),
+      ),
+      array(
+        'arguments' => array(
+          array('blocked', 'CONTAINS'),
+        ),
+        'result' => array(),
+      ),
+    );
+    $this->assertReferencable($field, $referencable_tests, 'User handler');
+
+    $GLOBALS['user'] = $users['admin'];
+    $referencable_tests = array(
+      array(
+        'arguments' => array(
+          array(NULL, 'CONTAINS'),
+        ),
+        'result' => array(
+          '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(
+        'arguments' => array(
+          array('blocked', 'CONTAINS'),
+        ),
+        'result' => array(
+          'user' => array(
+            $users['blocked']->uid => $user_labels['blocked'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('Anonymous', 'CONTAINS'),
+          array('anonymous', 'CONTAINS'),
+        ),
+        'result' => array(
+          'user' => array(
+            $users['anonymous']->uid => $user_labels['anonymous'],
+          ),
+        ),
+      ),
+    );
+    $this->assertReferencable($field, $referencable_tests, 'User handler (admin)');
+  }
+
+  /**
+   * Test the comment-specific overrides of the entity handler.
+   */
+  public function testCommentHandler() {
+    // Build a fake field instance.
+    $field = array(
+      'translatable' => FALSE,
+      'entity_types' => array(),
+      'settings' => array(
+        'handler' => 'base',
+        'target_type' => 'comment',
+        'handler_settings' => array(
+          'target_bundles' => array(),
+        ),
+      ),
+      'field_name' => 'test_field',
+      'type' => 'entityreference',
+      'cardinality' => '1',
+    );
+
+    // Build a set of test data.
+    $nodes = array(
+      'published' => (object) array(
+        'type' => 'article',
+        'status' => 1,
+        'title' => 'Node published',
+        'uid' => 1,
+      ),
+      'unpublished' => (object) array(
+        'type' => 'article',
+        'status' => 0,
+        'title' => 'Node unpublished',
+        'uid' => 1,
+      ),
+    );
+    foreach ($nodes as $node) {
+      node_save($node);
+    }
+
+    $comments = array(
+      'published_published' => (object) array(
+        'nid' => $nodes['published']->nid,
+        'uid' => 1,
+        'cid' => NULL,
+        'pid' => 0,
+        'status' => COMMENT_PUBLISHED,
+        'subject' => 'Comment Published <&>',
+        'hostname' => ip_address(),
+        'language' => LANGUAGE_NONE,
+      ),
+      'published_unpublished' => (object) array(
+        'nid' => $nodes['published']->nid,
+        'uid' => 1,
+        'cid' => NULL,
+        'pid' => 0,
+        'status' => COMMENT_NOT_PUBLISHED,
+        'subject' => 'Comment Unpublished <&>',
+        'hostname' => ip_address(),
+        'language' => LANGUAGE_NONE,
+      ),
+      'unpublished_published' => (object) array(
+        'nid' => $nodes['unpublished']->nid,
+        'uid' => 1,
+        'cid' => NULL,
+        'pid' => 0,
+        'status' => COMMENT_NOT_PUBLISHED,
+        'subject' => 'Comment Published on Unpublished node <&>',
+        'hostname' => ip_address(),
+        'language' => LANGUAGE_NONE,
+      ),
+    );
+
+    $comment_labels = array();
+    foreach ($comments as $key => $comment) {
+      comment_save($comment);
+      $comment_labels[$key] = check_plain($comment->subject);
+    }
+
+    // Test as a non-admin.
+    $normal_user = $this->drupalCreateUser(array('access content', 'access comments'));
+    $GLOBALS['user'] = $normal_user;
+    $referencable_tests = array(
+      array(
+        'arguments' => array(
+          array(NULL, 'CONTAINS'),
+        ),
+        'result' => array(
+          'comment_node_article' => array(
+            $comments['published_published']->cid => $comment_labels['published_published'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('Published', 'CONTAINS'),
+        ),
+        'result' => array(
+          'comment_node_article' => array(
+            $comments['published_published']->cid => $comment_labels['published_published'],
+          ),
+        ),
+      ),
+      array(
+        'arguments' => array(
+          array('invalid comment', 'CONTAINS'),
+        ),
+        'result' => array(),
+      ),
+      array(
+        'arguments' => array(
+          array('Comment Unpublished', 'CONTAINS'),
+        ),
+        'result' => array(),
+      ),
+    );
+    $this->assertReferencable($field, $referencable_tests, 'Comment handler');
+
+    // Test as a comment admin.
+    $admin_user = $this->drupalCreateUser(array('access content', 'access comments', 'administer comments'));
+    $GLOBALS['user'] = $admin_user;
+    $referencable_tests = array(
+      array(
+        'arguments' => array(
+          array(NULL, 'CONTAINS'),
+        ),
+        'result' => array(
+          'comment_node_article' => array(
+            $comments['published_published']->cid => $comment_labels['published_published'],
+            $comments['published_unpublished']->cid => $comment_labels['published_unpublished'],
+          ),
+        ),
+      ),
+    );
+    $this->assertReferencable($field, $referencable_tests, 'Comment handler (comment admin)');
+
+    // Test as a node and comment admin.
+    $admin_user = $this->drupalCreateUser(array('access content', 'access comments', 'administer comments', 'bypass node access'));
+    $GLOBALS['user'] = $admin_user;
+    $referencable_tests = array(
+      array(
+        'arguments' => array(
+          array(NULL, 'CONTAINS'),
+        ),
+        'result' => array(
+          '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
sites/all/modules/contrib/fields/entityreference/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));
+  }
+
+}

+ 136 - 0
sites/all/modules/contrib/fields/entityreference/views/entityreference.views.inc

@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * @file
+ * Views integration for Entity Reference.
+ */
+
+/**
+ * Implements hook_field_views_data().
+ */
+function entityreference_field_views_data($field) {
+  $data = field_views_field_default_views_data($field);
+  $entity_info = entity_get_info($field['settings']['target_type']);
+  foreach ($data as $table_name => $table_data) {
+    if (isset($entity_info['base table'])) {
+      $entity = $entity_info['label'];
+      if ($entity == t('Node')) {
+        $entity = t('Content');
+      }
+
+      $field_name = $field['field_name'] . '_target_id';
+      $parameters = array('@entity' => $entity, '!field_name' => $field['field_name']);
+      $data[$table_name][$field_name]['relationship'] = array(
+        'handler' => 'views_handler_relationship',
+        'base' => $entity_info['base table'],
+        'base field' => $entity_info['entity keys']['id'],
+        'label' => t('@entity entity referenced from !field_name', $parameters),
+        'group' => t('Entity Reference'),
+        'title' => t('Referenced Entity'),
+        'help' => t('A bridge to the @entity entity that is referenced via !field_name', $parameters),
+      );
+    }
+  }
+
+  // Invoke the behaviors to allow them to change the properties.
+  foreach (entityreference_get_behavior_handlers($field) as $handler) {
+    $handler->views_data_alter($data, $field);
+  }
+
+  return $data;
+}
+
+/**
+ * Options callback for Views handler views_handler_filter_in_operator.
+ */
+function entityreference_views_handler_options_list($field_name) {
+  $field = field_info_field($field_name);
+  return entityreference_options_list($field);
+}
+
+/**
+ * Implements hook_field_views_data_views_data_alter().
+ *
+ * Views integration to provide reverse relationships on entityreference fields.
+ */
+function entityreference_field_views_data_views_data_alter(&$data, $field) {
+  foreach ($field['bundles'] as $entity_type => $bundles) {
+    $target_entity_info = entity_get_info($field['settings']['target_type']);
+    if (isset($target_entity_info['base table'])) {
+      $entity_info = entity_get_info($entity_type);
+      $entity = $entity_info['label'];
+      if ($entity == t('Node')) {
+        $entity = t('Content');
+      }
+      $target_entity = $target_entity_info['label'];
+      if ($target_entity == t('Node')) {
+        $target_entity = t('Content');
+      }
+
+      $pseudo_field_name = 'reverse_' . $field['field_name'] . '_' . $entity_type;
+      $replacements = array('@entity' => $entity, '@target_entity' => $target_entity, '!field_name' => $field['field_name']);
+      $data[$target_entity_info['base table']][$pseudo_field_name]['relationship'] = array(
+        'handler' => 'views_handler_relationship_entity_reverse',
+        'field_name' => $field['field_name'],
+        'field table' => _field_sql_storage_tablename($field),
+        'field field' => $field['field_name'] . '_target_id',
+        'base' => $entity_info['base table'],
+        'base field' => $entity_info['entity keys']['id'],
+        'label' => t('@entity referencing @target_entity from !field_name', $replacements),
+        'group' => t('Entity Reference'),
+        'title' => t('Referencing entity'),
+        'help' => t('A bridge to the @entity entity that is referencing @target_entity via !field_name', $replacements),
+      );
+    }
+  }
+}
+
+/**
+ * Implements hook_views_plugins().
+ */
+function entityreference_views_plugins() {
+  $plugins = array(
+    'display' => array(
+      'entityreference' => array(
+        'title' => t('Entity Reference'),
+        'admin' => t('Entity Reference Source'),
+        'help' => 'Selects referenceable entities for an entity reference field',
+        'handler' => 'entityreference_plugin_display',
+        'uses hook menu' => FALSE,
+        'use ajax' => FALSE,
+        'use pager' => FALSE,
+        'accept attachments' => FALSE,
+        // Custom property, used with views_get_applicable_views() to retrieve
+        // all views with a 'Entity Reference' display.
+        'entityreference display' => TRUE,
+      ),
+    ),
+    'style' => array(
+      'entityreference_style' => array(
+        'title' => t('Entity Reference list'),
+        'help' => 'Returns results as a PHP array of labels and rendered rows.',
+        'handler' => 'entityreference_plugin_style',
+        'theme' => 'views_view_unformatted',
+        'uses row plugin' => TRUE,
+        'uses fields' => TRUE,
+        'uses options' => TRUE,
+        'type' => 'entityreference',
+        'even empty' => TRUE,
+      ),
+    ),
+    'row' => array(
+      'entityreference_fields' => array(
+        'title' => t('Inline fields'),
+        'help' => t('Displays the fields with an optional template.'),
+        'handler' => 'entityreference_plugin_row_fields',
+        'theme' => 'views_view_fields',
+        'theme path' => drupal_get_path('module', 'views') . '/theme',
+        'theme file' => 'theme.inc',
+        'uses fields' => TRUE,
+        'uses options' => TRUE,
+        'type' => 'entityreference',
+      ),
+    ),
+  );
+  return $plugins;
+}

+ 123 - 0
sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_display.inc

@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * @file
+ * Handler for entityreference_plugin_display.
+ */
+class entityreference_plugin_display extends views_plugin_display {
+
+  function option_definition() {
+    $options = parent::option_definition();
+
+    // Force the style plugin to 'entityreference_style' and the row plugin to
+    // 'fields'.
+    $options['style_plugin']['default'] = 'entityreference_style';
+    $options['defaults']['default']['style_plugin'] = FALSE;
+    $options['defaults']['default']['style_options'] = FALSE;
+    $options['row_plugin']['default'] = 'entityreference_fields';
+    $options['defaults']['default']['row_plugin'] = FALSE;
+    $options['defaults']['default']['row_options'] = FALSE;
+
+    // Set the display title to an empty string (not used in this display type).
+    $options['title']['default'] = '';
+    $options['defaults']['default']['title'] = FALSE;
+
+    return $options;
+  }
+
+  function get_style_type() {
+    return 'entityreference';
+  }
+
+  function execute() {
+    return $this->view->render($this->display->id);
+  }
+
+  function render() {
+    if (!empty($this->view->result) || !empty($this->view->style_plugin->definition['even empty'])) {
+      return $this->view->style_plugin->render($this->view->result);
+    }
+    return '';
+  }
+
+  function uses_exposed() {
+    return FALSE;
+  }
+
+  function query() {
+    $options = $this->get_option('entityreference_options');
+
+    // Play nice with Views UI 'preview' : if the view is not executed through
+    // EntityReference_SelectionHandler_Views::getReferencableEntities(),
+    // don't alter the query.
+    if (empty($options)) {
+      return;
+    }
+
+    // Make sure the id field is included in the results, and save its alias
+    // so that references_plugin_style can retrieve it.
+    $this->id_field_alias = $id_field = $this->view->query->add_field($this->view->base_table, $this->view->base_field);
+    if (strpos($id_field, '.') === FALSE) {
+      $id_field = $this->view->base_table . '.' . $this->id_field_alias;
+    }
+
+    // Restrict the autocomplete options based on what's been typed already.
+    if (isset($options['match'])) {
+      $style_options = $this->get_option('style_options');
+      $value = db_like($options['match']) . '%';
+      if ($options['match_operator'] != 'STARTS_WITH') {
+        $value = '%' . $value;
+      }
+
+      // Multiple search fields are OR'd together
+      $conditions = db_or();
+
+      // Build the condition using the selected search fields
+      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']];
+          }
+          // Add an OR condition for the field
+          $conditions->condition($field['table'] . '.' . $field['field'], $value, 'LIKE');
+        }
+      }
+
+      $this->view->query->add_where(NULL, $conditions);
+    }
+
+    // Add an IN condition for validation.
+    if (!empty($options['ids'])) {
+      $this->view->query->add_where(NULL, $id_field, $options['ids']);
+    }
+
+    $this->view->set_items_per_page($options['limit']);
+  }
+
+  /**
+   * Extend the default validation.
+   */
+  function validate() {
+    $errors = parent::validate();
+    // Verify that search fields are set up.
+    $style_options = $this->get_option('style_options');
+    if (!isset($style_options['search_fields'])) {
+      $errors[] = t('Display "@display" needs a selected search fields to work properly. See the settings for the Entity Reference list format.', array('@display' => $this->display->display_title));
+    }
+    else {
+      // Verify that the search fields used actually exist.
+      //$fields = array_keys($this->view->get_items('field'));
+      $fields = array_keys($this->handlers['field']);
+      foreach ($style_options['search_fields'] as $field_alias => $enabled) {
+        if ($enabled && !in_array($field_alias, $fields)) {
+          $errors[] = t('Display "@display" uses field %field as search field, but the field is no longer present. See the settings for the Entity Reference list format.', array('@display' => $this->display->display_title, '%field' => $field_alias));
+        }
+      }
+    }
+    return $errors;
+  }
+}

+ 36 - 0
sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_row_fields.inc

@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Handler for entityreference_plugin_row_fields.
+ */
+class entityreference_plugin_row_fields extends views_plugin_row_fields {
+
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['separator'] = array('default' => '-');
+
+    return $options;
+  }
+
+  /**
+   * Provide a form for setting options.
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+
+    // Expand the description of the 'Inline field' checkboxes.
+    $form['inline']['#description'] .= '<br />' . t("<strong>Note:</strong> In 'Entity Reference' displays, all fields will be displayed inline unless an explicit selection of inline fields is made here." );
+  }
+
+  function pre_render($row) {
+    // Force all fields to be inline by default.
+    if (empty($this->options['inline'])) {
+      $fields = $this->view->get_items('field', $this->display->id);
+      $this->options['inline'] = drupal_map_assoc(array_keys($fields));
+    }
+
+    return parent::pre_render($row);
+  }
+}

+ 66 - 0
sites/all/modules/contrib/fields/entityreference/views/entityreference_plugin_style.inc

@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * Handler for entityreference_plugin_style.
+ */
+class entityreference_plugin_style extends views_plugin_style {
+
+  function option_definition() {
+    $options = parent::option_definition();
+    $options['search_fields'] = array('default' => NULL);
+
+    return $options;
+  }
+
+  // Create the options form.
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $options = array();
+
+    if (isset($form['grouping'])) {
+      $options = $form['grouping'][0]['field']['#options'];
+      unset($options['']);
+      $form['search_fields'] = array(
+        '#type' => 'checkboxes',
+        '#title' => t('Search fields'),
+        '#options' => $options,
+        '#required' => TRUE,
+        '#default_value' => $this->options['search_fields'],
+        '#description' => t('Select the field(s) that will be searched when using the autocomplete widget.'),
+        '#weight' => -3,
+      );
+    }
+  }
+
+  function render() {
+    $options = $this->display->handler->get_option('entityreference_options');
+
+    // Play nice with Views UI 'preview' : if the view is not executed through
+    // EntityReference_SelectionHandler_Views::getReferencableEntities(), just
+    // display the HTML.
+    if (empty($options)) {
+      return parent::render();
+    }
+
+    // Group the rows according to the grouping field, if specified.
+    $sets = $this->render_grouping($this->view->result, $this->options['grouping']);
+
+    // Grab the alias of the 'id' field added by entityreference_plugin_display.
+    $id_field_alias = $this->display->handler->id_field_alias;
+
+    // @todo We don't display grouping info for now. Could be useful for select
+    // widget, though.
+    $results = array();
+    $this->view->row_index = 0;
+    foreach ($sets as $records) {
+      foreach ($records as $values) {
+        // Sanitize html, remove line breaks and extra whitespace.
+        $results[$values->{$id_field_alias}] = filter_xss_admin(preg_replace('/\s\s+/', ' ', str_replace("\n", '', $this->row_plugin->render($values))));
+        $this->view->row_index++;
+      }
+    }
+    unset($this->view->row_index);
+    return $results;
+  }
+}