Index: modules/nodereference/nodereference.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/nodereference/Attic/nodereference.module,v
retrieving revision 1.138.2.59
diff -u -p -r1.138.2.59 nodereference.module
--- modules/nodereference/nodereference.module	19 Jul 2009 13:03:56 -0000	1.138.2.59
+++ modules/nodereference/nodereference.module	17 Aug 2009 02:53:56 -0000
@@ -52,6 +52,16 @@ function nodereference_theme() {
 }
 
 /**
+ * Implementation of hook_views_api().
+ */
+function nodereference_views_api() {
+  return array(
+    'api' => 2,
+    'path' => drupal_get_path('module', 'nodereference') . '/views',
+  );
+}
+
+/**
  * Implementaion of hook_ctools_plugin_directory().
  */
 function nodereference_ctools_plugin_directory($module, $plugin) {
@@ -173,6 +183,30 @@ function nodereference_field_settings($o
         'label' => t($field['widget']['label']),
         'content_field_name' => $field['field_name'],
       );
+
+      // Add a reverse relationship to get information about referring items.
+      $field_types = _content_field_types();
+      $types = array();
+      foreach (content_types() as $type) {
+        if (isset($type['fields'][$field['field_name']])) {
+          $types[] = $type['name'];
+        }
+      }
+      $label_truncated = truncate_utf8(t($field['widget']['label']), 10, TRUE);
+      $data[$table_alias]['vid'] = array(
+        'group' => t('Content'),
+        'title' => t('@label (!name) - reverse reference', array('@label' => t($field['widget']['label']), '!name' => $field['field_name'])),
+        'title short' => t('@label-truncated reverse reference', array('@label-truncated' => $label_truncated)),
+        'help' => t($field_types[$field['type']]['label']) .' ('. t('reverse reference') .') - '. t('Appears in: @types', array('@types' => implode(', ', $types))),
+        'relationship' => array(
+          'base' => 'node',
+          'base field' => 'vid',
+          'left_field' => 'nid',
+          'field' => $db_info['columns']['nid']['column'],
+          'handler' => 'nodereference_handler_relationship_reverse',
+          'label' => t('@label reverse reference', array('@label' => t($field['widget']['label']))),
+        ),
+      );
       return $data;
   }
 }
