From 30b3fb5d9f061ebc245625fb24688b3ad3ecb592 Mon Sep 17 00:00:00 2001
From: aland <aland@198838.no-reply.drupal.org>
Date: Thu, 14 Jun 2012 00:46:33 +1000
Subject: [PATCH 1/3] =Issue 1365750 by Alan D., et al: Incomplete field handling and no entity support.

---
 diff.api.php          |  168 +++++++++++++++++++++++++++++++++++++++++
 diff.diff.inc         |  199 +++++++++++++++++++++++++++++++++++++++++++++++++
 diff.module           |   21 +++++
 diff.pages.inc        |   31 +--------
 includes/file.inc     |   38 +++++++++
 includes/image.inc    |   38 +++++++++
 includes/list.inc     |   25 ++++++
 includes/node.inc     |   94 +++++++-----------------
 includes/taxonomy.inc |   39 ++++++++++
 includes/text.inc     |   63 ++++++++++++++++
 readme.txt            |    3 +-
 11 files changed, 620 insertions(+), 99 deletions(-)
 create mode 100644 diff.api.php
 create mode 100644 diff.diff.inc
 create mode 100644 includes/file.inc
 create mode 100644 includes/image.inc
 create mode 100644 includes/list.inc
 create mode 100644 includes/taxonomy.inc
 create mode 100644 includes/text.inc

