taxonomy.module 51.2 KB
Newer Older
Dries's avatar
 
Dries committed
1
<?php
Kjartan's avatar
Kjartan committed
2
// $Id$
Dries's avatar
   
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
7
8
/**
 * @file
 * Enables the organization of content into categories.
 */

Dries's avatar
   
Dries committed
9
10
11
/**
 * Implementation of hook_perm().
 */
Kjartan's avatar
Kjartan committed
12
function taxonomy_perm() {
Dries's avatar
   
Dries committed
13
  return array('administer taxonomy');
Kjartan's avatar
Kjartan committed
14
}
Dries's avatar
 
Dries committed
15

Dries's avatar
   
Dries committed
16
17
18
19
20
21
22
23
24
25
26
/**
 * Implementation of hook_link().
 *
 * This hook is extended with $type = 'taxonomy terms' to allow themes to
 * print lists of terms associated with a node. Themes can print taxonomy
 * links with:
 *
 * if (module_exist('taxonomy')) {
 *   $this->links(taxonomy_link('taxonomy terms', $node));
 * }
 */
Dries's avatar
   
Dries committed
27
function taxonomy_link($type, $node = NULL) {
Dries's avatar
   
Dries committed
28
  if ($type == 'taxonomy terms' && $node != NULL) {
Kjartan's avatar
   
Kjartan committed
29
    $links = array();
Dries's avatar
   
Dries committed
30
    if (array_key_exists('taxonomy', $node)) {
31
      foreach ($node->taxonomy as $term) {
32
        $links[] = l($term->name, taxonomy_term_path($term), array('rel' => 'tag', 'title' => strip_tags($term->description)));
Dries's avatar
   
Dries committed
33
      }
Dries's avatar
   
Dries committed
34
35
36
    }
    return $links;
  }
Kjartan's avatar
Kjartan committed
37
38
}

39
40
41
42
43
44
45
46
function taxonomy_term_path($term) {
  $vocabulary = taxonomy_get_vocabulary($term->vid);
  if ($vocabulary->module != 'taxonomy' && $path = module_invoke($vocabulary->module, 'term_path', $term)) {
    return $path;
  }
  return 'taxonomy/term/'. $term->tid;
}

Dries's avatar
   
Dries committed
47
48
49
/**
 * Implementation of hook_menu().
 */
Dries's avatar
   
Dries committed
50
function taxonomy_menu($may_cache) {
Dries's avatar
   
Dries committed
51
  $items = array();
Dries's avatar
   
Dries committed
52

Dries's avatar
   
Dries committed
53
  if ($may_cache) {
54
55
56
    $items[] = array('path' => 'admin/taxonomy',
      'title' => t('categories'),
      'callback' => 'taxonomy_overview_vocabularies',
Dries's avatar
   
Dries committed
57
      'access' => user_access('administer taxonomy'));
58

59
60
61
62
    $items[] = array('path' => 'admin/taxonomy/list',
      'title' => t('list'),
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -10);
63

64
65
66
    $items[] = array('path' => 'admin/taxonomy/add/vocabulary',
      'title' => t('add vocabulary'),
      'callback' => 'taxonomy_admin_vocabulary_edit',
Dries's avatar
   
Dries committed
67
68
69
      'access' => user_access('administer taxonomy'),
      'type' => MENU_LOCAL_TASK);

70
71
72
    $items[] = array('path' => 'admin/taxonomy/edit/vocabulary',
      'title' => t('edit vocabulary'),
      'callback' => 'taxonomy_admin_vocabulary_edit',
73
74
75
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);

76
77
78
    $items[] = array('path' => 'admin/taxonomy/edit/term',
      'title' => t('edit term'),
      'callback' => 'taxonomy_admin_term_edit',
79
80
81
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);

82
83
    $items[] = array('path' => 'taxonomy/term',
      'title' => t('taxonomy term'),
Dries's avatar
   
Dries committed
84
85
86
      'callback' => 'taxonomy_term_page',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
Steven Wittens's avatar
Steven Wittens committed
87

88
89
    $items[] = array('path' => 'taxonomy/autocomplete',
      'title' => t('autocomplete taxonomy'),
Steven Wittens's avatar
Steven Wittens committed
90
91
92
      'callback' => 'taxonomy_autocomplete',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
Dries's avatar
   
Dries committed
93
  }
Dries's avatar
Dries committed
94
95
  else {
    if (is_numeric(arg(2))) {
96
97
98
99
      $items[] = array('path' => 'admin/taxonomy/' . arg(2),
        'title' => t('list terms'),
        'callback' => 'taxonomy_overview_terms',
        'callback arguments' => array(arg(2)),
Dries's avatar
Dries committed
100
101
102
        'access' => user_access('administer taxonomy'),
        'type' => MENU_CALLBACK);

103
104
105
106
      $items[] = array('path' => 'admin/taxonomy/' . arg(2) . '/list',
        'title' => t('list'),
        'type' => MENU_DEFAULT_LOCAL_TASK,
        'weight' => -10);
Dries's avatar
Dries committed
107

108
109
110
111
      $items[] = array('path' => 'admin/taxonomy/' . arg(2) . '/add/term',
        'title' => t('add term'),
        'callback' => 'taxonomy_admin_term_add',
        'callback arguments' => array(arg(2)),
Dries's avatar
Dries committed
112
113
114
115
        'access' => user_access('administer taxonomy'),
        'type' => MENU_LOCAL_TASK);
    }
  }
Dries's avatar
   
Dries committed
116

Dries's avatar
   
Dries committed
117
118
  return $items;
}
Dries's avatar
 
Dries committed
119

120
121
122
123
/**
 * List and manage vocabularies.
 */
function taxonomy_overview_vocabularies() {
124
  $vocabularies = taxonomy_get_vocabularies();
125
126
127
128
129
130
  foreach ($vocabularies as $vocabulary) {
    $types = array();
    foreach ($vocabulary->nodes as $type) {
      $node_type = node_get_name($type);
      $types[] = $node_type ? $node_type : $type;
    }
131
132
133
134
    $form[$vocabulary->vid]['name'] = array('#value' => check_plain($vocabulary->name));
    $form[$vocabulary->vid]['type'] = array('#value' => implode(', ', $types));
    $form[$vocabulary->vid]['edit'] = array('#value' => l(t('edit vocabulary'), "admin/taxonomy/edit/vocabulary/$vocabulary->vid"));
    $form[$vocabulary->vid]['list'] = array('#value' => l(t('list terms'), "admin/taxonomy/$vocabulary->vid"));
135
  }
136

137
138
  return drupal_get_form('taxonomy_overview_vocabularies', $form);
}
139

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**
 * Theme overview vocabularies.
 */
function theme_taxonomy_overview_vocabularies($form) {
  $rows = array();
  foreach (element_children($form) as $key) {
    if (isset($form[$key]['name'])) {
      $row = array();
      $row[] = form_render($form[$key]['name']);
      $row[] = form_render($form[$key]['type']);
      $row[] = form_render($form[$key]['edit']);
      $row[] = form_render($form[$key]['list']);
      $rows[] = $row;
    }
  }
155
156
157
  if (!$rows) {
    $rows[] = array(array('data' => t('No categories available.'), 'colspan' => '4', 'class' => 'message'));
  }
158
  $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '2'));
159

160
161
162
  $output = theme('table', $header, $rows, array('id' => 'taxonomy'));
  $output .= form_render($form);
  return $output;
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
}

