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 13 Jun 2009 05:25:15 -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.54 diff -u -p -r1.54 taxonomy.admin.inc --- modules/taxonomy/taxonomy.admin.inc 12 Jun 2009 13:59:56 -0000 1.54 +++ modules/taxonomy/taxonomy.admin.inc 13 Jun 2009 05:25:16 -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_type_get_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']); } } @@ -71,11 +62,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']); @@ -86,15 +72,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. * @@ -111,13 +105,11 @@ function taxonomy_form_vocabulary(&$form 'machine_name' => '', 'description' => '', 'help' => '', - 'nodes' => array(), 'hierarchy' => 0, 'relations' => 0, 'tags' => 0, 'multiple' => 0, 'required' => 0, - 'weight' => 0, ); $form['#vocabulary'] = (object) $edit; // Check whether we need a deletion confirmation form. @@ -154,13 +146,28 @@ 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_type_get_names()), - '#description' => t('Select content types to categorize using this vocabulary.'), - ); + if (module_exists('term')) { + $edit['nodes'] = array(); + if (array_key_exists('vid', $edit)) { + $instances = array(); + foreach (field_read_fields(array('field_name' => $edit['machine_name'], 'module' => 'term')) as $field) { + if ($field['settings']['vid'] == $edit['vid']) { + $fields[] = $field['field_name']; + $instances = array_merge(field_read_instances(array('field_name' => $fields)), $instances); + } + } + foreach ($instances as $instance) { + $edit['nodes'][] = $instance['bundle']; + } + } + $form['nodes'] = array( + '#type' => 'checkboxes', + '#title' => t('Content types'), + '#default_value' => $edit['nodes'], + '#options' => array_map('check_plain', node_type_get_names()), + '#description' => t('Select content types to categorize using this vocabulary.'), + ); + } $form['settings'] = array( '#type' => 'fieldset', '#title' => t('Settings'), @@ -184,6 +191,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( @@ -222,7 +231,7 @@ function taxonomy_form_vocabulary_valida // Do not allow duplicate machine names. $vocabularies = taxonomy_get_vocabularies(); foreach ($vocabularies as $vocabulary) { - if ($machine_name == $vocabulary->machine_name && (!(isset($form_state['values']['vid']) && $vocabulary->vid != $form_state['values']['vid']))) { + if ($machine_name == $vocabulary->machine_name && (!(isset($form_state['values']['vid']) && $vocabulary->vid == $form_state['values']['vid']))) { form_set_error('machine_name', t('This machine-readable name is already in use by another vocabulary and must be unique.')); } } @@ -255,6 +264,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->machine_name)) { + $field = array( + 'field_name' => $vocabulary->machine_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->machine_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->machine_name, $node_type)) { + field_create_instance($instance); + } + } + } $form_state['vid'] = $vocabulary->vid; $form_state['redirect'] = 'admin/content/taxonomy'; Index: modules/taxonomy/taxonomy.module =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v retrieving revision 1.478 diff -u -p -r1.478 taxonomy.module --- modules/taxonomy/taxonomy.module 12 Jun 2009 13:59:56 -0000 1.478 +++ modules/taxonomy/taxonomy.module 13 Jun 2009 05:25:18 -0000 @@ -258,21 +258,6 @@ function taxonomy_vocabulary_save($vocab if (!empty($vocabulary->vid) && !empty($vocabulary->name)) { $status = drupal_write_record('taxonomy_vocabulary', $vocabulary, 'vid'); - db_delete('taxonomy_vocabulary_node_type') - ->condition('vid', $vocabulary->vid) - ->execute(); - - if (!empty($vocabulary->nodes)) { - $query = db_insert('taxonomy_vocabulary_node_type') - ->fields(array('vid', 'type')); - foreach ($vocabulary->nodes as $type => $selected) { - $query->values(array( - 'vid' => $vocabulary->vid, - 'type' => $type, - )); - } - $query->execute(); - } module_invoke_all('taxonomy_vocabulary_update', $vocabulary); } elseif (empty($vocabulary->vid)) { @@ -313,9 +298,6 @@ function taxonomy_vocabulary_delete($vid db_delete('taxonomy_vocabulary') ->condition('vid', $vid) ->execute(); - db_delete('taxonomy_vocabulary_node_type') - ->condition('vid', $vid) - ->execute(); $result = db_query('SELECT tid FROM {taxonomy_term_data} WHERE vid = :vid', array(':vid' => $vid))->fetchCol(); foreach ($result as $tid) { taxonomy_term_delete($tid); @@ -605,6 +587,8 @@ 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) { $conditions = !empty($type) ? array('type' => $type) : NULL; @@ -648,12 +632,10 @@ function taxonomy_form_alter(&$form, $fo $terms = $node->taxonomy; } $query = db_select('taxonomy_vocabulary', 'v'); - $query->join('taxonomy_vocabulary_node_type', 'n', 'v.vid = n.vid'); $query->addTag('term_access'); $result = $query ->fields('v') - ->condition('n.type', $node->type) ->orderBy('v.weight') ->orderBy('v.name') ->execute(); @@ -1246,19 +1228,11 @@ function taxonomy_vocabulary_load_multip // we have $vids still to load, or if no $vids were passed. if ($vids || !$passed_vids) { $query = db_select('taxonomy_vocabulary', 'v'); - $query->addField('n', 'type'); $query ->fields('v') ->orderBy('v.weight') ->orderBy('v.name'); - if (!empty($type)) { - $query->leftJoin('taxonomy_vocabulary_node_type', 'n', 'v.vid = n.vid AND n.type = :type', array(':type' => $type)); - } - else { - $query->leftJoin('taxonomy_vocabulary_node_type', 'n', 'v.vid = n.vid'); - } - // If the $vids array is populated, add those to the query. if ($vids) { $query->condition('v.vid', $vids, 'IN'); 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 13 Jun 2009 05:25:18 -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 13 Jun 2009 05:25:18 -0000 @@ -0,0 +1,135 @@ + array( + 'arguments' => array('element' => NULL), + ), + ); +} + +/** + * Implement 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), + ), + ); +} + +/** + * Implement hook_field_schema(). + */ +function term_field_schema($field) { + $columns = array( + 'value' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + ), + ); + 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('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; +}