Index: modules/field/modules/options/options.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/modules/options/options.module,v
retrieving revision 1.4
diff -u -p -r1.4 options.module
--- modules/field/modules/options/options.module	26 Mar 2009 13:31:25 -0000	1.4
+++ modules/field/modules/options/options.module	28 Mar 2009 15:44:03 -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/taxonomy.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v
retrieving revision 1.47
diff -u -p -r1.47 taxonomy.admin.inc
--- modules/taxonomy/taxonomy.admin.inc	14 Mar 2009 20:13:27 -0000	1.47
+++ modules/taxonomy/taxonomy.admin.inc	28 Mar 2009 15:44:04 -0000
@@ -12,20 +12,15 @@
  * @ingroup forms
  * @see taxonomy_overview_vocabularies_submit()
  * @see theme_taxonomy_overview_vocabularies()
+ * @todo this function displays list of node types where a vocabulary applies. removal of taxonomy_vocabulary_node_type table
+ *  means this has to be done differently.
  */
 function taxonomy_overview_vocabularies() {
   $vocabularies = taxonomy_get_vocabularies();
   $form = array('#tree' => TRUE);
   foreach ($vocabularies as $vocabulary) {
-    $types = array();
-    foreach ($vocabulary->nodes as $type) {
-      $node_type = node_get_types('name', $type);
-      $types[] = $node_type ? check_plain($node_type) : check_plain($type);
-    }
     $form[$vocabulary->vid]['#vocabulary'] = $vocabulary;
     $form[$vocabulary->vid]['name'] = array('#markup' => check_plain($vocabulary->name));
-    $form[$vocabulary->vid]['types'] = array('#markup' => implode(', ', $types));
-    $form[$vocabulary->vid]['weight'] = array('#type' => 'weight', '#delta' => 10, '#default_value' => $vocabulary->weight);
     $form[$vocabulary->vid]['edit'] = array('#markup' => l(t('edit vocabulary'), "admin/content/taxonomy/$vocabulary->vid"));
     $form[$vocabulary->vid]['list'] = array('#markup' => l(t('list terms'), "admin/content/taxonomy/$vocabulary->vid/list"));
     $form[$vocabulary->vid]['add'] = array('#markup' => l(t('add terms'), "admin/content/taxonomy/$vocabulary->vid/add"));
@@ -36,9 +31,6 @@ function taxonomy_overview_vocabularies(
   if (count($vocabularies) > 1) {
     $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
   }
-  elseif (isset($vocabulary)) {
-    unset($form[$vocabulary->vid]['weight']);
-  }
   return $form;
 }
 
@@ -49,8 +41,7 @@ function taxonomy_overview_vocabularies(
  */
 function taxonomy_overview_vocabularies_submit($form, &$form_state) {
   foreach ($form_state['values'] as $vid => $vocabulary) {
-    if (is_numeric($vid) && $form[$vid]['#vocabulary']->weight != $form_state['values'][$vid]['weight']) {
-      $form[$vid]['#vocabulary']->weight = $form_state['values'][$vid]['weight'];
+    if (is_numeric($vid)) {
       taxonomy_vocabulary_save($form[$vid]['#vocabulary']);
     }
   }
@@ -70,11 +61,6 @@ function theme_taxonomy_overview_vocabul
 
       $row = array();
       $row[] = drupal_render($vocabulary['name']);
-      $row[] = drupal_render($vocabulary['types']);
-      if (isset($vocabulary['weight'])) {
-        $vocabulary['weight']['#attributes']['class'] = 'vocabulary-weight';
-        $row[] = drupal_render($vocabulary['weight']);
-      }
       $row[] = drupal_render($vocabulary['edit']);
       $row[] = drupal_render($vocabulary['list']);
       $row[] = drupal_render($vocabulary['add']);
@@ -85,15 +71,23 @@ function theme_taxonomy_overview_vocabul
     $rows[] = array(array('data' => t('No vocabularies available.'), 'colspan' => '5'));
   }
 
-  $header = array(t('Vocabulary name'), t('Content types'));
-  if (isset($form['submit'])) {
-    $header[] = t('Weight');
-    drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'vocabulary-weight');
-  }
+  $header = array(t('Vocabulary name'));
   $header[] = array('data' => t('Operations'), 'colspan' => '3');
   return theme('table', $header, $rows, array('id' => 'taxonomy')) . drupal_render_children($form);
 }
 
+
+/**
+ * Take a user-entered name and make it machine-safe.
+ *
+ * Replace spaces and all unknown characters with underscores and lowercase.
+ *
+ * @TODO get this into common.inc
+ */
+function drupal_system_string($string, $replacement = '_') {
+  return preg_replace('/[^a-z0-9]+/', $replacement, strtolower($string));
+}
+
 /**
  * Display form for adding and editing vocabularies.
  *
@@ -103,18 +97,16 @@ function theme_taxonomy_overview_vocabul
 function taxonomy_form_vocabulary(&$form_state, $edit = array()) {
   if (!is_array($edit)) {
     $edit = (array)$edit;
-  }
+  } 
   $edit += array(
     'name' => '',
     'description' => '',
     'help' => '',
-    'nodes' => array(),
     'hierarchy' => 0,
     'relations' => 0,
     'tags' => 0,
     'multiple' => 0,
     'required' => 0,
-    'weight' => 0,
   );
   // Check whether we need a deletion confirmation form.
   if (isset($form_state['confirm_delete']) && isset($form_state['values']['vid'])) {
@@ -127,6 +119,7 @@ function taxonomy_form_vocabulary(&$form
     '#maxlength' => 255,
     '#description' => t('The name for this vocabulary, e.g., <em>"Tags"</em>.'),
     '#required' => TRUE,
+    '#field_suffix' => ' <small id="vocabulary-field-name-suffix">&nbsp;</small>',
   );
   $form['help'] = array(
     '#type' => 'textfield',
@@ -141,13 +134,37 @@ function taxonomy_form_vocabulary(&$form
     '#default_value' => $edit['description'],
     '#description' => t('Description of the vocabulary; can be used by modules.'),
   );
-  $form['nodes'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Content types'),
-    '#default_value' => $edit['nodes'],
-    '#options' => array_map('check_plain', node_get_types('names')),
-    '#description' => t('Select content types to categorize using this vocabulary.'),
-  );
+  if (module_exists('term')) {
+    $edit['field_name'] = '';
+    $edit['nodes'] = array();
+    if (array_key_exists('vid', $edit)) {
+      $instances = array();
+      foreach (field_read_fields(array('module' => 'term')) as $field) {
+        if ($field['settings']['vid'] == $edit['vid']) {
+          $edit['field_name'] = $field['field_name'];
+          $fields[] = $field['field_name'];
+          $instances = array_merge(field_read_instances(array('field_name' => $fields)), $instances);
+        }
+      }
+      foreach ($instances as $instance) {
+        $edit['nodes'][] = $instance['bundle'];
+      }
+    }
+    $form['field_name'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Field name'),
+      '#default_value' => $edit['field_name'],
+      '#maxlength' => 32,
+      '#description' => t('The machine-readable name for this vocabulary\'s field. This name must contain only lowercase letters, numbers, and underscores. This name must be unique.'),
+    );
+    $form['nodes'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Content types'),
+      '#default_value' => $edit['nodes'],
+      '#options' => array_map('check_plain', node_get_types('names')),
+      '#description' => t('Select content types to categorize using this vocabulary.'),
+    );
+  }
   $form['settings'] = array(
     '#type' => 'fieldset',
     '#title' => t('Settings'),
@@ -158,7 +175,7 @@ function taxonomy_form_vocabulary(&$form
     '#title' => t('Tags'),
     '#default_value' => $edit['tags'],
     '#description' => t('Terms are created by users when submitting posts by typing a comma separated list.'),
-  );
+    );
   $form['settings']['multiple'] = array(
     '#type' => 'checkbox',
     '#title' => t('Multiple select'),
@@ -171,6 +188,8 @@ function taxonomy_form_vocabulary(&$form
     '#default_value' => $edit['required'],
     '#description' => t('At least one term in this vocabulary must be selected when submitting a post.'),
   );
+
+  // @TODO - what?  this *disables* multiple parents, and there's no UI way to turn it on!?!
   // Set the hierarchy to "multiple parents" by default. This simplifies the
   // vocabulary form and standardizes the term form.
   $form['hierarchy'] = array(
@@ -215,6 +234,36 @@ function taxonomy_form_vocabulary_submit
       watchdog('taxonomy', 'Updated vocabulary %name.', array('%name' => $vocabulary->name), WATCHDOG_NOTICE, l(t('edit'), 'admin/content/taxonomy/' . $vocabulary->vid));
       break;
   }
+  if (module_exists('term')) {
+    if (!field_read_field($vocabulary->field_name)) {
+      $field = array(
+        'field_name' => $vocabulary->field_name,
+        'module' => 'term',
+        'type' => 'term',
+        'cardinality' => ($vocabulary->multiple ? FIELD_CARDINALITY_UNLIMITED : 1),
+        'settings' => array(
+          'vid' => $vocabulary->vid,
+        ),
+      );
+      field_create_field($field);
+    }
+    $instance = array(
+      'field_name' => $vocabulary->field_name,
+      'bundle' => '',
+      'label' => $vocabulary->name,
+      'description' => $vocabulary->description,
+      'required' => $vocabulary->required,
+      'widget' => array(
+        'type' => 'options_select',
+      ),
+    );
+    foreach ($vocabulary->nodes as $node_type) {
+      $instance['bundle'] = $node_type;
+      if (!field_read_instance($vocabulary->field_name, $node_type)) {
+        field_create_instance($instance);
+      }
+    }
+  }
 
   $form_state['vid'] = $vocabulary->vid;
   $form_state['redirect'] = 'admin/content/taxonomy';
@@ -265,7 +314,7 @@ function taxonomy_overview_terms(&$form_
   if ($vocabulary->tags) {
     // We are not calling taxonomy_get_tree because that might fail with a big
     // number of tags in the freetagging vocabulary.
-    $results = pager_query(db_rewrite_sql('SELECT t.*, h.parent FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY weight, name', 't', 'tid'), $page_increment, 0, NULL, $vocabulary->vid);
+    $results = pager_query(db_rewrite_sql('SELECT t.*, h.parent FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY name', 't', 'tid'), $page_increment, 0, NULL, $vocabulary->vid);
     $total_entries = db_query(db_rewrite_sql('SELECT COUNT(*) FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_hierarchy} h ON t.tid = h.tid WHERE t.vid = :vid'), array(':vid' => $vocabulary->vid));
     while ($term = db_fetch_object($results)) {
       $key = 'tid:' . $term->tid . ':0';
@@ -623,7 +672,6 @@ function taxonomy_form_term(&$form_state
   $form['#term'] = $edit;
   $form['#term']['parent'] = $parent;
   $form['#vocabulary'] = $vocabulary;
-  $form['#vocabulary']->nodes = drupal_map_assoc($vocabulary->nodes);
 
   // Check for confirmation forms.
   if (isset($form_state['confirm_delete'])) {
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.463
diff -u -p -r1.463 taxonomy.module
--- modules/taxonomy/taxonomy.module	21 Mar 2009 00:59:02 -0000	1.463
+++ modules/taxonomy/taxonomy.module	28 Mar 2009 15:44:05 -0000
@@ -212,17 +212,10 @@ function taxonomy_vocabulary_save($vocab
 
   if (!empty($vocabulary->vid) && !empty($vocabulary->name)) {
     $status = drupal_write_record('taxonomy_vocabulary', $vocabulary, 'vid');
-    db_query("DELETE FROM {taxonomy_vocabulary_node_type} WHERE vid = %d", $vocabulary->vid);
-    foreach ($vocabulary->nodes as $type => $selected) {
-      db_query("INSERT INTO {taxonomy_vocabulary_node_type} (vid, type) VALUES (%d, '%s')", $vocabulary->vid, $type);
-    }
     module_invoke_all('taxonomy_vocabulary_update', $vocabulary);
   }
   elseif (empty($vocabulary->vid)) {
     $status = drupal_write_record('taxonomy_vocabulary', $vocabulary);
-    foreach ($vocabulary->nodes as $type => $selected) {
-      db_query("INSERT INTO {taxonomy_vocabulary_node_type} (vid, type) VALUES (%d, '%s')", $vocabulary->vid, $type);
-    }
     module_invoke_all('taxonomy_vocabulary_insert', $vocabulary);
   }
 
@@ -243,7 +236,6 @@ function taxonomy_vocabulary_delete($vid
   $vocabulary = (array) taxonomy_vocabulary_load($vid);
 
   db_query('DELETE FROM {taxonomy_vocabulary} WHERE vid = %d', $vid);
-  db_query('DELETE FROM {taxonomy_vocabulary_node_type} WHERE vid = %d', $vid);
   $result = db_query('SELECT tid FROM {taxonomy_term_data} WHERE vid = %d', $vid);
   while ($term = db_fetch_object($result)) {
     taxonomy_term_delete($term->tid);
@@ -451,28 +443,14 @@ function taxonomy_form_all($free_tags = 
  *
  * @param $type
  *   If set, return only those vocabularies associated with this node type.
+ * @todo in getting rid of the taxonomy_vocabulary_node_type table, the ability to get
+ *   vocabularies by type has gone. no core functions relied on this feature anyway.
  */
 function taxonomy_get_vocabularies($type = NULL) {
-  if ($type) {
-    $result = db_query(db_rewrite_sql("SELECT v.vid, v.*, n.type FROM {taxonomy_vocabulary} v LEFT JOIN {taxonomy_vocabulary_node_type} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $type);
-  }
-  else {
-    $result = db_query(db_rewrite_sql('SELECT v.*, n.type FROM {taxonomy_vocabulary} v LEFT JOIN {taxonomy_vocabulary_node_type} n ON v.vid = n.vid ORDER BY v.weight, v.name', 'v', 'vid'));
-  }
+  $result = db_query(db_rewrite_sql('SELECT * FROM {taxonomy_vocabulary} ORDER BY weight, name', 'taxonomy_vocabulary', 'vid'));
 
   $vocabularies = array();
-  $node_types = array();
   while ($voc = db_fetch_object($result)) {
-    // If no node types are associated with a vocabulary, the LEFT JOIN will
-    // return a NULL value for type.
-    if (isset($voc->type)) {
-      $node_types[$voc->vid][$voc->type] = $voc->type;
-      unset($voc->type);
-      $voc->nodes = $node_types[$voc->vid];
-    }
-    elseif (!isset($voc->nodes)) {
-      $voc->nodes = array();
-    }
     $vocabularies[$voc->vid] = $voc;
   }
 
@@ -501,7 +479,7 @@ function taxonomy_form_alter(&$form, $fo
       $terms = $node->taxonomy;
     }
 
-    $c = db_query(db_rewrite_sql("SELECT v.* FROM {taxonomy_vocabulary} v INNER JOIN {taxonomy_vocabulary_node_type} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $node->type);
+    $c = db_query(db_rewrite_sql("SELECT * FROM {taxonomy_vocabulary} ORDER BY weight, name", 'taxonomy_vocabulary', 'vid'), $node->type);
 
     while ($vocabulary = db_fetch_object($c)) {
       if ($vocabulary->tags) {
@@ -714,18 +692,6 @@ function taxonomy_node_save($node, $term
 }
 
 /**
- * Implementation of hook_node_type().
- */
-function taxonomy_node_type($op, $info) {
-  if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
-    db_query("UPDATE {taxonomy_vocabulary_node_type} SET type = '%s' WHERE type = '%s'", $info->type, $info->old_type);
-  }
-  elseif ($op == 'delete') {
-    db_query("DELETE FROM {taxonomy_vocabulary_node_type} WHERE type = '%s'", $info->type);
-  }
-}
-
-/**
  * Find all term objects related to a given term ID.
  */
 function taxonomy_get_related($tid, $key = 'tid') {
@@ -991,14 +957,8 @@ function taxonomy_vocabulary_load($vid, 
     // that cached, and we will not try to load this later.
     $vocabularies[$vid] = FALSE;
     // Try to load the data and fill up the object.
-    $result = db_query('SELECT v.*, n.type FROM {taxonomy_vocabulary} v LEFT JOIN {taxonomy_vocabulary_node_type} n ON v.vid = n.vid WHERE v.vid = %d', $vid);
-    $node_types = array();
+    $result = db_query('SELECT * FROM {taxonomy_vocabulary} WHERE vid = %d', $vid);
     while ($voc = db_fetch_object($result)) {
-      if (!empty($voc->type)) {
-        $node_types[$voc->type] = $voc->type;
-      }
-      unset($voc->type);
-      $voc->nodes = $node_types;
       $vocabularies[$vid] = $voc;
     }
   }
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	28 Mar 2009 15:44:05 -0000
@@ -0,0 +1,6 @@
+; $Id$
+name = Term
+description = Defines taxonomy term field types. Use with Options to create selection lists.
+package = Core - fields
+core = 7.x
+files[]=term.module
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	28 Mar 2009 15:44:06 -0000
@@ -0,0 +1,131 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Defines term field types that can be used with the Options module.
+ */
+
+/**
+ * Implementation of hook_theme().
+ */
+function term_theme() {
+  return array(
+    'field_formatter_term_default' => array(
+      'arguments' => array('element' => NULL),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_field_info().
+ */
+function term_field_info() {
+  return array(
+    'term' => array(
+      'label' => t('Term'),
+      'description' => t('This field stores keys from key/value terms of allowed numbers where the stored numeric key has significance and must be preserved, i.e. \'Lifetime in days\': 1|1 day, 7|1 week, 31|1 month.'),
+      'settings' => array('allowed_values_function' => ''),
+      'default_widget' => 'options_select',
+      'default_formatter' => 'term_default',
+      'settings' => array('vid' => 0),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_field_columns().
+ * 
+ * @TODO text.module calls this an implementation of hook_field_schema(),
+ * any deep meaning in that or just correct it in text.module?
+ */
+function term_field_columns($field) {
+  $columns = array(
+    'value' => array(
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => FALSE,
+    ),
+  );
+  return $columns;
+}
+
+/**
+ * Implementation of hook_field_validate().
+ */
+function term_field_validate($obj_type, $object, $field, $instance, $items, $form) {
+  $allowed_values = term_allowed_values($field);
+  if (is_array($items)) {
+    foreach ($items as $delta => $item) {
+      $error_element = isset($item['_error_element']) ? $item['_error_element'] : '';
+      if (is_array($item) && isset($item['_error_element'])) unset($item['_error_element']);
+      if (!empty($item['value'])) {
+        if (count($allowed_values) && !array_key_exists($item['value'], $allowed_values)) {
+          form_set_error($error_element, t('%name: illegal value.', array('%name' => t($instance['label']))));
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implementation of hook_field_is_empty().
+ */
+function term_field_is_empty($item, $field) {
+  if (empty($item['value']) && (string)$item['value'] !== '0') {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Implementation of hook_field_formatter_info().
+ */
+function term_field_formatter_info() {
+  return array(
+    'term_default' => array(
+      'label' => t('Default'),
+      '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) {
+  $field = field_info_field($element['#field_name']);
+  if (($allowed_values = term_allowed_values($field)) && isset($allowed_values[$element['#item']['value']])) {
+    $term = taxonomy_term_load($element['#item']['value']);
+    return l($term->name, taxonomy_term_path($term));
+  }
+  // If no match was found in allowed values, fall back to the key.
+  return 'invalid - '. $element['#item']['value'];
+}
+
+/**
+ *  Create an array of the allowed values for this field.
+ *
+ *  Call the allowed_values_function to retrieve the allowed
+ *  values array.
+ *
+ *  This function should imitate the features of _taxonomy_term_select
+ *
+ *  TODO Rework this to create a method of selecting plugable allowed values terms
+ *  TODO deal with excluded tids
+ *
+ */
+function term_allowed_values($field) {
+  $tree = taxonomy_get_tree($field['settings']['vid']);
+  $options = array();
+
+  if ($tree) {
+    foreach ($tree as $term) {
+      $options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
+    }
+  }
+  return $options;
+}