/**
 * Display a tree of all the terms in a vocabulary, with options to edit
 * each one.
 */
function taxonomy_overview_terms($vid) {
  $destination = drupal_get_destination();

  $header = array(t('Name'), t('Operations'));
  $vocabulary = taxonomy_get_vocabulary($vid);

  drupal_set_title(check_plain($vocabulary->name));
  $start_from      = $_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

  $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)) { continue; }
    $rows[] = array(_taxonomy_depth($term->depth) . ' ' . l($term->name, "taxonomy/term/$term->tid"), l(t('edit'), "admin/taxonomy/edit/term/$term->tid", array(), $destination));
    $displayed_count++; // we're counting tids displayed
  }

  if (!$rows) {
    $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '2'));
  }

  $GLOBALS['pager_page_array'][] = $start_from;  // FIXME
  $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME

  if ($total_entries >= $page_increment) {
    $rows[] = array(array('data' => theme('pager', NULL, $page_increment), 'colspan' => '2'));
  }

  return theme('table', $header, $rows, array('id' => 'taxonomy'));
}

/**
 * Display form for adding and editing vocabularies.
 */
Kjartan's avatar
Kjartan committed
206
function taxonomy_form_vocabulary($edit = array()) {
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  $form['name'] = array('#type' => 'textfield',
    '#title' => t('Vocabulary name'),
    '#default_value' => $edit['name'],
    '#maxlength' => 64,
    '#description' => t('The name for this vocabulary.  Example: "Topic".'),
    '#required' => TRUE,
  );
  $form['description'] = array('#type' => 'textarea',
    '#title' => t('Description'),
    '#default_value' => $edit['description'],
    '#description' => t('Description of the vocabulary; can be used by modules.'),
  );
  $form['help'] = array('#type' => 'textfield',
    '#title' => t('Help text'),
    '#default_value' => $edit['help'],
    '#maxlength' => 255,
    '#description' => t('Instructions to present to the user when choosing a term.'),
  );
  $form['nodes'] = array('#type' => 'checkboxes',
    '#title' => t('Types'),
    '#default_value' => $edit['nodes'],
    '#options' => node_get_types(),
    '#description' => t('A list of node types you want to associate with this vocabulary.'),
    '#required' => TRUE,
  );
  $form['hierarchy'] = array('#type' => 'radios',
    '#title' => t('Hierarchy'),
    '#default_value' => $edit['hierarchy'],
    '#options' => array(t('Disabled'), t('Single'), t('Multiple')),
    '#description' => t('Allows <a href="%help-url">a tree-like hierarchy</a> between terms of this vocabulary.', array('%help-url' => url('admin/help/taxonomy', NULL, NULL, 'hierarchy'))),
  );
  $form['relations'] = array('#type' => 'checkbox',
    '#title' => t('Related terms'),
    '#default_value' => $edit['relations'],
    '#description' => t('Allows <a href="%help-url">related terms</a> in this vocabulary.', array('%help-url' => url('admin/help/taxonomy', NULL, NULL, 'related-terms'))),
  );
  $form['tags'] = array('#type' => 'checkbox',
    '#title' => t('Free tagging'),
    '#default_value' => $edit['tags'],
    '#description' => t('Content is categorized by typing terms instead of choosing from a list.'),
  );
  $form['multiple'] = array('#type' => 'checkbox',
    '#title' => t('Multiple select'),
    '#default_value' => $edit['multiple'],
    '#description' => t('Allows nodes to have more than one term from this vocabulary (always true for free tagging).'),
  );
  $form['required'] = array('#type' => 'checkbox',
    '#title' => t('Required'),
    '#default_value' => $edit['required'],
    '#description' => t('If enabled, every node <strong>must</strong> have at least one term in this vocabulary.'),
  );
  $form['weight'] = array('#type' => 'weight',
    '#title' => t('Weight'),
    '#default_value' => $edit['weight'],
    '#description' => t('In listings, the heavier vocabularies will sink and the lighter vocabularies will be positioned nearer the top.'),
  );
263

264
265
266
267
  // Add extra vocabulary form elements.
  $extra = module_invoke_all('taxonomy', 'form', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
268
      $extra[$key]['#weight'] = isset($extra[$key]['#weight']) ? $extra[$key]['#weight'] : -18;
269
270
271
    }
    $form = array_merge($form, $extra);
  }
Dries's avatar
 
Dries committed
272

273
  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
Dries's avatar
   
Dries committed
274
  if ($edit['vid']) {
275
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
276
    $form['vid'] = array('#type' => 'value', '#value' => $edit['vid']);
277
    $form['module'] = array('#type' => 'value', '#value' => $edit['module']);
Dries's avatar
 
Dries committed
278
  }
279
  return drupal_get_form('taxonomy_form_vocabulary', $form);
Kjartan's avatar
Kjartan committed
280
}
Kjartan's avatar
Kjartan committed
281

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/**
 * Accept the form submission for a vocabulary and save the results.
 */
