Index: modules/taxonomy/taxonomy.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v retrieving revision 1.9 diff -u -p -r1.9 taxonomy.admin.inc --- modules/taxonomy/taxonomy.admin.inc 20 Nov 2007 15:31:34 -0000 1.9 +++ modules/taxonomy/taxonomy.admin.inc 21 Nov 2007 09:24:08 -0000 @@ -11,26 +11,62 @@ */ function taxonomy_overview_vocabularies() { $vocabularies = taxonomy_get_vocabularies(); - $rows = array(); foreach ($vocabularies as $vocabulary) { $types = array(); foreach ($vocabulary->nodes as $type) { $node_type = node_get_types('name', $type); $types[] = $node_type ? $node_type : $type; } - $rows[] = array('name' => check_plain($vocabulary->name), - 'type' => implode(', ', $types), - 'edit' => l(t('edit vocabulary'), "admin/content/taxonomy/edit/vocabulary/$vocabulary->vid"), - 'list' => l(t('list terms'), "admin/content/taxonomy/$vocabulary->vid"), - 'add' => l(t('add terms'), "admin/content/taxonomy/$vocabulary->vid/add/term") - ); + $form[$vocabulary->vid]['#vocabulary'] = (array)$vocabulary; + $form[$vocabulary->vid]['name'] = array('#value' => check_plain($vocabulary->name)); + $form[$vocabulary->vid]['types'] = array('#value' => implode(', ', $types)); + $form[$vocabulary->vid]['weight'] = array('#type' => 'weight', '#delta' => 10, '#default_value' => $vocabulary->weight); + $form[$vocabulary->vid]['edit'] = array('#value' => l(t('edit vocabulary'), "admin/content/taxonomy/edit/vocabulary/$vocabulary->vid")); + $form[$vocabulary->vid]['list'] = array('#value' => l(t('list terms'), "admin/content/taxonomy/$vocabulary->vid")); + $form[$vocabulary->vid]['add'] = array('#value' => l(t('add terms'), "admin/content/taxonomy/$vocabulary->vid/add/term")); + } + + $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); + return $form; +} + +/** + * Submit handler for vocabularies overview. Updates changed vocabulary weights. + */ +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']; + taxonomy_save_vocabulary($form[$vid]['#vocabulary']); + } + } +} + +function theme_taxonomy_overview_vocabularies($form) { + drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'vocabulary-weight'); + + $rows = array(); + foreach (element_children($form) as $key) { + if (isset($form[$key]['name'])) { + $vocabulary = &$form[$key]; + $vocabulary['weight']['#attributes']['class'] = 'vocabulary-weight'; + + $row = array(); + $row[] = drupal_render($vocabulary['name']); + $row[] = drupal_render($vocabulary['types']); + $row[] = drupal_render($vocabulary['weight']); + $row[] = drupal_render($vocabulary['edit']); + $row[] = drupal_render($vocabulary['list']); + $row[] = drupal_render($vocabulary['add']); + $rows[] = array('data' => $row, 'class' => 'draggable'); + } } if (empty($rows)) { - $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '5')); + $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '6')); } - $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '3')); + $header = array(t('Name'), t('Type'), t('Weight'), array('data' => t('Operations'), 'colspan' => '3')); - return theme('table', $header, $rows, array('id' => 'taxonomy')); + return theme('table', $header, $rows, array('id' => 'taxonomy')) . drupal_render($form); } /** @@ -114,7 +150,7 @@ function taxonomy_form_vocabulary(&$form // Set the hierarchy to "multiple parents" by default. This simplifies the // vocabulary form and standardizes the term form. $form['hierarchy'] = array('#type' => 'value', - '#value' => '2', + '#value' => '0', ); // Enable "related terms" by default. $form['relations'] = array('#type' => 'value', @@ -166,9 +202,6 @@ function taxonomy_admin_vocabulary_edit( * Page to edit a vocabulary term. */ function taxonomy_admin_term_edit($tid) { - if ((isset($_POST['op']) && $_POST['op'] == t('Delete')) || isset($_POST['confirm'])) { - return drupal_get_form('taxonomy_term_confirm_delete', $tid); - } if ($term = (array)taxonomy_get_term($tid)) { return drupal_get_form('taxonomy_form_term', taxonomy_vocabulary_load($term['vid']), $term); } @@ -179,55 +212,295 @@ function taxonomy_admin_term_edit($tid) * Display a tree of all the terms in a vocabulary, with options to edit * each one. */ -function taxonomy_overview_terms($vocabulary) { - $destination = drupal_get_destination(); - - $header = array(t('Name'), t('Operations')); - +function taxonomy_overview_terms(&$form_state, $vocabulary) { drupal_set_title(t('Terms in %vocabulary', array('%vocabulary' => $vocabulary->name))); - $start_from = isset($_GET['page']) ? $_GET['page'] : 0; - $total_entries = 0; // total count for pager - $page_increment = 25; // number of tids per page - $displayed_count = 0; // number of tids shown + $form = array( + '#vocabulary' => (array)$vocabulary, + '#tree' => TRUE, + '#parent_fields' => FALSE, + ); + + $page = isset($_GET['page']) ? $_GET['page'] : 0; + $page_increment = 100; // Number of terms per page. + $page_entries = 0; // Elements shown on this page. + $before_entries = 0; // Elements at the root level before this page. + $after_entries = 0; // Elements at the root level after this page. + $root_entries = 0; // Elements at the root level on this page. + + // Terms from previous and next pages are shown if the term tree would have + // been cut in the middle. Keep track of how many extra terms we show on each + // page of terms. + $back_peddle = NULL; + $forward_peddle = 0; + // 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. $results = pager_query(db_rewrite_sql('SELECT t.*, h.parent FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d ORDER BY weight, name', 't', 'tid'), $page_increment, 0, NULL, $vocabulary->vid); + $total_entries = db_query(db_rewrite_sql('SELECT count(*) FROM {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE t.vid = %d'), $page_increment, 0, NULL, $vocabulary->vid); while ($term = db_fetch_object($results)) { - $rows[] = array( - l($term->name, "taxonomy/term/$term->tid"), - l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array('query' => $destination)), - ); + $key = 'tid:'. $term->tid .':0'; + $form[$key]['#term'] = (array)$term; + $form[$key]['view'] = array('#value' => l($term->name, "taxonomy/term/$term->tid")); + $form[$key]['edit'] = array('#value' => l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array('query' => drupal_get_destination()))); + $page_entries++; } } + // Case for restricted vocabulary. else { + $term_deltas = array(); $tree = taxonomy_get_tree($vocabulary->vid); - foreach ($tree as $term) { - $total_entries++; // we're counting all-totals, not displayed - if (($start_from && ($start_from * $page_increment) >= $total_entries) || ($displayed_count == $page_increment)) { + $term = current($tree); + do { + // 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; } - $rows[] = array(str_repeat('--', $term->depth) .' '. l($term->name, "taxonomy/term/$term->tid"), l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array('query' => $destination))); - $displayed_count++; // we're counting tids displayed + 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++; + } + + // Save the term for the current page so we don't have to load it a second time. + $form[$key]['#term'] = (array)$term; + $form[$key]['#term']['parent'] = $term->parents[0]; + unset($form[$key]['#term']['parents']); + + // Build the form for this page. + $form[$key]['view'] = array('#value' => l($term->name, "taxonomy/term/$term->tid")); + if ($vocabulary->hierarchy < 2) { + $form['#parent_fields'] = TRUE; + $form[$key]['tid'] = array( + '#type' => 'hidden', + '#value' => $term->tid + ); + $form[$key]['parent'] = array( + '#type' => 'hidden', + '#default_value' => $term->parents[0], + ); + } + $form[$key]['edit'] = array('#value' => l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array('query' => drupal_get_destination()))); + + } while ($term = next($tree)); + } + + $form['#total_entries'] = $before_entries + $page_entries + $after_entries; + $form['#page_increment'] = $page_increment; + $form['#page_entries'] = $page_entries; + $form['#back_peddle'] = $back_peddle; + $form['#forward_peddle'] = $forward_peddle; + + if (!$vocabulary->tags && $vocabulary->hierarchy < 2) { + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Submit') + ); + $form['destination'] = array( + '#type' => 'hidden', + '#value' => $_GET['q'] . (isset($_GET['page']) ? '?page='. $_GET['page'] : '') + ); + } + + return $form; +} + +/** + * Submit handler for terms overview form. Rather than using a textfield or + * weight field, this form depends entirely upon the order of form elements on + * the page to determine new weights. + * + * Because there might be hundreds or thousands of taxonomy terms that need to + * be ordered, terms are weighted from 0 to the number of terms in the + * vocabulary, rather than the standard -10 to 10 scale. Numbers are sorted + * lowest to highest, but are not necessarily sequential. Numbers may be skipped + * when a term has children so that reordering is minimal when a child is + * added or removed from a term. + */ +function taxonomy_overview_terms_submit($form, &$form_state) { + $order = array_flip(array_keys($form['#post'])); // Get the $_POST order. + $form_state['values'] = array_merge($order, $form_state['values']); // Update our original form with the new order. + $vocabulary = $form['#vocabulary']; + $hierarchy = 0; // Update the current hierarchy type as we go. + + $changed_terms = array(); + $tree = taxonomy_get_tree($vocabulary['vid']); + + if (empty($tree)) { + return; + } + + // Build a list of all terms that need to be updated on previous pages. + $weight = 0; + $term = (array)$tree[0]; + while ($term['tid'] != $form['#first_tid']) { + if ($term['parents'][0] == 0 && $term['weight'] != $weight) { + $term['parent'] = $term['parents'][0]; + $term['weight'] = $weight; + $changed_terms[$term->tid] = $term; } + $weight++; + $hierarchy = $term['parent'] != 0 ? 1 : $hierarchy; + $term = (array)$tree[$weight]; + } - if (empty($rows)) { - $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '2')); + // Renumber the current page weights and assign any new parents. + $level_weights = array(); + foreach ($form_state['values'] as $tid => $values) { + if (isset($form[$tid]['#term'])) { + $term = $form[$tid]['#term']; + // Give terms at the root level a weight in sequence with terms on previous pages. + if ($values['parent'] == 0 && $term['weight'] != $weight) { + $term['weight'] = $weight; + $changed_terms[$term['tid']] = $term; + } + // Terms not at the root level can safely start from 0 because they're all on this page. + elseif ($values['parent'] > 0) { + $level_weights[$term['parent']] = isset($level_weights[$term['parent']]) ? $level_weights[$term['parent']] + 1 : 0; + if ($level_weights[$term['parent']] != $term['weight']) { + $term['weight'] = $level_weights[$term['parent']]; + $changed_terms[$term['tid']] = $term; + } + } + // Update any changed parents. + if ($values['parent'] != $term['parent']) { + $term['parent'] = $values['parent']; + $changed_terms[$term['tid']] = $term; + } } + $hierarchy = $term['parent'] != 0 ? 1 : $hierarchy; + $weight++; + } - $GLOBALS['pager_page_array'][] = $start_from; // FIXME - $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME + // Build a list of all terms that need to be updated on following pages. + for ($weight; $weight < count($tree); $weight++) { + $term = $tree[$weight]; + if ($term->parents[0] == 0 && $term->weight != $weight) { + $changed_terms[$term->tid] = (array)$term; + $changed_terms[$term->tid]['parent'] = $term->parents[0]; + $changed_terms[$term->tid]['weight'] = $weight; + unset($changed_terms[$term->tid]['parents']); + } + $hierarchy = $term['parent'] != 0 ? 1 : $hierarchy; } - $output = theme('table', $header, $rows, array('id' => 'taxonomy')); - if ($vocabulary->tags || $total_entries >= $page_increment) { - $output .= theme('pager', NULL, $page_increment); + // Save all updated terms. + foreach ($changed_terms as $term) { + taxonomy_save_term($term); } - return $output; + // Update the vocabulary hierarchy to flat or single hierarchy. + if ($vocabulary['hierarchy'] != $hierarchy) { + $vocabulary['hierarchy'] = $hierarchy; + taxonomy_save_vocabulary($vocabulary); + } } +function theme_taxonomy_overview_terms($form) { + $page = isset($_GET['page']) ? $_GET['page'] : 0; + $total_entries = $form['#total_entries']; + $page_increment = $form['#page_increment']; + $page_entries = $form['#page_entries']; + $back_peddle = $form['#back_peddle']; + $forward_peddle = $form['#forward_peddle']; + + if ($form['#parent_fields']) { + drupal_add_tabledrag('taxonomy', 'match', 'parent', 'term-parent', 'term-parent', 'term-id', FALSE); + drupal_add_js(drupal_get_path('module', 'taxonomy') .'/taxonomy.js'); + drupal_add_js(array('taxonomy' => array('backPeddle' => $back_peddle, 'forwardPeddle' => $forward_peddle)), 'setting'); + drupal_add_css(drupal_get_path('module', 'taxonomy') .'/taxonomy.css'); + } + + $rows = array(); + foreach (element_children($form) as $tid) { + if (isset($form[$tid]['#term'])) { + $term = &$form[$tid]; + + $row = array(); + $row[] = ($form['#vocabulary']['hierarchy'] > 0 ? theme('indentation', $term['#term']['depth']) : '') . drupal_render($term['view']); + if ($form['#parent_fields']) { + $term['tid']['#attributes']['class'] = 'term-id'; + $term['parent']['#attributes']['class'] = 'term-parent'; + $row[0] .= drupal_render($term['parent']) . drupal_render($term['tid']); + } + $row[] = drupal_render($term['edit']); + + $row = array('data' => $row); + if (isset($form['#parent_fields'])) { + $row['class'] = 'draggable'; + } + + // Add classes that mark which terms belong to previous and next pages. + $row_position = count($rows); + if ($row_position < $back_peddle || $row_position >= $page_entries - $forward_peddle) { + $row['class'] = isset($row['class']) ? $row['class'] . ' taxonomy-term-preview' : $row['class']; + } + if ($row_position == $back_peddle - 1 || $row_position == $page_entries - $forward_peddle - 1) { + $row['class'] = isset($row['class']) ? $row['class'] . ' taxonomy-term-divider-top' : $row['class']; + } + elseif ($row_position == $back_peddle || $row_position == $page_entries - $forward_peddle) { + $row['class'] = isset($row['class']) ? $row['class'] . ' taxonomy-term-divider-bottom' : $row['class']; + } + $rows[] = $row; + } + } + + if (empty($rows)) { + $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '2')); + } + + $GLOBALS['pager_page_array'][] = $page; // FIXME + $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME + + $header = array(t('Name'), t('Operations')); + + $output = theme('table', $header, $rows, array('id' => 'taxonomy')); + $output .= drupal_render($form); + $output .= theme('pager', NULL, $page_increment); + + return $output; +} /** * Menu callback; return the edit form for a new term after setting the title. @@ -250,6 +523,20 @@ function taxonomy_form_term(&$form_state 'tid' => NULL, 'weight' => 0, ); + + $parent = array_keys(taxonomy_get_parents($edit['tid'])); + $form['#term'] = $edit; + $form['#term']['parent'] = $parent; + $form['#vocabulary'] = (array)$vocabulary; + + // Check for confirmation forms. + if (isset($form_state['confirm_delete'])) { + return array_merge($form, taxonomy_term_confirm_delete($form_state, $edit['tid'])); + } + elseif (isset($form_state['confirm_parents'])) { + return array_merge($form, taxonomy_term_confirm_parents($form_state, $vocabulary)); + } + $form['identification'] = array( '#type' => 'fieldset', '#title' => t('Identification'), @@ -272,10 +559,9 @@ function taxonomy_form_term(&$form_state '#type' => 'fieldset', '#title' => 'Advanced options', '#collapsible' => TRUE, - '#collapsed' => TRUE, + '#collapsed' => $vocabulary->hierarchy > 1 ? FALSE : TRUE, ); - $parent = array_keys(taxonomy_get_parents($edit['tid'])); $children = taxonomy_get_tree($vocabulary->vid, $edit['tid']); // A term can't be the child of itself, nor of its children. @@ -284,7 +570,9 @@ function taxonomy_form_term(&$form_state } $exclude[] = $edit['tid']; - $form['advanced']['parent'] = _taxonomy_term_select(t('Parents'), 'parent', $parent, $vocabulary->vid, t('Parent terms') .'.', 1, '<'. t('root') .'>', $exclude); + if (!$vocabulary->tags) { + $form['advanced']['parent'] = _taxonomy_term_select(t('Parents'), 'parent', $parent, $vocabulary->vid, t('Parent terms') .'.', 1, '<'. t('root') .'>', $exclude); + } $form['advanced']['synonyms'] = array( '#type' => 'textarea', @@ -292,10 +580,12 @@ function taxonomy_form_term(&$form_state '#default_value' => implode("\n", taxonomy_get_synonyms($edit['tid'])), '#description' => t('Synonyms of this term, one synonym per line.')); $form['advanced']['weight'] = array( - '#type' => 'weight', + '#type' => 'textfield', '#title' => t('Weight'), + '#size' => 6, '#default_value' => $edit['weight'], - '#description' => t('Vocabularies are displayed in ascending order by weight.')); + '#description' => t('Terms are displayed in ascending order by weight.'), + '#required' => TRUE); $form['vid'] = array( '#type' => 'value', '#value' => $vocabulary->vid); @@ -318,10 +608,33 @@ function taxonomy_form_term(&$form_state return $form; } +function taxonomy_form_term_validate($form, &$form_state) { + if (isset($form_state['values']['weight']) && !is_numeric($form_state['values']['weight'])) { + form_set_error('weight', t('Weight value must be numeric.')); + } +} + /** * Accept the form submission for a taxonomy term and save the result. */ function taxonomy_form_term_submit($form, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Delete')) { + // Execute the term deletion. + if ($form_state['values']['delete'] === TRUE) { + return taxonomy_term_confirm_delete_submit($form, $form_state); + } + // Rebuild the form to confirm term deletion. + $form_state['rebuild'] = TRUE; + $form_state['confirm_delete'] = TRUE; + return; + } + // Rebuld the form to confirm enabling multiple parents. + elseif ($form_state['clicked_button']['#value'] == t('Save') && !$form['#vocabulary']['tags'] && count($form_state['values']['parent']) > 1 && $form['#vocabulary']['hierarchy'] < 2) { + $form_state['rebuild'] = TRUE; + $form_state['confirm_parents'] = TRUE; + return; + } + switch (taxonomy_save_term($form_state['values'])) { case SAVED_NEW: drupal_set_message(t('Created new term %term.', array('%term' => $form_state['values']['name']))); @@ -333,12 +646,54 @@ function taxonomy_form_term_submit($form break; } + if (!$form['#vocabulary']['tags']) { + $current_parent_count = count($form_state['values']['parent']); + $previous_parent_count = count($form['#term']['parent']); + // Root doesn't count if it's the only parent. + if ($current_parent_count == 1 && isset($form_state['values']['parent'][''])) { + $current_parent_count = 0; + $form_state['values']['parent'] = array(); + } + + // If the number of parents has been reduced to one or none, do a check on the + // parents of every term in the vocabulary value. + if ($current_parent_count < $previous_parent_count && $current_parent_count < 2) { + taxonomy_check_vocabulary_hierarchy($form['#vocabulary'], $form_state['values']); + } + // If we've increased the number of parents and this is a single or flat + // hierarchy, update the vocabulary immediately. + elseif ($current_parent_count > $previous_parent_count && $form['#vocabulary']['hierarchy'] < 2) { + $form['#vocabulary']['hierarchy'] = $current_parent_count == 1 ? 1 : 2; + taxonomy_save_vocabulary($form['#vocabulary']); + } + } + $form_state['tid'] = $form_state['values']['tid']; $form_state['redirect'] = 'admin/content/taxonomy'; return; } /** + * Form builder for the confirmation of multiple term parents. + * + * @ingroup forms + * @see taxonomy_form_term(). + */ +function taxonomy_term_confirm_parents(&$form_state, $vocabulary) { + $form = array(); + foreach (element_children($form_state['values']) as $key) { + $form[$key] = array( + '#type' => 'value', + '#value' => $form_state['values'][$key], + ); + } + $question = t('Set multiple term parents?'); + $description = '
'. t("Adding multiple parents to a term will cause the %vocabulary vocabulary to look for multiple parents on every term. Because multiple parents are not supported when using the drag and drop outline interface, drag and drop will be disabled if you enable this option. If you choose to have multiple parents, you will only be able to set parents by using the term edit form.", array('%vocabulary' => $vocabulary->name)) .'
'; + $description .= ''. t("You may re-enable the drag and drop interface at any time by reducing multiple parents to a single parent for the terms in this vocabulary.") .'
'; + return confirm_form($form, $question, drupal_get_destination(), $description, t('Set multiple parents')); +} + +/** * Form builder for the term delete form. * * @ingroup forms @@ -350,6 +705,7 @@ function taxonomy_term_confirm_delete(&$ $form['type'] = array('#type' => 'value', '#value' => 'term'); $form['name'] = array('#type' => 'value', '#value' => $term->name); $form['tid'] = array('#type' => 'value', '#value' => $tid); + $form['delete'] = array('#type' => 'value', '#value' => TRUE); return confirm_form($form, t('Are you sure you want to delete the term %title?', array('%title' => $term->name)), @@ -361,6 +717,7 @@ function taxonomy_term_confirm_delete(&$ function taxonomy_term_confirm_delete_submit($form, &$form_state) { taxonomy_del_term($form_state['values']['tid']); + taxonomy_check_vocabulary_hierarchy($form['#vocabulary'], $form_state['values']); drupal_set_message(t('Deleted term %name.', array('%name' => $form_state['values']['name']))); watchdog('taxonomy', 'Deleted term %name.', array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE); $form_state['redirect'] = 'admin/content/taxonomy'; Index: modules/taxonomy/taxonomy.css =================================================================== RCS file: modules/taxonomy/taxonomy.css diff -N modules/taxonomy/taxonomy.css --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/taxonomy/taxonomy.css 21 Nov 2007 09:24:08 -0000 @@ -0,0 +1,9 @@ +tr.taxonomy-term-preview { + background-color: #EEE; +} +tr.taxonomy-term-divider-top { + border-bottom: 1px dotted #CCC; +} +tr.taxonomy-term-divider-bottom { + border-top: none; +} \ No newline at end of file Index: modules/taxonomy/taxonomy.js =================================================================== RCS file: modules/taxonomy/taxonomy.js diff -N modules/taxonomy/taxonomy.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/taxonomy/taxonomy.js 21 Nov 2007 09:24:08 -0000 @@ -0,0 +1,36 @@ +// $Id $ + +/** + * Move a block in the blocks table from one region to another via select list. + * + * This behavior is dependent on the tableDrag behavior, since it uses the + * objects initialized in that behavior to update the row. + */ +Drupal.behaviors.termDrag = function(context) { + var table = $('#taxonomy', context); + var tableDrag = Drupal.tableDrag.taxonomy; // Get the blocks tableDrag object. + var rows = $('tr', table).size(); + + // When a row is swapped, keep previous and next page classes set. + tableDrag.row.prototype.onSwap = function(swappedRow) { + $('tr.taxonomy-term-preview', table).removeClass('taxonomy-term-preview'); + $('tr.taxonomy-term-divider-top', table).removeClass('taxonomy-term-divider-top'); + $('tr.taxonomy-term-divider-bottom', table).removeClass('taxonomy-term-divider-bottom'); + + if (Drupal.settings.taxonomy.backPeddle) { + for (var n = 0; n < Drupal.settings.taxonomy.backPeddle; n++) { + $(table[0].tBodies[0].rows[n]).addClass('taxonomy-term-preview'); + } + $(table[0].tBodies[0].rows[Drupal.settings.taxonomy.backPeddle - 1]).addClass('taxonomy-term-divider-top'); + $(table[0].tBodies[0].rows[Drupal.settings.taxonomy.backPeddle]).addClass('taxonomy-term-divider-bottom'); + } + + if (Drupal.settings.taxonomy.forwardPeddle) { + for (var n = rows - Drupal.settings.taxonomy.forwardPeddle - 1; n < rows - 1; n++) { + $(table[0].tBodies[0].rows[n]).addClass('taxonomy-term-preview'); + } + $(table[0].tBodies[0].rows[rows - Drupal.settings.taxonomy.forwardPeddle - 2]).addClass('taxonomy-term-divider-top'); + $(table[0].tBodies[0].rows[rows - Drupal.settings.taxonomy.forwardPeddle - 1]).addClass('taxonomy-term-divider-bottom'); + } + }; +}; Index: modules/taxonomy/taxonomy.module =================================================================== RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v retrieving revision 1.395 diff -u -p -r1.395 taxonomy.module --- modules/taxonomy/taxonomy.module 20 Nov 2007 15:53:59 -0000 1.395 +++ modules/taxonomy/taxonomy.module 21 Nov 2007 09:24:09 -0000 @@ -24,6 +24,12 @@ function taxonomy_theme() { 'taxonomy_term_page' => array( 'arguments' => array('tids' => array(), 'result' => NULL), ), + 'taxonomy_overview_vocabularies' => array( + 'arguments' => array('form' => array()), + ), + 'taxonomy_overview_terms' => array( + 'arguments' => array('form' => array()), + ), ); } @@ -106,7 +112,8 @@ function taxonomy_menu() { $items['admin/content/taxonomy'] = array( 'title' => 'Taxonomy', 'description' => 'Manage tagging, categorization, and classification of your content.', - 'page callback' => 'taxonomy_overview_vocabularies', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('taxonomy_overview_vocabularies'), 'access arguments' => array('administer taxonomy'), 'file' => 'taxonomy.admin.inc', ); @@ -158,8 +165,8 @@ function taxonomy_menu() { ); $items['admin/content/taxonomy/%taxonomy_vocabulary'] = array( 'title' => 'List terms', - 'page callback' => 'taxonomy_overview_terms', - 'page arguments' => array(3), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('taxonomy_overview_terms', 3), 'access arguments' => array('administer taxonomy'), 'type' => MENU_CALLBACK, 'file' => 'taxonomy.admin.inc', @@ -242,6 +249,47 @@ function taxonomy_del_vocabulary($vid) { } /** + * Dynamicly check and update the hierarachy flag of a vocabulary. + * + * Checks the current parents of all terms in a vocabulary and updates the + * vocabularies hierarchy setting to the lowest possible level. A hierarchy with + * no parents in any of its terms will be given a hierarchy of 0. If terms + * contain at most a single parent, the vocabulary will be given a hierarchy of + * 1. If any term contain multiple parents, the vocabulary will be given a + * hieararchy of 2. + * + * @param $vocabulary + * An array of the vocabulary structure. + * @param $changed_term + * An array of the term structure that was updated. + */ +function taxonomy_check_vocabulary_hierarchy($vocabulary, $changed_term) { + $tree = taxonomy_get_tree($vocabulary['vid']); + $hierarchy = 0; + foreach ($tree as $term) { + // Update the changed term with the new parent value before comparision. + if ($term->tid == $changed_term['tid']) { + $term = (object)$changed_term; + $term->parents = $term->parent; + } + // Check this term's parent count. + if (count($term->parents) > 1) { + $hierarchy = 2; + break; + } + elseif (count($term->parents) == 1 && 0 != array_shift($term->parents)) { + $hierarchy = 1; + } + } + if ($hierarchy != $vocabulary['hierarchy']) { + $vocabulary['hierarchy'] = $hierarchy; + taxonomy_save_vocabulary($vocabulary); + } + + return $hierarchy; +} + +/** * Helper function for taxonomy_form_term_submit(). * * @param $form_state['values'] @@ -1177,6 +1225,19 @@ function taxonomy_help($path, $arg) { return $output; case 'admin/content/taxonomy': return ''. t("The taxonomy module allows you to categorize your content using both tags and administrator defined terms. It is a flexible tool for classifying content with many advanced features. To begin, create a 'Vocabulary' to hold one set of terms or tags. You can create one free-tagging vocabulary for everything, or seperate controlled vocabularies to define the various properties of your content, for example 'Countries' or 'Colours'.") .'
'; + case 'admin/content/taxonomy/%': + $vocabulary = taxonomy_vocabulary_load($arg[3]); + if ($vocabulary->tags) { + return ''. t('%capital_name is a free-tagging vocabulary. To change the name or description of a term, click the edit link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) .'
'; + } + switch ($vocabulary->hierarchy) { + case 0: + return ''. t('%capital_name is a flat vocabulary. You may organize the terms in the %name vocabulary by using the handles on the left side of the table. To change the name or description of a term, click the edit link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) .'
'; + case 1: + return ''. t('%capital_name is a single hierarchy vocabulary. You may organize the terms in the %name vocabulary by using the handles on the left side of the table. To change the name or description of a term, click the edit link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) .'
'; + case 2: + return ''. t('%capital_name is a multiple hierarchy vocabulary. To change the name or description of a term, click the edit link next to the term. Drag and drop of multiple hierarchies is not supported, but you can re-enable drag and drop support by editing each term to include only a single parent.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) .'
'; + } case 'admin/content/taxonomy/add/vocabulary': return ''. t('Define how your vocabulary will be presented to administrators and users, and which content types to categorize with it. Tags allows users to create terms when submitting posts by typing a comma separated list. Otherwise terms are chosen from a select list and can only be created by users with the "administer taxonomy" permission.') .'
'; } Index: themes/garland/fix-ie.css =================================================================== RCS file: /cvs/drupal/drupal/themes/garland/fix-ie.css,v retrieving revision 1.7 diff -u -p -r1.7 fix-ie.css --- themes/garland/fix-ie.css 9 Nov 2007 22:14:41 -0000 1.7 +++ themes/garland/fix-ie.css 21 Nov 2007 09:24:09 -0000 @@ -59,6 +59,10 @@ tr.menu-disabled { height: 1em; } +tr.taxonomy-term-preview { + filter: alpha(opacity=50); +} + #attach-hide label, #uploadprogress div.message { /* Fading elements in IE causes the text to bleed unless they have a background. */ background-color: #ffffff; Index: themes/garland/style.css =================================================================== RCS file: /cvs/drupal/drupal/themes/garland/style.css,v retrieving revision 1.29 diff -u -p -r1.29 style.css --- themes/garland/style.css 14 Nov 2007 09:49:30 -0000 1.29 +++ themes/garland/style.css 21 Nov 2007 09:24:09 -0000 @@ -976,6 +976,18 @@ tr.selected td a:link, tr.selected td a: color: #d3e7f4; } +tr.taxonomy-term-preview { + opacity: 0.5; +} + +tr.taxonomy-term-divider-top { + border-bottom: 1px dotted #CCC; +} + +tr.taxonomy-term-divider-bottom { + border-top: none; +} + /** * CSS support */