Index: nodereference.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/nodereference.module,v
retrieving revision 1.27
diff -u -r1.27 nodereference.module
--- nodereference.module	5 May 2006 14:10:44 -0000	1.27
+++ nodereference.module	19 May 2006 14:15:07 -0000
@@ -25,6 +25,8 @@
   if ($may_cache) {
     $items[] = array('path' => 'nodereference/autocomplete', 'title' => t('node reference autocomplete'),
       'callback' => 'nodereference_autocomplete', 'access' => user_access('access content'), 'type' => MENU_CALLBACK);
+  } else {
+
   }
 
   return $items;
@@ -46,24 +48,65 @@
   switch ($op) {
     case 'form':
       $form = array();
-      $form['referenceable_types'] = array(
-        '#type' => 'checkboxes',
-        '#title' => t('Content types that can be referenced'),
+      $form['types'] = array(
+        '#type' => 'fieldset', 
+        '#title' => t('Referenceable types'),
+        '#description' => t('Select all content types that can be referenced.'),
+        );
+      $form['types']['referenceable_types'] = array(
+        '#type' => 'select',
         '#multiple' => TRUE,
         '#default_value' => isset($field['referenceable_types']) ? $field['referenceable_types'] : array(),
-        '#options' => node_get_types(),
+        '#options' => _nodereference_add_none(node_get_types()),
       );
+      $form['types1'] = array(
+        '#type' => 'fieldset', 
+        '#title' => t('Prepend to title'),
+        '#description' => t('Prepend additional information about the node to the title. Use if the title alone will not be descriptive enough to identify the field. List all fields to be appended to the title in selection lists.'),
+        );
+      $form['types1']['title_append'] = array(
+        '#type' => 'select',
+        '#multiple' => TRUE,
+        '#default_value' => isset($field['title_append']) ? $field['title_append'] : 0,
+        '#options' => _nodereference_add_none(_nodereference_append_fields()),
+        );
       return $form;
 
     case 'save':
-      return array('referenceable_types');
+      return array('referenceable_types', 'title_append');
 
     case 'database columns':
       $columns = array(
         'nid' => array('type' => 'int', 'not null' => TRUE, 'default' => '0'),
       );
       return $columns;
+
+    case 'filter':
+      return array(
+        'name' => t('Nodereference: Title'),
+        'operator' => 'views_handler_operator_like',
+        'handler' => 'views_handler_filter_like',
+        'help' => t('This filter allows nodereference fields to be filtered by their title.'),
+      );
+      /*
+      'type' => array(
+        'name' => t('Node: Type'),
+        'list' => 'views_handler_filter_nodetype',
+        'list-type' => 'list',
+        'operator' => 'views_handler_operator_or',
+        'value-type' => 'array',
+        'help' => t('Include or exclude nodes of the selected types.'),
+      ),
+      'title' => array(
+        'name' => t('Node: Title'),
+        'operator' => 'views_handler_operator_like',
+        'handler' => 'views_handler_filter_like',
+        'help' => t('This filter allows nodes to be filtered by their title.'),
+      ),
+      */
+      break;
   }
+
 }
 
 /**
@@ -123,17 +166,35 @@
       case 'form':
         $form = array();
 
+       // check whether the option to append extra fields to the title was chosen, and add that info in if required
+        if ($field['title_append']) {
+           $refs = _nodereference_potential_references($field, TRUE);
+           $append_fields = _nodereference_append_fields();
+           foreach ($refs as $nid => $value) {
+             unset($append);
+             foreach ($field['title_append'] as $option) {
+               $append[$append_fields[$option]] = _nodereference_append_fields_format($option, $value->$append_fields[$option]);
+             }
+             $options[$nid] = theme('nodereference_append', $append, $value->title);
+           }
+        } else {
+           $options = _nodereference_potential_references($field);
+        }
+
+        if (!$field['required']) $options = _nodereference_add_none($options);
+
         $form[$field['field_name']] = array('#tree' => TRUE);
         $form[$field['field_name']]['nids'] = array(
           '#type' => 'select',
           '#title' => t($field['widget']['label']),
           '#default_value' => $node_field['default nids'],
           '#multiple' => $field['multiple'],
-          '#options' => _nodereference_potential_references($field),
+          '#options' => $options,
           '#required' => $field['required'],
           '#description' => $field['widget']['description'],
         );
 
+  
         return $form;
 
       case 'process form values':
@@ -225,11 +286,14 @@
 
 /**
  * Fetch an array of all candidate referenced nodes, for use in presenting the selection form to the user.
+ * 
+ * @param $limit - an array of content_types to use as a sql filter, use when referenceable types needs to be limited
  */