function taxonomy_form_vocabulary_submit($form_id, $form_values) {
  // Fix up the nodes array to remove unchecked nodes.
  $form_values['nodes'] = array_filter($form_values['nodes']);
  switch (taxonomy_save_vocabulary($form_values)) {
  case SAVED_NEW:
    drupal_set_message(t('Created new vocabulary %name.', array('%name' => theme('placeholder', $form_values['name']))));
    break;
  case SAVED_UPDATED:
    drupal_set_message(t('Updated vocabulary %name.', array('%name' => theme('placeholder', $form_values['name']))));
    break;
  }
  return 'admin/taxonomy';
}

299
function taxonomy_save_vocabulary(&$edit) {
300
  $edit['nodes'] = empty($edit['nodes']) ? array() : $edit['nodes'];
Dries's avatar
   
Dries committed
301

Dries's avatar
   
Dries committed
302
  if ($edit['vid'] && $edit['name']) {
303
    db_query("UPDATE {vocabulary} SET name = '%s', description = '%s', help = '%s', multiple = %d, required = %d, hierarchy = %d, relations = %d, tags = %d, weight = %d, module = '%s' WHERE vid = %d", $edit['name'], $edit['description'], $edit['help'], $edit['multiple'], $edit['required'], $edit['hierarchy'], $edit['relations'], $edit['tags'], $edit['weight'], isset($edit['module']) ? $edit['module'] : 'taxonomy', $edit['vid']);
Dries's avatar
   
Dries committed
304
    db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']);
305
    foreach ($edit['nodes'] as $type => $selected) {
Dries's avatar
   
Dries committed
306
307
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries's avatar
   
Dries committed
308
    module_invoke_all('taxonomy', 'update', 'vocabulary', $edit);
309
    $status = SAVED_UPDATED;
Dries's avatar
 
Dries committed
310
  }
Dries's avatar
   
Dries committed
311
  else if ($edit['vid']) {
312
    $status = taxonomy_del_vocabulary($edit['vid']);
Kjartan's avatar
Kjartan committed
313
314
  }
  else {
315
316
    $edit['vid'] = db_next_id('{vocabulary}_vid');
    db_query("INSERT INTO {vocabulary} (vid, name, description, help, multiple, required, hierarchy, relations, tags, weight, module) VALUES (%d, '%s', '%s', '%s', %d, %d, %d, %d, %d, %d, '%s')", $edit['vid'], $edit['name'], $edit['description'], $edit['help'], $edit['multiple'], $edit['required'], $edit['hierarchy'], $edit['relations'], $edit['tags'], $edit['weight'], isset($edit['module']) ? $edit['module'] : 'taxonomy');
317
    foreach ($edit['nodes'] as $type => $selected) {
Dries's avatar
   
Dries committed
318
319
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries's avatar
   
Dries committed
320
    module_invoke_all('taxonomy', 'insert', 'vocabulary', $edit);
321
    $status = SAVED_NEW;
Kjartan's avatar
Kjartan committed
322
  }
Dries's avatar
   
Dries committed
323
324

  cache_clear_all();
Dries's avatar
   
Dries committed
325

326
  return $status;
Kjartan's avatar
Kjartan committed
327
}
Dries's avatar
 
Dries committed
328

Kjartan's avatar
Kjartan committed
329
function taxonomy_del_vocabulary($vid) {
330
  $vocabulary = (array) taxonomy_get_vocabulary($vid);
Dries's avatar
   
Dries committed
331

Dries's avatar
   
Dries committed
332
  db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
Dries's avatar
   
Dries committed
333
  db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
Dries's avatar
   
Dries committed
334
  $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
Kjartan's avatar
Kjartan committed
335
336
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries's avatar
 
Dries committed
337
  }
Dries's avatar
   
Dries committed
338

Dries's avatar
   
Dries committed
339
  module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
Dries's avatar
   
Dries committed
340

Dries's avatar
   
Dries committed
341
342
  cache_clear_all();

343
  return SAVED_DELETED;
Dries's avatar
   
Dries committed
344
345
346
347
348
}

function _taxonomy_confirm_del_vocabulary($vid) {
  $vocabulary = taxonomy_get_vocabulary($vid);

349
350
351
352
  $form['type'] = array('#type' => 'value', '#value' => 'vocabulary');
  $form['vid'] = array('#type' => 'value', '#value' => $vid);
  $form['name'] = array('#type' => 'value', '#value' => $vocabulary->name);
  return confirm_form('taxonomy_vocabulary_confirm_delete', $form,
353
354
355
                  t('Are you sure you want to delete the vocabulary %title?',
                  array('%title' => theme('placeholder', $vocabulary->name))),
                  'admin/taxonomy', t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'),
356
                  t('Delete'),
357
                  t('Cancel'));
Kjartan's avatar
Kjartan committed
358
}
Dries's avatar
 
Dries committed
359

360
361
362
363
364
365
function taxonomy_vocabulary_confirm_delete_submit($form_id, $form_values) {
  $status = taxonomy_del_vocabulary($form_values['vid']);
  drupal_set_message(t('Deleted vocabulary %name.', array('%name' => theme('placeholder', $form_values['name']))));
  return 'admin/taxonomy';
}

Kjartan's avatar
Kjartan committed
366
function taxonomy_form_term($edit = array()) {
Dries's avatar
   
Dries committed
367
  $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
Kjartan's avatar
Kjartan committed
368
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries's avatar
   
Dries committed
369

370
  $form['name'] = array('#type' => 'textfield', '#title' => t('Term name'), '#default_value' => $edit['name'], '#maxlength' => 64, '#description' => t('The name for this term.  Example: "Linux".'), '#required' => TRUE);
371

372
  $form['description'] = array('#type' => 'textarea', '#title' => t('Description'), '#default_value' => $edit['description'], '#description' => t('A description of the term.'));
Dries's avatar
 
Dries committed
373

Kjartan's avatar
Kjartan committed
374
  if ($vocabulary->hierarchy) {
Dries's avatar
   
Dries committed
375
376
    $parent = array_keys(taxonomy_get_parents($edit['tid']));
    $children = taxonomy_get_tree($vocabulary_id, $edit['tid']);
Dries's avatar
   
Dries committed
377

Dries's avatar
   
Dries committed
378
    // A term can't be the child of itself, nor of its children.
Dries's avatar
   
Dries committed
379
380
381
    foreach ($children as $child) {
      $exclude[] = $child->tid;
    }
Dries's avatar
   
Dries committed
382
    $exclude[] = $edit['tid'];
Dries's avatar
   
Dries committed
383

Kjartan's avatar
Kjartan committed
384
    if ($vocabulary->hierarchy == 1) {
385
      $form['parent'] = _taxonomy_term_select(t('Parent'), 'parent', $parent, $vocabulary_id, l(t('Parent term'), 'admin/help/taxonomy', NULL, NULL, 'parent') .'.', 0, '<'. t('root') .'>', $exclude);
Dries's avatar
 
Dries committed
386
    }
Kjartan's avatar
Kjartan committed
387
    elseif ($vocabulary->hierarchy == 2) {
388
      $form['parent'] = _taxonomy_term_select(t('Parents'), 'parent', $parent, $vocabulary_id, l(t('Parent terms'), 'admin/help/taxonomy', NULL, NULL, 'parent') .'.', 1, '<'. t('root') .'>', $exclude);
Dries's avatar
 
Dries committed
389
    }
Kjartan's avatar
Kjartan committed
390
  }
Dries's avatar
 
Dries committed
391

392
  if ($vocabulary->relations) {
393
    $form['relations'] = _taxonomy_term_select(t('Related terms'), 'relations', array_keys(taxonomy_get_related($edit['tid'])), $vocabulary_id, NULL, 1, '<'. t('none') .'>', array($edit['tid']));
394
395
  }

396
397
  $form['synonyms'] = array('#type' => 'textarea', '#title' => t('Synonyms'), '#default_value' => implode("\n", taxonomy_get_synonyms($edit['tid'])), '#description' => t('<a href="%help-url">Synonyms</a> of this term, one synonym per line.', array('%help-url' => url('admin/help/taxonomy', NULL, NULL, 'synonyms'))));
  $form['weight'] = array('#type' => 'weight', '#title' => t('Weight'), '#default_value' => $edit['weight'], '#description' => t('In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top.'));
398
399
400
401
402

  // Add extra term form elements.
  $extra = module_invoke_all('taxonomy', 'term', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
403
      $extra[$key]['#weight'] = isset($extra[$key]['#weight']) ? $extra[$key]['#weight'] : -18;
404
405
406
407
408
    }
    $form = array_merge($form, $extra);
  }


409
  $form['vid'] = array('#type' => 'value', '#value' => $vocabulary->vid);
410
  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
Kjartan's avatar
Kjartan committed
411

Dries's avatar
   
Dries committed
412
  if ($edit['tid']) {
413
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
414
    $form['tid'] = array('#type' => 'value', '#value' => $edit['tid']);
Dries's avatar
 
Dries committed
415
  }
416
  else {
417
    $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
418
  }
Dries's avatar
 
Dries committed
419

420
  return drupal_get_form('taxonomy_form_term', $form);
Kjartan's avatar
Kjartan committed
421
}
Dries's avatar
 
