<?php
// $Id: nodereference.module,v 1.25.2.11 2006/08/21 03:06:45 JonBob Exp $

/**
 * @file
 * Defines a field type for referencing one node from another.
 */

/**
 * Implementation of hook_help().
 */
function nodereference_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('<strong>CCK:</strong> Defines a field type for referencing one node from another. <em>Note: Requires content.module.</em>');
  }
}

/**
 * Implementation of hook_menu().
 */
function nodereference_menu($may_cache) {
  $items = array();

  if ($may_cache) {
    $items[] = array('path' => 'nodereference/autocomplete', 'title' => t('node reference autocomplete'),
      'callback' => 'nodereference_autocomplete', 'access' => user_access('access content'), 'type' => MENU_CALLBACK);
  }

  return $items;
}

/**
 * Implementation of hook_field_info().
 */
function nodereference_field_info() {
  return array(
    'nodereference' => array('label' => 'Node Reference'),
  );
}

/**
 * Implementation of hook_field_settings().
 */
function nodereference_field_settings($op, $field) {
  switch ($op) {
    case 'form':
      $form = array();
      $form['referenceable_types'] = array(
        '#type' => 'checkboxes',
        '#title' => t('Content types that can be referenced'),
        '#multiple' => TRUE,
        '#default_value' => isset($field['referenceable_types']) ? $field['referenceable_types'] : array(),
        '#options' => node_get_types(),
      );
      return $form;

    case 'save':
      return array('referenceable_types');

    case 'database columns':
      $columns = array(
        'nid' => array('type' => 'int', 'not null' => TRUE, 'default' => '0'),
      );
      return $columns;
  }
}

/**
 * Implementation of hook_field().
 */
function nodereference_field($op, &$node, $field, &$items, $teaser, $page) {
  switch ($op) {
    case 'view':
      foreach ($items as $delta => $item) {
        $items[$delta]['view'] = content_format($field, $item, 'default', $node);
      }
      return theme('field', $node, $field, $items, $teaser, $page);
  }
}

/**
 * Implementation of hook_field_formatter_info().
 */
function nodereference_field_formatter_info() {
  return array(
    'default' => array(
      'label' => 'Default',
      'field types' => array('nodereference'),
    ),
    'plain' => array(
      'label' => 'Plain text',
      'field types' => array('nodereference'),
    ),
  );
}

/**
 * Implementation of hook_field_formatter().
 */
function nodereference_field_formatter($field, $item, $formatter, $node) {
  $text = '';
  if (isset($item['nid'])) {
    $referenced_node = node_load($item['nid']);
    if ($referenced_node) {
      $text = l($referenced_node->title, 'node/'. $referenced_node->nid);
    }
  }

  switch ($formatter) {
    case 'plain':
      return strip_tags($text);
    
    default:
      return $text;
  }
}

/**
 * Implementation of hook_widget_info().
 */
function nodereference_widget_info() {
  return array(
    'nodereference_select' => array(
      'label' => 'Select List',
      'field types' => array('nodereference'),
    ),
    'nodereference_autocomplete' => array(
      'label' => 'Autocomplete Text Field',
      'field types' => array('nodereference'),
    ),
  );
}

/**
 * Implementation of hook_widget().
 */
function nodereference_widget($op, &$node, $field, &$node_field) {
  if ($field['widget']['type'] == 'nodereference_select') {
    switch ($op) {
      case 'prepare form values':
        $node_field_transposed = content_transpose_array_rows_cols($node_field);
        $node_field['default nids'] = $node_field_transposed['nid'];
        break;

      case 'form':
        $form = array();

        $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),
          '#required' => $field['required'],
          '#description' => $field['widget']['description'],
        );

        return $form;

      case 'process form values':
        if ($field['multiple']) {
          $node_field = content_transpose_array_rows_cols(array('nid' => $node_field['nids']));
        }
        else {
          $node_field[0]['nid'] = $node_field['nids'];
        }
        // Remove the widget's data representation so it isn't saved.
        unset($node_field['nids']);
    }
  }
  else {
    switch ($op) {
      case 'prepare form values':
        foreach ($node_field as $delta => $item) {
          $node_field[$delta]['default node_name'] = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $node_field[$delta]['nid']));
        }
        break;

      case 'form':
        $form = array();
        $form[$field['field_name']] = array('#tree' => TRUE);

        if ($field['multiple']) {
          $form[$field['field_name']]['#type'] = 'fieldset';
          $form[$field['field_name']]['#title'] = t($field['widget']['label']);
          $form[$field['field_name']]['#description'] = $field['widget']['description'];
          $delta = 0;
          foreach ($node_field as $item) {
            if ($item['nid']) {
              $form[$field['field_name']][$delta]['node_name'] = array(
                '#type' => 'textfield',
                '#title' => '',
                '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
                '#default_value' => $item['default node_name'],
                '#required' => ($delta == 0) ? $field['required'] : FALSE,
              );
              $delta++;
            }
          }
          foreach (range($delta, $delta + 2) as $delta) {
            $form[$field['field_name']][$delta]['node_name'] = array(
              '#type' => 'textfield',
              '#title' => '',
              '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
              '#default_value' => '',
              '#required' => ($delta == 0) ? $field['required'] : FALSE,
            );
          }
        }
        else {
          $form[$field['field_name']][0]['node_name'] = array(
            '#type' => 'textfield',
            '#title' => t($field['widget']['label']),
            '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
            '#default_value' => $node_field[0]['default node_name'],
            '#required' => $field['required'],
            '#description' => $field['widget']['description'],
          );
        }
        return $form;

      case 'validate':
        foreach ($node_field as $delta => $item) {
          if ($item['node_name'] && !in_array($item['node_name'], _nodereference_potential_references($field))) {
            form_set_error($field['field_name'], t('No post with that title exists.'));
          }
        }
        return;

      case 'process form values':
        foreach ($node_field as $delta => $item) {
          $nid = 0;
          if ($node_field[$delta]['node_name']) {
            $nid = db_result(db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.title = '%s'"), $node_field[$delta]['node_name']));
          }
          // Remove the widget's data representation so it isn't saved.
          unset($node_field[$delta]['node_name']);
          if ($nid) {
            $node_field[$delta]['nid'] = $nid;
          }
          else {
            unset($node_field[$delta]);
          }
        }
        break;
    }
  }
}


/**
 * Fetch an array of all candidate referenced nodes, for use in presenting the selection form to the user.
 */
function _nodereference_potential_references($field, $return_full_nodes = FALSE) {
  $related_types = array();

  if (isset($field['referenceable_types'])) {
    foreach ($field['referenceable_types'] as $related_type) {
      if ($related_type) {
        $related_types[] = " type = '". $related_type ."'";
      }
    }
  }

  $related_clause = implode(' OR ', $related_types);

  if (!count($related_types)) {
    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"));

  if (db_num_rows($result) == 0) {
    return array();
  }

  $rows = array();

  while ($node = db_fetch_object($result)) {
    if ($return_full_nodes) {
      $rows[$node->nid] = $node;
    }
    else {
      $rows[$node->nid] = $node->title;
    }
  }

  return $rows;
}

/**
 * Retrieve a pipe delimited string of autocomplete suggestions
 */
function nodereference_autocomplete($field_name, $string = '') {
  $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);
    }
  }

  print drupal_to_js($matches);
  exit();
}