Index: modules/node_reference/node_reference.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/node_reference/node_reference.module,v
retrieving revision 1.16
diff -u -p -r1.16 node_reference.module
--- modules/node_reference/node_reference.module	10 Nov 2009 11:30:24 -0000	1.16
+++ modules/node_reference/node_reference.module	29 Nov 2009 14:53:02 -0000
@@ -4,10 +4,16 @@
 /**
  * @file
  * Defines a field type for referencing one node from another.
+ *
+ * Specific changes (beyond fixes) from 1.16:
+ * - node_reference_field_schema
+ *   - added an index on nid for better performance on back references
  */
 
 /**
- * Implementation of hook_menu().
+ * Implement hook_menu().
+ *
+ * @return array
  */
 function node_reference_menu() {
   $items = array();
@@ -21,7 +27,9 @@ function node_reference_menu() {
 }
 
 /**
- * Implementation of hook_theme().
+ * Implement hook_theme().
+ *
+ * @return array
  */
 function node_reference_theme() {
   return array(
@@ -52,128 +60,215 @@ function node_reference_theme() {
 }
 
 /**
- * Implementation of hook_field_info().
+ * Implement hook_field_info().
+ *
+ * @return array
  */
 function node_reference_field_info() {
   return array(
     'node_reference' => array(
-      'label' => t('Node reference'),
-      'description' => t('This field stores the ID of a related node as an integer value.'),
-      'settings' => array('referenceable_types' => array()),
-      'default_widget' => 'node_reference_autocomplete',
+      'label'             => t('Node reference'),
+      'description'       => t('This field stores the ID of a related node as an integer value.'),
+      'settings'          => array('referenceable_types' => array()),
+      // It probably make more sense to have the referenceable types be per-field than per-instance
+      // 'instance settings' => array('referenceable_types' => array()),
+      'default_widget'    => 'node_reference_autocomplete',
       'default_formatter' => 'node_reference_default',
     ),
   );
 }
 
 /**
- * Implementation of hook_field_schema().
+ * Implement hook_field_schema().
+ *
+ * @return array
  */
 function node_reference_field_schema($field) {
   $columns = array(
-    'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE),
+    'nid' => array(
+      'type'     => 'int',
+      'unsigned' => TRUE,
+      'not null' => FALSE,
+    ),
+  );
+  return array(
+    'columns' => $columns,
+    'indexes' => array('nid' => array('nid')), //useful to find back-references
   );
-  return array('columns' => $columns);
 }
 
 /**
- * Implementation of hook_field().
+ * Implement hook_field_settings_form().
+ *
+ * @param array $field
+ * @param array $instance
+ * @param boolean $has_data
+ * @return array
  */
-function node_reference_field($op, $node, $field, &$items, $teaser, $page) {
-  switch ($op) {
-    // When preparing a translation, load any translations of existing references.
-    case 'prepare translation':
-      $addition = array();
-      $addition[$field['field_name']] = array();
-      if (isset($node->translation_source->$field['field_name']) && is_array($node->translation_source->$field['field_name'])) {
-        foreach ($node->translation_source->$field['field_name'] as $key => $reference) {
-          $reference_node = node_load($reference['nid']);
-          // Test if the referenced node type is translatable and, if so,
-          // load translations if the reference is not for the current language.
-          // We can assume the translation module is present because it invokes 'prepare translation'.
-          if (translation_supported_type($reference_node->type) && !empty($reference_node->language) && $reference_node->language != $node->language && $translations = translation_node_get_translations($reference_node->tnid)) {
-            // If there is a translation for the current language, use it.
-            $addition[$field['field_name']][] = array(
-              'nid' => isset($translations[$node->language]) ? $translations[$node->language]->nid : $reference['nid'],
-            );
-          }
-        }
-      }
-      return $addition;
+function node_reference_field_settings_form($field, $instance, $has_data) {
+  $settings = $field['settings'];
+  $form = array();
+  $form['referenceable_types'] = array(
+    '#type'          => 'checkboxes',
+    '#title'         => t('Content types that can be referenced'),
+    '#multiple'      => TRUE,
+    '#default_value' => is_array($settings['referenceable_types'])
+       ? $settings['referenceable_types']
+       : array(),
+    '#options' => array_map('check_plain', node_type_get_names()),
+    '#disabled' => $has_data,
+  );
+  return $form;
+}
 
-    case 'validate':
-      // Extract nids to check.
-      $ids = array();
-      foreach ($items as $delta => $item) {
-        if (is_array($item) && !empty($item['nid'])) {
-          if (is_numeric($item['nid'])) {
-            $ids[] = $item['nid'];
-          }
-          else {
-            $errors[$field['field_name']][$langcode][$delta][] = array(
-              'error' => 'valid_nid',
-              'message' => t("%name: invalid input.", array('%name' => t($field['widget']['label']))),
-            );
-          }
-        }
+/**
+ * Implement of hook_field_instance_settings_form().
+ *
+ * No per-instance settings in this version.
+ *
+ * @param array $field
+ * @param array $instance
+ * @return array
+ */
+function node_reference_field_instance_settings_form($field, $instance) {
+  $form = array();
+  return $form;
+}
+
+/**
+ * Implement hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'valid_nid': nid is not a valid node id. Maybe it is even not a number.
+ *
+ * @param string $obj_type
+ * @param object $object
+ * @param array $field
+ * @param array $instance
+ * @param string $langcode
+ * @param array $items
+ * @param array $errors
+ * @return void
+ */
+function node_reference_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) {
+  // Extract nids to check.
+  $ids = array();
+
+  // First check non-numeric "nid's to avoid losing time with them.
+  foreach ($items as $delta => $item) {
+    if (is_array($item) && !empty($item['nid'])) {
+      if (is_numeric($item['nid'])) {
+        $ids[] = $item['nid'];
       }
-      // Prevent performance hog if there are no ids to check.
-      if ($ids) {
-        $refs = _node_reference_potential_references($field, '', NULL, $ids);
-        foreach ($items as $delta => $item) {
-          if (is_array($item)) {
-            if (!empty($item['nid']) && !isset($refs[$item['nid']])) {
-              $errors[$field['field_name']][$langcode][$delta][] = array(
-                'error' => 'valid_nid',
-                'message' => t("%name: this post can't be referenced.", array('%name' => t($field['widget']['label']))),
-              );              
-            }
-          }
+      else {
+        $errors[$field['field_name']][$langcode][$delta][] = array(
+          'error' => 'valid_nid',
+          'message' => t("%name: invalid input.",
+             array('%name' => t($field['widget']['label']))),
+        );
+      }
+    }
+  }
+  // Prevent performance hog if there are no ids to check.
+  if ($ids) {
+    $refs = _node_reference_potential_references($field, '', NULL, $ids);
+    foreach ($items as $delta => $item) {
+      if (is_array($item)) {
+        if (!empty($item['nid']) && !isset($refs[$item['nid']])) {
+          $errors[$field['field_name']][$langcode][$delta][] = array(
+            'error' => 'valid_nid',
+            'message' => t("%name: this post can't be referenced.",
+              array('%name' => t($field['widget']['label']))),
+          );
         }
       }
-      return $items;
+    }
   }
 }
 
 /**
- * Implementation of hook_field_is_empty().
+ * Implement hook_field_load().
+ *
+ * This hook can not be used to load the referenced node(s) because
+ * nodes are fieldable entities, and this could cause infinite loops.
+ *
+ * @param string $obj_type
+ * @param array $objects
+ * @param array $field
+ * @param array $instances
+ * @param string $langcode
+ * @param array $items
+ * @param string $age
+ * @return void
+ */
+function node_reference_field_load($obj_type, $objects, $field, $instances, $langcode, &$items, $age) {
+}
+
+/**
+ * Implement hook_field_sanitize().
+ *
+ * Since nids are numbers, there is nothing to sanitize about them.
+ *
+ * @param string $obj_type
+ * @param object $object
+ * @param array $field
+ * @param arrya $instance
+ * @param string $langcode
+ * @param array $items
+ * @return void
+ */
+function node_reference_field_sanitize($obj_type, $object, $field, $instance, $langcode, &$items) {
+}
+
+/**
+ * Implement hook_field_is_empty().
+ *
+ * @return boolean
  */
 function node_reference_field_is_empty($item, $field) {
-  if (empty($item['nid'])) {
-    return TRUE;
-  }
-  return FALSE;
+  // nid = 0 îs empty too, which is exactly what we want
+  return empty($item['nid']) ? TRUE : FALSE;
 }
 
 /**
  * Implementation of hook_field_formatter_info().
+ *
+ * @return array
  */
 function node_reference_field_formatter_info() {
   return array(
     'node_reference_default' => array(
       'label' => t('Title (link)'),
+      'description' => t('Display the title of the referenced node as a link to the node page.'),
       'field types' => array('node_reference'),
+      // 'settings' => array(),
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_DEFAULT,
       ),
     ),
     'node_reference_plain' => array(
       'label' => t('Title (no link)'),
+      'description' => t('Display the title of the referenced node as plain text.'),
       'field types' => array('node_reference'),
+      // 'settings' => array(),
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_DEFAULT,
       ),
     ),
     'node_reference_full' => array(
       'label' => t('Full node'),
+      'description' => t('Display the title of the referenced node as a full node view.'),
       'field types' => array('node_reference'),
+      // 'settings' => array(),
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_DEFAULT,
       ),
     ),
     'node_reference_teaser' => array(
       'label' => t('Teaser'),
+      'description' => t('Display the title of the referenced node as a teaser node view.'),
       'field types' => array('node_reference'),
+      // 'settings' => array(),
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_DEFAULT,
       ),
@@ -183,35 +278,53 @@ function node_reference_field_formatter_
 
 /**
  * Theme function for 'default' node_reference field formatter.
+ *
+ * @param array $variables
+ * @return string
  */
-function theme_field_formatter_node_reference_default($element) {
-  $output = '';
-  if (!empty($element['#item']['nid']) && is_numeric($element['#item']['nid']) && ($title = _node_reference_titles($element['#item']['nid']))) {
+function theme_field_formatter_node_reference_default($variables) {
+  $element = $variables['element'];
+  if (!empty($element['#item']['nid']) && is_numeric($element['#item']['nid'])
+  && ($title = _node_reference_titles($element['#item']['nid']))) {
     $output = l($title, 'node/'. $element['#item']['nid']);
   }
+  else {
+    $output = '';
+  }
   return $output;
 }
 
 /**
  * Theme function for 'plain' node_reference field formatter.
+ *
+ * @param array $variables
+ * @return string
  */
-function theme_field_formatter_node_reference_plain($element) {
-  $output = '';
-  if (!empty($element['#item']['nid']) && is_numeric($element['#item']['nid']) && ($title = _node_reference_titles($element['#item']['nid']))) {
+function theme_field_formatter_node_reference_plain($variables) {
+  $element = $variables['element'];
+  if (!empty($element['#item']['nid']) && is_numeric($element['#item']['nid'])
+  && ($title = _node_reference_titles($element['#item']['nid']))) {
     $output = check_plain($title);
   }
+  else {
+    $output = '';
+  }
   return $output;
 }
 
 /**
  * Proxy theme function for 'full' and 'teaser' node_reference field formatters.
+ *
+ * @param array $variables
+ * @return string
  */
-function theme_field_formatter_node_reference_node($element) {
+function theme_field_formatter_node_reference_node($variables) {
+  $element = $variables['element'];
   static $recursion_queue = array();
   $output = '';
   if (!empty($element['#item']['nid']) && is_numeric($element['#item']['nid'])) {
-    $node = $element['#node'];
-    $field = field_fields($element['#field_name'], $element['#bundle']);
+    $node = $element['#object'];
+    $field = field_info_field($element['#field_name']);
     // If no 'referencing node' is set, we are starting a new 'reference thread'
     if (!isset($node->referencing_node)) {
       $recursion_queue = array();
@@ -221,15 +334,21 @@ function theme_field_formatter_node_refe
       // Prevent infinite recursion caused by reference cycles:
       // if the node has already been rendered earlier in this 'thread',
       // we fall back to 'default' (node title) formatter.
-      return theme('node_reference_formatter_default', $element);
+      return theme('field_formatter_node_reference_default', $element);
     }
     if ($referenced_node = node_load($element['#item']['nid'])) {
       $referenced_node->referencing_node = $node;
       $referenced_node->referencing_field = $field;
       _node_reference_titles($element['#item']['nid'], $referenced_node->title);
-      $output = node_view($referenced_node, $element['#formatter'] == 'teaser');
+      // other values: 'node_reference_teaser'
+      $build_mode = $element['#formatter'] == 'node_reference_full'
+        ? 'full'
+        : 'teaser';
+      $output = node_build($referenced_node, $build_mode);
+      $output = drupal_render($output);
     }
   }
+
   return $output;
 }
 
@@ -241,9 +360,21 @@ function theme_field_formatter_node_refe
 function _node_reference_titles($nid, $known_title = NULL) {
   static $titles = array();
   if (!isset($titles[$nid])) {
-    $title = $known_title ? $known_title : db_result(db_query("SELECT title FROM {node} WHERE nid=%d", $nid));
-    $titles[$nid] = $title ? $title : '';
+    if ($known_title) {
+      $title = $known_title;
+    }
+    else {
+      $q = db_select('node', 'n');
+      $node_title_alias = $q->addField('n', 'title');
+      $q->addTag('node_access')
+        ->condition('n.nid', $nid)
+        ->range(0, 1);
+      $result = $q->execute();
+      $title = $result->fetchField();
+    }
+  $titles[$nid] = $title ? $title : '';
   }
+
   return $titles[$nid];
 }
 
@@ -264,43 +395,78 @@ function _node_reference_titles($nid, $k
 function node_reference_field_widget_info() {
   return array(
     'node_reference_select' => array(
-      'label' => t('Select list'),
+      'label'       => t('Select list'),
+      'description' => t('Display the list of referenceable nodes in a SELECT.'),
       'field types' => array('node_reference'),
-      'settings' => array(
-        'autocomplete_match' => 'contains'
+      'settings'    => array(
+        'autocomplete_match' => 'contains',
       ),
-      'behaviors' => array(
+      'behaviors'   => array(
         'multiple values' => FIELD_BEHAVIOR_CUSTOM,
-        'default value' => FIELD_BEHAVIOR_DEFAULT,
+        'default value'   => FIELD_BEHAVIOR_DEFAULT,
       ),
     ),
     'node_reference_buttons' => array(
-      'label' => t('Check boxes/radio buttons'),
+      'label'       => t('Check boxes/radio buttons'),
+      'description' => t('Display the list of referenceable nodes as a set of (radio) boxes.'),
       'field types' => array('node_reference'),
-      'settings' => array(
-        'autocomplete_match' => 'contains'
+      'settings'    => array(
+        'autocomplete_match' => 'contains',
       ),
-      'behaviors' => array(
+      'behaviors'   => array(
         'multiple values' => FIELD_BEHAVIOR_CUSTOM,
-        'default value' => FIELD_BEHAVIOR_DEFAULT,
+        'default value'   => FIELD_BEHAVIOR_DEFAULT,
       ),
     ),
     'node_reference_autocomplete' => array(
-      'label' => t('Autocomplete text field'),
+      'label'       => t('Autocomplete text field'),
+      'description' => t('Display the list of referenceable nodes as a textfield with autocomplete behaviour.'),
       'field types' => array('node_reference'),
-      'settings' => array(
+      'settings'    => array(
         'autocomplete_match' => 'contains',
-        'size' => 60,
+        'size'               => 60,
       ),
-      'behaviors' => array(
+      'behaviors'   => array(
         'multiple values' => FIELD_BEHAVIOR_DEFAULT,
-        'default value' => FIELD_BEHAVIOR_DEFAULT,
+        'default value'   => FIELD_BEHAVIOR_DEFAULT,
       ),
     ),
   );
 }
 
 /**
+ * Implement hook_field_widget_settings_form().
+ *
+ * @return array
+ */
+function node_reference_field_widget_settings_form($field, $instance) {
+  $form     = array();
+  $widget   = $instance['widget'];
+  $defaults = field_info_widget_settings($widget['type']);
+  $settings = array_merge($defaults, $widget['settings']);
+  if ($widget['type'] == 'node_reference_autocomplete') {
+    $form['autocomplete_match'] = array(
+      '#type'             => 'select',
+      '#title'            => t('Autocomplete matching'),
+      '#default_value'    => $settings['autocomplete_match'],
+      '#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;
+}
+
+/**
  * Implementation of FAPI hook_element_info().
  *
  * Any FAPI callbacks needed for individual widgets can be declared here,
@@ -315,18 +481,21 @@ function node_reference_field_widget_inf
 function node_reference_element_info() {
   return array(
     'node_reference_select' => array(
-      '#input' => TRUE,
-      '#columns' => array('uid'), '#delta' => 0,
+      '#input'   => TRUE,
+      '#columns' => array('uid'),
+      '#delta'   => 0,
       '#process' => array('node_reference_select_process'),
     ),
     'node_reference_buttons' => array(
-      '#input' => TRUE,
-      '#columns' => array('uid'), '#delta' => 0,
+      '#input'   => TRUE,
+      '#columns' => array('uid'),
+      '#delta'   => 0,
       '#process' => array('node_reference_buttons_process'),
     ),
     'node_reference_autocomplete' => array(
-      '#input' => TRUE,
-      '#columns' => array('name'), '#delta' => 0,
+      '#input'   => TRUE,
+      '#columns' => array('name'),
+      '#delta'   => 0,
       '#process' => array('node_reference_autocomplete_process'),
       '#autocomplete_path' => FALSE,
       ),
@@ -334,36 +503,6 @@ function node_reference_element_info() {
 }
 
 /**
- * Implementation of hook_field_widget_settings_form().
- */
-function node_reference_field_widget_settings_form($instance) {
-  $form = array();
-  $widget = $instance['widget'];
-  $defaults = field_info_widget_settings($widget['type']);
-  $settings = array_merge($defaults, $widget['settings']);
-  if ($widget['type'] == 'node_reference_autocomplete') {
-    $form['autocomplete_match'] = array(
-      '#type' => 'select',
-      '#title' => t('Autocomplete matching'),
-      '#default_value' => $settings['autocomplete_match'],
-      '#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' => $size,
-      '#element_validate' => array('_element_validate_integer_positive'),
-      '#required' => TRUE,
-    );
-  }
-  return $form;
-}
-
-/**
  * Implementation of hook_field_widget().
  *
  * Attach a single form element to the form. It will be built out and
@@ -391,58 +530,118 @@ function node_reference_field_widget_set
  *   array of default values for this field
  * @param $delta
  *   the order of this item in the array of subelements (0, 1, 2, etc)
+ * @param $element
  *
  * @return
  *   the form item for a single element for this field
  */
-function node_reference_field_widget(&$form, &$form_state, $field, $instance, $langcode, $items, $delta = 0) {
+function node_reference_field_widget(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
   switch ($instance['widget']['type']) {
     case 'node_reference_select':
-      $element = array(
-        '#type' => 'node_reference_select',
-        '#default_value' => $items,
+      $element += array(
+        '#type'           => 'node_reference_select',
+        '#default_value'  => $items,
       );
       break;
 
     case 'node_reference_buttons':
-      $element = array(
-        '#type' => 'node_reference_buttons',
-        '#default_value' => $items,
+      $element += array(
+        '#type'           => 'node_reference_buttons',
+        '#default_value'  => $items,
       );
       break;
 
     case 'node_reference_autocomplete':
-      $element = array(
-        '#type' => 'node_reference_autocomplete',
-        '#default_value' => isset($items[$delta]) ? $items[$delta] : NULL,
+      $element += array(
+        '#type'           => 'node_reference_autocomplete',
+        '#default_value'  => isset($items[$delta]) ? $items[$delta] : NULL,
         '#value_callback' => 'node_reference_autocomplete_value',
       );
       break;
   }
+
   return $element;
 }
 
 /**
- * Implementation of hook_field_widget_error().
- */
-function nodereference_field_widget_error($element, $error) {
-  form_error($element['nid'], $error['message']);
-} 	  	 
-
-/**
  * Value for a node_reference autocomplete element.
  *
  * Substitute in the node title for the node nid.
  */
-function node_reference_autocomplete_value($element, $edit = FALSE) {
+function node_reference_autocomplete_value($element, $edit = FALSE, $form_state) {
   $field_key  = $element['#columns'][0];
   if (!empty($element['#default_value'][$field_key])) {
     $nid = $element['#default_value'][$field_key];
-    $value = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $nid));
+
+    $q = db_select('node', 'n');
+    $node_title_alias = $q->addField('n', 'title');
+    $q->addTag('node_access')
+      ->condition('n.nid', $nid)
+      ->range(0, 1);
+    $result = $q->execute();
+
+    $value = $result->fetchField();
     $value .= ' [nid:'. $nid .']';
-    return array($field_key => $value);
   }
-  return array($field_key => NULL);
+  else {
+    $value = NULL;
+  }
+
+  $ret = array($field_key => $value);
+  return $ret;
+}
+
+/**
+ * Validate an autocomplete element.
+ *
+ * Remove the wrapper layer and set the right element's value.
+ * This will move the nested value at 'field-name-0-nid-nid'
+ * back to its original location, 'field-name-0-nid'.
+ */
+function node_reference_autocomplete_validate($element, &$form_state, $form) {
+  $field_key = $element['#columns'][0];
+  $value = $element['#value'][$field_key];
+  $nid = NULL;
+  if (!empty($value)) {
+    $field_name = $element['#field_name'];
+    $instance = field_info_instance('node', $field_name, $element['#bundle']);
+    preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches);
+    if (!empty($matches)) {
+      // Explicit [nid:n].
+      list(, $title, $nid) = $matches;
+      if (!empty($title) && ($n = node_load($nid)) && $title != $n->title[FIELD_LANGUAGE_NONE][0]['value']) {
+        form_error($element[$field_key], t('%name: title mismatch. Please check your selection.', array('%name' => t($instance['label']))));
+      }
+    }
+    else {
+      // No explicit nid.
+      $field = field_info_field($field_name);
+      $reference = _node_reference_potential_references($field, $value, 'equals', NULL, 1);
+      if (empty($reference)) {
+        form_error($element[$field_key], t('%name: found no valid post with that title.', array('%name' => t($instance['label']))));
+      }
+      else {
+        // TODO: the best thing would be to present the user with an
+        // additional form, allowing the user to choose between valid
+        // candidates with the same title. ATM, we pick the first
+        // matching candidate...
+        $nid = key($reference);
+      }
+    }
+  }
+  form_set_value($element, $nid, $form_state);
+}
+
+/**
+ * Implement hook_field_widget_error().
+ *
+ * @param array $element
+ * @param array $error
+ * @return void
+ */
+function nodereference_field_widget_error($element, $error) {
+  $field_key = $element['#columns'][0];
+  form_error($element[$field_key], $error['message']);
 }
 
 /**
@@ -451,31 +650,39 @@ function node_reference_autocomplete_val
  * Build the form element. When creating a form using FAPI #process,
  * note that $element['#value'] is already set.
  *
- * The $field and $instance arrays are in $form['#fields'][$element['#field_name']].
+ * The $field and $instance arrays are in
+ * $form['#fields'][$element['#field_name']].
+ *
+ * The node_reference_select widget doesn't need to create its own
+ * element: it can wrap around the options_select element. This will
+ * create a new, nested instance of the field. Adding an element
+ * validation step allows the value to be unwrapped.
  */
 function node_reference_select_process($element, $form_state, $form) {
-  // The node_reference_select widget doesn't need to create its own
-  // element, it can wrap around the options_select element.
-  // This will create a new, nested instance of the field.
-  // Add a validation step where the value can be unwrapped.
   $field_key  = $element['#columns'][0];
   $element[$field_key] = array(
-    '#type' => 'options_select',
+    '#type'          => 'options_select',
     '#default_value' => isset($element['#value']) ? $element['#value'] : '',
     // The following values were set by the field module and need
     // to be passed down to the nested element.
-    '#title' => $element['#title'],
-    '#required' => $element['#required'],
-    '#description' => $element['#description'],
-    '#field_name' => $element['#field_name'],
-    '#bundle' => $element['#bundle'],
-    '#delta' => $element['#delta'],
-    '#columns' => $element['#columns'],
+    '#object_type'   => $element['#object_type'],
+    '#title'         => $element['#title'],
+    '#required'      => $element['#required'],
+    '#description'   => $element['#description'],
+    '#field_name'    => $element['#field_name'],
+    '#bundle'        => $element['#bundle'],
+    '#delta'         => $element['#delta'],
+    '#columns'       => $element['#columns'],
   );
   if (empty($element[$field_key]['#element_validate'])) {
     $element[$field_key]['#element_validate'] = array();
   }
-  array_unshift($element[$field_key]['#element_validate'], 'node_reference_options_validate');
+
+  // Our validator needs to come first to unwrap the element before
+  // other validators can act.
+  array_unshift($element[$field_key]['#element_validate'],
+    'node_reference_options_validate');
+
   return $element;
 }
 
@@ -485,31 +692,39 @@ function node_reference_select_process($
  * Build the form element. When creating a form using FAPI #process,
  * note that $element['#value'] is already set.
  *
- * The $field and $instance arrays are in $form['#fields'][$element['#field_name']].
+ * The $field and $instance arrays are in
+ * $form['#fields'][$element['#field_name']].
+ *
+ * The node_reference_select widget doesn't need to create its own
+ * element: it can wrap around the options_buttons element. This will
+ * create a new, nested instance of the field. Adding an element
+ * validation step where allows the value to be unwrapped.
  */
 function node_reference_buttons_process($element, $form_state, $form) {
-  // The node_reference_select widget doesn't need to create its own
-  // element, it can wrap around the options_select element.
-  // This will create a new, nested instance of the field.
-  // Add a validation step where the value can be unwrapped.
   $field_key  = $element['#columns'][0];
   $element[$field_key] = array(
-    '#type' => 'options_buttons',
+    '#type'          => 'options_buttons',
     '#default_value' => isset($element['#value']) ? $element['#value'] : '',
     // The following values were set by the field module and need
     // to be passed down to the nested element.
-    '#title' => $element['#title'],
-    '#required' => $element['#required'],
-    '#description' => $element['#description'],
-    '#field_name' => $element['#field_name'],
-    '#bundle' => $element['#bundle'],
-    '#delta' => $element['#delta'],
-    '#columns' => $element['#columns'],
+    '#object_type'   => $element['#object_type'],
+    '#title'         => $element['#title'],
+    '#required'      => $element['#required'],
+    '#description'   => $element['#description'],
+    '#field_name'    => $element['#field_name'],
+    '#bundle'        => $element['#bundle'],
+    '#delta'         => $element['#delta'],
+    '#columns'       => $element['#columns'],
   );
   if (empty($element[$field_key]['#element_validate'])) {
     $element[$field_key]['#element_validate'] = array();
   }
-  array_unshift($element[$field_key]['#element_validate'], 'node_reference_options_validate');
+
+  // Our validator needs to come first to unwrap the element before
+  // other validators can act.
+  array_unshift($element[$field_key]['#element_validate'],
+    'node_reference_options_validate');
+
   return $element;
 }
 
@@ -519,33 +734,36 @@ function node_reference_buttons_process(
  * Build the form element. When creating a form using FAPI #process,
  * note that $element['#value'] is already set.
  *
+ * The node_reference autocomplete widget doesn't need to create its own
+ * element: it can wrap around the text_textfield element and add an
+ * autocomplete path and some extra processing to it. Adding an element
+ * validation step allows the value to be unwrapped.
  */
 function node_reference_autocomplete_process($element, $form_state, $form) {
-
-  // The node_reference autocomplete widget doesn't need to create its own
-  // element, it can wrap around the text_textfield element and add an autocomplete
-  // path and some extra processing to it.
-  // Add a validation step where the value can be unwrapped.
   $field_key  = $element['#columns'][0];
-
   $element[$field_key] = array(
-    '#type' => 'text_textfield',
-    '#default_value' => isset($element['#value']) ? $element['#value'] : '',
-    '#autocomplete_path' => 'node_reference/autocomplete/'. $element['#field_name'],
+    '#type'              => 'text_textfield',
+    '#default_value'     => isset($element['#value']) ? $element['#value'] : '',
+    '#autocomplete_path' => 'node_reference/autocomplete/' . $element['#field_name'],
     // The following values were set by the field module and need
     // to be passed down to the nested element.
-    '#title' => $element['#title'],
-    '#required' => $element['#required'],
-    '#description' => $element['#description'],
-    '#field_name' => $element['#field_name'],
-    '#bundle' => $element['#bundle'],
-    '#delta' => $element['#delta'],
-    '#columns' => $element['#columns'],
+    '#object_type'       => $element['#object_type'],
+    '#title'             => $element['#title'],
+    '#required'          => $element['#required'],
+    '#description'       => $element['#description'],
+    '#field_name'        => $element['#field_name'],
+    '#bundle'            => $element['#bundle'],
+    '#delta'             => $element['#delta'],
+    '#columns'           => $element['#columns'],
   );
   if (empty($element[$field_key]['#element_validate'])) {
     $element[$field_key]['#element_validate'] = array();
   }
-  array_unshift($element[$field_key]['#element_validate'], 'node_reference_autocomplete_validate');
+
+  // Our validator needs to come first to unwrap the element before
+  // other validators can act.
+  array_unshift($element[$field_key]['#element_validate'],
+    'node_reference_autocomplete_validate');
 
   return $element;
 }
@@ -553,17 +771,17 @@ function node_reference_autocomplete_pro
 /**
  * Validate a select/buttons element.
  *
- * Remove the wrapper layer and set the right element's value.
- * We don't know exactly where this element is, so we drill down
+ * Actually, this is not a validation layer, but uses the validation
+ * process to remove the wrapper layer and set the right element's
+ * value. We don't know exactly where this element is, so we drill down
  * through the element until we get to our key.
  *
  * We use $form_state['values'] instead of $element['#value']
  * to be sure we have the most accurate value when other modules
  * like options are using #element_validate to alter the value.
  */
-function node_reference_options_validate($element, &$form_state) {
+function node_reference_options_validate($element, &$form_state, $form) {
   $field_key  = $element['#columns'][0];
-
   $value = $form_state['values'];
   $new_parents = array();
   foreach ($element['#parents'] as $parent) {
@@ -579,68 +797,15 @@ function node_reference_options_validate
 }
 
 /**
- * Validate an autocomplete element.
- *
- * Remove the wrapper layer and set the right element's value.
- * This will move the nested value at 'field-name-0-nid-nid'
- * back to its original location, 'field-name-0-nid'.
- */
-function node_reference_autocomplete_validate($element, &$form_state) {
-  $field_name = $element['#field_name'];
-  $bundle = $element['#bundle'];
-  $field = field_fields($field_name, $bundle);
-  $field_key  = $element['#columns'][0];
-  $delta = $element['#delta'];
-  $value = $element['#value'][$field_key];
-  $nid = NULL;
-  if (!empty($value)) {
-    preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches);
-    if (!empty($matches)) {
-      // Explicit [nid:n].
-      list(, $title, $nid) = $matches;
-      if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
-        form_error($element[$field_key], t('%name: title mismatch. Please check your selection.', array('%name' => t($field['widget']['label']))));
-      }
-    }
-    else {
-      // No explicit nid.
-      $reference = _node_reference_potential_references($field, $value, 'equals', NULL, 1);
-      if (empty($reference)) {
-        form_error($element[$field_key], t('%name: found no valid post with that title.', array('%name' => t($field['widget']['label']))));
-      }
-      else {
-        // TODO:
-        // the best thing would be to present the user with an additional form,
-        // allowing the user to choose between valid candidates with the same title
-        // ATM, we pick the first matching candidate...
-        $nid = key($reference);
-      }
-    }
-  }
-  form_set_value($element, $nid, $form_state);
-}
-
-/**
- * Implementation of hook_allowed_values().
- */
-function node_reference_allowed_values($field) {
-  $references = _node_reference_potential_references($field);
-
-  $options = array();
-  foreach ($references as $key => $value) {
-    $options[$key] = $value['rendered'];
-  }
-  return $options;
-}
-
-/**
  * Fetch an array of all candidate referenced nodes.
  *
- * This info is used in various places (aloowed values, autocomplete results,
- * input validation...). Some of them only need the nids, others nid + titles,
- * others yet nid + titles + rendered row (for display in widgets).
- * The array we return contains all the potentially needed information, and lets
- * consumers use the parts they actually need.
+ * This info is used in various places (allowed values, autocomplete
+ * results, input validation...). Some of them only need the nids,
+ * others nid + titles, others yet nid + titles + rendered row (for
+ * display in widgets).
+ *
+ * The array we return contains all the potentially needed information,
+ * and lets consumers use the parts they actually need.
  *
  * @param $field
  *   The field description.
@@ -669,13 +834,11 @@ function _node_reference_potential_refer
   static $results = array();
 
   // Create unique id for static cache.
-  $cid = $field['field_name'] .':'. $match .':'. ($string !== '' ? $string : implode('-', $ids)) .':'. $limit;
+  $cid = $field['field_name'] . ':' . $match . ':'
+    . ($string !== '' ? $string : implode('-', $ids))
+    . ':' . $limit;
   if (!isset($results[$cid])) {
-    $references = FALSE;
-    // TODO : reintegrate Views mode ?
-    if ($references === FALSE) {
-      $references = _node_reference_potential_references_standard($field, $string, $match, $ids, $limit);
-    }
+    $references = _node_reference_potential_references_standard($field, $string, $match, $ids, $limit);
 
     // Store the results.
     $results[$cid] = !empty($references) ? $references : array();
@@ -689,48 +852,61 @@ function _node_reference_potential_refer
  * referenceable nodes defined by content types.
  */
 function _node_reference_potential_references_standard($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) {
-  $related_types = array();
-  $where = array();
-  $args = array();
-
-  if (is_array($field['settings']['referenceable_types'])) {
-    foreach (array_filter($field['settings']['referenceable_types']) as $related_type) {
-      $related_types[] = "n.type = '%s'";
-      $args[] = $related_type;
-    }
+  // Avoid useless work
+  if (!count($field['settings']['referenceable_types'])) {
+    return array();
   }
 
-  $where[] = implode(' OR ', $related_types);
+  $q = db_select('node', 'n');
+  $node_nid_alias   = $q->addField('n', 'nid');
+  $node_title_alias = $q->addField('n', 'title', 'node_title');
+  $node_type_alias  = $q->addField('n', 'type',  'node_type');
+  $q->addTag('node_access');
 
-  if (!count($related_types)) {
-    return array();
+  if (is_array($field['settings']['referenceable_types'])) {
+    $q->condition('n.type', $field['settings']['referenceable_types'], 'IN');
   }
 
   if ($string !== '') {
-    $match_operators = array(
-      'contains' => "LIKE '%%%s%%'",
-      'equals' => "= '%s'",
-      'starts_with' => "LIKE '%s%%'",
-    );
-    $where[] = 'n.title '. (isset($match_operators[$match]) ? $match_operators[$match] : $match_operators['contains']);
-    $args[] = $string;
+    $args = array();
+    switch ($match) {
+      case 'contains':
+        $title_clause = 'n.title LIKE :match';
+        $args['match'] = '%' . $string . '%';
+        break;
+
+      case 'starts_with':
+        $title_clause = 'n.title LIKE :match';
+        $args['match'] = $string . '%';
+        break;
+
+      case 'equals':
+      default: // no match type or incorrect match type: use "="
+        $title_clause = 'n.title = :match';
+        $args['match'] = $string;
+        break;
+    }
+    $q->where($title_clause, $args);
   }
   elseif ($ids) {
-    $where[] = 'n.nid IN (' . db_placeholders($ids) . ')';
-    $args = array_merge($args, $ids);
+    $q = $q->condition($node_nid_alias, $ids, 'IN', $ids);
+  }
+
+  $q->orderBy($node_title_alias)
+    ->orderBy($node_type_alias);
+
+  if ($limit) {
+    $q->range(0, $limit);
   }
 
-  $where_clause = $where ? 'WHERE ('. implode(') AND (', $where) .')' : '';
-  $sql = db_rewrite_sql("SELECT n.nid, n.title AS node_title, n.type AS node_type FROM {node} n $where_clause ORDER BY n.title, n.type");
-  $result = $limit ? db_query_range($sql, $args, 0, $limit) : db_query($sql, $args);
+  $result = $q->execute();
   $references = array();
-  while ($node = db_fetch_object($result)) {
+  foreach ($result->fetchAll() as $node) {
     $references[$node->nid] = array(
-      'title' => $node->node_title,
+      'title'    => $node->node_title,
       'rendered' => check_plain($node->node_title),
     );
   }
-
   return $references;
 }
 
@@ -738,8 +914,9 @@ function _node_reference_potential_refer
  * Menu callback; Retrieve a pipe delimited string of autocomplete suggestions for existing users
  */
 function node_reference_autocomplete($field_name, $string = '') {
-  $fields = content_fields();
+  $fields = field_info_fields(); // content_fields();
   $field = $fields[$field_name];
+
   $match = isset($field['widget']['autocomplete_match']) ? $field['widget']['autocomplete_match'] : 'contains';
   $matches = array();
 
@@ -748,27 +925,30 @@ function node_reference_autocomplete($fi
     // Add a class wrapper for a few required CSS overrides.
     $matches[$row['title'] ." [nid:$id]"] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
   }
-  drupal_json($matches);
+  drupal_json_output($matches);
 }
 
 /**
- * Implementation of hook_node_types.
+ * Implementation of hook_node_type_update.
+ *
+ * Reflect type name changes to the 'referenceable types' settings: when
+ * the name of a type changes, the change needs to be reflected in the
+ * "referenceable types" setting for any node_reference field
+ * referencing it.
+ *
+ * @param object $info
+ * @return void
  */
-function node_reference_node_type($op, $info) {
-  switch ($op) {
-    case 'update':
-      // Reflect type name changes to the 'referenceable types' settings.
-      if (!empty($info->old_type) && $info->old_type != $info->type) {
-        $fields = field_info_fields();
-        foreach ($fields as $field_name => $field) {
-          if ($field['type'] == 'node_reference' && isset($field['settings']['referenceable_types'][$info->old_type])) {
-            $field['settings']['referenceable_types'][$info->type] = empty($field['settings']['referenceable_types'][$info->old_type]) ? 0 : $info->type;
-            unset($field['settings']['referenceable_types'][$info->old_type]);
-            content_field_instance_update($field);
-          }
-        }
+function node_reference_node_type_update($info) {
+  if (!empty($info->old_type) && $info->old_type != $info->type) {
+    $fields = field_info_fields();
+    foreach ($fields as $field_name => $field) {
+      if ($field['type'] == 'node_reference' && isset($field['settings']['referenceable_types'][$info->old_type])) {
+        $field['settings']['referenceable_types'][$info->type] = empty($field['settings']['referenceable_types'][$info->old_type]) ? 0 : $info->type;
+        unset($field['settings']['referenceable_types'][$info->old_type]);
+        field_update_field($field);
       }
-      break;
+    }
   }
 }
 
@@ -785,9 +965,9 @@ function node_reference_preprocess_node(
     $node = $vars['node'];
     $field = $node->referencing_field;
     $vars['template_files'][] = 'node-node_reference';
-    $vars['template_files'][] = 'node-node_reference-'. $field['field_name'];
-    $vars['template_files'][] = 'node-node_reference-'. $node->type;
-    $vars['template_files'][] = 'node-node_reference-'. $field['field_name'] .'-'. $node->type;
+    $vars['template_files'][] = 'node-node_reference-' . $field['field_name'];
+    $vars['template_files'][] = 'node-node_reference-' . $node->type;
+    $vars['template_files'][] = 'node-node_reference-' . $field['field_name'] .'-'. $node->type;
   }
 }
 
@@ -802,30 +982,74 @@ function node_reference_preprocess_node(
  * $element['#field_name'] contains the field name
  * $element['#delta]  is the position of this element in the group
  */
-function theme_node_reference_select($element) {
+function theme_node_reference_select($variables) {
+  $element = $variables['element'];
   return $element['#children'];
 }
 
-function theme_node_reference_buttons($element) {
+function theme_node_reference_buttons($variables) {
+  $element = $variables['element'];
   return $element['#children'];
 }
 
-function theme_node_reference_autocomplete($element) {
+function theme_node_reference_autocomplete($variables) {
+  $element = $variables['element'];
   return $element['#children'];
 }
 
 /**
- * Implementation of hook_field_settings_form() on behalf of core Nodereference module.
+ * Implementation of hook_field_prepare_translation().
+ *
+ * When preparing a translation, load any translations of existing
+ * references.
+ * TODO: Core doc: "This hook may or may not survive in Field API".
+ * So it is currently not verified.
+ */
+function node_reference_field_prepare_translation($obj_type, $object, $field, $instance, $langcode, &$items) {
+
+  $addition = array();
+  $addition[$field['field_name']] = array();
+  if (isset($object->translation_source->$field['field_name'])
+  && is_array($object->translation_source->$field['field_name'])) {
+    foreach ($object->translation_source->$field['field_name'] as $key => $reference) {
+      $reference_node = node_load($reference['nid']);
+      // Test if the referenced node type is translatable and, if so,
+      // load translations if the reference is not for the current language.
+      // We can assume the translation module is present because it invokes 'prepare translation'.
+      if (translation_supported_type($reference_node->type)
+      && !empty($reference_node->language)
+      && $reference_node->language != $node->language
+      && $translations = translation_node_get_translations($reference_node->tnid)) {
+        // If there is a translation for the current language, use it.
+        $addition[$field['field_name']][] = array(
+          'nid' => isset($translations[$node->language])
+            ? $translations[$node->language]->nid
+            : $reference['nid'],
+        );
+      }
+    }
+  }
+
+  return $addition;
+}
+
+/**
+ * Implementation of the pseudo-hook "hook_allowed_values()".
+ *
+ * @see options_options()
+ * @link http://drupal.org/node/639466 @endlink
+ *
+ * A problem with this function is that its result set can be as large
+ * as the whole set of nodes on a site, which can be huge.
+ *
+ * @param array $field
  */
-function node_reference_field_settings_form($field) {
-  $form = array();
-  $form['referenceable_types'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Content types that can be referenced'),
-    '#multiple' => TRUE,
-    '#default_value' => is_array($field['settings']['referenceable_types']) ? $field['settings']['referenceable_types'] : array(),
-    '#options' => array_map('check_plain', node_type_get_names()),
-    '#disabled' => $has_data,
-  );
-  return $form;
-}
\ No newline at end of file
+function node_reference_allowed_values($field) {
+  $references = _node_reference_potential_references($field);
+
+  $options = array();
+  foreach ($references as $key => $value) {
+    $options[$key] = $value['rendered'];
+  }
+  return $options;
+}