Dries committed
422

423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
/**
 * Accept the form submission for a taxonomy term and save the result.
 */
function taxonomy_form_term_submit($form_id, $form_values) {
  switch (taxonomy_save_term($form_values)) {
    case SAVED_NEW:
      drupal_set_message(t('Created new term %term.', array('%term' => theme('placeholder', $form_values['name']))));
      break;
    case SAVED_UPDATED:
      drupal_set_message(t('The term %term has been updated.', array('%term' => theme('placeholder', $form_values['name']))));
      break;
  }
  return 'admin/taxonomy';
}

438
function taxonomy_save_term(&$edit) {
Dries's avatar
   
Dries committed
439
  if ($edit['tid'] && $edit['name']) {
440
    db_query("UPDATE {term_data} SET name = '%s', description = '%s', weight = %d WHERE tid = %d", $edit['name'], $edit['description'], $edit['weight'], $edit['tid']);
Dries's avatar
   
Dries committed
441
    module_invoke_all('taxonomy', 'update', 'term', $edit);
442
    $status = SAVED_UPDATED;
Kjartan's avatar
Kjartan committed
443
  }
Dries's avatar
   
Dries committed
444
445
  else if ($edit['tid']) {
    return taxonomy_del_term($edit['tid']);
Kjartan's avatar
Kjartan committed
446
447
  }
  else {
Dries's avatar
   
Dries committed
448
    $edit['tid'] = db_next_id('{term_data}_tid');
449
    db_query("INSERT INTO {term_data} (tid, name, description, vid, weight) VALUES (%d, '%s', '%s', %d, %d)", $edit['tid'], $edit['name'], $edit['description'], $edit['vid'], $edit['weight']);
Dries's avatar
   
Dries committed
450
    module_invoke_all('taxonomy', 'insert', 'term', $edit);
451
    $status = SAVED_NEW;
Kjartan's avatar
Kjartan committed
452
  }
Dries's avatar
 
Dries committed
453

Dries's avatar
   
Dries committed
454
455
456
  db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $edit['tid'], $edit['tid']);
  if ($edit['relations']) {
    foreach ($edit['relations'] as $related_id) {
Kjartan's avatar
Kjartan committed
457
      if ($related_id != 0) {
Dries's avatar
   
Dries committed
458
        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $edit['tid'], $related_id);
Dries's avatar
 
Dries committed
459
      }
Kjartan's avatar
Kjartan committed
460
    }
Kjartan's avatar
Kjartan committed
461
  }
Dries's avatar
 
Dries committed
462

Dries's avatar
   
Dries committed
463
  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $edit['tid']);
464
  if (!isset($edit['parent']) || empty($edit['parent'])) {
465
    $edit['parent'] = array(0);
Kjartan's avatar
Kjartan committed
466
  }
Dries's avatar
   
Dries committed
467
468
  if (is_array($edit['parent'])) {
    foreach ($edit['parent'] as $parent) {
469
470
471
472
473
474
475
476
      if (is_array($parent)) {
        foreach ($parent as $tid) {
          db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $edit['tid'], $tid);
        }
      }
      else {
        db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $edit['tid'], $parent);
      }
