Code coverage for /20080809/modules/taxonomy/taxonomy.admin.inc

Line #Times calledCode
1
<?php
2
// $Id: taxonomy.admin.inc,v 1.27 2008/07/16 21:59:28 dries Exp $
3
4
/**
5
 * @file
6
 * Administrative page callbacks for the taxonomy module.
7
 */
8
9
/**
10
 * Form builder to list and manage vocabularies.
11
 *
12
 * @ingroup forms
13
 * @see taxonomy_overview_vocabularies_submit()
14
 * @see theme_taxonomy_overview_vocabularies()
15
 */
1615
function taxonomy_overview_vocabularies() {
173
  $vocabularies = taxonomy_get_vocabularies();
183
  $form = array('#tree' => TRUE);
193
  foreach ($vocabularies as $vocabulary) {
203
    $types = array();
213
    foreach ($vocabulary->nodes as $type) {
223
      $node_type = node_get_types('name', $type);
233
      $types[] = $node_type ? check_plain($node_type) :
check_plain($type);
243
    }
253
    $form[$vocabulary->vid]['#vocabulary'] = (array)$vocabulary;
263
    $form[$vocabulary->vid]['name'] = array('#markup' =>
check_plain($vocabulary->name));
273
    $form[$vocabulary->vid]['types'] = array('#markup' => implode(', ',
$types));
283
    $form[$vocabulary->vid]['weight'] = array('#type' => 'weight', '#delta'
=> 10, '#default_value' => $vocabulary->weight);
293
    $form[$vocabulary->vid]['edit'] = array('#markup' => l(t('edit
vocabulary'), "admin/content/taxonomy/edit/vocabulary/$vocabulary->vid"));
303
    $form[$vocabulary->vid]['list'] = array('#markup' => l(t('list terms'),
"admin/content/taxonomy/$vocabulary->vid"));
313
    $form[$vocabulary->vid]['add'] = array('#markup' => l(t('add terms'),
"admin/content/taxonomy/$vocabulary->vid/add/term"));
323
  }
33
34
  // Only make this form include a submit button and weight if more than
one
35
  // vocabulary exists.
363
  if (count($vocabularies) > 1) {
373
    $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
383
  }
390
  elseif (isset($vocabulary)) {
400
    unset($form[$vocabulary->vid]['weight']);
410
  }
423
  return $form;
430
}
44
45
/**
46
 * Submit handler for vocabularies overview. Updates changed vocabulary
weights.
47
 *
48
 * @see taxonomy_overview_vocabularies()
49
 */
5015
function taxonomy_overview_vocabularies_submit($form, &$form_state) {
510
  foreach ($form_state['values'] as $vid => $vocabulary) {
520
    if (is_numeric($vid) && $form[$vid]['#vocabulary']['weight'] !=
$form_state['values'][$vid]['weight']) {
530
      $form[$vid]['#vocabulary']['weight'] =
$form_state['values'][$vid]['weight'];
540
      taxonomy_save_vocabulary($form[$vid]['#vocabulary']);
550
    }
560
  }
570
}
58
59
/**
60
 * Theme the vocabulary overview as a sortable list of vocabularies.
61
 *
62
 * @ingroup themeable
63
 * @see taxonomy_overview_vocabularies()
64
 */
6515
function theme_taxonomy_overview_vocabularies($form) {
663
  $rows = array();
673
  foreach (element_children($form) as $key) {
683
    if (isset($form[$key]['name'])) {
693
      $vocabulary = &$form[$key];
70
713
      $row = array();
723
      $row[] = drupal_render($vocabulary['name']);
733
      $row[] = drupal_render($vocabulary['types']);
743
      if (isset($vocabulary['weight'])) {
753
        $vocabulary['weight']['#attributes']['class'] =
'vocabulary-weight';
763
        $row[] = drupal_render($vocabulary['weight']);
773
      }
783
      $row[] = drupal_render($vocabulary['edit']);
793
      $row[] = drupal_render($vocabulary['list']);
803
      $row[] = drupal_render($vocabulary['add']);
813
      $rows[] = array('data' => $row, 'class' => 'draggable');
823
    }
833
  }
843
  if (empty($rows)) {
850
    $rows[] = array(array('data' => t('No vocabularies available.'),
'colspan' => '5'));
860
  }
87
883
  $header = array(t('Vocabulary name'), t('Content types'));
893
  if (isset($form['submit'])) {
903
    $header[] = t('Weight');
913
    drupal_add_tabledrag('taxonomy', 'order', 'sibling',
'vocabulary-weight');
923
  }
933
  $header[] = array('data' => t('Operations'), 'colspan' => '3');
943
  return theme('table', $header, $rows, array('id' => 'taxonomy')) .
drupal_render($form);
950
}
96
97
/**
98
 * Display form for adding and editing vocabularies.
99
 *
100
 * @ingroup forms
101
 * @see taxonomy_form_vocabulary_submit()
102
 */
10315
function taxonomy_form_vocabulary(&$form_state, $edit = array()) {
104
  $edit += array(
1056
    'name' => '',
1066
    'description' => '',
1076
    'help' => '',
1086
    'nodes' => array(),
1096
    'hierarchy' => 0,
1106
    'relations' => 0,
1116
    'tags' => 0,
1126
    'multiple' => 0,
1136
    'required' => 0,
1146
    'weight' => 0,
1150
  );
116
  // Check whether we need a deletion confirmation form.
1176
  if (isset($form_state['confirm_delete']) &&
isset($form_state['values']['vid'])) {
1180
    return taxonomy_vocabulary_confirm_delete($form_state,
$form_state['values']['vid']);
1190
  }
1206
  $form['identification'] = array(
1216
    '#type' => 'fieldset',
1226
    '#title' => t('Identification'),
1236
    '#collapsible' => TRUE,
124
  );
1256
  $form['identification']['name'] = array('#type' => 'textfield',
1266
    '#title' => t('Vocabulary name'),
1276
    '#default_value' => $edit['name'],
1286
    '#maxlength' => 255,
1296
    '#description' => t('The name for this vocabulary, e.g.,
<em>"Tags"</em>.'),
1306
    '#required' => TRUE,
131
  );
1326
  $form['identification']['description'] = array('#type' => 'textarea',
1336
    '#title' => t('Description'),
1346
    '#default_value' => $edit['description'],
1356
    '#description' => t('Description of the vocabulary; can be used by
modules.'),
136
  );
1376
  $form['identification']['help'] = array('#type' => 'textfield',
1386
    '#title' => t('Help text'),
1396
    '#maxlength' => 255,
1406
    '#default_value' => $edit['help'],
1416
    '#description' => t('Instructions to present to the user when selecting
terms, e.g., <em>"Enter a comma separated list of words"</em>.'),
142
  );
1436
  $form['content_types'] = array(
1446
    '#type' => 'fieldset',
1456
    '#title' => t('Content types'),
1466
    '#collapsible' => TRUE,
147
  );
1486
  $form['content_types']['nodes'] = array('#type' => 'checkboxes',
1496
    '#title' => t('Content types'),
1506
    '#default_value' => $edit['nodes'],
1516
    '#options' => array_map('check_plain', node_get_types('names')),
1526
    '#description' => t('Select content types to categorize using this
vocabulary.'),
153
  );
1546
  $form['settings'] = array(
1556
    '#type' => 'fieldset',
1566
    '#title' => t('Settings'),
1576
    '#collapsible' => TRUE,
158
  );
1596
  $form['settings']['tags'] = array('#type' => 'checkbox',
1606
    '#title' => t('Tags'),
1616
    '#default_value' => $edit['tags'],
1626
    '#description' => t('Terms are created by users when submitting posts
by typing a comma separated list.'),
163
  );
1646
  $form['settings']['multiple'] = array('#type' => 'checkbox',
1656
    '#title' => t('Multiple select'),
1666
    '#default_value' => $edit['multiple'],
1676
    '#description' => t('Allows posts to have more than one term from this
vocabulary (always true for tags).'),
168
  );
1696
  $form['settings']['required'] = array('#type' => 'checkbox',
1706
    '#title' => t('Required'),
1716
    '#default_value' => $edit['required'],
1726
    '#description' => t('At least one term in this vocabulary must be
selected when submitting a post.'),
173
  );
1746
  $form['settings']['weight'] = array('#type' => 'weight',
1756
    '#title' => t('Weight'),
1766
    '#default_value' => $edit['weight'],
1776
    '#description' => t('Vocabularies are displayed in ascending order by
weight.'),
178
  );
179
  // Set the hierarchy to "multiple parents" by default. This simplifies
the
180
  // vocabulary form and standardizes the term form.
1816
  $form['hierarchy'] = array('#type' => 'value',
1826
    '#value' => '0',
183
  );
184
  // Enable "related terms" by default.
1856
  $form['relations'] = array('#type' => 'value',
1866
    '#value' => '1',
187
  );
188
1896
  $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
1906
  if (isset($edit['vid'])) {
1914
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
1924
    $form['vid'] = array('#type' => 'value', '#value' => $edit['vid']);
1934
    $form['module'] = array('#type' => 'value', '#value' =>
$edit['module']);
1944
  }
1956
  return $form;
1960
}
197
198
/**
199
 * Accept the form submission for a vocabulary and save the results.
200
 */
20115
function taxonomy_form_vocabulary_submit($form, &$form_state) {
2023
  if ($form_state['clicked_button']['#value'] == t('Delete')) {
203
    // Rebuild the form to confirm vocabulary deletion.
2040
    $form_state['rebuild'] = TRUE;
2050
    $form_state['confirm_delete'] = TRUE;
2060
    return;
2070
  }
208
  // Fix up the nodes array to remove unchecked nodes.
2093
  $form_state['values']['nodes'] =
array_filter($form_state['values']['nodes']);
2103
  switch (taxonomy_save_vocabulary($form_state['values'])) {
2113
    case SAVED_NEW:
2121
      drupal_set_message(t('Created new vocabulary %name.', array('%name'
=> $form_state['values']['name'])));
2131
      watchdog('taxonomy', 'Created new vocabulary %name.', array('%name'
=> $form_state['values']['name']), WATCHDOG_NOTICE, l(t('edit'),
'admin/content/taxonomy/edit/vocabulary/' .
$form_state['values']['vid']));
2141
      break;
2152
    case SAVED_UPDATED:
2162
      drupal_set_message(t('Updated vocabulary %name.', array('%name' =>
$form_state['values']['name'])));
2172
      watchdog('taxonomy', 'Updated vocabulary %name.', array('%name' =>
$form_state['values']['name']), WATCHDOG_NOTICE, l(t('edit'),
'admin/content/taxonomy/edit/vocabulary/' .
$form_state['values']['vid']));
2182
      break;
2190
  }
220
2213
  $form_state['vid'] = $form_state['values']['vid'];
2223
  $form_state['redirect'] = 'admin/content/taxonomy';
2233
  return;
2240
}
225
226
/**
227
 * Page to edit a vocabulary.
228
 */
22915
function taxonomy_admin_vocabulary_edit($vocabulary) {
2304
  return drupal_get_form('taxonomy_form_vocabulary', (array)$vocabulary);
2310
}
232
233
/**
234
 * Page to edit a vocabulary term.
235
 */
23615
function taxonomy_admin_term_edit($tid) {
2370
  if ($term = (array)taxonomy_get_term($tid)) {
2380
    return drupal_get_form('taxonomy_form_term',
taxonomy_vocabulary_load($term['vid']), $term);
2390
  }
2400
  return drupal_not_found();
2410
}
242
243
/**
244
 * Form builder for the taxonomy terms overview.
245
 *
246
 * Display a tree of all the terms in a vocabulary, with options to edit
247
 * each one. The form is made drag and drop by the theme function.
248
 *
249
 * @ingroup forms
250
 * @see taxonomy_overview_terms_submit()
251
 * @see theme_taxonomy_overview_terms()
252
 */
25315
function taxonomy_overview_terms(&$form_state, $vocabulary) {
2543
  global $pager_page_array, $pager_total, $pager_total_items;
255
256
  // Check for confirmation forms.
2573
  if (isset($form_state['confirm_reset_alphabetical'])) {
2580
    return taxonomy_vocabulary_confirm_reset_alphabetical($form_state,
$vocabulary->vid);
2590
  }
260
2613
  drupal_set_title(t('Terms in %vocabulary', array('%vocabulary' =>
$vocabulary->name)));
262
  $form = array(
2633
    '#vocabulary' => (array)$vocabulary,
2643
    '#tree' => TRUE,
2653
    '#parent_fields' => FALSE,
2663
  );
267
2683
  $page            = isset($_GET['page']) ? $_GET['page'] : 0;
2693
  $page_increment  = 10;  // Number of terms per page.
2703
  $page_entries    = 0;   // Elements shown on this page.
2713
  $before_entries  = 0;   // Elements at the root level before this page.
2723
  $after_entries   = 0;   // Elements at the root level after this page.
2733
  $root_entries    = 0;   // Elements at the root level on this page.
274
275
  // Terms from previous and next pages are shown if the term tree would
have
276
  // been cut in the middle. Keep track of how many extra terms we show on
each
277
  // page of terms.
2783
  $back_peddle    = NULL;
2793
  $forward_peddle = 0;
280
281
  // An array of the terms to be displayed on this page.
2823
  $current_page = array();
283
284
  // Case for free tagging.
2853
  if ($vocabulary->tags) {
286
    // We are not calling taxonomy_get_tree because that might fail with a
big
287
    // number of tags in the freetagging vocabulary.
2880
    $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);
2890
    $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);
2900
    while ($term = db_fetch_object($results)) {
2910
      $key = 'tid:' . $term->tid . ':0';
2920
      $current_page[$key] = $term;
2930
      $page_entries++;
2940
    }
2950
  }
296
  // Case for restricted vocabulary.
297
  else {
2983
    $term_deltas = array();
2993
    $tree = taxonomy_get_tree($vocabulary->vid);
3003
    $term = current($tree);
301
    do {
302
      // In case this tree is completely empty.
3033
      if (empty($term)) {
3040
        break;
3050
      }
306
      // Count entries before the current page.
3073
      if ($page && ($page * $page_increment) > $before_entries &&
!isset($back_peddle)) {
3080
        $before_entries++;
3090
        continue;
3100
      }
311
      // Count entries after the current page.
3123
      elseif ($page_entries > $page_increment && isset($complete_tree)) {
3130
        $after_entries++;
3140
        continue;
3150
      }
316
317
      // Do not let a term start the page that is not at the root.
3183
      if (isset($term->depth) && ($term->depth > 0) &&
!isset($back_peddle)) {
3190
        $back_peddle = 0;
3200
        while ($pterm = prev($tree)) {
3210
          $before_entries--;
3220
          $back_peddle++;
3230
          if ($pterm->depth == 0) {
3240
            prev($tree);
3250
            continue 2; // Jump back to the start of the root level
parent.
3260
          }
3270
        }
3280
      }
3293
      $back_peddle = isset($back_peddle) ? $back_peddle : 0;
330
331
      // Continue rendering the tree until we reach the a new root item.
3323
      if ($page_entries >= $page_increment + $back_peddle + 1 &&
$term->depth == 0 && $root_entries > 1) {
3330
        $complete_tree = TRUE;
334
        // This new item at the root level is the first item on the next
page.
3350
        $after_entries++;
3360
        continue;
3370
      }
3383
      if ($page_entries >= $page_increment + $back_peddle) {
3390
        $forward_peddle++;
3400
      }
341
342
      // Finally, if we've gotten down this far, we're rendering a term on
this page.
3433
      $page_entries++;
3443
      $term_deltas[$term->tid] = isset($term_deltas[$term->tid]) ?
$term_deltas[$term->tid] + 1 : 0;
3453
      $key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid];
346
347
      // Keep track of the first term displayed on this page.
3483
      if ($page_entries == 1) {
3493
        $form['#first_tid'] = $term->tid;
3503
      }
351
      // Keep a variable to make sure at least 2 root elements are
displayed.
3523
      if ($term->parents[0] == 0) {
3533
        $root_entries++;
3543
      }
3553
      $current_page[$key] = $term;
3563
    } while ($term = next($tree));
357
358
    // Because we didn't use a pager query, set the necessary pager
variables.
3593
    $total_entries = $before_entries + $page_entries + $after_entries;
3603
    $pager_total_items[0] = $total_entries;
3613
    $pager_page_array[0] = $page;
3623
    $pager_total[0] = ceil($total_entries / $page_increment);
363
  }
364
365
  // If this form was already submitted once, it's probably hit a
validation
366
  // error. Ensure the form is rebuilt in the same order as the user
submitted.
3673
  if (!empty($form_state['post'])) {
3680
    $order = array_flip(array_keys($form_state['post'])); // Get the $_POST
order.
3690
    $current_page = array_merge($order, $current_page); // Update our form
with the new order.
3700
    foreach ($current_page as $key => $term) {
371
      // Verify this is a term for the current page and set at the current
depth.
3720
      if (is_array($form_state['post'][$key]) &&
is_numeric($form_state['post'][$key]['tid'])) {
3730
        $current_page[$key]->depth = $form_state['post'][$key]['depth'];
3740
      }
375
      else {
3760
        unset($current_page[$key]);
377
      }
3780
    }
3790
  }
380
381
  // Build the actual form.
3823
  foreach ($current_page as $key => $term) {
383
    // Save the term for the current page so we don't have to load it a
second time.
3843
    $form[$key]['#term'] = (array)$term;
3853
    if (isset($term->parents)) {
3863
      $form[$key]['#term']['parent'] = $term->parent = $term->parents[0];
3873
      unset($form[$key]['#term']['parents'], $term->parents);
3883
    }
389
3903
    $form[$key]['view'] = array('#markup' => l($term->name,
"taxonomy/term/$term->tid"));
3913
    if (!$vocabulary->tags && $vocabulary->hierarchy < 2 && count($tree) >
1) {
3922
      $form['#parent_fields'] = TRUE;
3932
      $form[$key]['tid'] = array(
3942
        '#type' => 'hidden',
3952
        '#value' => $term->tid
3962
      );
3972
      $form[$key]['parent'] = array(
3982
        '#type' => 'hidden',
399
        // Yes, default_value on a hidden. It needs to be changeable by the
javascript.
4002
        '#default_value' => $term->parent,
401
      );
4022
      $form[$key]['depth'] = array(
4032
        '#type' => 'hidden',
404
        // Same as above, the depth is modified by javascript, so it's a
default_value.
4052
        '#default_value' => $term->depth,
406
      );
4072
    }
4083
    $form[$key]['edit'] = array('#markup' => l(t('edit'),
"admin/content/taxonomy/edit/term/$term->tid", array('query' =>
drupal_get_destination())));
4093
  }
410
4113
  $form['#total_entries'] = $total_entries;
4123
  $form['#page_increment'] = $page_increment;
4133
  $form['#page_entries'] = $page_entries;
4143
  $form['#back_peddle'] = $back_peddle;
4153
  $form['#forward_peddle'] = $forward_peddle;
4163
  $form['#empty_text'] = t('No terms available.');
417
4183
  if (!$vocabulary->tags && $vocabulary->hierarchy < 2 && count($tree) > 1)
{
4192
    $form['submit'] = array(
4202
      '#type' => 'submit',
4212
      '#value' => t('Save')
4222
    );
4232
    $form['reset_alphabetical'] = array(
4242
      '#type' => 'submit',
4252
      '#value' => t('Reset to alphabetical')
4262
    );
4272
    $form['destination'] = array(
4282
      '#type' => 'hidden',
4292
      '#value' => $_GET['q'] . (isset($_GET['page']) ? '?page=' .
$_GET['page'] : '')
4302
    );
4312
  }
432
4333
  return $form;
4340
}
435
436
/**
437
 * Submit handler for terms overview form.
438
 *
439
 * Rather than using a textfield or weight field, this form depends
entirely
440
 * upon the order of form elements on the page to determine new weights.
441
 *
442
 * Because there might be hundreds or thousands of taxonomy terms that need
to
443
 * be ordered, terms are weighted from 0 to the number of terms in the
444
 * vocabulary, rather than the standard -10 to 10 scale. Numbers are
sorted
445
 * lowest to highest, but are not necessarily sequential. Numbers may be
skipped
446
 * when a term has children so that reordering is minimal when a child is
447
 * added or removed from a term.
448
 *
449
 * @see taxonomy_overview_terms()
450
 */
45115
function taxonomy_overview_terms_submit($form, &$form_state) {
4520
  if ($form_state['clicked_button']['#value'] == t('Reset to
alphabetical')) {
453
    // Execute the reset action.
4540
    if ($form_state['values']['reset_alphabetical'] === TRUE) {
4550
      return taxonomy_vocabulary_confirm_reset_alphabetical_submit($form,
$form_state);
4560
    }
457
    // Rebuild the form to confirm the reset action.
4580
    $form_state['rebuild'] = TRUE;
4590
    $form_state['confirm_reset_alphabetical'] = TRUE;
4600
    return;
4610
  }
462
4630
  $order = array_flip(array_keys($form['#post'])); // Get the $_POST
order.
4640
  $form_state['values'] = array_merge($order, $form_state['values']); //
Update our original form with the new order.
465
4660
  $vocabulary = $form['#vocabulary'];
4670
  $hierarchy = 0; // Update the current hierarchy type as we go.
468
4690
  $changed_terms = array();
4700
  $tree = taxonomy_get_tree($vocabulary['vid']);
471
4720
  if (empty($tree)) {
4730
    return;
4740
  }
475
476
  // Build a list of all terms that need to be updated on previous pages.
4770
  $weight = 0;
4780
  $term = (array)$tree[0];
4790
  while ($term['tid'] != $form['#first_tid']) {
4800
    if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
4810
      $term['parent'] = $term['parents'][0];
4820
      $term['weight'] = $weight;
4830
      $changed_terms[$term['tid']] = $term;
4840
    }
4850
    $weight++;
4860
    $hierarchy = $term['parents'][0] != 0 ? 1 : $hierarchy;
4870
    $term = (array)$tree[$weight];
4880
  }
489
490
  // Renumber the current page weights and assign any new parents.
4910
  $level_weights = array();
4920
  foreach ($form_state['values'] as $tid => $values) {
4930
    if (isset($form[$tid]['#term'])) {
4940
      $term = $form[$tid]['#term'];
495
      // Give terms at the root level a weight in sequence with terms on
previous pages.
4960
      if ($values['parent'] == 0 && $term['weight'] != $weight) {
4970
        $term['weight'] = $weight;
4980
        $changed_terms[$term['tid']] = $term;
4990
      }
500
      // Terms not at the root level can safely start from 0 because
they're all on this page.
5010
      elseif ($values['parent'] > 0) {
5020
        $level_weights[$values['parent']] =
isset($level_weights[$values['parent']]) ?
$level_weights[$values['parent']] + 1 : 0;
5030
        if ($level_weights[$values['parent']] != $term['weight']) {
5040
          $term['weight'] = $level_weights[$values['parent']];
5050
          $changed_terms[$term['tid']] = $term;
5060
        }
5070
      }
508
      // Update any changed parents.
5090
      if ($values['parent'] != $term['parent']) {
5100
        $term['parent'] = $values['parent'];
5110
        $changed_terms[$term['tid']] = $term;
5120
      }
5130
      $hierarchy = $term['parent'] != 0 ? 1 : $hierarchy;
5140
      $weight++;
5150
    }
5160
  }
517
518
  // Build a list of all terms that need to be updated on following pages.
5190
  for ($weight; $weight < count($tree); $weight++) {
5200
    $term = (array)$tree[$weight];
5210
    if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
5220
      $term['parent'] = $term['parents'][0];
5230
      $term['weight'] = $weight;
5240
      $changed_terms[$term['tid']] = $term;
5250
    }
5260
    $hierarchy = $term['parents'][0] != 0 ? 1 : $hierarchy;
5270
  }
528
529
  // Save all updated terms.
5300
  foreach ($changed_terms as $term) {
5310
    taxonomy_save_term($term);
5320
  }
533
534
  // Update the vocabulary hierarchy to flat or single hierarchy.
5350
  if ($vocabulary['hierarchy'] != $hierarchy) {
5360
    $vocabulary['hierarchy'] = $hierarchy;
5370
    taxonomy_save_vocabulary($vocabulary);
5380
  }
5390
}
540
541
/**
542
 * Theme the terms overview as a sortable list of terms.
543
 *
544
 * @ingroup themeable
545
 * @see taxonomy_overview_terms()
546
 */
54715
function theme_taxonomy_overview_terms($form) {
5483
  $page_increment  = $form['#page_increment'];
5493
  $page_entries    = $form['#page_entries'];
5503
  $back_peddle     = $form['#back_peddle'];
5513
  $forward_peddle  = $form['#forward_peddle'];
552
553
  // Add drag and drop if parent fields are present in the form.
5543
  if ($form['#parent_fields']) {
5552
    drupal_add_tabledrag('taxonomy', 'match', 'parent', 'term-parent',
'term-parent', 'term-id', FALSE);
5562
    drupal_add_tabledrag('taxonomy', 'depth', 'group', 'term-depth', NULL,
NULL, FALSE);
5572
    drupal_add_js(drupal_get_path('module', 'taxonomy') . '/taxonomy.js');
5582
    drupal_add_js(array('taxonomy' => array('backPeddle' => $back_peddle,
'forwardPeddle' => $forward_peddle)), 'setting');
5592
    drupal_add_css(drupal_get_path('module', 'taxonomy') .
'/taxonomy.css');
5602
  }
561
5623
  $errors = form_get_errors() != FALSE ? form_get_errors() : array();
5633
  $rows = array();
5643
  foreach (element_children($form) as $key) {
5653
    if (isset($form[$key]['#term'])) {
5663
      $term = &$form[$key];
567
5683
      $row = array();
5693
      $row[] = (isset($term['#term']['depth']) && $term['#term']['depth'] >
0 ? theme('indentation', $term['#term']['depth']) : '') .
drupal_render($term['view']);
5703
      if ($form['#parent_fields']) {
5712
        $term['tid']['#attributes']['class'] = 'term-id';
5722
        $term['parent']['#attributes']['class'] = 'term-parent';
5732
        $term['depth']['#attributes']['class'] = 'term-depth';
5742
        $row[0] .= drupal_render($term['parent']) .
drupal_render($term['tid']) . drupal_render($term['depth']);
5752
      }
5763
      $row[] = drupal_render($term['edit']);
577
5783
      $row = array('data' => $row);
5793
      $rows[$key] = $row;
5803
    }
5813
  }
582
583
  // Add necessary classes to rows.
5843
  $row_position = 0;
5853
  foreach ($rows as $key => $row) {
5863
    $classes = array();
5873
    if (isset($form['#parent_fields'])) {
5883
      $classes[] = 'draggable';
5893
    }
590
591
    // Add classes that mark which terms belong to previous and next
pages.
5923
    if ($row_position < $back_peddle || $row_position >= $page_entries -
$forward_peddle) {
5930
      $classes[] = 'taxonomy-term-preview';
5940
    }
595
5963
    if ($row_position !== 0 && $row_position !== count($rows) - 1) {
5971
      if ($row_position == $back_peddle - 1 || $row_position ==
$page_entries - $forward_peddle - 1) {
5980
        $classes[] = 'taxonomy-term-divider-top';
5990
      }
6001
      elseif ($row_position == $back_peddle || $row_position ==
$page_entries - $forward_peddle) {
6010
        $classes[] = 'taxonomy-term-divider-bottom';
6020
      }
6031
    }
604
605
    // Add an error class if this row contains a form error.
6063
    foreach ($errors as $error_key => $error) {
6070
      if (strpos($error_key, $key) === 0) {
6080
        $classes[] = 'error';
6090
      }
6100
    }
6113
    $rows[$key]['class'] = implode(' ', $classes);
6123
    $row_position++;
6133
  }
614
6153
  if (empty($rows)) {
6160
    $rows[] = array(array('data' => $form['#empty_text'], 'colspan' =>
'2'));
6170
  }
618
6193
  $header = array(t('Name'), t('Operations'));
6203
  $output = theme('table', $header, $rows, array('id' => 'taxonomy'));
6213
  $output .= drupal_render($form);
6223
  $output .= theme('pager', NULL, $page_increment);
623
6243
  return $output;
6250
}
626
627
/**
628
 * Menu callback; return the edit form for a new term after setting the
title.
629
 */
63015
function taxonomy_add_term_page($vocabulary) {
6313
  drupal_set_title(t('Add term to %vocabulary', array('%vocabulary' =>
$vocabulary->name)));
6323
  return drupal_get_form('taxonomy_form_term' , $vocabulary);
6330
}
634
635
/**
636
 * Form function for the term edit form.
637
 *
638
 * @ingroup forms
639
 * @see taxonomy_form_term_submit()
640
 */
64115
function taxonomy_form_term(&$form_state, $vocabulary, $edit = array()) {
642
  $edit += array(
6433
    'name' => '',
6443
    'description' => '',
6453
    'tid' => NULL,
6463
    'weight' => 0,
6470
  );
648
6493
  $parent = array_keys(taxonomy_get_parents($edit['tid']));
6503
  $form['#term'] = $edit;
6513
  $form['#term']['parent'] = $parent;
6523
  $form['#vocabulary'] = (array)$vocabulary;
6533
  $form['#vocabulary']['nodes'] = drupal_map_assoc($vocabulary->nodes);;
654
655
  // Check for confirmation forms.
6563
  if (isset($form_state['confirm_delete'])) {
6570
    return array_merge($form, taxonomy_term_confirm_delete($form_state,
$edit['tid']));
6580
  }
6593
  elseif (isset($form_state['confirm_parents'])) {
6600
    return array_merge($form, taxonomy_term_confirm_parents($form_state,
$vocabulary));
6610
  }
662
6633
  $form['identification'] = array(
6643
    '#type' => 'fieldset',
6653
    '#title' => t('Identification'),
6663
    '#collapsible' => TRUE,
667
  );
6683
  $form['identification']['name'] = array(
6693
    '#type' => 'textfield',
6703
    '#title' => t('Term name'),
6713
    '#default_value' => $edit['name'],
6723
    '#maxlength' => 255,
6733
    '#description' => t('The name of this term.'),
6743
    '#required' => TRUE);
6753
  $form['identification']['description'] = array(
6763
    '#type' => 'textarea',
6773
    '#title' => t('Description'),
6783
    '#default_value' => $edit['description'],
6793
    '#description' => t('A description of the term. To be displayed on
taxonomy/term pages and RSS feeds.'));
680
6813
  $form['advanced'] = array(
6823
    '#type' => 'fieldset',
6833
    '#title' => t('Advanced options'),
6843
    '#collapsible' => TRUE,
6853
    '#collapsed' => $vocabulary->hierarchy > 1 ? FALSE : TRUE,
686
  );
687
688
  // taxonomy_get_tree and taxonomy_get_parents may contain large numbers
of
689
  // items so we check for taxonomy_override_selector before loading the
690
  // full vocabulary. Contrib modules can then intercept before
691
  // hook_form_alter to provide scalable alternatives.
6923
  if (!variable_get('taxonomy_override_selector', FALSE)) {
6933
    $parent = array_keys(taxonomy_get_parents($edit['tid']));
6943
    $children = taxonomy_get_tree($vocabulary->vid, $edit['tid']);
695
696
    // A term can't be the child of itself, nor of its children.
6973
    foreach ($children as $child) {
6980
      $exclude[] = $child->tid;
6990
    }
7003
    $exclude[] = $edit['tid'];
701
7023
    $form['advanced']['parent'] = _taxonomy_term_select(t('Parents'),
'parent', $parent, $vocabulary->vid, t('Parent terms') . '.', 1, '<' .
t('root') . '>', $exclude);
7033
    $form['advanced']['relations'] = _taxonomy_term_select(t('Related
terms'), 'relations', array_keys(taxonomy_get_related($edit['tid'])),
$vocabulary->vid, NULL, 1, '<' . t('none') . '>', array($edit['tid']));
7043
  }
7053
  $form['advanced']['synonyms'] = array(
7063
    '#type' => 'textarea',
7073
    '#title' => t('Synonyms'),
7083
    '#default_value' => implode("\n",
taxonomy_get_synonyms($edit['tid'])),
7093
    '#description' => t('Synonyms of this term, one synonym per line.'));
7103
  $form['advanced']['weight'] = array(
7113
    '#type' => 'textfield',
7123
    '#title' => t('Weight'),
7133
    '#size' => 6,
7143
    '#default_value' => $edit['weight'],
7153
    '#description' => t('Terms are displayed in ascending order by
weight.'),
7163
    '#required' => TRUE);
7173
  $form['vid'] = array(
7183
    '#type' => 'value',
7193
    '#value' => $vocabulary->vid);
7203
  $form['submit'] = array(
7213
    '#type' => 'submit',
7223
    '#value' => t('Save'));
723
7243
  if ($edit['tid']) {
7250
    $form['delete'] = array(
7260
      '#type' => 'submit',
7270
      '#value' => t('Delete'));
7280
    $form['tid'] = array(
7290
      '#type' => 'value',
7300
      '#value' => $edit['tid']);
7310
  }
732
  else {
7333
    $form['destination'] = array('#type' => 'hidden', '#value' =>
$_GET['q']);
734
  }
735
7363
  return $form;
7370
}
738
739
/**
740
 * Validation handler for the term edit form. Ensure numeric weight
values.
741
 *
742
 * @see taxonomy_form_term()
743
 */
74415
function taxonomy_form_term_validate($form, &$form_state) {
7451
  if (isset($form_state['values']['weight']) &&
!is_numeric($form_state['values']['weight'])) {
7460
    form_set_error('weight', t('Weight value must be numeric.'));
7470
  }
7481
}
749
750
/**
751
 * Submit handler to insert or update a term.
752
 *
753
 * @see taxonomy_form_term()
754
 */
75515
function taxonomy_form_term_submit($form, &$form_state) {
7561
  if ($form_state['clicked_button']['#value'] == t('Delete')) {
757
    // Execute the term deletion.
7580
    if ($form_state['values']['delete'] === TRUE) {
7590
      return taxonomy_term_confirm_delete_submit($form, $form_state);
7600
    }
761
    // Rebuild the form to confirm term deletion.
7620
    $form_state['rebuild'] = TRUE;
7630
    $form_state['confirm_delete'] = TRUE;
7640
    return;
7650
  }
766
  // Rebuild the form to confirm enabling multiple parents.
7671
  elseif ($form_state['clicked_button']['#value'] == t('Save') &&
!$form['#vocabulary']['tags'] && count($form_state['values']['parent']) > 1
&& $form['#vocabulary']['hierarchy'] < 2) {
7680
    $form_state['rebuild'] = TRUE;
7690
    $form_state['confirm_parents'] = TRUE;
7700
    return;
7710
  }
772
7731
  switch (taxonomy_save_term($form_state['values'])) {
7741
    case SAVED_NEW:
7751
      drupal_set_message(t('Created new term %term.', array('%term' =>
$form_state['values']['name'])));
7761
      watchdog('taxonomy', 'Created new term %term.', array('%term' =>
$form_state['values']['name']), WATCHDOG_NOTICE, l(t('edit'),
'admin/content/taxonomy/edit/term/' . $form_state['values']['tid']));
7771
      break;
7780
    case SAVED_UPDATED:
7790
      drupal_set_message(t('Updated term %term.', array('%term' =>
$form_state['values']['name'])));
7800
      watchdog('taxonomy', 'Updated term %term.', array('%term' =>
$form_state['values']['name']), WATCHDOG_NOTICE, l(t('edit'),
'admin/content/taxonomy/edit/term/' . $form_state['values']['tid']));
7810
      break;
7820
  }
783
7841
  if (!$form['#vocabulary']['tags']) {
7851
    $current_parent_count = count($form_state['values']['parent']);
7861
    $previous_parent_count = count($form['#term']['parent']);
787
    // Root doesn't count if it's the only parent.
7881
    if ($current_parent_count == 1 &&
isset($form_state['values']['parent'][''])) {
7890
      $current_parent_count = 0;
7900
      $form_state['values']['parent'] = array();
7910
    }
792
793
    // If the number of parents has been reduced to one or none, do a check
on the
794
    // parents of every term in the vocabulary value.
7951
    if ($current_parent_count < $previous_parent_count &&
$current_parent_count < 2) {
7960
      taxonomy_check_vocabulary_hierarchy($form['#vocabulary'],
$form_state['values']);
7970
    }
798
    // If we've increased the number of parents and this is a single or
flat
799
    // hierarchy, update the vocabulary immediately.
8001
    elseif ($current_parent_count > $previous_parent_count &&
$form['#vocabulary']['hierarchy'] < 2) {
8011
      $form['#vocabulary']['hierarchy'] = $current_parent_count == 1 ? 1 :
2;
8021
      taxonomy_save_vocabulary($form['#vocabulary']);
8031
    }
8041
  }
805
8061
  $form_state['tid'] = $form_state['values']['tid'];
8071
  $form_state['redirect'] = 'admin/content/taxonomy';
8081
  return;
8090
}
810
811
/**
812
 * Form builder for the confirmation of multiple term parents.
813
 *
814
 * @ingroup forms
815
 * @see taxonomy_form_term()
816
 */
81715
function taxonomy_term_confirm_parents(&$form_state, $vocabulary) {
8180
  $form = array();
8190
  foreach (element_children($form_state['values']) as $key) {
8200
    $form[$key] = array(
8210
      '#type' => 'value',
8220
      '#value' => $form_state['values'][$key],
823
    );
8240
  }
8250
  $question = t('Set multiple term parents?');
8260
  $description = '<p>' . 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)) . '</p>';
8270
  $description .= '<p>' . 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.") . '</p>';
8280
  return confirm_form($form, $question, drupal_get_destination(),
$description, t('Set multiple parents'));
8290
}
830
831
/**
832
 * Form builder for the term delete form.
833
 *
834
 * @ingroup forms
835
 * @see taxonomy_term_confirm_delete_submit()
836
 */
83715
function taxonomy_term_confirm_delete(&$form_state, $tid) {
8380
  $term = taxonomy_get_term($tid);
839
8400
  $form['type'] = array('#type' => 'value', '#value' => 'term');
8410
  $form['name'] = array('#type' => 'value', '#value' => $term->name);
8420
  $form['tid'] = array('#type' => 'value', '#value' => $tid);
8430
  $form['delete'] = array('#type' => 'value', '#value' => TRUE);
8440
  return confirm_form($form,
8450
                  t('Are you sure you want to delete the term %title?',
8460
                  array('%title' => $term->name)),
8470
                  'admin/content/taxonomy',
8480
                  t('Deleting a term will delete all its children if there
are any. This action cannot be undone.'),
8490
                  t('Delete'),
8500
                  t('Cancel'));
8510
}
852
853
/**
854
 * Submit handler to delete a term after confirmation.
855
 *
856
 * @see taxonomy_term_confirm_delete()
857
 */
85815
function taxonomy_term_confirm_delete_submit($form, &$form_state) {
8590
  taxonomy_del_term($form_state['values']['tid']);
8600
  taxonomy_check_vocabulary_hierarchy($form['#vocabulary'],
$form_state['values']);
8610
  drupal_set_message(t('Deleted term %name.', array('%name' =>
$form_state['values']['name'])));
8620
  watchdog('taxonomy', 'Deleted term %name.', array('%name' =>
$form_state['values']['name']), WATCHDOG_NOTICE);
8630
  $form_state['redirect'] = 'admin/content/taxonomy';
8640
  return;
8650
}
866
867
/**
868
 * Form builder for the vocabulary delete confirmation form.
869
 *
870
 * @ingroup forms
871
 * @see taxonomy_vocabulary_confirm_delete_submit()
872
 */
87315
function taxonomy_vocabulary_confirm_delete(&$form_state, $vid) {
8740
  $vocabulary = taxonomy_vocabulary_load($vid);
875
8760
  $form['#id'] = 'taxonomy_vocabulary_confirm_delete';
8770
  $form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
8780
  $form['vid'] = array('#type' => 'value', '#value' => $vid);
8790
  $form['name'] = array('#type' => 'value', '#value' =>
$vocabulary->name);
8800
  $form['#submit'] = array('taxonomy_vocabulary_confirm_delete_submit');
8810
  return confirm_form($form,
8820
                  t('Are you sure you want to delete the vocabulary
%title?',
8830
                  array('%title' => $vocabulary->name)),
8840
                  'admin/content/taxonomy',
8850
                  t('Deleting a vocabulary will delete all the terms in it.
This action cannot be undone.'),
8860
                  t('Delete'),
8870
                  t('Cancel'));
8880
}
889
890
/**
891
 * Submit handler to delete a vocabulary after confirmation.
892
 *
893
 * @see taxonomy_vocabulary_confirm_delete()
894
 */
89515
function taxonomy_vocabulary_confirm_delete_submit($form, &$form_state) {
8960
  $status = taxonomy_del_vocabulary($form_state['values']['vid']);
8970
  drupal_set_message(t('Deleted vocabulary %name.', array('%name' =>
$form_state['values']['name'])));
8980
  watchdog('taxonomy', 'Deleted vocabulary %name.', array('%name' =>
$form_state['values']['name']), WATCHDOG_NOTICE);
8990
  $form_state['redirect'] = 'admin/content/taxonomy';
9000
  return;
9010
}
902
903
/**
904
 * Form builder to confirm reseting a vocabulary to alphabetical order.
905
 *
906
 * @ingroup forms
907
 * @see taxonomy_vocabulary_confirm_reset_alphabetical_submit()
908
 */
90915
function taxonomy_vocabulary_confirm_reset_alphabetical(&$form_state, $vid)
{
9100
  $vocabulary = taxonomy_vocabulary_load($vid);
911
9120
  $form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
9130
  $form['vid'] = array('#type' => 'value', '#value' => $vid);
9140
  $form['name'] = array('#type' => 'value', '#value' =>
$vocabulary->name);
9150
  $form['reset_alphabetical'] = array('#type' => 'value', '#value' =>
TRUE);
9160
  return confirm_form($form,
9170
                  t('Are you sure you want to reset the vocabulary %title
to alphabetical order?',
9180
                  array('%title' => $vocabulary->name)),
9190
                  'admin/content/taxonomy/' . $vid,
9200
                  t('Resetting a vocabulary will discard all custom
ordering and sort items alphabetically.'),
9210
                  t('Reset to alphabetical'),
9220
                  t('Cancel'));
9230
}
924
925
/**
926
 * Submit handler to reset a vocabulary to alphabetical order after
confirmation.
927
 *
928
 * @see taxonomy_vocabulary_confirm_reset_alphabetical()
929
 */
93015
function taxonomy_vocabulary_confirm_reset_alphabetical_submit($form,
&$form_state) {
9310
  db_query('UPDATE {term_data} t SET weight = 0 WHERE vid = %d',
$form_state['values']['vid']);
9320
  drupal_set_message(t('Reset vocabulary %name to alphabetical order.',
array('%name' => $form_state['values']['name'])));
9330
  watchdog('taxonomy', 'Reset vocabulary %name to alphabetical order.',
array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE);
9340
  $form_state['redirect'] = 'admin/content/taxonomy/' .
$form_state['values']['vid'];
9350
}
93615