-function _nodereference_potential_references($field, $return_full_nodes = FALSE) {
+function _nodereference_potential_references($field, $return_full_nodes = FALSE, $limit = array()) {
   $related_types = array();
 
-  foreach ($field['referenceable_types'] as $related_type) {
+  $referenceable_types = $limit ? $limit : $field['referenceable_types'];
+  foreach ($referenceable_types as $related_type) {
     if ($related_type) {
       $related_types[] = " type = '". $related_type ."'";
     }
@@ -241,7 +305,7 @@
     return array();
   }
 
-  $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, n.type FROM {node} n WHERE ". $related_clause ." ORDER BY n.title, n.type"));
+  $result = db_query(db_rewrite_sql("SELECT n.* FROM {node} n WHERE ". $related_clause ." ORDER BY n.title, n.type"));
 
   if (db_num_rows($result) == 0) {
     return array();
@@ -257,25 +321,46 @@
       $rows[$node->nid] = $node->title;
     }
   }
-
   return $rows;
 }
 
+/**
+ *  Fetch array of linked node information
+ *  Return a 2-level array of content-type and node info
+ * 
+ * @param $content_type - either a string name or array of content types, used to limit the results
+ */
 function nodereference_referenced_by_list($nid, $content_type = '') {
+  
+  // find cck nodereference field tables and search through them for matching node ids
+  $links = array();
+
+  // limit search to specific content type(s), if desired
   if ($content_type) {
-    $result = db_query("SELECT n.* FROM {node_field_nodereference_data} node_ref INNER JOIN {node} n ON n.vid = node_ref.vid AND n.type = '%s' WHERE node_ref.field_nid = %d ORDER BY delta", $nid, $content_type);
-  }
-  else {
-    $result = db_query("SELECT n.* FROM {node_field_nodereference_data} node_ref INNER JOIN {node} n ON n.vid = node_ref.vid WHERE node_ref.field_nid = %d ORDER BY delta", $nid);
+    if (is_array($content_type)) {
+      $where = " AND ni.type_name IN(' ',". implode("','", $content_type) ."') ";
+    } else {
+      $where = " AND ni.type_name = '$content_type' ";
+    }
   }
-
-  $values = array();
-
-  while ($value = db_fetch_object($result)) {
-    $values[] = $value;
+  
+  // identify nodereference fields
+  $result = db_query("SELECT * FROM {node_field} nf  INNER JOIN {node_field_instance} ni ON nf.field_name = ni.field_name WHERE nf.type='nodereference' $where");
+  while ($field = db_fetch_array($result)) {
+
+    // for each field instance, find the table that contains data and perform lookup for matching nids
+    $db_info   = content_database_info($field);
+    $ids = db_query("SELECT nref.vid, n.* FROM {". $db_info['table'] ."} nref INNER JOIN {node} n ON nref.vid = n.nid WHERE ". $db_info['columns']['nid']['column'] ."=". $nid);
+    while ($data = db_fetch_object($ids)) {
+
+      // add field and table info to the value returned, table values can be used if the nodelink needs to be updated
+      // there is little cost to adding db info here and it may eliminate the need for another db lookup if the fields need update
+      $data->field_name = $field['field_name'];
+      $data->table_name = $db_info['table'];
+      $links[$data->type][$data->vid] = $data;
+    }
   }
-
-  return $values;
+  return $links;
 }
 
 /**
@@ -285,13 +370,175 @@
   $fields = _content_fields();
   $field = $fields[$field_name];
   $matches = array();
-
+  
   foreach (_nodereference_potential_references($field, TRUE) as $key => $value) {
     if (stristr($value->title, $string)) {
-      $matches[$value->title] = check_plain('('. str_replace('content-', '', $value->type) .') '. $value->title);
+      
+      $append = array();
+      $append_fields = _nodereference_append_fields();
+        
+      foreach ($field['title_append'] as $option) {
+        $append[$append_fields[$option]] = _nodereference_append_fields_format($option, $value->$append_fields[$option]);
+      }
+      $matches[$value->title] = check_plain(theme('nodereference_append', $append, $value->title));
     }
   }
 
   print drupal_to_js($matches);
   exit();
-}
\ No newline at end of file
+}
+
+/**
+ * A list of possible fields to append to the node title to help clarify which node is which
+ */
+function _nodereference_append_fields() {
+  return array(0 => '', 1 => 'nid', 2 => 'uid', 3 => 'created', 4 => 'changed', 5 => 'status', 6 => 'type');
+}
+
+/**
+ * Formatting for append fields
+ */
+function _nodereference_append_fields_format($type, $value) {
+  switch ($type) {
+  case ('created'):
+  case ('changed'):
+    return format_date($value);
+    break;
+  case ('type'):
+    return node_get_name($value);
+    break;
+  default:
+    return $value;
+  }
+}
+
+/**
+ * Add a blank value to not-required select lists
+ */
+function _nodereference_add_none($array) {
+  return array(0 => '') + $array;
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function nodereference_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
+  
+  switch ($op) {
+  case 'pre delete':
+
+    // check for nodereference links when a node is deleted
+    if (nodereference_referenced_by_list($node->nid)) {
+      drupal_set_message(t('This record is linked to nodereference fields. If you proceed, all linked nodereference fields will be reset to 0.'), 'error');
+    }
+    break;
+  
+  case 'delete':
+
+    // reset nodereference links if node is deleted
+    $fields = nodereference_referenced_by_list($node->nid);
+    foreach ($fields as $nid => $field) {
+      db_query("UPDATE ". $field->table_name ." SET ". $field->field_name ." = 0 WHERE vid = ". $nid);
+    }
+    drupal_set_message(t('Linked nodereference fields have been reset.'), 'error');
+    break;
+  
+  case("view"):
+
+    // move all handling of reverse links to the theme for maximum flexibility
+    $node->body .= theme('nodereference_reverse_links', $node, $op, $teaser, $page);
+    break;
+  }
+}
+
+/**
+ *  A theme for displaying the append fields with the node title
+ *  Use to change the way that the append fields are displayed in node lists
+ */
+function theme_nodereference_append($append, $title) {
+  foreach ($append as $name => $value) {
+     if ($value) $output[] = t($name) .': '. $value;
+  }
+  if ($output) $add = ' ('. implode('; ', $output) .') ';
+  return $title .$add;
+}
+
+/**
+ * A theme for the reverse link display
+ * 
+ * alter theme as needed
+ * to hide reverse links, return nothing from the theme
+ * or use nodereference_show_views as an alternate way to view multiple links
+ * 
+ */
+function theme_nodereference_reverse_links($node, $op, $teaser, $page) {
+
+  // move the reverse link lookup to the theme so it won't be executed if not needed
+  // it won't be needed if reverse links are themed out, nor if reverse links are shown in views
+
+  if ($view = theme('nodereference_show_views', $node)) return $view;
+
+  // if no views view was created above, display reverse links in a dl
+
+  $list = nodereference_referenced_by_list($node->nid);
+  $links = array();
+  foreach ($list as $content_type => $items) {
+    foreach ($items as $nid => $item ) {
+      $links[node_get_name($content_type)][] = l($item->title, 'node/'. $nid);
+    }
+  }
+  if (sizeof($links) > 0) {
+    
+    ksort($links);
+    $output  = '<h3>'. t('Related Content') .'</h3>';
+    $output .= '<dl>';
+    
+    foreach ($links as $label => $links) {
+      $output .= '<dt>'. $label .'</dt>';
+      foreach ($links as $link) {
+        $output .= '<dd>'. $link .'</dd>';
+      }
+    }
+    $output .= '</dl>';
+    return $output;
+  }
+  return;
+}
+
+/**
+ *  A placeholder function for showing a views view of reverse link nodes 
+ *   instead of the standard reverse links
+ *   
+ */
+function theme_nodereference_show_views($node) {
+
+  // will do nothing if views handling is not set up as in the example below
+  return false;
+
+}
+
+/**
+ *  Views theme example, create the following function in your theme folder 
+ *  using your theme name or phptemplate instead of 'example'
+ *  
+ *  identify the name of the view that should be displayed for each content type
+ *  pass in any arguments required, then build the view
+ *  the examples below are just examples, change the content types and arguments to match your setup
+ * 
+ *  function example_nodereference_show_views($node) {
+ *    switch ($node->type) {
+ *      case ('content-team'):
+ *        if ($view = views_get_view('Contacts')) {
+ *          // this example is a view that expects the value of field_contact as an argument
+ *          // $arg could be an array of values, or can be empty if no args are required
+ *          $arg = $node->field_contact[0]['nid'];
+ *          return views_build_view('embed', $view, array($arg), $view->use_pager, $view->nodes_per_block);
+ *        }
+ *        break;
+ *    }
+ * 
+ *    // if no view was generated, return false to fall back to standard reverse link display
+ *    return false;
+ *  }
+ */
+
