From cddb76cfcccaaedcfe5a702e3a3d0889d4134ee9 Mon Sep 17 00:00:00 2001
From: Frederic G. MARAND <fgm@osinet.fr>
Date: Sat, 11 Aug 2012 15:32:02 +0200
Subject: [PATCH] Issue #1586428 by fgm: Integrate entyreference from nodes to terms with core taxonomy_index.

---
 entityreference.module                             |   47 ++++++-
 ...EntityReferenceBehavior_TaxonomyIndex.class.php |  169 ++++++++++++++++++++
 plugins/behavior/taxonomy-index.inc                |   13 ++
 3 files changed, 228 insertions(+), 1 deletions(-)
 create mode 100644 plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php
 create mode 100644 plugins/behavior/taxonomy-index.inc

diff --git a/entityreference.module b/entityreference.module
index 425c8aa..610c66b 100644
--- a/entityreference.module
+++ b/entityreference.module
@@ -46,7 +46,7 @@ function entityreference_behavior_plugin_process(&$plugin, $info) {
 }
 
 /**
- * Implementation of hook_field_info().
+ * Implements hook_field_info().
  */
 function entityreference_field_info() {
   $field_info['entityreference'] = array(
@@ -195,6 +195,51 @@ function entityreference_get_selection_handler($field, $instance = NULL, $entity
 }
 
 /**
+ * Implements hook_entity_delete().
+ */
+function entityreference_entity_delete($entity, $entity_type) {
+  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->entityDelete($entity_type, $entity, $field, $instance);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function entityreference_entity_insert($entity, $entity_type) {
+  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->entityInsert($entity_type, $entity, $field, $instance);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function entityreference_entity_update($entity, $entity_type) {
+  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->entityUpdate($entity_type, $entity, $field, $instance);
+      }
+    }
+  }
+}
+
+/**
  * Implements hook_field_load().
  */
 function entityreference_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) {
diff --git a/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php b/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php
new file mode 100644
index 0000000..23c5156
--- /dev/null
+++ b/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php
@@ -0,0 +1,169 @@
+<?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.
+ *
+ * TODO: hardcoded to SQL field storage. Remove that dependency.
+ *
+ * Note, unlike entityInsert/entityDelete, entityDelete() is not needed: cleanup
+ * is performed by taxonomy.module.
+ */
+class EntityReferenceBehavior_TaxonomyIndex extends EntityReference_BehaviorHandler_Abstract {
+
+  /**
+   * Validate use of the taxonomy_index behavior.
+   *
+   * Ensure that it is only enabled for ER instances on nodes targeting terms.
+   */
+  public function access($field, $instance) {
+    $ret = parent::access($field, $instance);
+    if ($ret) {
+      if ($instance['entity_type'] != 'node') {
+        drupal_set_message(t('Taxonomy index can only be applied when the entity type is "node".', 'warning'));
+        $ret = FALSE;
+      }
+      elseif ($field['settings']['target_type'] != 'taxonomy_term') {
+        drupal_set_message(t('Taxonomy index can only be applied when the target type is "taxonomy_term".'));
+        $ret = FALSE;
+      }
+    }
+    return $ret;
+  }
+
+  /**
+   * Builds and inserts taxonomy index entries for a given node.
+   *
+   * Taxonomy_index behavior : the index lists all terms that are related to a
+   * given node entity, and is therefore maintained at the entity level.
+   *
+   * Derived from taxonomy_build_node_index().
+   *
+   * @see taxonomy_build_node_index()
+   *
+   * @param $node
+   *   The node object.
+   */
+  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 ($field['module'] == 'entityreference' && $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'];
+              }
+            }
+          }
+        }
+      }
+      // 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();
+      }
+    }
+  }
+
+  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,
+          )),
+      );
+    }
+    return $form;
+  }
+
+  public function entityDelete($entity_type, $entity, $field, $instance) {
+    if ($entity_type != 'node') {
+      return;
+    }
+    // Remove taxonomy index entries for the node.
+    // Its has been validated as a node entity in self::access().
+    taxonomy_delete_node_index($entity);
+  }
+
+  /**
+   * Implements hook_entity_insert().
+   *
+   * Runs after hook_node_insert() used by taxonomy.module.
+   */
+  public function entityInsert($entity_type, $entity, $field, $instance) {
+    if ($entity_type != 'node') {
+      return;
+    }
+    // Add taxonomy index entries for the node.
+    $this->buildNodeIndex($entity);
+  }
+
+  /**
+   * Implements hook_entity_update().
+   *
+   * Runs after hook_node_update() used by taxonomy.module.
+   */
+  public function entityUpdate($entity_type, $entity, $field, $instance) {
+    if ($entity_type != 'node') {
+      return;
+    }
+    // Taxonomy.module always rebuilds the node's taxonomy index entries on node
+    // save, and is invoked before hook_entity_update(), so this must not call
+    // taxonomy_delete_node_index($node);
+    $this->buildNodeIndex($entity);
+  }
+}
diff --git a/plugins/behavior/taxonomy-index.inc b/plugins/behavior/taxonomy-index.inc
new file mode 100644
index 0000000..58b5ea7
--- /dev/null
+++ b/plugins/behavior/taxonomy-index.inc
@@ -0,0 +1,13 @@
+<?php
+
+/**
+ * @file
+ * CTools plugin declaration for taxonomy-index behavior.
+ */
+
+$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',
+);
-- 
1.7.4.1