Index: modules/nodereference/views/nodereference.views.inc
===================================================================
RCS file: modules/nodereference/views/nodereference.views.inc
diff -N modules/nodereference/views/nodereference.views.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/nodereference/views/nodereference.views.inc	17 Aug 2009 02:10:01 -0000
@@ -0,0 +1,23 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Views integration interface for nodereference module.
+ */
+
+/**
+ * Implementation of hook_views_handlers().
+ */
+function nodereference_views_handlers() {
+  return array(
+    'info' => array(
+      'path' => drupal_get_path('module', 'nodereference') . '/views/handlers',
+    ),
+    'handlers' => array(
+      'nodereference_handler_relationship_reverse' => array(
+        'parent' => 'views_handler_relationship',
+      ),
+    ),
+  );
+}
Index: modules/nodereference/views/handlers/nodereference_handler_relationship_reverse.inc
===================================================================
RCS file: modules/nodereference/views/handlers/nodereference_handler_relationship_reverse.inc
diff -N modules/nodereference/views/handlers/nodereference_handler_relationship_reverse.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/nodereference/views/handlers/nodereference_handler_relationship_reverse.inc	18 Aug 2009 20:04:12 -0000
@@ -0,0 +1,128 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Handles reverse references using a nested join.
+ *
+ * Tipical relationships for node reference fields generate two joins that
+ * look like this:
+ *
+ * @code
+ *   FROM {node} node
+ *     LEFT JOIN {content_field_table} node_data_field_table ON node.vid = node_data_field_table.vid
+ *     {join-type} JOIN {node} node_node_data_field_table ON node_data_field_table.field_nid = node_node_data_field_table.nid
+ * @code
+ *
+ * However, this kind of joins handle relationships between referrer nodes
+ * and their parents. A referrer node can display information about the
+ * referred node, but not the other way around. We cannot display fields
+ * of the referring node from a parent node because the link goes from child
+ * to parent.
+ * To provide a relationships from parent to child, we need to generate a join
+ * from the nid of the parent node to the reference field on the referrer node,
+ * and because this is using the nid, we should do it in a way that does not
+ * generate duplicate records when the referrer nodes have support for node
+ * revisions enabled.
+ *
+ * This can be done using a nested join like the following:
+ *
+ * @code
+ *   FROM {node} node
+ *     {join-type} JOIN (
+ *       {content_field_table} node_data_field_table
+ *       INNER JOIN {node} node_node_data_field_table ON node_data_field_table.vid = node_node_data_field_table.vid
+ *     ) ON node.nid = node_data_field_table.field_nid
+ * @code
+ *
+ * Note that {join-type} depends on whether the relationship is
+ * required (INNER) or not (LEFT).
+ */
+class nodereference_handler_relationship_reverse extends views_handler_relationship {
+  function ensure_my_table() {
+    if (!isset($this->table_alias)) {
+      if (!method_exists($this->query, 'ensure_table')) { vpr_trace(); exit; }
+
+      // Since nested joins are not directly supported by Views, we are going
+      // to do here part of the job performed by views_query::ensure_table().
+
+      if (empty($this->relationship)) {
+        $this->relationship = $this->query->base_table;
+      }
+
+      if ($this->relationship == $this->query->base_table && !empty($this->query->tables[$this->relationship][$this->table])) {
+        $this->table_alias = $this->query->tables[$relationship][$this->table]['alias'];
+      }
+      elseif (!array_key_exists($this->relationship, $this->query->relationships)) {
+        $this->table_alias = FALSE;
+      }
+      elseif ($this->table == $this->query->relationships[$this->relationship]['base']) {
+        $this->table_alias = $this->relationship;
+      }
+      else {
+        // Build the main join based on the views data cache.
+        $main_join = $this->query->get_join_data($this->table, $this->query->relationships[$this->relationship]['base']);
+
+        // We want to alter the join so that it uses the field value instead of
+        // the vid.
+        $main_join_definition = $main_join->definition;
+        $main_join_definition['left_field'] = $this->definition['left_field'];
+        $main_join_definition['field'] = $this->definition['field'];
+        if (!empty($this->options['required'])) {
+          $main_join_definition['type'] = 'INNER';
+        }
+        // Build a new join object with our new definition.
+        $main_join = new nodereference_nested_join();
+        $main_join->definition = $main_join_definition;
+        $main_join->construct();
+        $main_join->adjusted = TRUE;
+        $this->table_alias = $this->query->ensure_table($this->table, $this->relationship, $main_join);
+
+        // Let's build the nested join.
+        $nested_join_definition = $this->definition;
+        $nested_join_definition['table'] = $this->definition['base'];
+        $nested_join_definition['left_field'] = $nested_join_definition['field'] = $nested_join_definition['base field'];
+        $nested_join_definition['left_table'] = $this->table_alias;
+        $nested_join_definition['type'] = 'INNER';
+
+        $nested_join = new nodereference_nested_join();
+        $nested_join->definition = $nested_join_definition;
+        $nested_join->construct();
+        $nested_join->adjusted = TRUE;
+        $nested_alias = $nested_join_definition['table'] .'_'. $this->table;
+
+        $this->alias = $this->query->add_relationship($nested_alias, $nested_join, $this->definition['base'], $this->relationship);
+
+        $main_join->_set_nested_join($nested_join, $this->alias);
+      }
+    }
+    return $this->table_alias;
+  }
+
+  function query() {
+    $this->ensure_my_table();
+  }
+}
+
+class nodereference_nested_join extends views_join {
+  var $nested_join;
+  var $nested_alias;
+
+  function _set_nested_join($nested_join, $nested_alias) {
+    $this->nested_join = $nested_join;
+    $this->nested_alias = $nested_alias;
+  }
+
+  function join($table, &$query, $build_main_join = FALSE) {
+    if (!isset($this->nested_join)) {
+      if ($build_main_join) {
+        return parent::join($table, $query);
+      }
+      return '';
+    }
+    $output = parent::join($table, $query);
+    $nested_join = $this->nested_join->join($query->table_queue[$this->nested_alias], $query, TRUE);
+    $output = preg_replace('`JOIN ({[_a-z0-9]+} [_a-z0-9]+) ON`', "JOIN (\n\\1\n{$nested_join}\n) ON", $output);
+    return $output;
+  }
+}
