Fix for the autotcomplete widget
authorYves Chedemois
Sat, 26 Dec 2009 19:33:39 +0000 (19:33 +0000)
committerYves Chedemois
Sat, 26 Dec 2009 19:33:39 +0000 (19:33 +0000)
modules/node_reference/node_reference.module

index 39c6b23..ad6f803 100644 (file)
@@ -161,6 +161,9 @@ function node_reference_field_formatter_info() {
 function node_reference_field_formatter_view($obj_type, $object, $field, $instance, $langcode, $items, $display) {
   $result = array();
 
+  // @todo Optimisation: use hook_field_formatter_prepare_view() to load
+  // node titles or full nodes in 'multiple' mode.
+
   // Collect the list of node ids.
   $nids = array();
   foreach ($items as $delta => $item) {
@@ -231,7 +234,7 @@ function node_reference_field_formatter_view($obj_type, $object, $field, $instan
           $nodes[$nid]->referencing_object = $object;
           $nodes[$nid]->referencing_field = $field['field_name'];
         }
-        $nodes_built = node_build_multiple($nodes, $build_mode);
+        $nodes_built = node_view_multiple($nodes, $build_mode);
       }
 
       // Fetch the titles of the other nodes.
@@ -260,7 +263,7 @@ function node_reference_field_formatter_view($obj_type, $object, $field, $instan
 }
 
 /**
- * Helper function for formatters.
+ * Helper function for widgets and formatters.
  *
  * Store node titles collected in the curent request.
  */
@@ -308,7 +311,7 @@ function node_reference_field_widget_info() {
       'field types' => array('node_reference'),
       'settings'    => array(
         'autocomplete_match' => 'contains',
-        'size'               => 60,
+        'size' => 60,
         'autocomplete_path' => 'node_reference/autocomplete',
       ),
     ),
@@ -357,17 +360,16 @@ function node_reference_field_widget_settings_form($field, $instance) {
 /**
  * Implements hook_field_widget_form().
  */
-function node_reference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+function node_reference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $base) {
   switch ($instance['widget']['type']) {
     case 'node_reference_autocomplete':
-      $element += array(
-        '#type'              => 'textfield',
-        '#default_value'     => isset($items[$delta]) ? $items[$delta] : NULL,
+      $element['nid'] = $base + array(
+        '#type' => 'textfield',
+        '#default_value' => isset($items[$delta]['nid']) ? $items[$delta]['nid'] : NULL,
         '#autocomplete_path' => $instance['widget']['settings']['autocomplete_path'] . '/' . $field['field_name'],
-        '#size'              => $instance['widget']['settings']['size'],
-        '#element_validate'  => array('node_reference_autocomplete_validate'),
-        '#input'             => TRUE, // allow #value_callback to be invoked
-        '#value_callback'    => 'node_reference_autocomplete_value',
+        '#size' => $instance['widget']['settings']['size'],
+        '#element_validate' => array('node_reference_autocomplete_validate'),
+        '#value_callback' => 'node_reference_autocomplete_value',
       );
       break;
   }
@@ -376,63 +378,73 @@ function node_reference_field_widget_form(&$form, &$form_state, $field, $instanc
 }
 
 /**
- * Value for a node_reference autocomplete element.
+ * Value callback for a node_reference autocomplete element.
  *
- * Replace the node title with a node nid.
+ * Replace the node nid with a node title.
  */
-function node_reference_autocomplete_value($element, $edit = FALSE, $form_state) {
-  if (!empty($element['#default_value'])) {
+function node_reference_autocomplete_value($element, $input = FALSE, $form_state) {
+  if ($input === FALSE) {
+    // We're building the displayed 'default value': expand the raw nid into
+    // "node title [nid:n]".
     $nid = $element['#default_value'];
-
-    $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 .']';
-  }
-  else {
-    $value = NULL;
+    if (!empty($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();
+      // @todo If no result (node doesn't exist or no access).
+      $value = $result->fetchField();
+      $value .= ' [nid:'. $nid .']';
+      return $value;
+    }
   }
-  return $value;
 }
 
 /**
- * Validate an autocomplete element.
+ * Validation callback for a node_reference autocomplete element.
  */
 function node_reference_autocomplete_validate($element, &$form_state, $form) {
+  $field = $form['#fields'][$element['#field_name']]['field'];
+  $instance = $form['#fields'][$element['#field_name']]['instance'];
+
   $value = $element['#value'];
   $nid = NULL;
+
   if (!empty($value)) {
-    $field_name = $element['#field_name'];
-    $instance = field_info_instance('node', $field_name, $element['#bundle']);
+    // Check whether we have an explicit "[nid:n]" input.
     preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches);
     if (!empty($matches)) {
-      // Explicit [nid:n].
+      // Explicit nid. Check that the 'title' part matches the actual title for
+      // the nid.
       list(, $title, $nid) = $matches;
-      if (!empty($title) && ($n = node_load($nid)) && $title != $n->title[LANGUAGE_NONE][0]['value']) {
-        form_error($element, t('%name: title mismatch. Please check your selection.', array('%name' => t($instance['label']))));
+      if (!empty($title)) {
+        $titles = _node_reference_get_node_titles(array($nid));
+        if ($title != $titles[$nid]) {
+          form_error($element, t('%name: title mismatch. Please check your selection.', array('%name' => t($instance['label']))));
+        }
       }
     }
     else {
-      // No explicit nid.
-      $field = field_info_field($field_name);
+      // No explicit nid (the submitted value was not populated by autocomplete
+      // selection). Get the nid of a referencable node from the entered title.
       $reference = _node_reference_potential_references($field, $value, 'equals', NULL, 1);
-      if (empty($reference)) {
-        form_error($element, t('%name: found no valid post with that title.', array('%name' => t($instance['label']))));
-      }
-      else {
+      if ($reference) {
         // @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);
       }
+      else {
+        form_error($element, t('%name: found no valid post with that title.', array('%name' => t($instance['label']))));
+      }
     }
   }
+
+  // Set the element's value as the node id that was extracted from the entered
+  // input.
   form_set_value($element, $nid, $form_state);
 }