Dries's avatar
 
Dries committed
477
    }
Kjartan's avatar
Kjartan committed
478
  }
479
480
481
  else {
    db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $edit['tid'], $edit['parent']);
  }
Dries's avatar
 
Dries committed
482

Dries's avatar
   
Dries committed
483
484
485
  db_query('DELETE FROM {term_synonym} WHERE tid = %d', $edit['tid']);
  if ($edit['synonyms']) {
    foreach (explode ("\n", str_replace("\r", '', $edit['synonyms'])) as $synonym) {
Dries's avatar
   
Dries committed
486
      if ($synonym) {
Dries's avatar
   
Dries committed
487
        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $edit['tid'], chop($synonym));
Dries's avatar
   
Dries committed
488
      }
Kjartan's avatar
Kjartan committed
489
    }
Dries's avatar
 
Dries committed
490
  }
Dries's avatar
   
Dries committed
491

Dries's avatar
   
Dries committed
492
493
  cache_clear_all();

494
  return $status;
Kjartan's avatar
Kjartan committed
495
}
Dries's avatar
 
Dries committed
496

Kjartan's avatar
Kjartan committed
497
function taxonomy_del_term($tid) {
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  $tids = array($tid);
  while ($tids) {
    $children_tids = $orphans = array();
    foreach ($tids as $tid) {
      // See if any of the term's children are about to be become orphans:
      if ($children = taxonomy_get_children($tid)) {
        foreach ($children as $child) {
          // If the term has multiple parents, we don't delete it.
          $parents = taxonomy_get_parents($child->tid);
          if (count($parents) == 1) {
            $orphans[] = $child->tid;
          }
        }
      }
Dries's avatar
   
Dries committed
512

513
      $term = (array) taxonomy_get_term($tid);
Dries's avatar
   
Dries committed
514

515
516
517
518
519
      db_query('DELETE FROM {term_data} WHERE tid = %d', $tid);
      db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $tid);
      db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $tid, $tid);
      db_query('DELETE FROM {term_synonym} WHERE tid = %d', $tid);
      db_query('DELETE FROM {term_node} WHERE tid = %d', $tid);
Dries's avatar
   
Dries committed
520

521
522
      module_invoke_all('taxonomy', 'delete', 'term', $term);
    }
Dries's avatar
   
Dries committed
523

524
525
    $tids = $orphans;
  }
Dries's avatar
   
Dries committed
526

Dries's avatar
   
Dries committed
527
  cache_clear_all();
528
529

  return SAVED_DELETED;
Dries's avatar
   
Dries committed
530
531
532
533
534
}

function _taxonomy_confirm_del_term($tid) {
  $term = taxonomy_get_term($tid);

535
536
537
538
  $form['type'] = array('#type' => 'value', '#value' => 'term');
  $form['name'] = array('#type' => 'value', '#value' => $term->name);
  $form['tid'] = array('#type' => 'value', '#value' => $tid);
  return confirm_form('taxonomy_term_confirm_delete', $form,
539
540
                  t('Are you sure you want to delete the term %title?',
                  array('%title' => theme('placeholder', $term->name))),
541
542
543
                  'admin/taxonomy',
                  t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
                  t('Delete'),
544
                  t('Cancel'));
Kjartan's avatar
Kjartan committed
545
}
Dries's avatar
 
Dries committed
546

547
548
549
550
function taxonomy_term_confirm_delete_submit($form_id, $form_values) {
  taxonomy_del_term($form_values['tid']);
  drupal_set_message(t('Deleted term %name.', array('%name' => theme('placeholder', $form_values['name']))));
  return 'admin/taxonomy';
Kjartan's avatar
Kjartan committed
551
552
}

Dries's avatar
   
Dries committed
553
554
555
/**
 * Generate a form element for selecting terms from a vocabulary.
 */
Dries's avatar
   
Dries committed
556
function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
Dries's avatar
   
Dries committed
557
  $vocabulary = taxonomy_get_vocabulary($vid);
558
  $help = ($help) ? $help : $vocabulary->help;
Kjartan's avatar
Kjartan committed
559
560
561
562
  if ($vocabulary->required) {
    $blank = 0;
  }
  else {
Dries's avatar
   
Dries committed
563
    $blank = '<'. t('none') .'>';
Kjartan's avatar
Kjartan committed
564
  }
Dries's avatar
   
Dries committed
565

566
  return _taxonomy_term_select(check_plain($vocabulary->name), $name, $value, $vid, $help, intval($vocabulary->multiple), $blank);
Kjartan's avatar
Kjartan committed
567
}
Dries's avatar
 
Dries committed
568

Dries's avatar
Dries committed
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
/**
 * Generate a set of options for selecting a term from all vocabularies. Can be
 * passed to form_select.
 */
function taxonomy_form_all($free_tags = 0) {
  $vocabularies = taxonomy_get_vocabularies();
  $options = array();
  foreach ($vocabularies as $vid => $vocabulary) {
    if ($vocabulary->tags && !$free_tags) { continue; }
    $tree = taxonomy_get_tree($vid);
    $options[$vocabulary->name] = array();
    if ($tree) {
      foreach ($tree as $term) {
        $options[$vocabulary->name][$term->tid] = _taxonomy_depth($term->depth, '-') . $term->name;
      }
    }
  }
  return $options;
}

Dries's avatar
   
Dries committed
589
590
591
592
593
594
/**
 * Return an array of all vocabulary objects.
 *
 * @param $type
 *   If set, return only those vocabularies associated with this node type.
 */
Dries's avatar
   
