Index: modules/taxonomy/taxonomy.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v retrieving revision 1.64 diff -u -p -r1.64 taxonomy.admin.inc --- modules/taxonomy/taxonomy.admin.inc 4 Aug 2009 06:50:07 -0000 1.64 +++ modules/taxonomy/taxonomy.admin.inc 18 Aug 2009 20:11:39 -0000 @@ -17,14 +17,8 @@ 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/structure/taxonomy/$vocabulary->vid")); $form[$vocabulary->vid]['list'] = array('#markup' => l(t('list terms'), "admin/structure/taxonomy/$vocabulary->vid/list")); @@ -71,7 +65,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']); @@ -87,7 +80,7 @@ function theme_taxonomy_overview_vocabul $rows[] = array(array('data' => t('No vocabularies available. Add vocabulary.', array('@link' => url('admin/structure/taxonomy/add'))), 'colspan' => '5')); } - $header = array(t('Vocabulary name'), t('Content types')); + $header = array(t('Vocabulary name')); if (isset($form['submit'])) { $header[] = t('Weight'); drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'vocabulary-weight'); @@ -111,12 +104,7 @@ function taxonomy_form_vocabulary(&$form 'name' => '', 'machine_name' => '', 'description' => '', - 'help' => '', - 'nodes' => array(), 'hierarchy' => 0, - 'tags' => 0, - 'multiple' => 0, - 'required' => 0, 'weight' => 0, ); $form['#vocabulary'] = (object) $edit; @@ -140,47 +128,16 @@ function taxonomy_form_vocabulary(&$form '#description' => t('The unique machine readable name for this vocabulary, used for theme templates, can only contain lowercase letters, numbers and underscores.'), '#required' => TRUE, ); - $form['help'] = array( - '#type' => 'textfield', - '#title' => t('Help text'), - '#maxlength' => 255, - '#default_value' => $edit['help'], - '#description' => t('Instructions to present to the user when selecting terms, e.g., "Enter a comma separated list of words".'), - ); $form['description'] = array( '#type' => 'textfield', '#title' => t('Description'), '#default_value' => $edit['description'], ); - $form['nodes'] = array( - '#type' => 'checkboxes', - '#title' => t('Apply to content types'), - '#default_value' => $edit['nodes'], - '#options' => array_map('check_plain', node_type_get_names()), - ); $form['settings'] = array( '#type' => 'fieldset', '#title' => t('Settings'), '#collapsible' => TRUE, ); - $form['settings']['tags'] = array( - '#type' => 'checkbox', - '#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'), - '#default_value' => $edit['multiple'], - '#description' => t('Allows posts to have more than one term from this vocabulary (always true for tags).'), - ); - $form['settings']['required'] = array( - '#type' => 'checkbox', - '#title' => t('Required'), - '#default_value' => $edit['required'], - '#description' => t('At least one term in this vocabulary must be selected when submitting a post.'), - ); // Set the hierarchy to "multiple parents" by default. This simplifies the // vocabulary form and standardizes the term form. $form['hierarchy'] = array( @@ -231,8 +188,6 @@ function taxonomy_form_vocabulary_submit $form_state['confirm_delete'] = TRUE; return; } - // Fix up the nodes array to remove unchecked nodes. - $form_state['values']['nodes'] = array_filter($form_state['values']['nodes']); $vocabulary = (object) $form_state['values']; if ($vocabulary->machine_name != $old_vocabulary->machine_name) { field_attach_rename_bundle($old_vocabulary->machine_name, $vocabulary->machine_name); @@ -293,106 +248,71 @@ function taxonomy_overview_terms(&$form_ // An array of the terms to be displayed on this page. $current_page = array(); - // Case for free tagging. - if ($vocabulary->tags) { - // We are not calling taxonomy_get_tree because that might fail with a big - // number of tags in the freetagging vocabulary. - $query = db_select('taxonomy_term_data', 't')->extend('PagerDefault'); - $query->join('taxonomy_term_hierarchy', 'h', 't.tid = h.tid'); - $query->addTag('term_access'); - $query->condition('t.vid', $vocabulary->vid); - - // Store count in total entries and use this as count query. - $count_query = db_select('taxonomy_term_data', 't'); - $count_query->join('taxonomy_term_hierarchy', 'h', 't.tid = h.tid'); - $count_query->addTag('term_access'); - $count_query->condition('t.vid', $vocabulary->vid); - $count_query->addExpression('COUNT(t.tid)'); - $total_entries = $count_query->execute(); - $query->setCountQuery($count_query); - - $result = $query - ->fields('t') - ->fields('h', array('parent')) - ->orderBy('weight') - ->orderBy('name') - ->limit($page_increment) - ->execute(); - - foreach ($result as $term) { - $key = 'tid:' . $term->tid . ':0'; - $current_page[$key] = $term; - $page_entries++; + $term_deltas = array(); + $tree = taxonomy_get_tree($vocabulary->vid); + $term = current($tree); + do { + // In case this tree is completely empty. + if (empty($term)) { + break; } - } - // Case for restricted vocabulary. - else { - $term_deltas = array(); - $tree = taxonomy_get_tree($vocabulary->vid); - $term = current($tree); - do { - // In case this tree is completely empty. - if (empty($term)) { - break; - } - // Count entries before the current page. - if ($page && ($page * $page_increment) > $before_entries && !isset($back_peddle)) { - $before_entries++; - continue; - } - // Count entries after the current page. - elseif ($page_entries > $page_increment && isset($complete_tree)) { - $after_entries++; - continue; - } - - // Do not let a term start the page that is not at the root. - if (isset($term->depth) && ($term->depth > 0) && !isset($back_peddle)) { - $back_peddle = 0; - while ($pterm = prev($tree)) { - $before_entries--; - $back_peddle++; - if ($pterm->depth == 0) { - prev($tree); - continue 2; // Jump back to the start of the root level parent. - } - } - } - $back_peddle = isset($back_peddle) ? $back_peddle : 0; - - // Continue rendering the tree until we reach the a new root item. - if ($page_entries >= $page_increment + $back_peddle + 1 && $term->depth == 0 && $root_entries > 1) { - $complete_tree = TRUE; - // This new item at the root level is the first item on the next page. - $after_entries++; - continue; - } - if ($page_entries >= $page_increment + $back_peddle) { - $forward_peddle++; - } - - // Finally, if we've gotten down this far, we're rendering a term on this page. - $page_entries++; - $term_deltas[$term->tid] = isset($term_deltas[$term->tid]) ? $term_deltas[$term->tid] + 1 : 0; - $key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid]; - - // Keep track of the first term displayed on this page. - if ($page_entries == 1) { - $form['#first_tid'] = $term->tid; - } - // Keep a variable to make sure at least 2 root elements are displayed. - if ($term->parents[0] == 0) { - $root_entries++; - } - $current_page[$key] = $term; - } while ($term = next($tree)); - - // Because we didn't use a pager query, set the necessary pager variables. - $total_entries = $before_entries + $page_entries + $after_entries; - $pager_total_items[0] = $total_entries; - $pager_page_array[0] = $page; - $pager_total[0] = ceil($total_entries / $page_increment); - } + // Count entries before the current page. + if ($page && ($page * $page_increment) > $before_entries && !isset($back_peddle)) { + $before_entries++; + continue; + } + // Count entries after the current page. + elseif ($page_entries > $page_increment && isset($complete_tree)) { + $after_entries++; + continue; + } + + // Do not let a term start the page that is not at the root. + if (isset($term->depth) && ($term->depth > 0) && !isset($back_peddle)) { + $back_peddle = 0; + while ($pterm = prev($tree)) { + $before_entries--; + $back_peddle++; + if ($pterm->depth == 0) { + prev($tree); + continue 2; // Jump back to the start of the root level parent. + } + } + } + $back_peddle = isset($back_peddle) ? $back_peddle : 0; + + // Continue rendering the tree until we reach the a new root item. + if ($page_entries >= $page_increment + $back_peddle + 1 && $term->depth == 0 && $root_entries > 1) { + $complete_tree = TRUE; + // This new item at the root level is the first item on the next page. + $after_entries++; + continue; + } + if ($page_entries >= $page_increment + $back_peddle) { + $forward_peddle++; + } + + // Finally, if we've gotten down this far, we're rendering a term on this page. + $page_entries++; + $term_deltas[$term->tid] = isset($term_deltas[$term->tid]) ? $term_deltas[$term->tid] + 1 : 0; + $key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid]; + + // Keep track of the first term displayed on this page. + if ($page_entries == 1) { + $form['#first_tid'] = $term->tid; + } + // Keep a variable to make sure at least 2 root elements are displayed. + if ($term->parents[0] == 0) { + $root_entries++; + } + $current_page[$key] = $term; + } while ($term = next($tree)); + + // Because we didn't use a pager query, set the necessary pager variables. + $total_entries = $before_entries + $page_entries + $after_entries; + $pager_total_items[0] = $total_entries; + $pager_page_array[0] = $page; + $pager_total[0] = ceil($total_entries / $page_increment); // If this form was already submitted once, it's probably hit a validation // error. Ensure the form is rebuilt in the same order as the user submitted. @@ -447,7 +367,7 @@ function taxonomy_overview_terms(&$form_ $form['#forward_peddle'] = $forward_peddle; $form['#empty_text'] = t('No terms available. Add term.', array('@link' => url('admin/structure/taxonomy/' . $vocabulary->vid . '/add'))); - if (!$vocabulary->tags && $vocabulary->hierarchy < 2 && count($tree) > 1) { + if ($vocabulary->hierarchy < 2 && count($tree) > 1) { $form['submit'] = array( '#type' => 'submit', '#value' => t('Save') Index: modules/taxonomy/taxonomy.install =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.install,v retrieving revision 1.21 diff -u -p -r1.21 taxonomy.install --- modules/taxonomy/taxonomy.install 4 Aug 2009 06:50:07 -0000 1.21 +++ modules/taxonomy/taxonomy.install 18 Aug 2009 20:11:40 -0000 @@ -202,13 +202,6 @@ function taxonomy_schema() { 'size' => 'big', 'description' => 'Description of the vocabulary.', ), - 'help' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'Help text to display for the vocabulary.', - ), 'relations' => array( 'type' => 'int', 'unsigned' => TRUE, @@ -225,30 +218,6 @@ function taxonomy_schema() { 'size' => 'tiny', 'description' => 'The type of hierarchy allowed within the vocabulary. (0 = disabled, 1 = single, 2 = multiple)', ), - 'multiple' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'Whether or not multiple terms from this vocabulary may be assigned to a node. (0 = disabled, 1 = enabled)', - ), - 'required' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'Whether or not terms are required for nodes using this vocabulary. (0 = disabled, 1 = enabled)', - ), - 'tags' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'Whether or not free tagging is enabled for the vocabulary. (0 = disabled, 1 = enabled)', - ), 'module' => array( 'type' => 'varchar', 'length' => 255, @@ -256,13 +225,6 @@ function taxonomy_schema() { 'default' => '', 'description' => 'The module which created the vocabulary.', ), - 'weight' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'The weight of the vocabulary in relation to other vocabularies.', - ), ), 'primary key' => array('vid'), 'indexes' => array( @@ -270,33 +232,6 @@ function taxonomy_schema() { ), ); - $schema['taxonomy_vocabulary_node_type'] = array( - 'description' => 'Stores which node types vocabularies may be used with.', - 'fields' => array( - 'vid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Primary Key: the {taxonomy_vocabulary}.vid of the vocabulary.', - ), - 'type' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The {node}.type of the node type for which the vocabulary may be used.', - ), - ), - 'primary key' => array('type', 'vid'), - 'indexes' => array( - 'vid' => array('vid'), - ), - 'foreign keys' => array( - 'vid' => array('taxonomy_vocabulary' => 'vid'), - ), - ); - return $schema; } @@ -338,3 +273,79 @@ function taxonomy_update_7003() { return $ret; } + +/** + * Move taxonomy vocabulary associations for nodes to fields and field instances. + */ +function taxonomy_update_7005() { + $ret = array(); + foreach (taxonomy_get_vocabularies() as $vocabulary) { + $field_name = 'taxonomy_vocabulary_' . $vocabulary->machine_name; + $field = array( + 'field_name' => $field_name, + 'type' => 'taxonomy_term', + 'cardinality' => $vocabulary->multiple || $vocabulary->tags ? FIELD_CARDINALITY_UNLIMITED : 1, + 'settings' => array( + 'required' => $vocabulary->required ? TRUE : FALSE, + 'allowed_values' => array( + array( + 'vid' => $vocabulary->vid, + 'parent' => 0, + ), + ), + ), + ); + field_create_field($field); + + foreach ($vocabulary->nodes as $bundle) { + $instance = array( + 'label' => $vocabulary->name, + 'field_name' => $field_name, + 'bundle' => $bundle, + 'description' => $vocabulary->help, + 'widget' => array( + 'type' => $vocabulary->tags ? 'taxonomy_autocomplete' : 'select', + ), + ); + field_create_instance($instance); + } + // Migrate {term_node} data to the new field tables. + // Since we are upgrading from Drupal 6, we know that only + // field_sql_storage.module will be enabled. + $field = field_info_field($field['field_name']); + $data_table = _field_sql_storage_tablename($field); + $revision_table = _field_sql_storage_revision_tablename($field); + $etid = _field_sql_storage_etid('node'); + $value_column = $field['field_name'] . '_value'; + $columns = array('etid', 'entity_id', 'revision_id', 'bundle', 'delta', $value_column); + + // Query and save data for the current revision. + $result = db_query('SELECT td.tid, tn.nid, td.weight, tn.vid FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid INNER JOIN {node} n WHERE tn.vid = n.vid AND td.vid = :vocabulary_id ORDER BY td.weight ASC', array(':vocabulary_id' => $vocabulary->vid)); + $deltas = array(); + foreach ($result as $record) { + // Start deltas from 0, and increment by one for each + // term attached to a node. + $deltas[$record->nid] = isset($deltas[$record->nid]) ? ++$deltas[$record->nid] : 0; + $values = array($etid, $record->nid, $record->vid, $bundle, $deltas[$record->nid], $record->tid); + db_insert($data_table)->fields($columns)->values($values)->execute(); + } + + // Query and save data for all revisions. + $result = db_query('SELECT td.tid, tn.nid, td.weight, tn.vid FROM {taxonomy_term_data} td INNER JOIN {taxonomy_term_node} tn ON td.tid = tn.tid AND td.vid = :vocabulary_id ORDER BY td.weight ASC', array(':vocabulary_id' => $vocabulary->vid)); + $deltas = array(); + foreach ($result as $record) { + // Start deltas at 0, and increment by one for each term attached to a revision. + $deltas[$record->vid] = isset($deltas[$record->vid]) ? ++$deltas[$record->vid] : 0; + $values = array($etid, $record->nid, $record->vid, $bundle, $deltas[$record->vid], $record->tid); + db_insert($revision_table)->fields($columns)->values($values)->execute(); + } + } + + db_drop_table($ret, 'taxonomy_vocabulary_node_type'); + $fields = array('help', 'multiple', 'required', 'tags'); + foreach ($fields as $field) { + db_drop_field($ret, 'taxonomy_vocabulary', $field); + } + + return $ret; +} Index: modules/taxonomy/taxonomy.module =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v retrieving revision 1.496 diff -u -p -r1.496 taxonomy.module --- modules/taxonomy/taxonomy.module 18 Aug 2009 06:01:07 -0000 1.496 +++ modules/taxonomy/taxonomy.module 18 Aug 2009 20:11:41 -0000 @@ -65,6 +65,51 @@ function taxonomy_field_build_modes($obj } /** + * Return nodes attached to the default per-vocabulary fields. + * + * @param $term + * The term object. + * @todo + * Pager support. + * @todo + * Order by node.created, node.sticky. + * + * @return + * An array of nids matching the query. + */ +function taxonomy_select_nodes($term, $pager = FALSE, $order = array('n.sticky' => 'DESC', 'n.created' => 'DESC')) { + $field_info = field_info_field('taxonomy_vocabulary_' . $term->vocabulary_machine_name); + $conditions = array(array('value', $term->tid, '='), array('type', 'node', '=')); + $nids = array_keys(reset(field_attach_query($field_info['id'], $conditions, FIELD_QUERY_NO_LIMIT))); + $query = db_select('node', 'n'); + $query->addTag('node_access'); + $query->condition('n.status', 1); + if ($pager) { + $count_query = clone $query; + $count_query->addExpression('COUNT(n.nid)'); + + $query = $query + ->extend('PagerDefault') + ->limit(variable_get('default_nodes_main', 10)); + $query->setCountQuery($count_query); + } + else { + $query->range(0, variable_get('feed_default_items', 10)); + } + $query->condition('n.nid', $nids, 'IN'); + $query->addField('n', 'nid'); + foreach ($order as $field => $direction) { + $query->orderBy($field, $direction); + // ORDER BY fields need to be loaded too, assume they are in the form + // table_alias.name + list($table_alias, $name) = explode('.', $field); + $query->addField($table_alias, $name); + } + //dpm($query); + return $query->execute()->fetchCol(); +} + +/** * Implement hook_theme(). */ function taxonomy_theme() { @@ -91,66 +136,6 @@ function taxonomy_theme() { } /** - * Implement hook_node_view(). - */ -function taxonomy_node_view($node, $build_mode) { - if (empty($node->taxonomy)) { - return; - } - - if ($build_mode == 'rss') { - // Provide category information for RSS feeds. - foreach ($node->taxonomy as $term) { - $node->rss_elements[] = array( - 'key' => 'category', - 'value' => $term->name, - 'attributes' => array('domain' => url(taxonomy_term_path($term), array('absolute' => TRUE))), - ); - } - } - else { - $links = array(); - - // If previewing, the terms must be converted to objects first. - if (!empty($node->in_preview)) { - $node->taxonomy = taxonomy_preview_terms($node); - } - - foreach ($node->taxonomy as $term) { - // During preview the free tagging terms are in an array unlike the - // other terms which are objects. So we have to check if a $term - // is an object or not. - if (is_object($term)) { - $links['taxonomy_term_' . $term->tid] = array( - 'title' => $term->name, - 'href' => taxonomy_term_path($term), - 'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description)) - ); - } - // Previewing free tagging terms; we don't link them because the - // term-page might not exist yet. - else { - foreach ($term as $free_typed) { - $typed_terms = drupal_explode_tags($free_typed); - foreach ($typed_terms as $typed_term) { - $links['taxonomy_preview_term_' . $typed_term] = array( - 'title' => $typed_term, - ); - } - } - } - } - - $node->content['links']['terms'] = array( - '#theme' => 'links', - '#links' => $links, - '#attributes' => array('class' => 'links inline'), - '#sorted' => TRUE, - ); - } -} - -/** * For vocabularies not maintained by taxonomy.module, give the maintaining * module a chance to provide a path for terms in that vocabulary. * @@ -231,13 +216,6 @@ function taxonomy_menu() { 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); - // TODO: remove with taxonomy_term_node_* - $items['taxonomy/autocomplete/legacy'] = array( - 'title' => 'Autocomplete taxonomy', - 'page callback' => 'taxonomy_autocomplete_legacy', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - ); $items['admin/structure/taxonomy/%taxonomy_vocabulary'] = array( 'title' => 'Vocabulary', // this is replaced by callback @@ -286,9 +264,6 @@ function taxonomy_admin_vocabulary_title * Save a vocabulary given a vocabulary object. */ function taxonomy_vocabulary_save($vocabulary) { - if (empty($vocabulary->nodes)) { - $vocabulary->nodes = array(); - } if (!empty($vocabulary->name)) { // Prevent leading and trailing spaces in vocabulary names. @@ -301,37 +276,10 @@ 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)) { $status = drupal_write_record('taxonomy_vocabulary', $vocabulary); - - 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(); - } field_attach_create_bundle($vocabulary->machine_name); module_invoke_all('taxonomy_vocabulary_insert', $vocabulary); } @@ -356,9 +304,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); @@ -539,9 +484,6 @@ function taxonomy_term_delete($tid) { db_delete('taxonomy_term_synonym') ->condition('tid', $tid) ->execute(); - db_delete('taxonomy_term_node') - ->condition('tid', $tid) - ->execute(); field_attach_delete('taxonomy_term', $term); _taxonomy_clean_field_cache($term); @@ -589,14 +531,9 @@ function taxonomy_form($vid, $value = 0, $vocabulary = taxonomy_vocabulary_load($vid); $help = ($help) ? $help : filter_xss_admin($vocabulary->help); - if (!$vocabulary->multiple) { - $blank = ($vocabulary->required) ? t('- Please choose -') : t('- None selected -'); - } - else { - $blank = ($vocabulary->required) ? 0 : t('- None -'); - } + $blank = t('- Please choose -'); - return _taxonomy_term_select(check_plain($vocabulary->name), $value, $vid, $help, intval($vocabulary->multiple), $blank); + return _taxonomy_term_select(check_plain($vocabulary->name), $value, $vid, $help, $blank); } /** @@ -647,331 +584,6 @@ function taxonomy_vocabulary_get_names() } /** - * Implement hook_form_alter(). - * Generate a form for selecting terms to associate with a node. - * We check for taxonomy_override_selector before loading the full - * vocabulary, so contrib modules can intercept before hook_form_alter - * and provide scalable alternatives. - */ -function taxonomy_form_alter(&$form, $form_state, $form_id) { - if (!variable_get('taxonomy_override_selector', FALSE) && !empty($form['#node_edit_form'])) { - $node = $form['#node']; - - if (!isset($node->taxonomy)) { - $terms = empty($node->nid) ? array() : taxonomy_node_get_terms($node); - } - else { - // After preview the terms must be converted to objects. - if (isset($form_state['node_preview'])) { - $node->taxonomy = taxonomy_preview_terms($node); - } - $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(); - - foreach ($result as $vocabulary) { - if ($vocabulary->tags) { - if (isset($form_state['node_preview'])) { - // Typed string can be changed by the user before preview, - // so we just insert the tags directly as provided in the form. - $typed_string = $node->taxonomy['tags'][$vocabulary->vid]; - } - else { - $typed_string = taxonomy_implode_tags($terms, $vocabulary->vid) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL); - } - if ($vocabulary->help) { - $help = filter_xss_admin($vocabulary->help); - } - else { - $help = t('A comma-separated list of terms describing this content. Example: funny, bungee jumping, "Company, Inc."'); - } - $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield', - '#title' => $vocabulary->name, - '#description' => $help, - '#required' => $vocabulary->required, - '#default_value' => $typed_string, - '#autocomplete_path' => 'taxonomy/autocomplete/legacy/' . $vocabulary->vid, - '#weight' => $vocabulary->weight, - '#maxlength' => 1024, - ); - } - else { - // Extract terms belonging to the vocabulary in question. - $default_terms = array(); - foreach ($terms as $term) { - // Free tagging has no default terms and also no vid after preview. - if (isset($term->vid) && $term->vid == $vocabulary->vid) { - $default_terms[$term->tid] = $term; - } - } - $form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, array_keys($default_terms), filter_xss_admin($vocabulary->help)); - $form['taxonomy'][$vocabulary->vid]['#weight'] = $vocabulary->weight; - $form['taxonomy'][$vocabulary->vid]['#required'] = $vocabulary->required; - } - } - if (!empty($form['taxonomy']) && is_array($form['taxonomy'])) { - if (count($form['taxonomy']) > 1) { - // Add fieldset only if form has more than 1 element. - $form['taxonomy'] += array( - '#type' => 'fieldset', - '#title' => t('Vocabularies'), - '#collapsible' => TRUE, - '#collapsed' => FALSE, - ); - } - $form['taxonomy']['#weight'] = -3; - $form['taxonomy']['#tree'] = TRUE; - } - } -} - -/** - * Helper function to convert terms after a preview. - * - * After preview the tags are an array instead of proper objects. This function - * converts them back to objects with the exception of 'free tagging' terms, - * because new tags can be added by the user before preview and those do not - * yet exist in the database. We therefore save those tags as a string so - * we can fill the form again after the preview. - */ -function taxonomy_preview_terms($node) { - $taxonomy = array(); - if (isset($node->taxonomy)) { - foreach ($node->taxonomy as $key => $term) { - unset($node->taxonomy[$key]); - // A 'Multiple select' and a 'Free tagging' field returns an array. - if (is_array($term)) { - foreach ($term as $tid) { - if ($key == 'tags') { - // Free tagging; the values will be saved for later as strings - // instead of objects to fill the form again. - $taxonomy['tags'] = $term; - } - else { - $taxonomy[$tid] = taxonomy_term_load($tid); - } - } - } - // A 'Single select' field returns the term id. - elseif ($term) { - $taxonomy[$term] = taxonomy_term_load($term); - } - } - } - return $taxonomy; -} - -/** - * Find all terms associated with the given node, within one vocabulary. - */ -function taxonomy_node_get_terms_by_vocabulary($node, $vid, $key = 'tid') { - $query = db_select('taxonomy_term_data', 't'); - $query->join('taxonomy_term_node', 'r', 'r.tid = t.tid'); - $query->addTag('term_access'); - - $result = $query - ->fields('t') - ->condition('t.vid', $vid) - ->condition('r.vid', $node->vid) - ->orderBy('weight') - ->execute(); - - $terms = array(); - foreach ($result as $term) { - $terms[$term->$key] = $term; - } - return $terms; -} - -/** - * Find all term IDs associated with a set of nodes. - * - * @param $nodes - * An array of node objects. - * - * @return - * An array of term and node IDs ordered by vocabulary and term weight. - */ -function taxonomy_get_tids_from_nodes($nodes) { - $node_vids = array(); - foreach ($nodes as $node) { - $node_vids[] = $node->vid; - } - $query = db_select('taxonomy_term_node', 'r'); - $query->join('taxonomy_term_data', 't', 'r.tid = t.tid'); - $query->join('taxonomy_vocabulary', 'v', 't.vid = v.vid'); - $query->addTag('term_access'); - - return $query - ->fields('r', array('tid', 'nid', 'vid')) - ->condition('r.vid', $node_vids, 'IN') - ->orderBy('v.weight') - ->orderBy('t.weight') - ->orderBy('t.name') - ->execute() - ->fetchAll(); -} - -/** - * Find all terms associated with the given node, ordered by vocabulary and term weight. - */ -function taxonomy_node_get_terms($node, $key = 'tid') { - $terms = &drupal_static(__FUNCTION__); - - if (!isset($terms[$node->vid][$key])) { - $query = db_select('taxonomy_term_node', 'r'); - $query->join('taxonomy_term_data', 't', 'r.tid = t.tid'); - $query->join('taxonomy_vocabulary', 'v', 't.vid = v.vid'); - $query->addTag('term_access'); - - $result = $query - ->fields('r', array('tid', 'nid', 'vid')) - ->condition('r.vid', $node->vid) - ->orderBy('v.weight') - ->orderBy('t.weight') - ->orderBy('t.name') - ->execute(); - $terms[$node->vid][$key] = array(); - foreach ($result as $term) { - $terms[$node->vid][$key][$term->$key] = $term; - } - } - return $terms[$node->vid][$key]; -} - -/** - * Save term associations for a given node. - */ -function taxonomy_node_save($node, $terms) { - - taxonomy_node_delete_revision($node); - - // Free tagging vocabularies do not send their tids in the form, - // so we'll detect them here and process them independently. - if (isset($terms['tags'])) { - $typed_input = $terms['tags']; - unset($terms['tags']); - - foreach ($typed_input as $vid => $vid_value) { - $vocabulary = taxonomy_vocabulary_load($vid); - $typed_terms = drupal_explode_tags($vid_value); - - $inserted = array(); - foreach ($typed_terms as $typed_term) { - // See if the term exists in the chosen vocabulary - // and return the tid; otherwise, add a new record. - $possibilities = taxonomy_get_term_by_name($typed_term); - $typed_term_tid = NULL; // tid match, if any. - foreach ($possibilities as $possibility) { - if ($possibility->vid == $vid) { - $typed_term_tid = $possibility->tid; - } - } - - if (!$typed_term_tid) { - $edit = array( - 'vid' => $vid, - 'name' => $typed_term, - 'vocabulary_machine_name' => $vocabulary->machine_name, - ); - $term = (object)$edit; - $status = taxonomy_term_save($term); - $typed_term_tid = $term->tid; - } - - // Defend against duplicate, differently cased tags - if (!isset($inserted[$typed_term_tid])) { - db_insert('taxonomy_term_node') - ->fields(array( - 'nid' => $node->nid, - 'vid' => $node->vid, - 'tid' => $typed_term_tid - )) - ->execute(); - $inserted[$typed_term_tid] = TRUE; - } - } - } - } - - if (is_array($terms) && !empty($terms)) { - $query = db_insert('taxonomy_term_node') - ->fields(array('nid', 'vid', 'tid')); - - foreach ($terms as $term) { - if (is_array($term)) { - foreach ($term as $tid) { - if ($tid) { - $query->values(array( - 'nid' => $node->nid, - 'vid' => $node->vid, - 'tid' => $tid, - )); - } - } - } - elseif (is_object($term)) { - $query->values(array( - 'nid' => $node->nid, - 'vid' => $node->vid, - 'tid' => $term->tid, - )); - } - elseif ($term) { - $query->values(array( - 'nid' => $node->nid, - 'vid' => $node->vid, - 'tid' => $term, - )); - } - } - $query->execute(); - } -} - -/** - * Implement hook_node_type_insert(). - */ -function taxonomy_node_type_insert($info) { - drupal_static_reset('taxonomy_term_count_nodes'); -} - -/** - * Implement hook_node_type_update(). - */ -function taxonomy_node_type_update($info) { - if (!empty($info->old_type) && $info->type != $info->old_type) { - db_update('taxonomy_vocabulary_node_type') - ->fields(array( - 'type' => $info->type, - )) - ->condition('type', $info->old_type) - ->execute(); - } - drupal_static_reset('taxonomy_term_count_nodes'); -} - -/** - * Implement hook_node_type_delete(). - */ -function taxonomy_node_type_delete($info) { - db_delete('taxonomy_vocabulary_node_type') - ->condition('type', $info->type) - ->execute(); - - drupal_static_reset('taxonomy_term_count_nodes'); -} - -/** * Find all parents of a given term ID. */ function taxonomy_get_parents($tid, $key = 'tid') { @@ -1138,51 +750,6 @@ function taxonomy_get_synonym_root($syno } /** - * Count the number of published nodes classified by a term. - * - * @param $tid - * The term ID - * @param $type - * (Optional) The $node->type. If given, taxonomy_term_count_nodes only counts - * nodes of $type that are classified with the term $tid. - * - * @return - * An integer representing a number of nodes. - * Results are statically cached. - */ -function taxonomy_term_count_nodes($tid, $type = NULL) { - $count = &drupal_static(__FUNCTION__, array()); - // Reset the taxonomy tree when first called (or if reset). - if (empty($count)) { - drupal_static_reset('taxonomy_get_tree'); - } - // If $type is NULL, change it to 0 to allow it to be used as an array key - // for the static cache. - $type = empty($type) ? 0 : $type; - - if (!isset($count[$type][$tid])) { - $term = taxonomy_term_load($tid); - $tree = taxonomy_get_tree($term->vid, $tid, NULL); - $tids = array($tid); - foreach ($tree as $descendent) { - $tids[] = $descendent->tid; - } - - $query = db_select('taxonomy_term_node', 't'); - $query->addExpression('COUNT(DISTINCT(n.nid))', 'nid_count'); - $query->join('node', 'n', 't.vid = n.vid'); - $query->condition('t.tid', $tids, 'IN'); - $query->condition('n.status', 1); - if (!is_numeric($type)) { - $query->condition('n.type', $type); - } - $query->addTag('term_access'); - $count[$type][$tid] = $query->execute()->fetchField(); - } - return $count[$type][$tid]; -} - -/** * Try to map a string to an existing term, as for glossary use. * * Provides a case-insensitive and trimmed mapping, to maximize the @@ -1215,13 +782,6 @@ function taxonomy_get_term_by_name($name */ function taxonomy_vocabulary_load_multiple($vids = array(), $conditions = array()) { $vocabulary_cache = &drupal_static(__FUNCTION__, array()); - // Node type associations are not stored in the vocabulary table, so remove - // this from conditions into it's own variable. - if (isset($conditions['type'])) { - $type = $conditions['type']; - unset($conditions['type']); - } - $vocabularies = array(); // Create a new variable which is either a prepared version of the $vids @@ -1246,15 +806,12 @@ function taxonomy_vocabulary_load_multip } // Remove any loaded terms from the array if they don't match $conditions. - if ($conditions || isset($type)) { + if ($conditions) { foreach ($vocabularies as $vocabulary) { $vocabulary_values = (array) $vocabulary; if (array_diff_assoc($conditions, $vocabulary_values)) { unset($vocabularies[$vocabulary->vid]); } - if (isset($type) && !in_array($type, $vocabulary->nodes)) { - unset($vocabularies[$vocabulary->vid]); - } } } @@ -1262,20 +819,12 @@ 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') ->addTag('vocabulary_access'); - if (!empty($type)) { - $query->join('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'); @@ -1290,18 +839,7 @@ function taxonomy_vocabulary_load_multip $result = $query->execute(); $queried_vocabularies = array(); - $node_types = array(); foreach ($result as $record) { - // If no node types are associated with a vocabulary, the LEFT JOIN will - // return a NULL value for type. - if (isset($record->type)) { - $node_types[$record->vid][$record->type] = $record->type; - unset($record->type); - $record->nodes = $node_types[$record->vid]; - } - elseif (!isset($record->nodes)) { - $record->nodes = array(); - } $queried_vocabularies[$record->vid] = $record; } @@ -1363,13 +901,6 @@ function taxonomy_vocabulary_load($vid) function taxonomy_term_load_multiple($tids = array(), $conditions = array()) { $term_cache = &drupal_static(__FUNCTION__, array()); - // Node type associations are not stored in the taxonomy_term_data table, so - // remove this from conditions into it's own variable. - if (isset($conditions['type'])) { - $type = $conditions['type']; - unset($conditions['type']); - } - $terms = array(); // Create a new variable which is either a prepared version of the $tids @@ -1425,10 +956,6 @@ function taxonomy_term_load_multiple($ti $query->condition('t.tid', $tids, 'IN'); } - if (!empty($type)) { - $query->join('taxonomy_vocabulary_node_type', 'n', 't.vid = n.vid AND n.type = :type', array(':type' => $type)); - } - // If the conditions array is populated, add those to the query. if ($conditions) { // When name is passed as a condition use LIKE. @@ -1516,7 +1043,7 @@ function taxonomy_term_load($tid) { * @see taxonomy_form() * @see taxonomy_form_term() */ -function _taxonomy_term_select($title, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) { +function _taxonomy_term_select($title, $value, $vocabulary_id, $description, $blank, $exclude = array()) { $tree = taxonomy_get_tree($vocabulary_id); $options = array(); @@ -1538,7 +1065,6 @@ function _taxonomy_term_select($title, $ '#default_value' => $value, '#options' => $options, '#description' => $description, - '#multiple' => $multiple, '#size' => $multiple ? min(9, count($options)) : 0, '#weight' => -15, '#theme' => 'taxonomy_term_select', @@ -1556,186 +1082,6 @@ function theme_taxonomy_term_select($ele } /** - * Finds all nodes that match selected taxonomy conditions. - * - * @param $tids - * An array of term IDs to match. - * @param $operator - * How to interpret multiple IDs in the array. Can be "or" or "and". - * @param $depth - * How many levels deep to traverse the taxonomy tree. Can be a non-negative - * integer or "all". - * @param $pager - * Whether the nodes are to be used with a pager (the case on most Drupal - * pages) or not (in an XML feed, for example). - * @param $order - * The order clause for the query that retrieve the nodes. - * It is important to specifc the table alias (n). - * - * Example: - * array('table_alias.field_name' = 'DESC'); - * @return - * An array of node IDs. - */ -function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = array('n.sticky' => 'DESC', 'n.created' => 'DESC')) { - if (count($tids) <= 0) { - return array(); - } - // For each term ID, generate an array of descendant term IDs to the right depth. - $descendant_tids = array(); - if ($depth === 'all') { - $depth = NULL; - } - $terms = taxonomy_term_load_multiple($tids); - foreach ($terms as $term) { - $tree = taxonomy_get_tree($term->vid, $term->tid, $depth); - $descendant_tids[] = array_merge(array($term->tid), array_map('_taxonomy_get_tid_from_term', $tree)); - } - - $query = db_select('node', 'n'); - $query->addTag('node_access'); - $query->condition('n.status', 1); - - if ($operator == 'or') { - $args = call_user_func_array('array_merge', $descendant_tids); - $query->join('taxonomy_term_node', 'tn', 'n.vid = tn.vid'); - $query->condition('tn.tid', $args, 'IN'); - } - else { - foreach ($descendant_tids as $tids) { - $alias = $query->join('taxonomy_term_node', 'tn', 'n.vid = tn.vid'); - $query->condition($alias . '.tid', $tids, 'IN'); - } - } - - if ($pager) { - $count_query = clone $query; - $count_query->addExpression('COUNT(DISTINCT n.nid)'); - - $query = $query - ->extend('PagerDefault') - ->limit(variable_get('default_nodes_main', 10)); - $query->setCountQuery($count_query); - } - else { - $query->range(0, variable_get('feed_default_items', 10)); - } - - $query->distinct(TRUE); - $query->addField('n', 'nid'); - foreach ($order as $field => $direction) { - $query->orderBy($field, $direction); - // ORDER BY fields need to be loaded too, assume they are in the form table_alias.name - list($table_alias, $name) = explode('.', $field); - $query->addField($table_alias, $name); - } - - return $query->execute()->fetchCol(); -} - -/** - * Implement hook_node_load(). - */ -function taxonomy_node_load($nodes) { - // Get an array of tid, vid associations ordered by vocabulary and term - // weight. - $tids = taxonomy_get_tids_from_nodes($nodes); - - // Extract the tids only from this array. - $term_ids = array(); - foreach ($tids as $term) { - $term_ids[$term->tid] = $term->tid; - } - - // Load the full term objects for these tids. - $terms = taxonomy_term_load_multiple($term_ids); - foreach ($tids as $term) { - $nodes[$term->nid]->taxonomy[$term->tid] = $terms[$term->tid]; - } - foreach ($nodes as $node) { - if (!isset($nodes[$node->nid]->taxonomy)) { - $node->taxonomy = array(); - } - } -} - -/** - * Implement hook_node_insert(). - */ -function taxonomy_node_insert($node) { - if (!empty($node->taxonomy)) { - taxonomy_node_save($node, $node->taxonomy); - } -} - -/** - * Implement hook_node_update(). - */ -function taxonomy_node_update($node) { - if (!empty($node->taxonomy)) { - taxonomy_node_save($node, $node->taxonomy); - } -} - -/** - * Implement hook_node_delete(). - * - * Remove associations of a node to its terms. - */ -function taxonomy_node_delete($node) { - db_delete('taxonomy_term_node') - ->condition('nid', $node->nid) - ->execute(); - drupal_static_reset('taxonomy_term_count_nodes'); -} - -/** - * Implement hook_node_delete_revision(). - * - * Remove associations of a node to its terms. - */ -function taxonomy_node_delete_revision($node) { - db_delete('taxonomy_term_node') - ->condition('vid', $node->vid) - ->execute(); - drupal_static_reset('taxonomy_term_count_nodes'); -} - -/** - * Implement hook_node_validate(). - * - * Make sure incoming vids are free tagging enabled. - */ -function taxonomy_node_validate($node, $form) { - if (!empty($node->taxonomy)) { - $terms = $node->taxonomy; - if (!empty($terms['tags'])) { - foreach ($terms['tags'] as $vid => $vid_value) { - $vocabulary = taxonomy_vocabulary_load($vid); - if (empty($vocabulary->tags)) { - // see form_get_error $key = implode('][', $element['#parents']); - // on why this is the key - form_set_error("taxonomy][tags][$vid", t('The %name vocabulary can not be modified in this way.', array('%name' => $vocabulary->name))); - } - } - } - } -} - -/** - * Implement hook_node_update_index(). - */ -function taxonomy_node_update_index($node) { - $output = array(); - foreach ($node->taxonomy as $term) { - $output[] = $term->name; - } - if (count($output)) { - return '(' . implode(', ', $output) . ')'; - } -} - -/** * Implement hook_help(). */ function taxonomy_help($path, $arg) { Index: modules/taxonomy/taxonomy.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.pages.inc,v retrieving revision 1.32 diff -u -p -r1.32 taxonomy.pages.inc --- modules/taxonomy/taxonomy.pages.inc 18 Aug 2009 06:01:07 -0000 1.32 +++ modules/taxonomy/taxonomy.pages.inc 18 Aug 2009 20:11:41 -0000 @@ -40,8 +40,7 @@ function taxonomy_term_page($term) { '#suffix' => '', ); } - - if ($nids = taxonomy_select_nodes(array($term->tid), NULL, TRUE)) { + if ($nids = taxonomy_select_nodes($term)) { $nodes = node_load_multiple($nids); $build += node_build_multiple($nodes); $build['pager'] = array( @@ -71,7 +70,7 @@ function taxonomy_term_feed($term) { // Only display the description if we have a single term, to avoid clutter and confusion. // HTML will be removed from feed description, so no need to filter here. $channel['description'] = $term->description; - $nids = taxonomy_select_nodes(array($term->tid, NULL, NULL, FALSE)); + $nids = taxonomy_select_nodes(array($term->tid, FALSE)); node_feed($nids, $channel); } @@ -90,56 +89,6 @@ function taxonomy_term_edit($term) { /** * Helper function for autocompletion */ -function taxonomy_autocomplete_legacy($vid = 0, $tags_typed = '') { - // The user enters a comma-separated list of tags. We only autocomplete the last tag. - $tags_typed = drupal_explode_tags($tags_typed); - $tag_last = drupal_strtolower(array_pop($tags_typed)); - - $matches = array(); - if ($tag_last != '') { - $query = db_select('taxonomy_term_data', 't'); - $query->addTag('term_access'); - $query->leftJoin('taxonomy_term_synonym', 'ts', 't.tid = ts.tid'); - // Don't select already entered terms. - if (count($tags_typed)) { - $query->condition('t.name', $tags_typed, 'NOT IN'); - } - $tags_return = $query - ->fields('t', array('tid', 'name')) - ->condition('t.vid', $vid) - // Select rows that either match by term or synonym name. - ->condition(db_or() - ->where("LOWER(t.name) LIKE :last_string", array(':last_string' => '%' . $tag_last . '%')) - ->where("LOWER(ts.name) LIKE :last_string", array(':last_string' => '%' . $tag_last . '%')) - ) - ->range(0, 10) - ->execute() - ->fetchAllKeyed(); - - $prefix = count($tags_typed) ? implode(', ', $tags_typed) . ', ' : ''; - - // We use two arrays to make sure synonym suggestions appear last. - $term_matches = $synonym_matches = array(); - foreach ($tags_return as $tid => $name) { - $n = $name; - // Commas and quotes in terms are special cases, so encode 'em. - if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) { - $n = '"' . str_replace('"', '""', $name) . '"'; - } - // Inform the user his query matched a synonym rather than a term. - if (strpos(drupal_strtolower($name), $tag_last) === FALSE) { - $name = t('Did you mean %suggestion', array('%suggestion' => $name)); - $synonym_matches[$prefix . $n] = filter_xss($name); - } - } - } - - drupal_json(array_merge($term_matches, $synonym_matches)); -} - -/** - * Helper function for autocompletion - */ function taxonomy_autocomplete($field_name, $bundle, $tags_typed = '') { $instance = field_info_instance($field_name, $bundle); $field = field_info_field($field_name);