Index: modules/field/modules/options/options.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/options/options.module,v
retrieving revision 1.7
diff -u -p -r1.7 options.module
--- modules/field/modules/options/options.module	28 May 2009 16:44:06 -0000	1.7
+++ modules/field/modules/options/options.module	16 Jun 2009 22:48:08 -0000
@@ -44,7 +44,7 @@ function options_field_widget_info() {
   return array(
     'options_select' => array(
       'label' => t('Select list'),
-      'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
+      'field types' => array('list', 'list_boolean', 'list_text', 'list_number', 'term'),
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_CUSTOM,
         'default value' => FIELD_BEHAVIOR_DEFAULT,
@@ -52,7 +52,7 @@ function options_field_widget_info() {
     ),
     'options_buttons' => array(
       'label' => t('Check boxes/radio buttons'),
-      'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
+      'field types' => array('list', 'list_boolean', 'list_text', 'list_number', 'term'),
       'behaviors' => array(
         'multiple values' => FIELD_BEHAVIOR_CUSTOM,
         'default value' => FIELD_BEHAVIOR_DEFAULT,
Index: modules/taxonomy/term.info
===================================================================
RCS file: modules/taxonomy/term.info
diff -N modules/taxonomy/term.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/taxonomy/term.info	16 Jun 2009 22:48:11 -0000
@@ -0,0 +1,7 @@
+; $Id$
+name = Term
+description = Defines taxonomy term field types. Use with Options to create selection lists.
+dependencies[] = taxonomy
+package = Core - fields
+core = 7.x
+files[]=term.module
\ No newline at end of file
Index: modules/taxonomy/term.module
===================================================================
RCS file: modules/taxonomy/term.module
diff -N modules/taxonomy/term.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/taxonomy/term.module	16 Jun 2009 22:48:11 -0000
@@ -0,0 +1,266 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Defines term field types that can be used with the Options module.
+ */
+
+/**
+ * Implement hook_theme().
+ */
+function term_theme() {
+  return array(
+    'field_formatter_term_default' => array(
+      'arguments' => array('element' => NULL),
+    ),
+    'field_formatter_term_plain' => array(
+      'arguments' => array('element' => NULL),
+    ),
+  );
+}
+
+/**
+ * Implement hook_field_info().
+ */
+function term_field_info() {
+  return array(
+    'term' => array(
+      'label' => t('Term'),
+      'description' => t('This field represents a taxonomy term reference.'),
+      'default_widget' => 'options_select',
+      'default_formatter' => 'term_default',
+      'settings' => array('vid' => array(0)),
+    ),
+  );
+}
+
+/**
+ * Implement hook_field_schema().
+ */
+function term_field_schema($field) {
+  switch ($field['type']) {
+    default:
+      $columns = array(
+        'value' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => FALSE,
+        ),
+      );
+      break;
+  }
+  return array(
+    'columns' => $columns,
+    'indexes' => array(
+      'value' => array('value'),
+    ),
+  );
+}
+
+/**
+ * Implement hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'term_illegal_value': The value is not part of the list of allowed values.
+ */
+function term_field_validate($obj_type, $object, $field, $instance, $items, &$errors) {
+  $allowed_values = term_allowed_values($field);
+  foreach ($items as $delta => $item) {
+    if (!empty($item['value'])) {
+      if (count($allowed_values) && !array_key_exists($item['value'], $allowed_values)) {
+        $errors[$field['field_name']][$delta][] = array(
+          'error' => 'term_illegal_value',
+          'message' => t('%name: illegal value.', array('%name' => t($instance['label']))),
+        );
+      }
+    }
+  }
+}
+
+/**
+ * Implement hook_field_is_empty().
+ */
+function term_field_is_empty($item, $field) {
+  if (empty($item['value']) && (string)$item['value'] !== '0') {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Implement hook_field_formatter_info().
+ */
+function term_field_formatter_info() {
+  return array(
+    'term_default' => array(
+      'label' => t('Link'),
+      'field types' => array('term'),
+      'behaviors' => array(
+        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
+      ),
+    ),
+    'term_plain' => array(
+      'label' => t('Plain text'),
+      'field types' => array('term'),
+      'behaviors' => array(
+        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
+      ),
+    ),
+  );
+}
+
+/**
+ * Theme function for 'default' term field formatter.
+ */
+function theme_field_formatter_term_default($element) {
+  $term = $element['#item']['term'];
+  return l($term->name, taxonomy_term_path($term));
+}
+
+/**
+ * Theme function for 'default' term field formatter.
+ */
+function theme_field_formatter_term_plain($element) {
+  $term = $element['#item']['term'];
+  return $term->name;
+}
+
+/**
+ *  Create an array of the allowed values for this field.
+ *
+ *  Call the field's allowed_values function to retrieve the allowed
+ *  values array.
+ *
+ *  This function should imitate the features of _taxonomy_term_select
+ *
+ *  TODO deal with excluded tids?
+ *  TODO support scope limiting to a particular subtree of the vocabulary
+ *  TODO support multiple vocabularies in one field
+ *  TODO support multiple subtrees of the same or different vocabularies
+ *  in one field
+ *  TODO access control?
+ *  TODO the field settings could also enforce some constraints on the user's
+ *  choosing behavior. e.g. force user to choose a term with no children, etc.
+ */
+function term_allowed_values($field) {
+  $options = array();
+  foreach ($field['settings']['vid'] as $vid) {
+    $tree = taxonomy_get_tree($vid);
+    if ($tree) {
+      foreach ($tree as $term) {
+        $options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
+      }
+    }
+  }
+  return $options;
+}
+
+/*
+ * Implement hook_field_load().
+ *
+ * This preloads all taxonomy terms for a given object at once using taxonomy_term_load_multiple
+ * and unsets values for invalid terms which don't exist.
+ *
+ * @return
+ *   array with 'term' object at that index
+ */
+function term_field_load($obj_type, $objects, $field, $instances, &$items, $age) {
+  $term_cache = &drupal_static(__FUNCTION__, array());
+  $tids = array();
+
+  foreach ($objects as $id => $object) {
+    foreach ($items[$id] as $delta => $item) {
+      $tids[$item['value']] = $item['value'];
+    }
+  }
+  // this code is taken from taxonomy_term_load_multiple but all of the
+  // code related to query conditions is removed.
+  if (count($tids)) {
+    $terms = array();
+    $passed_tids = !empty($tids) ? array_flip($tids) : FALSE;
+    // Load any available terms from the internal cache.
+    if ($term_cache) {
+      $terms += array_intersect_key($term_cache, $passed_tids);
+      // If any terms were loaded, remove them from the $tids still to load.
+      $tids = array_keys(array_diff_key($passed_tids, $terms));
+    }
+    // Load any remaining terms from the database, this is necessary if we have
+    // $tids still to load, or if $conditions was passed without $tids.
+    if ($tids) {
+      $query = db_select('taxonomy_term_data', 't');
+      $query->join('taxonomy_vocabulary', 'v', 't.vid = v.vid');
+      $taxonomy_term_data = drupal_schema_fields_sql('taxonomy_term_data');
+      $query->fields('t', $taxonomy_term_data);
+      $query->addField('v', 'machine_name', 'vocabulary_machine_name');
+
+      // If the $tids array is populated, add those to the query.
+      if ($tids) {
+        $query->condition('t.tid', $tids, 'IN');
+      }
+
+      $queried_terms = $query->execute()->fetchAllAssoc('tid');
+
+      if (!empty($queried_terms)) {
+        $terms += $queried_terms;
+        $term_cache += $queried_terms;
+      }
+    }
+
+    // Ensure that the returned array is ordered the same as the original $tids
+    // array if this was passed in and remove any invalid tids.
+    if ($passed_tids) {
+      // Remove any invalid tids from the array.
+      $passed_tids = array_intersect_key($passed_tids, $terms);
+      foreach ($terms as $term) {
+        $passed_tids[$term->tid] = $term;
+      }
+      $terms = $passed_tids;
+    }
+    foreach ($objects as $id => $object) {
+      foreach ($items[$id] as $delta => $item) {
+        if (isset($terms[$item['value']])) {
+          $items[$id][$delta]['term'] = $terms[$item['value']];
+        }
+        else {
+          unset($items[$id][$delta]);
+        }
+      }
+    }
+  }
+}
+
+/*
+ * Implement hook_taxonomy_term_insert().
+ */
+function term_taxonomy_term_insert($term) {
+  _term_clean_field_cache($term);
+}
+
+/*
+ * Implement hook_taxonomy_term_update().
+ */
+function term_taxonomy_term_update($term) {
+  _term_clean_field_cache($term);
+}
+
+/*
+ * Implement hook_taxonomy_term_delete().
+ */
+function term_taxonomy_term_delete($term) {
+  _term_clean_field_cache($term);
+}
+
+function _term_clean_field_cache($term) {
+  $fields = field_read_fields(array('type' => 'term'));
+  foreach ($fields as $field) {
+    if ($field['settings']['vid'] == $term->vid) {
+      $objects = field_attach_query($field['field_name'], array(array('value', $term->tid)));
+      foreach ($objects as $obj_type => $ids) {
+        foreach ($ids as $id) {
+          cache_clear_all("field:$obj_type:$id", 'cache_field');
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