Dries committed
595
function taxonomy_get_vocabularies($type = NULL) {
Kjartan's avatar
Kjartan committed
596
  if ($type) {
597
    $result = db_query(db_rewrite_sql("SELECT v.vid, v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $type);
Kjartan's avatar
Kjartan committed
598
599
  }
  else {
600
    $result = db_query(db_rewrite_sql('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid ORDER BY v.weight, v.name', 'v', 'vid'));
Kjartan's avatar
Kjartan committed
601
  }
Dries's avatar
   
Dries committed
602

Kjartan's avatar
Kjartan committed
603
  $vocabularies = array();
Dries's avatar
   
Dries committed
604
  $node_types = array();
Kjartan's avatar
Kjartan committed
605
  while ($voc = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
606
607
608
609
    $node_types[$voc->vid][] = $voc->type;
    unset($voc->type);
    $voc->nodes = $node_types[$voc->vid];
    $vocabularies[$voc->vid] = $voc;
Dries's avatar
 
Dries committed
610
  }
Dries's avatar
   
Dries committed
611

Kjartan's avatar
Kjartan committed
612
613
  return $vocabularies;
}
Dries's avatar
 
Dries committed
614

Dries's avatar
   
Dries committed
615
616
617
/**
 * Generate a form for selecting terms to associate with a node.
 */
618
619
620
621
function taxonomy_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
    $node = $form['#node'];

622
    if (!isset($node->taxonomy)) {
623
624
625
626
627
628
      if ($node->nid) {
        $terms = taxonomy_node_get_terms($node->nid);
      }
      else {
        $terms = array();
      }
Kjartan's avatar
Kjartan committed
629
630
    }
    else {
631
      $terms = $node->taxonomy;
Dries's avatar
 
Dries committed
632
633
    }

634
    $c = db_query(db_rewrite_sql("SELECT v.* FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $node->type);
Dries's avatar
Dries committed
635

636
637
638
639
    while ($vocabulary = db_fetch_object($c)) {
      if ($vocabulary->tags) {
        $typed_terms = array();
        foreach ($terms as $term) {
640
          // Extract terms belonging to the vocabulary in question.
641
          if ($term->vid == $vocabulary->vid) {
Dries's avatar
Dries committed
642

643
644
645
646
647
648
649
            // Commas and quotes in terms are special cases, so encode 'em.
            if (preg_match('/,/', $term->name) || preg_match('/"/', $term->name)) {
              $term->name = '"'.preg_replace('/"/', '""', $term->name).'"';
            }

            $typed_terms[] = $term->name;
          }
Dries's avatar
Dries committed
650
        }
651
        $typed_string = implode(', ', $typed_terms) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
652

653
654
655
656
657
658
        if ($vocabulary->help) {
          $help = $vocabulary->help;
        }
        else {
          $help = t('A comma-separated list of terms describing this content.  Example: funny, bungie jumping, "Company, Inc.".');
        }
659
660
661
662
663
664
665
666
667
        $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield',
          '#title' => $vocabulary->name,
          '#description' => $help,
          '#required' => $vocabulary->required,
          '#default_value' => $typed_string,
          '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid,
          '#weight' => $vocabulary->weight,
          '#maxlength' => 100,
        );
668
669
      }
      else {
670
671
672
673
674
675
676
677
        // Extract terms belonging to the vocabulary in question.
        $default_terms = array();
        foreach ($terms as $term) {
          if ($term->vid == $vocabulary->vid) {
            $default_terms[$term->tid] = $term;
          }
        }
        $form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, array_keys($default_terms), $vocabulary->help);
678
        $form['taxonomy'][$vocabulary->vid]['#weight'] = $vocabulary->weight;
679
        $form['taxonomy'][$vocabulary->vid]['#required'] = $vocabulary->required;
680
      }
Dries's avatar
Dries committed
681
    }
682
    if (isset($form['taxonomy'])) {
683
      $form['taxonomy'] += array('#type' => 'fieldset', '#title' => t('Categories'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#tree' => TRUE, '#weight' => -3);
Dries's avatar
Dries committed
684
    }
Dries's avatar
 
Dries committed
685
  }
Kjartan's avatar
Kjartan committed
686
}
Dries's avatar
 
Dries committed
687

Dries's avatar
   
Dries committed
688
689
690
691
/**
 * Find all terms associated to the given node, within one vocabulary.
 */
function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') {
692
  $result = db_query(db_rewrite_sql('SELECT t.tid, t.* FROM {term_data} t INNER JOIN {term_node} r ON r.tid = t.tid WHERE t.vid = %d AND r.nid = %d ORDER BY weight', 't', 'tid'), $vid, $nid);
Kjartan's avatar
Kjartan committed
693
694
695
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries's avatar
 
Dries committed
696
  }
Kjartan's avatar
Kjartan committed
697
698
699
  return $terms;
}

Dries's avatar
   
Dries committed
700
/**
701
 * Find all terms associated to the given node, ordered by vocabulary and term weight.
Dries's avatar
   
Dries committed
702
703
 */
function taxonomy_node_get_terms($nid, $key = 'tid') {
Kjartan's avatar
Kjartan committed
704
  static $terms;
Dries's avatar
 
Dries committed
705

Dries's avatar
   
Dries committed
706
  if (!isset($terms[$nid])) {
707
    $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_node} r INNER JOIN {term_data} t ON r.tid = t.tid INNER JOIN {vocabulary} v ON t.vid = v.vid WHERE r.nid = %d ORDER BY v.weight, t.weight, t.name', 't', 'tid'), $nid);
Kjartan's avatar
Kjartan committed
708
    $terms[$nid] = array();
Dries's avatar
 
Dries committed
709
    while ($term = db_fetch_object($result)) {
Kjartan's avatar
Kjartan committed
710
      $terms[$nid][$term->$key] = $term;
Dries's avatar
 
Dries committed
711
712
    }
  }
Kjartan's avatar
Kjartan committed
713
714
  return $terms[$nid];
}
Dries's avatar
 
Dries committed
715

Dries's avatar
Dries committed
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
/**
 * Make sure incoming vids are free tagging enabled.
 */
function taxonomy_node_validate(&$node) {
  if ($node->taxonomy) {
    $terms = $node->taxonomy;
    if ($terms['tags']) {
      foreach ($terms['tags'] as $vid => $vid_value) {
        $vocabulary = taxonomy_get_vocabulary($vid);
        if (!$vocabulary->tags) {
          form_set_error("taxonomy[tags][$vid", t('The %name vocabulary can not be modified in this way.', array('%name' => theme('placeholder', $vocabulary->name))));
        }
      }
    }
  }
}

Dries's avatar
   
Dries committed
733
734
735
/**
 * Save term associations for a given node.
 */
