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

Line #Times calledCode
1
<?php
2
// $Id: taxonomy.admin.inc,v 1.32 2008/10/29 10:08:52 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
 */
1628
function taxonomy_overview_vocabularies() {
176
  $vocabularies = taxonomy_get_vocabularies();
186
  $form = array('#tree' => TRUE);
196
  foreach ($vocabularies as $vocabulary) {
206
    $types = array();
216
    foreach ($vocabulary->nodes as $type) {
226
      $node_type = node_get_types('name', $type);
236
      $types[] = $node_type ? check_plain($node_type) : check_plain($type);
246
    }
256
    $form[$vocabulary->vid]['#vocabulary'] = (array)$vocabulary;
266
    $form[$vocabulary->vid]['name'] = array('#markup' =>
check_plain($vocabulary->name));
276
    $form[$vocabulary->vid]['types'] = array('#markup' => implode(', ',
$types));
286
    $form[$vocabulary->vid]['weight'] = array('#type' => 'weight', '#delta'
=> 10, '#default_value' => $vocabulary->weight);
296
    $form[$vocabulary->vid]['edit'] = array('#markup' => l(t('edit
vocabulary'), "admin/content/taxonomy/$vocabulary->vid"));
306
    $form[$vocabulary->vid]['list'] = array('#markup' => l(t('list terms'),
"admin/content/taxonomy/$vocabulary->vid/list"));
316
    $form[$vocabulary->vid]['add'] = array('#markup' => l(t('add terms'),
"admin/content/taxonomy/$vocabulary->vid/add"));
326
  }
33
34
  // Only make this form include a submit button and weight if more than
one
35
  // vocabulary exists.
366
  if (count($vocabularies) > 1) {
373
    $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
383
  }
393
  elseif (isset($vocabulary)) {
403
    unset($form[$vocabulary->vid]['weight']);
413
  }
426
  return $form;
430
}
44
45
/**
46
 * Submit handler for vocabularies overview. Updates changed vocabulary
weights.
47
 *
48
 * @see taxonomy_overview_vocabularies()
49
 */
5028
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
 */
6528
function theme_taxonomy_overview_vocabularies($form) {
666
  $rows = array();
676
  foreach (element_children($form) as $key) {
686
    if (isset($form[$key]['name'])) {
696
      $vocabulary = &$form[$key];
70
716
      $row = array();
726
      $row[] = drupal_render($vocabulary['name']);
736
      $row[] = drupal_render($vocabulary['types']);
746
      if (isset($vocabulary['weight'])) {
753
        $vocabulary['weight']['#attributes']['class'] =
'vocabulary-weight';
763
        $row[] = drupal_render($vocabulary['weight']);
773
      }
786
      $row[] = drupal_render($vocabulary['edit']);
796
      $row[] = drupal_render($vocabulary['list']);
806
      $row[] = drupal_render($vocabulary['add']);
816
      $rows[] = array('data' => $row, 'class' => 'draggable');
826
    }
836
  }
846
  if (empty($rows)) {
850
    $rows[] = array(array('data' => t('No vocabularies available.'),
'colspan' => '5'));
860
  }
87
886
  $header = array(t('Vocabulary name'), t('Content types'));
896
  if (isset($form['submit'])) {
903
    $header[] = t('Weight');
913
    drupal_add_tabledrag('taxonomy', 'order', 'sibling',
'vocabulary-weight');
923
  }
936
  $header[] = array('data' => t('Operations'), 'colspan' => '3');
946
  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
 */
10328
function taxonomy_form_vocabulary(&$form_state, $edit = array()) {
1047
  if (!is_array($edit)) {
1055
    $edit = (array)$edit;
1065
  }
107
  $edit += array(
1087
    'name' => '',
1097
    'description' => '',
1107
    'help' => '',
1117
    'nodes' => array(),
1127
    'hierarchy' => 0,
1137
    'relations' => 0,
1147
    'tags' => 0,
1157
    'multiple' => 0,
1167
    'required' => 0,
1177
    'weight' => 0,
1180
  );
119
  // Check whether we need a deletion confirmation form.
1207
  if (isset($form_state['confirm_delete']) &&
isset($form_state['values']['vid'])) {
1210
    return taxonomy_vocabulary_confirm_delete($form_state,
$form_state['values']['vid']);
1220
  }
1237
  $form['identification'] = array(
1247
    '#type' => 'fieldset',
1257
    '#title' => t('Identification'),
1267
    '#collapsible' => TRUE,
127
  );
1287
  $form['identification']['name'] = array('#type' => 'textfield',
1297
    '#title' => t('Vocabulary name'),
1307
    '#default_value' => $edit['name'],
1317
    '#maxlength' => 255,
1327
    '#description' => t('The name for this vocabulary, e.g.,
<em>"Tags"</em>.'),
1337
    '#required' => TRUE,
134
  );
1357
  $form['identification']['description'] = array('#type' => 'textarea',
1367
    '#title' => t('Description'),
1377
    '#default_value' => $edit['description'],
1387
    '#description' => t('Description of the vocabulary; can be used by
modules.'),
139
  );
1407
  $form['identification']['help'] = array('#type' => 'textfield',
1417
    '#title' => t('Help text'),
1427
    '#maxlength' => 255,
1437
    '#default_value' => $edit['help'],
1447
    '#description' => t('Instructions to present to the user when selecting
terms, e.g., <em>"Enter a comma separated list of words"</em>.'),
145
  );
1467
  $form['content_types'] = array(
1477
    '#type' => 'fieldset',
1487
    '#title' => t('Content types'),
1497
    '#collapsible' => TRUE,
150
  );
1517
  $form['content_types']['nodes'] = array('#type' => 'checkboxes',
1527
    '#title' => t('Content types'),
1537
    '#default_value' => $edit['nodes'],
1547
    '#options' => array_map('check_plain', node_get_types('names')),
1557
    '#description' => t('Select content types to categorize using this
vocabulary.'),
156
  );
1577
  $form['settings'] = array(
1587
    '#type' => 'fieldset',
1597
    '#title' => t('Settings'),
1607
    '#collapsible' => TRUE,
161
  );
1627
  $form['settings']['tags'] = array('#type' => 'checkbox',
1637
    '#title' => t('Tags'),
1647
    '#default_value' => $edit['tags'],
1657
    '#description' => t('Terms are created by users when submitting posts
by typing a comma separated list.'),
166
  );
1677
  $form['settings']['multiple'] = array('#type' => 'checkbox',
1687
    '#title' => t('Multiple select'),
1697
    '#default_value' => $edit['multiple'],
1707
    '#description' => t('Allows posts to have more than one term from this
vocabulary (always true for tags).'),
171
  );
1727
  $form['settings']['required'] = array('#type' => 'checkbox',
1737
    '#title' => t('Required'),
1747
    '#default_value' => $edit['required'],
1757
    '#description' => t('At least one term in this vocabulary must be
selected when submitting a post.'),
176
  );
1777
  $form['settings']['weight'] = array('#type' => 'weight',
1787
    '#title' => t('Weight'),
1797
    '#default_value' => $edit['weight'],
1807
    '#description' => t('Vocabularies are displayed in ascending order by
weight.'),
181
  );
182
  // Set the hierarchy to "multiple parents" by default. This simplifies
the
183
  // vocabulary form and standardizes the term form.
1847
  $form['hierarchy'] = array('#type' => 'value',
1857
    '#value' => '0',
186
  );
187
  // Enable "related terms" by default.
1887
  $form['relations'] = array('#type' => 'value',
1897
    '#value' => '1',
190
  );
191
1927
  $form['submit'] = array('#type' => 'submit', '#value' => t('Save'));
1937
  if (isset($edit['vid'])) {
1943
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
1953
    $form['vid'] = array('#type' => 'value', '#value' => $edit['vid']);
1963
    $form['module'] = array('#type' => 'value', '#value' =>
$edit['module']);
1973
  }
1987
  return $form;
1990
}
200
201
/**
202
 * Accept the form submission for a vocabulary and save the results.
203
 */
20428
function taxonomy_form_vocabulary_submit($form, &$form_state) {
2053
  if ($form_state['clicked_button']['#value'] == t('Delete')) {
206
    // Rebuild the form to confirm vocabulary deletion.
2070
    $form_state['rebuild'] = TRUE;
2080
    $form_state['confirm_delete'] = TRUE;
2090
    return;
2100
  }
211
  // Fix up the nodes array to remove unchecked nodes.
2123
  $form_state['values']['nodes'] =
array_filter($form_state['values']['nodes']);
2133
  switch (taxonomy_save_vocabulary($form_state['values'])) {
2143
    case SAVED_NEW:
2152
      drupal_set_message(t('Created new vocabulary %name.', array('%name'
=> $form_state['values']['name'])));
2162
      watchdog('taxonomy', 'Created new vocabulary %name.', array('%name'
=> $form_state['values']['name']), WATCHDOG_NOTICE, l(t('edit'),
'admin/content/taxonomy/' . $form_state['values']['vid']));
2172
      break;
2181
    case SAVED_UPDATED:
2191
      drupal_set_message(t('Updated vocabulary %name.', array('%name' =>
$form_state['values']['name'])));
2201
      watchdog('taxonomy', 'Updated vocabulary %name.', array('%name' =>
$form_state['values']['name']), WATCHDOG_NOTICE, l(t('edit'),
'admin/content/taxonomy/' . $form_state['values']['vid']));
2211
      break;
2220
  }
223
2243
  $form_state['vid'] = $form_state['values']['vid'];
2253
  $form_state['redirect'] = 'admin/content/taxonomy';
2263
  return;
2270
}
228
229
/**
230
 * Form builder for the taxonomy terms overview.
231
 *
232
 * Display a tree of all the terms in a vocabulary, with options to edit
233
 * each one. The form is made drag and drop by the theme function.
234
 *
235
 * @ingroup forms
236
 * @see taxonomy_overview_terms_submit()
237
 * @see theme_taxonomy_overview_terms()
238
 */
23928
function taxonomy_overview_terms(&$form_state, $vocabulary) {
2405
  global $pager_page_array, $pager_total, $pager_total_items;
241
242
  // Check for confirmation forms.
2435
  if (isset($form_state['confirm_reset_alphabetical'])) {
2440
    return taxonomy_vocabulary_confirm_reset_alphabetical($form_state,
$vocabulary->vid);
2450
  }
246
247
  $form = array(
2485
    '#vocabulary' => (array)$vocabulary,
2495
    '#tree' => TRUE,
2505
    '#parent_fields' => FALSE,
2515
  );
252
2535
  $page            = isset($_GET['page']) ? $_GET['page'] : 0;
2545
  $page_increment  = variable_get('taxonomy_terms_per_page_admin', 100); 
// Number of terms per page.
2555
  $page_entries    = 0;   // Elements shown on this page.
2565
  $before_entries  = 0;   // Elements at the root level before this page.
2575
  $after_entries   = 0;   // Elements at the root level after this page.
2585
  $root_entries    = 0;   // Elements at the root level on this page.
259
260
  // Terms from previous and next pages are shown if the term tree would
have
261
  // been cut in the middle. Keep track of how many extra terms we show on
each
262
  // page of terms.
2635
  $back_peddle    = NULL;
2645
  $forward_peddle = 0;
265
266
  // An array of the terms to be displayed on this page.
2675
  $current_page = array();
268
269
  // Case for free tagging.
2705
  if ($vocabulary->tags) {
271
    // We are not calling taxonomy_get_tree because that might fail with a
big
272
    // number of tags in the freetagging vocabulary.
2732
    $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);
2742
    $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 =
:vid'), array(':vid' => $vocabulary->vid));
2752
    while ($term = db_fetch_object($results)) {
2761
      $key = 'tid:' . $term->tid . ':0';
2771
      $current_page[$key] = $term;
2781
      $page_entries++;
2791
    }
2802
  }
281
  // Case for restricted vocabulary.
282
  else {
2833
    $term_deltas = array();
2843
    $tree = taxonomy_get_tree($vocabulary->vid);
2853
    $term = current($tree);
286
    do {
287
      // In case this tree is completely empty.
2883
      if (empty($term)) {
2890
        break;
2900
      }
291
      // Count entries before the current page.
2923
      if ($page && ($page * $page_increment) > $before_entries &&
!isset($back_peddle)) {
2930
        $before_entries++;
2940
        continue;
2950
      }
296
      // Count entries after the current page.
2973
      elseif ($page_entries > $page_increment && isset($complete_tree)) {
2980
        $after_entries++;
2990
        continue;
3000
      }
301
302
      // Do not let a term start the page that is not at the root.
3033
      if (isset($term->depth) && ($term->depth > 0) &&
!isset($back_peddle)) {
3040
        $back_peddle = 0;
3050
        while ($pterm = prev($tree)) {
3060
          $before_entries--;
3070
          $back_peddle++;
3080
          if ($pterm->depth == 0) {
3090
            prev($tree);
3100
            continue 2; // Jump back to the start of the root level parent.
3110
          }
3120
        }
3130
      }
3143
      $back_peddle = isset($back_peddle) ? $back_peddle : 0;
315
316
      // Continue rendering the tree until we reach the a new root item.
3173
      if ($page_entries >= $page_increment + $back_peddle + 1 &&
$term->depth == 0 && $root_entries > 1) {
3180
        $complete_tree = TRUE;
319
        // This new item at the root level is the first item on the next
page.
3200
        $after_entries++;
3210
        continue;
3220
      }
3233
      if ($page_entries >= $page_increment + $back_peddle) {
3240
        $forward_peddle++;
3250
      }
326
327
      // Finally, if we've gotten down this far, we're rendering a term on
this page.
3283
      $page_entries++;
3293
      $term_deltas[$term->tid] = isset($term_deltas[$term->tid]) ?
$term_deltas[$term->tid] + 1 : 0;
3303
      $key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid];
331
332
      // Keep track of the first term displayed on this page.
3333
      if ($page_entries == 1) {
3343
        $form['#first_tid'] = $term->tid;
3353
      }
336
      // Keep a variable to make sure at least 2 root elements are
displayed.
3373
      if ($term->parents[0] == 0) {
3383
        $root_entries++;
3393
      }
3403
      $current_page[$key] = $term;
3413
    } while ($term = next($tree));
342
343
    // Because we didn't use a pager query, set the necessary pager
variables.
3443
    $total_entries = $before_entries + $page_entries + $after_entries;
3453
    $pager_total_items[0] = $total_entries;
3463
    $pager_page_array[0] = $page;
3473
    $pager_total[0] = ceil($total_entries / $page_increment);
348
  }
349
350
  // If this form was already submitted once, it's probably hit a
validation
351
  // error. Ensure the form is rebuilt in the same order as the user
submitted.
3525
  if (!empty($form_state['post'])) {
3530
    $order = array_flip(array_keys($form_state['post'])); // Get the $_POST
order.
3540
    $current_page = array_merge($order, $current_page); // Update our form
with the new order.
3550
    foreach ($current_page as $key => $term) {
356
      // Verify this is a term for the current page and set at the current
depth.
3570
      if (is_array($form_state['post'][$key]) &&
is_numeric($form_state['post'][$key]['tid'])) {
3580
        $current_page[$key]->depth = $form_state['post'][$key]['depth'];
3590
      }
360
      else {
3610
        unset($current_page[$key]);
362
      }
3630
    }
3640
  }
365
366
  // Build the actual form.
3675
  foreach ($current_page as $key => $term) {
368
    // Save the term for the current page so we don't have to load it a
second time.
3694
    $form[$key]['#term'] = (array)$term;
3704
    if (isset($term->parents)) {
3713
      $form[$key]['#term']['parent'] = $term->parent = $term->parents[0];
3723
      unset($form[$key]['#term']['parents'], $term->parents);
3733
    }
374
3754
    $form[$key]['view'] = array('#markup' => l($term->name,
"taxonomy/term/$term->tid"));
3764
    if (!$vocabulary->tags && $vocabulary->hierarchy < 2 && count($tree) >
1) {
3772
      $form['#parent_fields'] = TRUE;
3782
      $form[$key]['tid'] = array(
3792
        '#type' => 'hidden',
3802
        '#value' => $term->tid
3812
      );
3822
      $form[$key]['parent'] = array(
3832
        '#type' => 'hidden',
384
        // Yes, default_value on a hidden. It needs to be changeable by the
javascript.
3852
        '#default_value' => $term->parent,
386
      );
3872
      $form[$key]['depth'] = array(
3882
        '#type' => 'hidden',
389
        // Same as above, the depth is modified by javascript, so it's a
default_value.
3902
        '#default_value' => $term->depth,
391
      );
3922
    }
3934
    $form[$key]['edit'] = array('#markup' => l(t('edit'), 'taxonomy/term/'
. $term->tid . '/edit', array('query' => drupal_get_destination())));
3944
  }
395
3965
  $form['#total_entries'] = $total_entries;
3975
  $form['#page_increment'] = $page_increment;
3985
  $form['#page_entries'] = $page_entries;
3995
  $form['#back_peddle'] = $back_peddle;
4005
  $form['#forward_peddle'] = $forward_peddle;
4015
  $form['#empty_text'] = t('No terms available.');
402
4035
  if (!$vocabulary->tags && $vocabulary->hierarchy < 2 && count($tree) > 1)
{
4042
    $form['submit'] = array(
4052
      '#type' => 'submit',
4062
      '#value' => t('Save')
4072
    );
4082
    $form['reset_alphabetical'] = array(
4092
      '#type' => 'submit',
4102
      '#value' => t('Reset to alphabetical')
4112
    );
4122
    $form['destination'] = array(
4132
      '#type' => 'hidden',
4142
      '#value' => $_GET['q'] . (isset($_GET['page']) ? '?page=' .
$_GET['page'] : '')
4152
    );
4162
  }
417
4185
  return $form;
4190
}
420
421
/**
422
 * Submit handler for terms overview form.
423
 *
424
 * Rather than using a textfield or weight field, this form depends
entirely
425
 * upon the order of form elements on the page to determine new weights.
426
 *
427
 * Because there might be hundreds or thousands of taxonomy terms that need
to
428
 * be ordered, terms are weighted from 0 to the number of terms in the
429
 * vocabulary, rather than the standard -10 to 10 scale. Numbers are sorted
430
 * lowest to highest, but are not necessarily sequential. Numbers may be
skipped
431
 * when a term has children so that reordering is minimal when a child is
432
 * added or removed from a term.
433
 *
434
 * @see taxonomy_overview_terms()
435
 */
43628
function taxonomy_overview_terms_submit($form, &$form_state) {
4370
  if ($form_state['clicked_button']['#value'] == t('Reset to
alphabetical')) {
438
    // Execute the reset action.
4390
    if ($form_state['values']['reset_alphabetical'] === TRUE) {
4400
      return taxonomy_vocabulary_confirm_reset_alphabetical_submit($form,
$form_state);
4410
    }
442
    // Rebuild the form to confirm the reset action.
4430
    $form_state['rebuild'] = TRUE;
4440
    $form_state['confirm_reset_alphabetical'] = TRUE;
4450
    return;
4460
  }
447
4480
  $order = array_flip(array_keys($form['#post'])); // Get the $_POST order.
4490
  $form_state['values'] = array_merge($order, $form_state['values']); //
Update our original form with the new order.
450
4510
  $vocabulary = $form['#vocabulary'];
4520
  $hierarchy = 0; // Update the current hierarchy type as we go.
453
4540
  $changed_terms = array();
4550
  $tree = taxonomy_get_tree($vocabulary['vid']);
456
4570
  if (empty($tree)) {
4580
    return;
4590
  }
460
461
  // Build a list of all terms that need to be updated on previous pages.
4620
  $weight = 0;
4630
  $term = (array)$tree[0];
4640
  while ($term['tid'] != $form['#first_tid']) {
4650
    if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
4660
      $term['parent'] = $term['parents'][0];
4670
      $term['weight'] = $weight;
4680
      $changed_terms[$term['tid']] = $term;
4690
    }
4700
    $weight++;
4710
    $hierarchy = $term['parents'][0] != 0 ? 1 : $hierarchy;
4720
    $term = (array)$tree[$weight];
4730
  }
474
475
  // Renumber the current page weights and assign any new parents.
4760
  $level_weights = array();
4770
  foreach ($form_state['values'] as $tid => $values) {
4780
    if (isset($form[$tid]['#term'])) {
4790
      $term = $form[$tid]['#term'];
480
      // Give terms at the root level a weight in sequence with terms on
previous pages.
4810
      if ($values['parent'] == 0 && $term['weight'] != $weight) {
4820
        $term['weight'] = $weight;
4830
        $changed_terms[$term['tid']] = $term;
4840
      }
485
      // Terms not at the root level can safely start from 0 because
they're all on this page.
4860
      elseif ($values['parent'] > 0) {
4870
        $level_weights[$values['parent']] =
isset($level_weights[$values['parent']]) ?
$level_weights[$values['parent']] + 1 : 0;
4880
        if ($level_weights[$values['parent']] != $term['weight']) {
4890
          $term['weight'] = $level_weights[$values['parent']];
4900
          $changed_terms[$term['tid']] = $term;
4910
        }
4920
      }
493
      // Update any changed parents.
4940
      if ($values['parent'] != $term['parent']) {
4950
        $term['parent'] = $values['parent'];
4960
        $changed_terms[$term['tid']] = $term;
4970
      }
4980
      $hierarchy = $term['parent'] != 0 ? 1 : $hierarchy;
4990
      $weight++;
5000
    }
5010
  }
502
503
  // Build a list of all terms that need to be updated on following pages.
5040
  for ($weight; $weight < count($tree); $weight++) {
5050
    $term = (array)$tree[$weight];
5060
    if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
5070
      $term['parent'] = $term['parents'][0];
5080
      $term['weight'] = $weight;
5090
      $changed_terms[$term['tid']] = $term;
5100
    }
5110
    $hierarchy = $term['parents'][0] != 0 ? 1 : $hierarchy;
5120
  }
513
514
  // Save all updated terms.
5150
  foreach ($changed_terms as $term) {
5160
    taxonomy_save_term($term);
5170
  }
518
519
  // Update the vocabulary hierarchy to flat or single hierarchy.
5200
  if ($vocabulary['hierarchy'] != $hierarchy) {
5210
    $vocabulary['hierarchy'] = $hierarchy;
5220
    taxonomy_save_vocabulary($vocabulary);
5230
  }
5240
}
525
526
/**
527
 * Theme the terms overview as a sortable list of terms.
528
 *
529
 * @ingroup themeable
530
 * @see taxonomy_overview_terms()
531
 */
53228
function theme_taxonomy_overview_terms($form) {
5335
  $page_increment  = $form['#page_increment'];
5345
  $page_entries    = $form['#page_entries'];
5355
  $back_peddle     = $form['#back_peddle'];
5365
  $forward_peddle  = $form['#forward_peddle'];
537
538
  // Add drag and drop if parent fields are present in the form.
5395
  if ($form['#parent_fields']) {
5402
    drupal_add_tabledrag('taxonomy', 'match', 'parent', 'term-parent',
'term-parent', 'term-id', FALSE);
5412
    drupal_add_tabledrag('taxonomy', 'depth', 'group', 'term-depth', NULL,
NULL, FALSE);
5422
    drupal_add_js(drupal_get_path('module', 'taxonomy') . '/taxonomy.js');
5432
    drupal_add_js(array('taxonomy' => array('backPeddle' => $back_peddle,
'forwardPeddle' => $forward_peddle)), 'setting');
5442
    drupal_add_css(drupal_get_path('module', 'taxonomy') .
'/taxonomy.css');
5452
  }
546
5475
  $errors = form_get_errors() != FALSE ? form_get_errors() : array();
5485
  $rows = array();
5495
  foreach (element_children($form) as $key) {
5505
    if (isset($form[$key]['#term'])) {
5514
      $term = &$form[$key];
552
5534
      $row = array();
5544
      $row[] = (isset($term['#term']['depth']) && $term['#term']['depth'] >
0 ? theme('indentation', $term['#term']['depth']) : '') .
drupal_render($term['view']);
5554
      if ($form['#parent_fields']) {
5562
        $term['tid']['#attributes']['class'] = 'term-id';
5572
        $term['parent']['#attributes']['class'] = 'term-parent';
5582
        $term['depth']['#attributes']['class'] = 'term-depth';
5592
        $row[0] .= drupal_render($term['parent']) .
drupal_render($term['tid']) . drupal_render($term['depth']);
5602
      }
5614
      $row[] = drupal_render($term['edit']);
562
5634
      $row = array('data' => $row);
5644
      $rows[$key] = $row;
5654
    }
5665
  }
567
568
  // Add necessary classes to rows.
5695
  $row_position = 0;
5705
  foreach ($rows as $key => $row) {
5714
    $classes = array();
5724
    if (isset($form['#parent_fields'])) {
5734
      $classes[] = 'draggable';
5744
    }
575
576
    // Add classes that mark which terms belong to previous and next pages.
5774
    if ($row_position < $back_peddle || $row_position >= $page_entries -
$forward_peddle) {
5780
      $classes[] = 'taxonomy-term-preview';
5790
    }
580
5814
    if ($row_position !== 0 && $row_position !== count($rows) - 1) {
5821
      if ($row_position == $back_peddle - 1 || $row_position ==
$page_entries - $forward_peddle - 1) {
5830
        $classes[] = 'taxonomy-term-divider-top';
5840
      }
5851
      elseif ($row_position == $back_peddle || $row_position ==
$page_entries - $forward_peddle) {
5860
        $classes[] = 'taxonomy-term-divider-bottom';
5870
      }
5881
    }
589
590
    // Add an error class if this row contains a form error.
5914
    foreach ($errors as $error_key => $error) {
5920
      if (strpos($error_key, $key) === 0) {
5930
        $classes[] = 'error';
5940
      }
5950
    }
5964
    $rows[$key]['class'] = implode(' ', $classes);
5974
    $row_position++;
5984
  }
599
6005
  if (empty($rows)) {
6011
    $rows[] = array(array('data' => $form['#empty_text'], 'colspan' =>
'2'));
6021
  }
603
6045
  $header = array(t('Name'), t('Operations'));
6055
  $output = theme('table', $header, $rows, array('id' => 'taxonomy'));
6065
  $output .= drupal_render($form);
6075
  $output .= theme('pager', NULL, $page_increment);
608
6095
  return $output;
6100
}
611
612
/**
613
 * Form function for the term edit form.
614
 *
615
 * @ingroup forms
616
 * @see taxonomy_form_term_submit()
617
 */
61828
function taxonomy_form_term(&$form_state, $vocabulary, $edit = array()) {
619
  $edit += array(
62010
    'name' => '',
62110
    'description' => '',
62210
    'tid' => NULL,
62310
    'weight' => 0,
6240
  );
625
62610
  $parent = array_keys(taxonomy_get_parents($edit['tid']));
62710
  $form['#term'] = $edit;
62810
  $form['#term']['parent'] = $parent;
62910
  $form['#vocabulary'] = (array)$vocabulary;
63010
  $form['#vocabulary']['nodes'] = drupal_map_assoc($vocabulary->nodes);
631
632
  // Check for confirmation forms.
63310
  if (isset($form_state['confirm_delete'])) {
6340
    return array_merge($form, taxonomy_term_confirm_delete($form_state,
$edit['tid']));
6350
  }
63610
  elseif (isset($form_state['confirm_parents'])) {
6370
    return array_merge($form, taxonomy_term_confirm_parents($form_state,
$vocabulary));
6380
  }
639
64010
  $form['identification'] = array(
64110
    '#type' => 'fieldset',
64210
    '#title' => t('Identification'),
64310
    '#collapsible' => TRUE,
644
  );
64510
  $form['identification']['name'] = array(
64610
    '#type' => 'textfield',
64710
    '#title' => t('Term name'),
64810
    '#default_value' => $edit['name'],
64910
    '#maxlength' => 255,
65010
    '#description' => t('The name of this term.'),
65110
    '#required' => TRUE);
65210
  $form['identification']['description'] = array(
65310
    '#type' => 'textarea',
65410
    '#title' => t('Description'),
65510
    '#default_value' => $edit['description'],
65610
    '#description' => t('A description of the term. To be displayed on
taxonomy/term pages and RSS feeds.'));
657
65810
  $form['advanced'] = array(
65910
    '#type' => 'fieldset',
66010
    '#title' => t('Advanced options'),
66110
    '#collapsible' => TRUE,
66210
    '#collapsed' => $vocabulary->hierarchy < 2,
663
  );
664
665
  // taxonomy_get_tree and taxonomy_get_parents may contain large numbers
of
666
  // items so we check for taxonomy_override_selector before loading the
667
  // full vocabulary. Contrib modules can then intercept before
668
  // hook_form_alter to provide scalable alternatives.
66910
  if (!variable_get('taxonomy_override_selector', FALSE)) {
67010
    $parent = array_keys(taxonomy_get_parents($edit['tid']));
67110
    $children = taxonomy_get_tree($vocabulary->vid, $edit['tid']);
672
673
    // A term can't be the child of itself, nor of its children.
67410
    foreach ($children as $child) {
6750
      $exclude[] = $child->tid;
6760
    }
67710
    $exclude[] = $edit['tid'];
678
67910
    $form['advanced']['parent'] = _taxonomy_term_select(t('Parents'),
'parent', $parent, $vocabulary->vid, t('Parent terms') . '.', 1, '<' .
t('root') . '>', $exclude);
68010
    $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']));
68110
  }
68210
  $form['advanced']['synonyms'] = array(
68310
    '#type' => 'textarea',
68410
    '#title' => t('Synonyms'),
68510
    '#default_value' => implode("\n", taxonomy_get_synonyms($edit['tid'])),
68610
    '#description' => t('Synonyms of this term, one synonym per line.'));
68710
  $form['advanced']['weight'] = array(
68810
    '#type' => 'textfield',
68910
    '#title' => t('Weight'),
69010
    '#size' => 6,
69110
    '#default_value' => $edit['weight'],
69210
    '#description' => t('Terms are displayed in ascending order by
weight.'),
69310
    '#required' => TRUE);
69410
  $form['vid'] = array(
69510
    '#type' => 'value',
69610
    '#value' => $vocabulary->vid);
69710
  $form['submit'] = array(
69810
    '#type' => 'submit',
69910
    '#value' => t('Save'));
700
70110
  if ($edit['tid']) {
7023
    $form['delete'] = array(
7033
      '#type' => 'submit',
7043
      '#value' => t('Delete'));
7053
    $form['tid'] = array(
7063
      '#type' => 'value',
7073
      '#value' => $edit['tid']);
7083
  }
709
  else {
7107
    $form['destination'] = array('#type' => 'hidden', '#value' =>
$_GET['q']);
711
  }
712
71310
  return $form;
7140
}
715
716
/**
717
 * Validation handler for the term edit form. Ensure numeric weight values.
718
 *
719
 * @see taxonomy_form_term()
720
 */
72128
function taxonomy_form_term_validate($form, &$form_state) {
7223
  if (isset($form_state['values']['weight']) &&
!is_numeric($form_state['values']['weight'])) {
7230
    form_set_error('weight', t('Weight value must be numeric.'));
7240
  }
7253
}
726
727
/**
728
 * Submit handler to insert or update a term.
729
 *
730
 * @see taxonomy_form_term()
731
 */
73228
function taxonomy_form_term_submit($form, &$form_state) {
7333
  if ($form_state['clicked_button']['#value'] == t('Delete')) {
734
    // Execute the term deletion.
7350
    if ($form_state['values']['delete'] === TRUE) {
7360
      return taxonomy_term_confirm_delete_submit($form, $form_state);
7370
    }
738
    // Rebuild the form to confirm term deletion.
7390
    $form_state['rebuild'] = TRUE;
7400
    $form_state['confirm_delete'] = TRUE;
7410
    return;
7420
  }
743
  // Rebuild the form to confirm enabling multiple parents.
7443
  elseif ($form_state['clicked_button']['#value'] == t('Save') &&
!$form['#vocabulary']['tags'] && count($form_state['values']['parent']) > 1
&& $form['#vocabulary']['hierarchy'] < 2) {
7450
    $form_state['rebuild'] = TRUE;
7460
    $form_state['confirm_parents'] = TRUE;
7470
    return;
7480
  }
749
7503
  switch (taxonomy_save_term($form_state['values'])) {
7513
    case SAVED_NEW:
7522
      drupal_set_message(t('Created new term %term.', array('%term' =>
$form_state['values']['name'])));
7532
      watchdog('taxonomy', 'Created new term %term.', array('%term' =>
$form_state['values']['name']), WATCHDOG_NOTICE, l(t('edit'),
'taxonomy/term/' . $form_state['values']['tid'] . '/edit'));
7542
      break;
7551
    case SAVED_UPDATED:
7561
      drupal_set_message(t('Updated term %term.', array('%term' =>
$form_state['values']['name'])));
7571
      watchdog('taxonomy', 'Updated term %term.', array('%term' =>
$form_state['values']['name']), WATCHDOG_NOTICE, l(t('edit'),
'taxonomy/term/' . $form_state['values']['tid'] . '/edit'));
7581
      break;
7590
  }
760
7613
  if (!$form['#vocabulary']['tags']) {
7621
    $current_parent_count = count($form_state['values']['parent']);
7631
    $previous_parent_count = count($form['#term']['parent']);
764
    // Root doesn't count if it's the only parent.
7651
    if ($current_parent_count == 1 &&
isset($form_state['values']['parent'][''])) {
7660
      $current_parent_count = 0;
7670
      $form_state['values']['parent'] = array();
7680
    }
769
770
    // If the number of parents has been reduced to one or none, do a check
on the
771
    // parents of every term in the vocabulary value.
7721
    if ($current_parent_count < $previous_parent_count &&
$current_parent_count < 2) {
7730
      taxonomy_check_vocabulary_hierarchy($form['#vocabulary'],
$form_state['values']);
7740
    }
775
    // If we've increased the number of parents and this is a single or
flat
776
    // hierarchy, update the vocabulary immediately.
7771
    elseif ($current_parent_count > $previous_parent_count &&
$form['#vocabulary']['hierarchy'] < 2) {
7781
      $form['#vocabulary']['hierarchy'] = $current_parent_count == 1 ? 1 :
2;
7791
      taxonomy_save_vocabulary($form['#vocabulary']);
7801
    }
7811
  }
782
7833
  $form_state['tid'] = $form_state['values']['tid'];
7843
  $form_state['redirect'] = 'admin/content/taxonomy';
7853
  return;
7860
}
787
788
/**
789
 * Form builder for the confirmation of multiple term parents.
790
 *
791
 * @ingroup forms
792
 * @see taxonomy_form_term()
793
 */
79428
function taxonomy_term_confirm_parents(&$form_state, $vocabulary) {
7950
  $form = array();
7960
  foreach (element_children($form_state['values']) as $key) {
7970
    $form[$key] = array(
7980
      '#type' => 'value',
7990
      '#value' => $form_state['values'][$key],
800
    );
8010
  }
8020
  $question = t('Set multiple term parents?');
8030
  $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>';
8040
  $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>';
8050
  return confirm_form($form, $question, drupal_get_destination(),
$description, t('Set multiple parents'));
8060
}
807
808
/**
809
 * Form builder for the term delete form.
810
 *
811
 * @ingroup forms
812
 * @see taxonomy_term_confirm_delete_submit()
813
 */
81428
function taxonomy_term_confirm_delete(&$form_state, $tid) {
8150
  $term = taxonomy_term_load($tid);
816
8170
  $form['type'] = array('#type' => 'value', '#value' => 'term');
8180
  $form['name'] = array('#type' => 'value', '#value' => $term->name);
8190
  $form['tid'] = array('#type' => 'value', '#value' => $tid);
8200
  $form['delete'] = array('#type' => 'value', '#value' => TRUE);
8210
  return confirm_form($form,
8220
                  t('Are you sure you want to delete the term %title?',
8230
                  array('%title' => $term->name)),
8240
                  'admin/content/taxonomy',
8250
                  t('Deleting a term will delete all its children if there
are any. This action cannot be undone.'),
8260
                  t('Delete'),
8270
                  t('Cancel'));
8280
}
829
830
/**
831
 * Submit handler to delete a term after confirmation.
832
 *
833
 * @see taxonomy_term_confirm_delete()
834
 */
83528
function taxonomy_term_confirm_delete_submit($form, &$form_state) {
8360
  taxonomy_del_term($form_state['values']['tid']);
8370
  taxonomy_check_vocabulary_hierarchy($form['#vocabulary'],
$form_state['values']);
8380
  drupal_set_message(t('Deleted term %name.', array('%name' =>
$form_state['values']['name'])));
8390
  watchdog('taxonomy', 'Deleted term %name.', array('%name' =>
$form_state['values']['name']), WATCHDOG_NOTICE);
8400
  $form_state['redirect'] = 'admin/content/taxonomy';
8410
  return;
8420
}
843
844
/**
845
 * Form builder for the vocabulary delete confirmation form.
846
 *
847
 * @ingroup forms
848
 * @see taxonomy_vocabulary_confirm_delete_submit()
849
 */
85028
function taxonomy_vocabulary_confirm_delete(&$form_state, $vid) {
8510
  $vocabulary = taxonomy_vocabulary_load($vid);
852
8530
  $form['#id'] = 'taxonomy_vocabulary_confirm_delete';
8540
  $form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
8550
  $form['vid'] = array('#type' => 'value', '#value' => $vid);
8560
  $form['name'] = array('#type' => 'value', '#value' => $vocabulary->name);
8570
  $form['#submit'] = array('taxonomy_vocabulary_confirm_delete_submit');
8580
  return confirm_form($form,
8590
                  t('Are you sure you want to delete the vocabulary
%title?',
8600
                  array('%title' => $vocabulary->name)),
8610
                  'admin/content/taxonomy',
8620
                  t('Deleting a vocabulary will delete all the terms in it.
This action cannot be undone.'),
8630
                  t('Delete'),
8640
                  t('Cancel'));
8650
}
866
867
/**
868
 * Submit handler to delete a vocabulary after confirmation.
869
 *
870
 * @see taxonomy_vocabulary_confirm_delete()
871
 */
87228
function taxonomy_vocabulary_confirm_delete_submit($form, &$form_state) {
8730
  $status = taxonomy_del_vocabulary($form_state['values']['vid']);
8740
  drupal_set_message(t('Deleted vocabulary %name.', array('%name' =>
$form_state['values']['name'])));
8750
  watchdog('taxonomy', 'Deleted vocabulary %name.', array('%name' =>
$form_state['values']['name']), WATCHDOG_NOTICE);
8760
  $form_state['redirect'] = 'admin/content/taxonomy';
8770
  return;
8780
}
879
880
/**
881
 * Form builder to confirm reseting a vocabulary to alphabetical order.
882
 *
883
 * @ingroup forms
884
 * @see taxonomy_vocabulary_confirm_reset_alphabetical_submit()
885
 */
88628
function taxonomy_vocabulary_confirm_reset_alphabetical(&$form_state, $vid)
{
8870
  $vocabulary = taxonomy_vocabulary_load($vid);
888
8890
  $form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
8900
  $form['vid'] = array('#type' => 'value', '#value' => $vid);
8910
  $form['name'] = array('#type' => 'value', '#value' => $vocabulary->name);
8920
  $form['reset_alphabetical'] = array('#type' => 'value', '#value' =>
TRUE);
8930
  return confirm_form($form,
8940
                  t('Are you sure you want to reset the vocabulary %title
to alphabetical order?',
8950
                  array('%title' => $vocabulary->name)),
8960
                  'admin/content/taxonomy/' . $vid,
8970
                  t('Resetting a vocabulary will discard all custom
ordering and sort items alphabetically.'),
8980
                  t('Reset to alphabetical'),
8990
                  t('Cancel'));
9000
}
901
902
/**
903
 * Submit handler to reset a vocabulary to alphabetical order after
confirmation.
904
 *
905
 * @see taxonomy_vocabulary_confirm_reset_alphabetical()
906
 */
90728
function taxonomy_vocabulary_confirm_reset_alphabetical_submit($form,
&$form_state) {
9080
  db_query('UPDATE {term_data} t SET weight = 0 WHERE vid = :vid',
array(':vid' => $form_state['values']['vid']));
9090
  drupal_set_message(t('Reset vocabulary %name to alphabetical order.',
array('%name' => $form_state['values']['name'])));
9100
  watchdog('taxonomy', 'Reset vocabulary %name to alphabetical order.',
array('%name' => $form_state['values']['name']), WATCHDOG_NOTICE);
9110
  $form_state['redirect'] = 'admin/content/taxonomy/' .
$form_state['values']['vid'];
9120
}
91328