diff --git a/diff.api.php b/diff.api.php
new file mode 100644
index 0000000..3f423b0
--- /dev/null
+++ b/diff.api.php
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the diff module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Allow modules to provide comparitive data about nodes.
+ *
+ * @param object $old_node
+ *   The older node revision.
+ * @param object $new_node
+ *   The newer node revision.
+ *
+ * @return array
+ *   An associative array of values keyed by the node property.
+ *
+ * @deprecated
+ *    hook_entity_diff() should be used instead.
+ *
+ * @see hook_entity_diff()
+ */
+function hook_diff($old_node, $new_node) {
+}
+
+/**
+ * Allow modules to provide a comparison about entities.
+ *
+ * @param object $old_entity
+ *   The older entity revision.
+ * @param object $new_entity
+ *   The newer entity revision.
+ * @param string $entity_type
+ *   The entity type of the entities being compared.
+ *
+ * @return array
+ *   An associative array of values keyed by the entity property.
+ *
+ * @see hook_diff()
+ */
+function hook_entity_diff($old_entity, $new_entity, $entity_type) {
+}
+
+/**
+ * Callback to the module that defined the field to prepare items comparison.
+ *
+ * This allows the module to alter all items prior to rendering the comparitive
+ * values. It is mainly used to bulk load entities to reduce overheads
+ * associated with loading entities individually.
+ *
+ * @param array $old_items
+ *   An array of field items from the older revision.
+ * @param array $new_items
+ *   An array of field items from the newer revision.
+ * @param $context
+ *   An associative array containing:
+ *   - entity_type: The entity type; e.g., 'node' or 'user'.
+ *   - bundle: The bundle name.
+ *   - field: The field that the items belong to.
+ *   - instance: The instance that the items belong to.
+ *   - language: The language associated with $items.
+ *   - old_entity: The older entity.
+ *   - new_entity: The newer entity.
+ *
+ * @see MODULE_field_diff_view()
+ */
+function MODULE_field_diff_view_prepare(&$old_items, &$new_items, $context) {
+  $fids = array();
+  foreach (array_merge_recursive($old_items, $new_items) as $info) {
+    $fids[$info['fid']] = $info['fid'];
+  }
+  // A single load is much faster than individual loads.
+  $files = file_load_multiple($fids);
+
+  // For ease of processing, store a reference of the entity on the item array.
+  foreach ($old_items as $delta => $info) {
+    $old_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
+  }
+  foreach ($new_items as $delta => $info) {
+    $new_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
+  }
+}
+
+/**
+ * Callback to the module that defined the field to generate items comparisons.
+ *
+ * @param array $items
+ *   An array of field items from the entity.
+ * @param $context
+ *   An associative array containing:
+ *   - entity: The entity being compared.
+ *   - entity_type: The entity type; e.g., 'node' or 'user'.
+ *   - bundle: The bundle name.
+ *   - field: The field that the items belong to.
+ *   - instance: The instance that the items belong to.
+ *   - language: The language associated with $items.
+ *   - old_entity: The older entity.
+ *   - new_entity: The newer entity.
+ *
+ * @see MODULE_field_diff_view_prepare()
+ */
+function MODULE_field_diff_view($items, $context) {
+  $diff_items = array();
+  foreach ($items as $delta => $item) {
+    if (isset($item['file'])) {
+      $diff_items[$delta] = $item['file']->filename . ' [fid: ' . $item['fid'] . ']';
+    }
+  }
+
+  return $diff_items;
+}
+
+/**
+ * Allow other modules to interact with MODULE_field_diff_view_prepare().
+ *
+ * @param array $old_items
+ *   An array of field items from the older revision.
+ * @param array $new_items
+ *   An array of field items from the newer revision.
+ * @param $context
+ *   An associative array containing:
+ *   - entity_type: The entity type; e.g., 'node' or 'user'.
+ *   - bundle: The bundle name.
+ *   - field: The field that the items belong to.
+ *   - instance: The instance that the items belong to.
+ *   - language: The language associated with $items.
+ *   - old_entity: The older entity.
+ *   - new_entity: The newer entity.
+ *
+ * @see MODULE_field_diff_view_prepare()
+ */
+function hook_field_diff_view_prepare_alter($old_items, $new_items, $context) {
+
+}
+
+/**
+ * Allow other modules to interact with MODULE_field_diff_view().
+ *
+ * @param array $values
+ *   An array of field items from the entity ready for comparison.
+ * @param array $items
+ *   An array of field items from the entity.
+ * @param $context
+ *   An associative array containing:
+ *   - entity: The entity being compared.
+ *   - entity_type: The entity type; e.g., 'node' or 'user'.
+ *   - bundle: The bundle name.
+ *   - field: The field that the items belong to.
+ *   - instance: The instance that the items belong to.
+ *   - language: The language associated with $items.
+ *   - old_entity: The older entity.
+ *   - new_entity: The newer entity.
+ *
+ * @see MODULE_field_diff_view()
+ */
+function hook_field_diff_view_alter($values, $items, $context) {
+
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/diff.diff.inc b/diff.diff.inc
new file mode 100644
index 0000000..0eae31c
--- /dev/null
+++ b/diff.diff.inc
@@ -0,0 +1,199 @@
+<?php
+
+/**
+ * @file
+ * Includes the hooks defined by diff_hook_info().
+ */
+
+/**
+ * Implements hook_entity_diff().
+ *
+ * Helper function to invoke the depricated hook_diff() for node entities.
+ *
+ * This manually invokes hook_diff() to avoid a function name clash with the
+ * PHP 5 (>= 5.3.0) date_diff() function or the Dates modules implementation.
+ */
+function diff_entity_diff($old_node, $new_node, $entity_type) {
+  $return = array();
+
+  $info = entity_get_info($entity_type);
+  if (empty($info['fieldable'])) {
+    $return = diff_entity_fields_diff($old_entity, $new_entity, $entity_type);
+  }
+
+  if ($entity_type == 'node') {
+    foreach (module_implements('diff') as $module) {
+      if ($module == 'date') {
+        // Avoid function name collision with date_diff().
+        continue;
+      }
+      $function = "{$module}_diff";
+      $result = $function($old_node, $new_node);
+      if (isset($result) && is_array($result)) {
+        $return = array_merge_recursive($return, $result);
+      }
+      elseif (isset($result)) {
+        $return[] = $result;
+      }
+    }
+  }
+  return $return;
+}
+
+/**
+ * @file
+ * Provide diff functions for fieldable entities.
+ */
+
+/**
+ * Internal callback to handle fieldable entities.
+ *
+ * Field comparison is handled for core modules, but is expandable to any other
+ * fields if the module defines MODULE_field_diff_view().
+ *
+ * @param object $old_entity
+ *   The older entity entity revision.
+ * @param object $new_entity
+ *   The newer entity entity revision.
+ *
+ * @return array
+ *   An associative array of values keyed by the field name and delta value.
+ */
+function diff_entity_fields_diff($old_entity, $new_entity, $entity_type) {
+  $result = array();
+
+  $includes = array(
+    'list' => module_exists('list'),
+    'text' => module_exists('text'),
+    'file' => module_exists('file'),
+    'image' => module_exists('image'),
+    'taxonomy' => module_exists('taxonomy'),
+  );
+
+  list(,, $bundle_name) = entity_extract_ids($entity_type, $new_entity);
+  $instances = field_info_instances($entity_type, $bundle_name);
+  foreach ($instances as $instance) {
+    $field_name = $instance['field_name'];
+    $field = field_info_field($field_name);
+
+    // We provide a loose check on the field access.
+    if (field_access('view', $field, $entity_type) || field_access('edit', $field, $entity_type)) {
+      $langcode = field_language($entity_type, $new_entity, $field_name);
+      $context = array(
+        'old_entity' => $old_entity,
+        'new_entity' => $new_entity,
+        'entity_type' => 'node',
+        'bundle' => $bundle_name,
+        'language' => $langcode,
+        'field' => $field,
+        'instance' => $instance,
+      );
+
+      $old_items = array();
+      if (!empty($old_entity->{$field_name}[$langcode])) {
+        $old_items = $old_entity->{$field_name}[$langcode];
+      }
+
+      $new_items = array();
+      if (!empty($new_entity->{$field_name}[$langcode])) {
+        $new_items = $new_entity->{$field_name}[$langcode];
+      }
+
+      // Load file, image and taxonomy field callbacks.
+      if (!empty($includes[$field['module']])) {
+        module_load_include('inc', 'diff', 'includes/' . $field['module']);
+        $includes[$field['module']] = 0;
+      }
+
+      // Reference fields can optionally prepare objects in bulk to reduce
+      // overheads related to multiple database calls. If a field considers
+      // that the delta values is meaningless, they can order and rearrange
+      // to provide cleaner results.
+      $func = $field['module'] . '_field_diff_view_prepare';
+      if (function_exists($func)) {
+        $func = $func($old_items, $new_items, $context);
+      }
+      // Allow other modules to ack safely on behalf of the core field module.
+      drupal_alter('field_diff_view_prepare', $old_items, $new_items, $context);
+
+      // These functions compiles the items into comparable arrays of strings.
+      $func = $field['module'] . '_field_diff_view';
+      if (!function_exists($func)) {
+        $func = 'diff_field_diff_view';
+      }
+
+      // These callbacks should be independant of revision.
+      $old_context = $context;
+      $old_context['entity'] = $old_entity;
+      $old_values = $func($old_items, $old_context);
+      $new_context = $context;
+      $new_context['entity'] = $new_entity;
+      $new_values = $func($new_items, $new_context);
+
+      // Allow other modules to act safely on behalf of the core field module.
+      drupal_alter('field_diff_view', $old_values, $old_items, $old_context);
+      drupal_alter('field_diff_view', $new_values, $new_items, $new_context);
+
+      $max = max(array(count($old_values), count($new_values)));
+      if ($max) {
+        $result[$field_name] = array(
+          '#name' => $instance['label'],
+          '#old' => array(),
+          '#new' => array(),
+        );
+        for ($delta = 0; $delta < $max; $delta++) {
+          $result[$field_name]['#old'][] = isset($old_values[$delta]) ? $old_values[$delta] : '';
+          $result[$field_name]['#new'][] = isset($new_values[$delta]) ? $new_values[$delta] : '';
+        }
+        $result[$field_name]['#old'] = implode("\n", $result[$field_name]['#old']);
+        $result[$field_name]['#new'] = implode("\n", $result[$field_name]['#new']);
+      }
+    }
+  }
+  return $result;
+}
+
+/**
+ * A generic handler for parsing field values.
+ *
+ * This callback can only handle the most basic of fields that populates the
+ * safe_value during field load or use the value column for data storage.
+ *
+ * @param array $items
+ *   An array of field items.
+ * @param $context
+ *   An associative array containing:
+ *   - entity: The entity that the items belong to.
+ *   - entity_type: The entity type; e.g., 'node' or 'user'.
+ *   - bundle: The bundle name.
+ *   - field: The field that the items belong to.
+ *   - instance: The instance that the items belong to.
+ *   - language: The language associated with $items.
+ *   - old_entity: The older entity.
+ *   - new_entity: The newer entity.
+ *
+ * @return array
+ *   An array of strings representing the value, keyed by delta index.
+ */
+function diff_field_diff_view($items, $context) {
+  $diff_items = array();
+  foreach ($items as $delta => $item) {
+    if (isset($item['safe_value'])) {
+      if (is_scalar($item['safe_value'])) {
+        $diff_items[$delta] = (string) $item['safe_value'];
+      }
+      else {
+        $diff_items[$delta] = implode("\n", $item['safe_value']);
+      }
+    }
+    elseif (isset($item['value'])) {
+      if (is_scalar($item['value'])) {
+        $diff_items[$delta] = (string) $item['value'];
+      }
+      else {
+        $diff_items[$delta] = implode("\n", $item['value']);
+      }
+    }
+  }
+  return $diff_items;
+}
diff --git a/diff.module b/diff.module
index 61a4147..452efc5 100644
--- a/diff.module
+++ b/diff.module
@@ -112,6 +112,27 @@ function diff_node_revision_access($node, $op = 'view') {
 }
 
 /**
+ * Implements hook_hook_info().
+ */
+function diff_hook_info() {
+  $hooks['entity_diff'] = array(
+    'group' => 'diff',
+  );
+  $hooks['diff'] = array(
+    'group' => 'diff',
+  );
+  $hooks['field_diff_view_prepare_alter'] = array(
+    'group' => 'diff',
+  );
+  $hooks['field_diff_view_alter'] = array(
+    'group' => 'diff',
+  );
+
+  return $hooks;
+}
+
+
+/**
  * Implements hook_block_info().
  */
 function diff_block_info() {
diff --git a/diff.pages.inc b/diff.pages.inc
index aee9b1b..04ca0fd 100644
--- a/diff.pages.inc
+++ b/diff.pages.inc
@@ -251,8 +251,7 @@ function _diff_body_rows($old_node, $new_node) {
 
   $rows = array();
   $any_visible_change = FALSE;
-  // @todo quick workaround for PHP >= 5.3.0 date_diff() conflict.
-  $node_diffs = _diff_module_invoke_all($old_node, $new_node);
+  $node_diffs = module_invoke_all('entity_diff', $old_node, $new_node, 'node');
 
   // We start off assuming all form elements are in the correct order.
   $node_diffs['#sorted'] = TRUE;
@@ -309,34 +308,6 @@ function _diff_body_rows($old_node, $new_node) {
 }
 
 /**
- * Helper function to invoke hook_diff in all enabled modules that implement it.
- *
- * Don't use module_invoke_all() since if date.module is enabled will clash with
- * PHP 5.3's date_diff() function.
- *
- * @todo figure out any else possible solution but not workaround.
- * @link http://drupal.org/node/639320
- * @see module_invoke_all()
- */
-function _diff_module_invoke_all($old_node, $new_node) {
-  $return = array();
-  foreach (module_implements('diff') as $module) {
-    if ($module == 'date') {
-      continue; // Avoid function name collision with date_diff().
-    }
-    $function = "{$module}_diff";
-    $result = $function($old_node, $new_node);
-    if (isset($result) && is_array($result)) {
-      $return = array_merge_recursive($return, $result);
-    }
-    elseif (isset($result)) {
-      $return[] = $result;
-    }
-  }
-  return $return;
-}
-
-/**
  * Get the entry in the revisions list after $vid.
  * Returns FALSE if $vid is the last entry.
  *
diff --git a/includes/file.inc b/includes/file.inc
new file mode 100644
index 0000000..a8c3170
--- /dev/null
+++ b/includes/file.inc
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Provide diff field functions for the file module.
+ */
+
+/**
+ * Diff field callback for preloading file entities.
+ */
+function file_field_diff_view_prepare(&$old_items, &$new_items, $context) {
+  $fids = array();
+  foreach (array_merge_recursive($old_items, $new_items) as $info) {
+    $fids[$info['fid']] = $info['fid'];
+  }
+  $files = file_load_multiple($fids);
+
+  foreach ($old_items as $delta => $info) {
+    $old_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
+  }
+  foreach ($new_items as $delta => $info) {
+    $new_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
+  }
+}
+
+/**
+ * Diff field callback for parsing file field comparitive values.
+ */
+function file_field_diff_view($items, $context) {
+  $diff_items = array();
+  foreach ($items as $delta => $item) {
+    if (isset($item['file'])) {
+      $diff_items[$delta] = $item['file']->filename . ' [fid: ' . $item['fid'] . ']';
+    }
+  }
+
+  return $diff_items;
+}
diff --git a/includes/image.inc b/includes/image.inc
new file mode 100644
index 0000000..1087010
--- /dev/null
+++ b/includes/image.inc
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Provide diff field functions for the image module.
+ */
+
+/**
+ * Diff field callback for preloading the image file entities.
+ */
+function image_field_diff_view_prepare(&$old_items, &$new_items, $context) {
+  $fids = array();
+  foreach (array_merge_recursive($old_items, $new_items) as $info) {
+    $fids[$info['fid']] = $info['fid'];
+  }
+  $files = file_load_multiple($fids);
+
+  foreach ($old_items as $delta => $info) {
+    $old_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
+  }
+  foreach ($new_items as $delta => $info) {
+    $new_items[$delta]['file'] = isset($files[$info['fid']]) ? $files[$info['fid']] : NULL;
+  }
+}
+
+/**
+ * Diff field callback for parsing image field comparitive values.
+ */
+function image_field_diff_view($items, $context) {
+  $diff_items = array();
+  foreach ($items as $delta => $item) {
+    if (isset($item['file'])) {
+      $diff_items[$delta] = $item['file']->filename . ' [fid: ' . $item['fid'] . ']';
+    }
+  }
+
+  return $diff_items;
+}
diff --git a/includes/list.inc b/includes/list.inc
new file mode 100644
index 0000000..1af0787
--- /dev/null
+++ b/includes/list.inc
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Provide diff field functions for the List module.
+ */
+
+/**
+ * Diff field callback for parsing list field comparitive values.
+ */
+function list_field_diff_view($items, $context) {
+  $diff_items = array();
+  $allowed_values = list_allowed_values($context['field'], $context['instance'], $context['entity_type'], $context['entity']);
+  foreach ($items as $delta => $item) {
+    if (isset($allowed_values[$item['value']])) {
+      $diff_items[$delta] = $allowed_values[$item['value']];
+    }
+    else {
+      // If no match was found in allowed values, fall back to the key.
+      $diff_items[$delta] = $item['value'];
+    }
+  }
+
+  return $diff_items;
+}
diff --git a/includes/node.inc b/includes/node.inc
index 5b1d74a..7a011fa 100644
--- a/includes/node.inc
+++ b/includes/node.inc
@@ -1,79 +1,39 @@
 <?php
-// $Id$
 
 /**
  * @file
- * Implements hook_diff() for node.module (body and title).
+ * Provide diff functions for the node module.
  */
 
 /**
- * Implements hook_diff() for node.module (body and title).
+ * Implements hook_entity_diff().
+ *
+ * This function compares core node properties. This is currently limited to:
+ *   - title: The title of the node.
+ *
+ * @param object $old_node
+ *   The older node revision.
+ * @param object $new_node
+ *   The newer node revision.
+ * @param string $entity_type
+ *   The entity type of the entities being compared.
+ *
+ * @return array
+ *   An associative array of values keyed by the node property.
  */
-function node_diff($old_node, $new_node) {
-
+function node_entity_diff($old_node, $new_node, $entity_type) {
   $result = array();
-  $type = node_type_get_type($new_node);
-  $result['title'] = array(
-    '#name' => $type->title_label,
-    '#old' => array($old_node->title),
-    '#new' => array($new_node->title),
-    '#weight' => -5,
-    '#format' => array(
-      'show_header' => FALSE,
-    )
-  );
-
-  // @TODO: abstract this to work with all field types and/or split this
-  // integration out to be more generic.
-  $instances = field_info_instances('node', field_extract_bundle('node', $type));
-  foreach ($instances as $instance) {
-    $field_name = $instance['field_name'];
-    $langcode = field_language('node', $new_node, $field_name);
-    if (isset($new_node->{$field_name}[$langcode]) && !empty($new_node->{$field_name}[$langcode])) {
-      foreach (array_keys($new_node->{$field_name}[$langcode]) as $delta) {
-        if (isset($new_node->{$field_name}[$langcode][$delta]['value']) && isset($old_node->{$field_name}[$langcode][$delta]['value'])) {
-          $view_old = $old_node->{$field_name}[$langcode][$delta]['value'];
-          $view_new = $new_node->{$field_name}[$langcode][$delta]['value'];
-          $result["{$field_name}_{$delta}"] = array(
-            '#name' => $instance['label'],
-            '#old' => explode("\n", $view_old),
-            '#new' => explode("\n", $view_new),
-          );
-        }
-        elseif (isset($new_node->{$field_name}[$langcode][$delta]['value'])) {
-          // We have a newly input value where there was none in the previous
-          // version of the node.
-          $view_new = $new_node->{$field_name}[$langcode][$delta]['value'];
-          $result["{$field_name}_{$delta}"] = array(
-            '#name' => $instance['label'],
-            '#old' => '',
-            '#new' => explode("\n", $view_new),
-          );
-        }
-        elseif (isset($old_node->{$field_name}[$langcode][$delta]['value']) && !empty($old_node->{$field_name}[$langcode][$delta]['value'])) {
-          // We have a value that has been removed from the field.
-          $view_old = $old_node->{$field_name}[$langcode][$delta]['value'];
-          $result["{$field_name}_{$delta}"] = array(
-            '#name' => $instance['label'],
-            '#old' => explode("\n", $view_old),
-            '#new' => '',
-          );
-        }
-      }
-    }
-    elseif (isset($old_node->{$field_name}[$langcode])) {
-      // We have a value that has been removed from the field.
-      foreach (array_keys($old_node->{$field_name}[$langcode]) as $delta) {
-        if (isset($old_node->{$field_name}[$langcode][$delta]['value'])) {
-          $view_old = $old_node->{$field_name}[$langcode][$delta]['value'];
-          $result["{$field_name}_{$delta}"] = array(
-            '#name' => $instance['label'],
-            '#old' => explode("\n", $view_old),
-            '#new' => '',
-          );
-        }
-      }
-    }
+  if ($entity_type == 'node') {
+    $type = node_type_get_type($new_node);
+    $result['title'] = array(
+      '#name' => $type->title_label,
+      '#old' => array($old_node->title),
+      '#new' => array($new_node->title),
+      '#weight' => -5,
+      '#format' => array(
+        'show_header' => FALSE,
+      ),
+    );
   }
   return $result;
 }
diff --git a/includes/taxonomy.inc b/includes/taxonomy.inc
new file mode 100644
index 0000000..0a3c9a4
--- /dev/null
+++ b/includes/taxonomy.inc
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Implements pusdeo-hook hook_field_diff_view() for the Taxonomy module.
+ */
+
+/**
+ * Diff field callback for preloading term entities.
+ */
+function taxonomy_field_diff_view_prepare(&$old_items, &$new_items, $context) {
+  $tids = array();
+  foreach (array_merge_recursive($old_items, $new_items) as $info) {
+    $tids[$info['tid']] = $info['tid'];
+  }
+  $terms = taxonomy_term_load_multiple($tids);
+  foreach ($old_items as $delta => $info) {
+    $old_items[$delta]['term'] = isset($terms[$info['tid']]) ? $terms[$info['tid']] : NULL;
+  }
+  foreach ($new_items as $delta => $info) {
+    $new_items[$delta]['term'] = isset($terms[$info['tid']]) ? $terms[$info['tid']] : NULL;
+  }
+}
+
+/**
+ * Diff field callback for parsing term field comparitive values.
+ */
+function taxonomy_field_diff_view($items, $context) {
+  $diff_items = array();
+  foreach ($items as $delta => $item) {
+    if (empty($item['term'])) {
+      $diff_items[$delta] = t('Invalid term reference');
+    }
+    else {
+      $diff_items[$delta] = $item['term']->name . ' [tid:' . $item['term']->tid . ']';
+    }
+  }
+  return $diff_items;
+}
diff --git a/includes/text.inc b/includes/text.inc
new file mode 100644
index 0000000..cc9fe1c
--- /dev/null
+++ b/includes/text.inc
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Provide diff field functions for the Text module.
+ */
+
+/**
+ * Diff field callback for parsing text field comparitive values.
+ */
+function text_field_diff_view($items, $context) {
+  $diff_items = array();
+
+  $field = $context['field'];
+  $instance = $context['instance'];
+
+  foreach ($items as $delta => $item) {
+    $diff_items[$delta] = array();
+
+    // Compare the summary fields.
+    if ($field['type'] == 'text_with_summary') {
+      $diff_items[$delta][] = t('Summary:');
+      if ($instance['settings']['text_processing']) {
+        $diff_items[$delta][] = check_markup($item['summary'], $item['format'], $context['language']);
+      }
+      else {
+        $diff_items[$delta][] = $item['summary'];
+      }
+    }
+
+    // Only show label if field has multiple components.
+    if ($field['type'] == 'text_with_summary' || $instance['settings']['text_processing']) {
+      $diff_items[$delta][] = t('Content:');
+    }
+
+    // Compare the main content value.
+    if ($instance['settings']['text_processing']) {
+      $diff_items[$delta][] = check_markup($item['value'], $item['format'], $context['language']);
+    }
+    else {
+      $diff_items[$delta][] = $item['value'];
+    }
+
+    // Compare the format selection.
+    if ($instance['settings']['text_processing']) {
+      $diff_items[$delta][] = t('Text format:');
+      if (!isset($format_id)) {
+        $format_id = filter_fallback_format();
+      }
+      // If the requested text format does not exist, the text cannot be filtered.
+      if ($format = filter_format_load($format_id)) {
+        $diff_items[$delta][] = "{$format->name} ($format_id)";
+      }
+      else {
+        $diff_items[$delta][] = t('Format: Missing (!format)', array('!format' => $format_id));
+      }
+    }
+
+    $diff_items[$delta] = implode("\n", $diff_items[$delta]);
+  }
+
+  return $diff_items;
+}
diff --git a/readme.txt b/readme.txt
index 5294d4b..f4a20dc 100644
--- a/readme.txt
+++ b/readme.txt
@@ -34,8 +34,7 @@ changes to HTML entities, etc.
 
 API
 ---
-This module offers `hook_diff()` which modules may use to inject their changes
-into the presentation of the diff. For example, this is used by `node.inc`.
+See diff.api.php
 
 Maintainers
 -----------
-- 
1.7.3.1.msysgit.0


From f5aea575a8c2406e27671151c0277c04f4f3a2c3 Mon Sep 17 00:00:00 2001
From: aland <aland@198838.no-reply.drupal.org>
Date: Thu, 14 Jun 2012 01:14:14 +1000
Subject: [PATCH 2/3] =Issue 1365750 by Alan D., et al: Minor fixes.

---
 diff.diff.inc |   10 +++++-----
 1 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/diff.diff.inc b/diff.diff.inc
index 0eae31c..5c5c66c 100644
--- a/diff.diff.inc
+++ b/diff.diff.inc
@@ -13,11 +13,11 @@
  * This manually invokes hook_diff() to avoid a function name clash with the
  * PHP 5 (>= 5.3.0) date_diff() function or the Dates modules implementation.
  */
-function diff_entity_diff($old_node, $new_node, $entity_type) {
+function diff_entity_diff($old_entity, $new_entity, $entity_type) {
   $return = array();
 
   $info = entity_get_info($entity_type);
-  if (empty($info['fieldable'])) {
+  if (!empty($info['fieldable'])) {
     $return = diff_entity_fields_diff($old_entity, $new_entity, $entity_type);
   }
 
@@ -28,7 +28,7 @@ function diff_entity_diff($old_node, $new_node, $entity_type) {
         continue;
       }
       $function = "{$module}_diff";
-      $result = $function($old_node, $new_node);
+      $result = $function($old_entity, $new_entity);
       if (isset($result) && is_array($result)) {
         $return = array_merge_recursive($return, $result);
       }
@@ -63,11 +63,11 @@ function diff_entity_fields_diff($old_entity, $new_entity, $entity_type) {
   $result = array();
 
   $includes = array(
-    'list' => module_exists('list'),
-    'text' => module_exists('text'),
     'file' => module_exists('file'),
     'image' => module_exists('image'),
+    'list' => module_exists('list'),
     'taxonomy' => module_exists('taxonomy'),
+    'text' => module_exists('text'),
   );
 
   list(,, $bundle_name) = entity_extract_ids($entity_type, $new_entity);
-- 
1.7.3.1.msysgit.0


From 0f289ae141917e030ebdf15652e020dcfb09f57e Mon Sep 17 00:00:00 2001
From: aland <aland@198838.no-reply.drupal.org>
Date: Thu, 14 Jun 2012 01:23:06 +1000
Subject: [PATCH 3/3] =Issue 1365750 by Alan D., et al: Now loads field module MODULE.diff.inc for field callbacks.

---
 diff.diff.inc |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/diff.diff.inc b/diff.diff.inc
index 5c5c66c..fdbeac0 100644
--- a/diff.diff.inc
+++ b/diff.diff.inc
@@ -99,8 +99,14 @@ function diff_entity_fields_diff($old_entity, $new_entity, $entity_type) {
         $new_items = $new_entity->{$field_name}[$langcode];
       }
 
-      // Load file, image and taxonomy field callbacks.
-      if (!empty($includes[$field['module']])) {
+      // Load files containing the field callbacks.
+      // Since this is not a real hook, we manually load the field modules
+      // versions of the files.
+      if (!isset($includes[$field['module']])) {
+        module_load_include('diff.inc', $field['module']);
+        $includes[$field['module']] = 0;
+      }
+      elseif (!empty($includes[$field['module']])) {
         module_load_include('inc', 'diff', 'includes/' . $field['module']);
         $includes[$field['module']] = 0;
       }
-- 
1.7.3.1.msysgit.0