Kjartan's avatar
Kjartan committed
736
function taxonomy_node_save($nid, $terms) {
737
  taxonomy_node_delete($nid);
Dries's avatar
Dries committed
738
739
740

  // Free tagging vocabularies do not send their tids in the form,
  // so we'll detect them here and process them independently.
741
  if (isset($terms['tags'])) {
Dries's avatar
Dries committed
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
    $typed_input = $terms['tags'];
    unset($terms['tags']);

    foreach ($typed_input as $vid => $vid_value) {
      // This regexp allows the following types of user input:
      // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar
      $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
      preg_match_all($regexp, $vid_value, $matches);
      $typed_terms = $matches[1];

      foreach ($typed_terms as $typed_term) {
        // If a user has escaped a term (to demonstrate that it is a group,
        // or includes a comma or quote character), we remove the escape
        // formatting so to save the term into the DB as the user intends.
        $typed_term = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_term));
        $typed_term = trim($typed_term);
        if ($typed_term == "") { continue; }

        // See if the term exists in the chosen vocabulary
        // and return the tid, otherwise, add a new record.
        $possibilities = taxonomy_get_term_by_name($typed_term);
        $typed_term_tid = NULL; // tid match if any.
        foreach ($possibilities as $possibility) {
          if ($possibility->vid == $vid) {
            $typed_term_tid = $possibility->tid;
          }
        }

        if (!$typed_term_tid) {
771
772
773
          $edit = array('vid' => $vid, 'name' => $typed_term);
          $status = taxonomy_save_term($edit);
          $typed_term_tid = $edit['tid'];
Dries's avatar
Dries committed
774
775
776
777
778
779
        }

        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $typed_term_tid);
      }
    }
  }
Dries's avatar
 
Dries committed
780

781
  if (is_array($terms)) {
Dries's avatar
   
Dries committed
782
    foreach ($terms as $term) {
783
784
785
786
787
788
789
      if (is_array($term)) {
        foreach ($term as $tid) {
          if ($tid) {
            db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $tid);
          }
        }
      }
790
791
792
      else if (is_object($term)) {
        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term->tid);
      }
793
      else if ($term) {
Dries's avatar
   
Dries committed
794
        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term);
795
      }
Dries's avatar
 
Dries committed
796
797
    }
  }
Kjartan's avatar
Kjartan committed
798
}
Dries's avatar
 
Dries committed
799

Dries's avatar
   
Dries committed
800
801
802
/**
 * Remove associations of a node to its terms.
 */
Kjartan's avatar
Kjartan committed
803
function taxonomy_node_delete($nid) {
Dries's avatar
   
Dries committed
804
  db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
Kjartan's avatar
Kjartan committed
805
}
Dries's avatar
 
Dries committed
806

Dries's avatar
   
Dries committed
807
808
809
810
/**
 * Find all term objects related to a given term ID.
 */
function taxonomy_get_related($tid, $key = 'tid') {
Kjartan's avatar
Kjartan committed
811
  if ($tid) {
Dries's avatar
   
Dries committed
812
    $result = db_query('SELECT t.*, tid1, tid2 FROM {term_relation}, {term_data} t WHERE (t.tid = tid1 OR t.tid = tid2) AND (tid1 = %d OR tid2 = %d) AND t.tid != %d ORDER BY weight, name', $tid, $tid, $tid);
Kjartan's avatar
Kjartan committed
813
814
815
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries's avatar
 
Dries committed
816
    }
Kjartan's avatar
Kjartan committed
817
    return $related;
Dries's avatar
 
Dries committed
818
  }
Kjartan's avatar
Kjartan committed
819
820
  else {
    return array();
Dries's avatar
 
Dries committed
821
  }
Kjartan's avatar
Kjartan committed
822
}
Dries's avatar
 
Dries committed
823

Dries's avatar
   
Dries committed
824
825
826
827
/**
 * Find all parents of a given term ID.
 */
function taxonomy_get_parents($tid, $key = 'tid') {
Kjartan's avatar
Kjartan committed
828
  if ($tid) {
829
    $result = db_query(db_rewrite_sql('SELECT t.tid, t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON h.parent = t.tid WHERE h.tid = %d ORDER BY weight, name', 't', 'tid'), $tid);
Kjartan's avatar
Kjartan committed
830
831
832
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries's avatar
   
Dries committed
833
    }
Kjartan's avatar
Kjartan committed
834
    return $parents;
Dries's avatar
 
Dries committed
835
  }
Kjartan's avatar
Kjartan committed
836
837
838
839
  else {
    return array();
  }
}
Dries's avatar
 
Dries committed
840

Dries's avatar
   
Dries committed
841
842
843
844
/**
 * Find all ancestors of a given term ID.
 */
function taxonomy_get_parents_all($tid) {
Dries's avatar
   
Dries committed
845
846
847
848
849
850
851
852
853
854
855
856
  $parents = array();
  if ($tid) {
    $parents[] = taxonomy_get_term($tid);
    $n = 0;
    while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
      $parents = array_merge($parents, $parent);
      $n++;
    }
  }
  return $parents;
}

Dries's avatar
   
Dries committed
857
858
859
860
/**
 * Find all children of a term ID.
 */
function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
Kjartan's avatar
Kjartan committed
861
  if ($vid) {
862
    $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON h.tid = t.tid WHERE t.vid = %d AND h.parent = %d ORDER BY weight, name', 't', 'tid'), $vid, $tid);
Dries's avatar
 
Dries committed
863
  }
Kjartan's avatar
Kjartan committed
864
  else {
865
    $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_data} t INNER JOIN {term_hierarchy} h ON h.tid = t.tid WHERE parent = %d ORDER BY weight, name', 't', 'tid'), $tid);
Kjartan's avatar
Kjartan committed
866
867
868
869
870
871
872
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries's avatar
 
Dries committed
873

Dries's avatar
   
Dries committed
874
875
876
877
878
879
880
881
882
883
884
885
886
/**
 * Create a hierarchical representation of a vocabulary.
 *
 * @param $vid
 *   Which vocabulary to generate the tree for.
 *
 * @param $parent
 *   The term ID under which to generate the tree. If 0, generate the tree
 *   for the entire vocabulary.
 *
 * @param $depth
 *   Internal use only.
 *
Dries's avatar
   
Dries committed
887
888
889
 * @param $max_depth
 *   The number of levels of the tree to return. Leave NULL to return all levels.
 *
Dries's avatar
   
Dries committed
890
891
892
893
894
 * @return
 *   An array of all term objects in the tree. Each term object is extended
 *   to have "depth" and "parents" attributes in addition to its normal ones.
 */
function taxonomy_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
Dries's avatar
   
Dries committed
895
  static $children, $parents, $terms;
Dries's avatar
   
Dries committed
896

Kjartan's avatar
Kjartan committed
897
  $depth++;
Dries's avatar
   
Dries committed
898

Dries's avatar
   
Dries committed
899
900
901
902
  // We cache trees, so it's not CPU-intensive to call get_tree() on a term
  // and its children, too.
  if (!isset($children[$vid])) {
    $children[$vid] = array();
Dries's avatar
   
Dries committed
903

904
    $result = db_query(db_rewrite_sql('SELECT t.tid, t.*, 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'), $vid);
Dries's avatar
   
Dries committed
905
    while ($term = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
906
907
908
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries's avatar
 
Dries committed
909
910
    }
  }
Dries's avatar
   
Dries committed
911

Dries's avatar
   
Dries committed
912
  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
Dries's avatar
Dries committed
913
914
915
916
917
918
919
920
921
922
923
924
  if ($children[$vid][$parent]) {
    foreach ($children[$vid][$parent] as $child) {
      if ($max_depth > $depth) {
        $terms[$vid][$child]->depth = $depth;
        // The "parent" attribute is not useful, as it would show one parent only.
        unset($terms[$vid][$child]->parent);
        $terms[$vid][$child]->parents = $parents[$vid][$child];
        $tree[] = $terms[$vid][$child];

        if ($children[$vid][$child]) {
          $tree = array_merge($tree, taxonomy_get_tree($vid, $child, $depth, $max_depth));
        }
Dries's avatar
   
Dries committed
925
      }
Dries's avatar
   
Dries committed
926
    }
Kjartan's avatar
Kjartan committed
927
  }
Dries's avatar
   
Dries committed
928

Dries's avatar
   
Dries committed
929
  return $tree ? $tree : array();
Kjartan's avatar
Kjartan committed
930
}
Dries's avatar
 
Dries committed
931

Dries's avatar
   
Dries committed
932
933
934
/**
 * Return an array of synonyms of the given term ID.
 */
Kjartan's avatar
Kjartan committed
935
936
function taxonomy_get_synonyms($tid) {
  if ($tid) {
Dries's avatar
   
Dries committed
937
    $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid);
Kjartan's avatar
Kjartan committed
938
    while ($synonym = db_fetch_array($result)) {
Dries's avatar
   
Dries committed
939
      $synonyms[] = $synonym['name'];
Dries's avatar
 
Dries committed
940
    }
Kjartan's avatar
Kjartan committed
941
    return $synonyms ? $synonyms : array();
Dries's avatar
 
Dries committed
942
  }
Kjartan's avatar
Kjartan committed
943
944
  else {
    return array();
Dries's avatar
   
Dries committed
945
  }
Kjartan's avatar
Kjartan committed
946
}
Dries's avatar
   
Dries committed
947

Dries's avatar
   
Dries committed
948
949
950
951
952
/**
 * Return the term object that has the given string as a synonym.
 */
function taxonomy_get_synonym_root($synonym) {
  return db_fetch_object(db_query("SELECT * FROM {term_synonym} s, {term_data} t WHERE t.tid = s.tid AND s.name = '%s'", $synonym));
Kjartan's avatar
Kjartan committed
953
}
Dries's avatar
   
Dries committed
954

Dries's avatar
   
Dries committed
955
956
957
/**
 * Given a term id, count the number of published nodes in it.
 */
Dries's avatar
   
Dries committed
958
function taxonomy_term_count_nodes($tid, $type = 0) {
Kjartan's avatar
Kjartan committed
959
  static $count;
Dries's avatar
   
Dries committed
960

Dries's avatar
   
Dries committed
961
962
963
  if (!isset($count[$type])) {
    // $type == 0 always evaluates true is $type is a string
    if (is_numeric($type)) {
964
      $result = db_query(db_rewrite_sql('SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 GROUP BY t.tid'));
Dries's avatar
   
Dries committed
965
966
    }
    else {
967
      $result = db_query(db_rewrite_sql("SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND n.type = '%s' GROUP BY t.tid"), $type);
Dries's avatar
   
Dries committed
968
    }
Kjartan's avatar
Kjartan committed
969
    while ($term = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
970
      $count[$type][$term->tid] = $term->c;
Dries's avatar
   
Dries committed
971
972
973
    }
  }

Kjartan's avatar
Kjartan committed
974
  foreach (_taxonomy_term_children($tid) as $c) {
Dries's avatar
   
Dries committed
975
    $children_count += taxonomy_term_count_nodes($c, $type);
Kjartan's avatar
Kjartan committed
976
  }
Dries's avatar
   
Dries committed
977
  return $count[$type][$tid] + $children_count;
Kjartan's avatar
Kjartan committed
978
979
}

Dries's avatar
   
Dries committed
980
981
982
/**
 * Helper for taxonomy_term_count_nodes().
 */
Kjartan's avatar
Kjartan committed
983
984
function _taxonomy_term_children($tid) {
  static $children;
Dries's avatar
   
Dries committed
985

Dries's avatar
   
Dries committed
986
  if (!isset($children)) {
Dries's avatar
   
Dries committed
987
    $result = db_query('SELECT tid, parent FROM {term_hierarchy}');
Kjartan's avatar
Kjartan committed
988
989
    while ($term = db_fetch_object($result)) {
      $children[$term->parent][] = $term->tid;
Dries's avatar
   
Dries committed
990
    }
Dries's avatar
 
Dries committed
991
  }
Kjartan's avatar
Kjartan committed
992
993
  return $children[$tid] ? $children[$tid] : array();
}
Dries's avatar
 
Dries committed
994

Dries's avatar
   
Dries committed
995
/**
Dries's avatar