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

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

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

Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
27
function taxonomy_link($type, $node = NULL) {
Dries Buytaert's avatar
   
Dries Buytaert committed
28
  if ($type == 'taxonomy terms' && $node != NULL) {
Kjartan Mannes's avatar
   
Kjartan Mannes committed
29
    $links = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
30
    if (array_key_exists('taxonomy', $node)) {
31
      foreach ($node->taxonomy as $term) {
32
        $links['taxonomy_term_'. $term->tid] = array(
33
34
35
          'title' => $term->name,
          'href' => taxonomy_term_path($term),
          'attributes' => array('rel' => 'tag', 'title' => strip_tags($term->description))
36
        );
Dries Buytaert's avatar
   
Dries Buytaert committed
37
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
38
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
39

40
41
42
43
44
45
46
    // We call this hook again because some modules and themes call taxonomy_link('taxonomy terms') directly
    foreach (module_implements('link_alter') AS $module) {
      $function = $module .'_link_alter';
      $function($node, $links);
    }

    return $links;
47
48
49
  }
}

50
51
52
53
54
55
56
57
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 Buytaert's avatar
   
Dries Buytaert committed
58
59
60
/**
 * Implementation of hook_menu().
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
61
function taxonomy_menu($may_cache) {
Dries Buytaert's avatar
   
Dries Buytaert committed
62
  $items = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
63

Dries Buytaert's avatar
   
Dries Buytaert committed
64
  if ($may_cache) {
65
    $items[] = array('path' => 'admin/content/taxonomy',
66
      'title' => t('categories'),
67
      'description' => t('Create vocabularies and terms to categorize your content.'),
68
      'callback' => 'taxonomy_overview_vocabularies',
Dries Buytaert's avatar
   
Dries Buytaert committed
69
      'access' => user_access('administer taxonomy'));
70

71
    $items[] = array('path' => 'admin/content/taxonomy/list',
72
73
74
      'title' => t('list'),
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -10);
75

76
    $items[] = array('path' => 'admin/content/taxonomy/add/vocabulary',
77
78
      'title' => t('add vocabulary'),
      'callback' => 'taxonomy_admin_vocabulary_edit',
Dries Buytaert's avatar
   
Dries Buytaert committed
79
80
81
      'access' => user_access('administer taxonomy'),
      'type' => MENU_LOCAL_TASK);

82
    $items[] = array('path' => 'admin/content/taxonomy/edit/vocabulary',
83
84
      'title' => t('edit vocabulary'),
      'callback' => 'taxonomy_admin_vocabulary_edit',
85
86
87
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);

88
    $items[] = array('path' => 'admin/content/taxonomy/edit/term',
89
90
      'title' => t('edit term'),
      'callback' => 'taxonomy_admin_term_edit',
91
92
93
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);

94
95
    $items[] = array('path' => 'taxonomy/term',
      'title' => t('taxonomy term'),
Dries Buytaert's avatar
   
Dries Buytaert committed
96
97
98
      'callback' => 'taxonomy_term_page',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
Steven Wittens's avatar
Steven Wittens committed
99

100
101
    $items[] = array('path' => 'taxonomy/autocomplete',
      'title' => t('autocomplete taxonomy'),
Steven Wittens's avatar
Steven Wittens committed
102
103
104
      'callback' => 'taxonomy_autocomplete',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
Dries Buytaert's avatar
   
Dries Buytaert committed
105
  }
106
  else {
107
108
    if (is_numeric(arg(3))) {
      $items[] = array('path' => 'admin/content/taxonomy/' . arg(3),
109
110
        'title' => t('list terms'),
        'callback' => 'taxonomy_overview_terms',
111
        'callback arguments' => array(arg(3)),
112
113
114
        'access' => user_access('administer taxonomy'),
        'type' => MENU_CALLBACK);

115
      $items[] = array('path' => 'admin/content/taxonomy/' . arg(3) . '/list',
116
117
118
        'title' => t('list'),
        'type' => MENU_DEFAULT_LOCAL_TASK,
        'weight' => -10);
119

120
      $items[] = array('path' => 'admin/content/taxonomy/' . arg(3) . '/add/term',
121
        'title' => t('add term'),
122
        'callback' => 'taxonomy_form_term',
123
        'callback arguments' => array(array('vid' => arg(3))),
124
125
126
127
        'access' => user_access('administer taxonomy'),
        'type' => MENU_LOCAL_TASK);
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
128

Dries Buytaert's avatar
   
Dries Buytaert committed
129
130
  return $items;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
131

132
133
134
135
/**
 * List and manage vocabularies.
 */
function taxonomy_overview_vocabularies() {
136
  $vocabularies = taxonomy_get_vocabularies();
137
  $rows = array();
138
139
140
  foreach ($vocabularies as $vocabulary) {
    $types = array();
    foreach ($vocabulary->nodes as $type) {
141
      $node_type = node_get_types('name', $type);
142
143
      $types[] = $node_type ? $node_type : $type;
    }
144
145
    $rows[] = array('name' => check_plain($vocabulary->name),
      'type' => implode(', ', $types),
146
147
148
      'edit' => l(t('edit vocabulary'), "admin/content/taxonomy/edit/vocabulary/$vocabulary->vid"),
      'list' => l(t('list terms'), "admin/content/taxonomy/$vocabulary->vid"),
      'add' => l(t('add terms'), "admin/content/taxonomy/$vocabulary->vid/add/term")
149
150
151
    );
  }
  if (empty($rows)) {
Neil Drumm's avatar
Neil Drumm committed
152
    $rows[] = array(array('data' => t('No categories available.'), 'colspan' => '5', 'class' => 'message'));
153
  }
Neil Drumm's avatar
Neil Drumm committed
154
  $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '3'));
155

156
  return theme('table', $header, $rows, array('id' => 'taxonomy'));
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
}

/**
 * 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; }
179
    $rows[] = array(_taxonomy_depth($term->depth) . ' ' . l($term->name, "taxonomy/term/$term->tid"), l(t('edit'), "admin/content/taxonomy/edit/term/$term->tid", array(), $destination));
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
    $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 Mannes's avatar
Kjartan Mannes committed
200
function taxonomy_form_vocabulary($edit = array()) {
201
202
203
204
  $form['name'] = array('#type' => 'textfield',
    '#title' => t('Vocabulary name'),
    '#default_value' => $edit['name'],
    '#maxlength' => 64,
205
    '#description' => t('The name for this vocabulary. Example: "Topic".'),
206
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
    '#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'],
    '#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.'),
  );
256

257
  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
Dries Buytaert's avatar
   
Dries Buytaert committed
258
  if ($edit['vid']) {
259
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
260
    $form['vid'] = array('#type' => 'value', '#value' => $edit['vid']);
261
    $form['module'] = array('#type' => 'value', '#value' => $edit['module']);
Dries Buytaert's avatar
 
Dries Buytaert committed
262
  }
263
  return drupal_get_form('taxonomy_form_vocabulary', $form);
Kjartan Mannes's avatar
Kjartan Mannes committed
264
}
Kjartan Mannes's avatar
Kjartan Mannes committed
265

266
267
268
269
270
271
272
273
274
275
276
277
278
279
/**
 * 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;
  }
280
  return 'admin/content/taxonomy';
281
282
}

283
function taxonomy_save_vocabulary(&$edit) {
284
  $edit['nodes'] = empty($edit['nodes']) ? array() : $edit['nodes'];
Dries Buytaert's avatar
   
Dries Buytaert committed
285

Dries Buytaert's avatar
   
Dries Buytaert committed
286
  if ($edit['vid'] && $edit['name']) {
287
    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 Buytaert's avatar
   
Dries Buytaert committed
288
    db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']);
289
    foreach ($edit['nodes'] as $type => $selected) {
Dries Buytaert's avatar
   
Dries Buytaert committed
290
291
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
292
    module_invoke_all('taxonomy', 'update', 'vocabulary', $edit);
293
    $status = SAVED_UPDATED;
Dries Buytaert's avatar
 
Dries Buytaert committed
294
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
295
  else if ($edit['vid']) {
296
    $status = taxonomy_del_vocabulary($edit['vid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
297
298
  }
  else {
299
300
    $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');
301
    foreach ($edit['nodes'] as $type => $selected) {
Dries Buytaert's avatar
   
Dries Buytaert committed
302
303
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
304
    module_invoke_all('taxonomy', 'insert', 'vocabulary', $edit);
305
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
306
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
307
308

  cache_clear_all();
Dries Buytaert's avatar
   
Dries Buytaert committed
309

310
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
311
}
Dries Buytaert's avatar
 
Dries Buytaert committed
312

Kjartan Mannes's avatar
Kjartan Mannes committed
313
function taxonomy_del_vocabulary($vid) {
314
  $vocabulary = (array) taxonomy_get_vocabulary($vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
315

Dries Buytaert's avatar
   
Dries Buytaert committed
316
  db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
317
  db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
318
  $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
Kjartan Mannes's avatar
Kjartan Mannes committed
319
320
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries Buytaert's avatar
 
Dries Buytaert committed
321
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
322

Dries Buytaert's avatar
   
Dries Buytaert committed
323
  module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
Dries Buytaert's avatar
   
Dries Buytaert committed
324

Dries Buytaert's avatar
   
Dries Buytaert committed
325
326
  cache_clear_all();

327
  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert committed
328
329
330
331
332
}

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

333
334
335
336
  $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,
337
338
                  t('Are you sure you want to delete the vocabulary %title?',
                  array('%title' => theme('placeholder', $vocabulary->name))),
339
                  'admin/content/taxonomy', t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'),
340
                  t('Delete'),
341
                  t('Cancel'));
Kjartan Mannes's avatar
Kjartan Mannes committed
342
}
Dries Buytaert's avatar
 
Dries Buytaert committed
343

344
345
346
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']))));
347
  return 'admin/content/taxonomy';
348
349
}

Kjartan Mannes's avatar
Kjartan Mannes committed
350
function taxonomy_form_term($edit = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
351
  $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
Kjartan Mannes's avatar
Kjartan Mannes committed
352
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries Buytaert's avatar
   
Dries Buytaert committed
353

354
  $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);
355

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

Kjartan Mannes's avatar
Kjartan Mannes committed
358
  if ($vocabulary->hierarchy) {
Dries Buytaert's avatar
   
Dries Buytaert committed
359
360
    $parent = array_keys(taxonomy_get_parents($edit['tid']));
    $children = taxonomy_get_tree($vocabulary_id, $edit['tid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
361

Dries Buytaert's avatar
   
Dries Buytaert committed
362
    // A term can't be the child of itself, nor of its children.
Dries Buytaert's avatar
   
Dries Buytaert committed
363
364
365
    foreach ($children as $child) {
      $exclude[] = $child->tid;
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
366
    $exclude[] = $edit['tid'];
Dries Buytaert's avatar
   
Dries Buytaert committed
367

Kjartan Mannes's avatar
Kjartan Mannes committed
368
    if ($vocabulary->hierarchy == 1) {
369
      $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 Buytaert's avatar
 
Dries Buytaert committed
370
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
371
    elseif ($vocabulary->hierarchy == 2) {
372
      $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 Buytaert's avatar
 
Dries Buytaert committed
373
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
374
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
375

376
  if ($vocabulary->relations) {
377
    $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']));
378
379
  }

380
381
  $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.'));
382
383
384
385
386

  // Add extra term form elements.
  $extra = module_invoke_all('taxonomy', 'term', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
387
      $extra[$key]['#weight'] = isset($extra[$key]['#weight']) ? $extra[$key]['#weight'] : -18;
388
389
390
391
392
    }
    $form = array_merge($form, $extra);
  }


393
  $form['vid'] = array('#type' => 'value', '#value' => $vocabulary->vid);
394
  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
Kjartan Mannes's avatar
Kjartan Mannes committed
395

Dries Buytaert's avatar
   
Dries Buytaert committed
396
  if ($edit['tid']) {
397
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
398
    $form['tid'] = array('#type' => 'value', '#value' => $edit['tid']);
Dries Buytaert's avatar
 
Dries Buytaert committed
399
  }
400
  else {
401
    $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
402
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
403

404
  return drupal_get_form('taxonomy_form_term', $form);
Kjartan Mannes's avatar
Kjartan Mannes committed
405
}
Dries Buytaert's avatar
 
Dries Buytaert committed
406

407
408
409
410
411
412
413
414
415
416
417
418
/**
 * 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;
  }
419
  return 'admin/content/taxonomy';
420
421
}

422
function taxonomy_save_term(&$edit) {
Dries Buytaert's avatar
   
Dries Buytaert committed
423
  if ($edit['tid'] && $edit['name']) {
424
    db_query("UPDATE {term_data} SET name = '%s', description = '%s', weight = %d WHERE tid = %d", $edit['name'], $edit['description'], $edit['weight'], $edit['tid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
425
    module_invoke_all('taxonomy', 'update', 'term', $edit);
426
    $status = SAVED_UPDATED;
Kjartan Mannes's avatar
Kjartan Mannes committed
427
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
428
429
  else if ($edit['tid']) {
    return taxonomy_del_term($edit['tid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
430
431
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
432
    $edit['tid'] = db_next_id('{term_data}_tid');
433
    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 Buytaert's avatar
   
Dries Buytaert committed
434
    module_invoke_all('taxonomy', 'insert', 'term', $edit);
435
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
436
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
437

Dries Buytaert's avatar
   
Dries Buytaert committed
438
439
440
  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 Mannes's avatar
Kjartan Mannes committed
441
      if ($related_id != 0) {
Dries Buytaert's avatar
   
Dries Buytaert committed
442
        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $edit['tid'], $related_id);
Dries Buytaert's avatar
 
Dries Buytaert committed
443
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
444
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
445
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
446

Dries Buytaert's avatar
   
Dries Buytaert committed
447
  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $edit['tid']);
448
  if (!isset($edit['parent']) || empty($edit['parent'])) {
449
    $edit['parent'] = array(0);
Kjartan Mannes's avatar
Kjartan Mannes committed
450
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
451
452
  if (is_array($edit['parent'])) {
    foreach ($edit['parent'] as $parent) {
453
454
455
456
457
458
459
460
      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 Buytaert's avatar
 
Dries Buytaert committed
461
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
462
  }
463
464
465
  else {
    db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $edit['tid'], $edit['parent']);
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
466

Dries Buytaert's avatar
   
Dries Buytaert committed
467
468
469
  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 Buytaert's avatar
   
Dries Buytaert committed
470
      if ($synonym) {
Dries Buytaert's avatar
   
Dries Buytaert committed
471
        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $edit['tid'], chop($synonym));
Dries Buytaert's avatar
   
Dries Buytaert committed
472
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
473
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
474
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
475

Dries Buytaert's avatar
   
Dries Buytaert committed
476
477
  cache_clear_all();

478
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
479
}
Dries Buytaert's avatar
 
Dries Buytaert committed
480

Kjartan Mannes's avatar
Kjartan Mannes committed
481
function taxonomy_del_term($tid) {
482
483
484
485
486
487
488
489
490
491
492
493
494
495
  $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 Buytaert's avatar
   
Dries Buytaert committed
496

497
      $term = (array) taxonomy_get_term($tid);
Dries Buytaert's avatar
   
Dries Buytaert committed
498

499
500
501
502
503
      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 Buytaert's avatar
   
Dries Buytaert committed
504

505
506
      module_invoke_all('taxonomy', 'delete', 'term', $term);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
507

508
509
    $tids = $orphans;
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
510

Dries Buytaert's avatar
   
Dries Buytaert committed
511
  cache_clear_all();
512
513

  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert committed
514
515
516
517
518
}

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

519
520
521
522
  $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,
523
524
                  t('Are you sure you want to delete the term %title?',
                  array('%title' => theme('placeholder', $term->name))),
525
                  'admin/content/taxonomy',
526
527
                  t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
                  t('Delete'),
528
                  t('Cancel'));
Kjartan Mannes's avatar
Kjartan Mannes committed
529
}
Dries Buytaert's avatar
 
Dries Buytaert committed
530

531
532
533
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']))));
534
  return 'admin/content/taxonomy';
Kjartan Mannes's avatar
Kjartan Mannes committed
535
536
}

Dries Buytaert's avatar
   
Dries Buytaert committed
537
538
539
/**
 * Generate a form element for selecting terms from a vocabulary.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
540
function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
Dries Buytaert's avatar
   
Dries Buytaert committed
541
  $vocabulary = taxonomy_get_vocabulary($vid);
542
  $help = ($help) ? $help : $vocabulary->help;
Kjartan Mannes's avatar
Kjartan Mannes committed
543
544
545
546
  if ($vocabulary->required) {
    $blank = 0;
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
547
    $blank = '<'. t('none') .'>';
Kjartan Mannes's avatar
Kjartan Mannes committed
548
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
549

550
  return _taxonomy_term_select(check_plain($vocabulary->name), $name, $value, $vid, $help, intval($vocabulary->multiple), $blank);
Kjartan Mannes's avatar
Kjartan Mannes committed
551
}
Dries Buytaert's avatar
 
Dries Buytaert committed
552

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
/**
 * 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 Buytaert's avatar
   
Dries Buytaert committed
573
574
575
576
577
578
/**
 * Return an array of all vocabulary objects.
 *
 * @param $type
 *   If set, return only those vocabularies associated with this node type.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
579
function taxonomy_get_vocabularies($type = NULL) {
Kjartan Mannes's avatar
Kjartan Mannes committed
580
  if ($type) {
581
    $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 Mannes's avatar
Kjartan Mannes committed
582
583
  }
  else {
584
    $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 Mannes's avatar
Kjartan Mannes committed
585
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
586

Kjartan Mannes's avatar
Kjartan Mannes committed
587
  $vocabularies = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
588
  $node_types = array();
Kjartan Mannes's avatar
Kjartan Mannes committed
589
  while ($voc = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
590
591
592
593
    $node_types[$voc->vid][] = $voc->type;
    unset($voc->type);
    $voc->nodes = $node_types[$voc->vid];
    $vocabularies[$voc->vid] = $voc;
Dries Buytaert's avatar
 
Dries Buytaert committed
594
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
595

Kjartan Mannes's avatar
Kjartan Mannes committed
596
597
  return $vocabularies;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
598

Dries Buytaert's avatar
   
Dries Buytaert committed
599
600
601
/**
 * Generate a form for selecting terms to associate with a node.
 */
602
603
604
605
function taxonomy_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
    $node = $form['#node'];

606
    if (!isset($node->taxonomy)) {
607
608
609
610
611
612
      if ($node->nid) {
        $terms = taxonomy_node_get_terms($node->nid);
      }
      else {
        $terms = array();
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
613
614
    }
    else {
615
      $terms = $node->taxonomy;
Dries Buytaert's avatar
 
Dries Buytaert committed
616
617
    }

618
    $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);
619

620
621
622
623
    while ($vocabulary = db_fetch_object($c)) {
      if ($vocabulary->tags) {
        $typed_terms = array();
        foreach ($terms as $term) {
624
          // Extract terms belonging to the vocabulary in question.
625
          if ($term->vid == $vocabulary->vid) {
626

627
628
629
630
631
632
633
            // 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;
          }
634
        }
635
        $typed_string = implode(', ', $typed_terms) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
636

637
638
639
640
        if ($vocabulary->help) {
          $help = $vocabulary->help;
        }
        else {
641
          $help = t('A comma-separated list of terms describing this content. Example: funny, bungee jumping, "Company, Inc.".');
642
        }
643
644
645
646
647
648
649
        $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,
650
          '#maxlength' => 255,
651
        );
652
653
      }
      else {
654
655
656
657
658
659
660
661
        // 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);
662
        $form['taxonomy'][$vocabulary->vid]['#weight'] = $vocabulary->weight;
663
        $form['taxonomy'][$vocabulary->vid]['#required'] = $vocabulary->required;
664
      }
665
    }
666
    if (isset($form['taxonomy'])) {
667
      $form['taxonomy'] += array('#type' => 'fieldset', '#title' => t('Categories'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#tree' => TRUE, '#weight' => -3);
668
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
669
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
670
}
Dries Buytaert's avatar
 
Dries Buytaert committed
671

Dries Buytaert's avatar
   
Dries Buytaert committed
672
673
674
675
/**
 * Find all terms associated to the given node, within one vocabulary.
 */
function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') {
676
  $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 Mannes's avatar
Kjartan Mannes committed
677
678
679
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
680
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
681
682
683
  return $terms;
}

Dries Buytaert's avatar
   
Dries Buytaert committed
684
/**
685
 * Find all terms associated to the given node, ordered by vocabulary and term weight.
Dries Buytaert's avatar
   
Dries Buytaert committed
686
687
 */
function taxonomy_node_get_terms($nid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
688
  static $terms;
Dries Buytaert's avatar
 
Dries Buytaert committed
689

Dries Buytaert's avatar
   
Dries Buytaert committed
690
  if (!isset($terms[$nid])) {
691
    $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 Mannes's avatar
Kjartan Mannes committed
692
    $terms[$nid] = array();
Dries Buytaert's avatar
 
Dries Buytaert committed
693
    while ($term = db_fetch_object($result)) {
Kjartan Mannes's avatar
Kjartan Mannes committed
694
      $terms[$nid][$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
695
696
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
697
698
  return $terms[$nid];
}
Dries Buytaert's avatar
 
Dries Buytaert committed
699

700
701
702
703
704
705
706
707
708
709
/**
 * 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) {
710
711
712
          // see form_get_error $key = implode('][', $element['#parents']);
          // on why this is the key
          form_set_error("taxonomy][tags][$vid", t('The %name vocabulary can not be modified in this way.', array('%name' => theme('placeholder', $vocabulary->name))));
713
714
715
716
717
718
        }
      }
    }
  }
}

Dries Buytaert's avatar
   
Dries Buytaert committed
719
720
721
/**
 * Save term associations for a given node.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
722
function taxonomy_node_save($nid, $terms) {
723
  taxonomy_node_delete($nid);
724
725
726

  // Free tagging vocabularies do not send their tids in the form,
  // so we'll detect them here and process them independently.
727
  if (isset($terms['tags'])) {
728
729
730
731
732
733
734
735
    $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);
736
      $typed_terms = array_unique($matches[1]);
737

738
      $inserted = array();
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
      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) {
758
759
760
          $edit = array('vid' => $vid, 'name' => $typed_term);
          $status = taxonomy_save_term($edit);
          $typed_term_tid = $edit['tid'];
761
762
        }

763
764
765
766
767
        // Defend against duplicate, different cased tags
        if (!isset($inserted[$typed_term_tid])) {
          db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $typed_term_tid);
          $inserted[$typed_term_tid] = TRUE;
        }
768
769
770
      }
    }
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
771

772
  if (is_array($terms)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
773
    foreach ($terms as $term) {
774
775
776
777
778
779
780
      if (is_array($term)) {
        foreach ($term as $tid) {
          if ($tid) {
            db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $tid);
          }
        }
      }
781
782
783
      else if (is_object($term)) {
        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term->tid);
      }
784
      else if ($term) {
Dries Buytaert's avatar
   
Dries Buytaert committed
785
        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term);
786
      }
Dries Buytaert's avatar
 
Dries Buytaert committed
787
788
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
789
}
Dries Buytaert's avatar
 
Dries Buytaert committed
790

Dries Buytaert's avatar
   
Dries Buytaert committed
791
792
793
/**
 * Remove associations of a node to its terms.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
794
function taxonomy_node_delete($nid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
795
  db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
796
}
Dries Buytaert's avatar
 
Dries Buytaert committed
797

Dries Buytaert's avatar
   
Dries Buytaert committed
798
799
800
801
/**
 * Find all term objects related to a given term ID.
 */
function taxonomy_get_related($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
802
  if ($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
803
    $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 Mannes's avatar
Kjartan Mannes committed
804
805
806
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
807
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
808
    return $related;
Dries Buytaert's avatar
 
Dries Buytaert committed
809
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
810
811
  else {
    return array();
Dries Buytaert's avatar
 
Dries Buytaert committed
812
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
813
}
Dries Buytaert's avatar
 
Dries Buytaert committed
814

Dries Buytaert's avatar
   
Dries Buytaert committed
815
816
817
818
/**
 * Find all parents of a given term ID.
 */
function taxonomy_get_parents($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
819
  if ($tid) {
820
    $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 Mannes's avatar
Kjartan Mannes committed
821
822
823
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries Buytaert's avatar
   
Dries Buytaert committed
824
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
825
    return $parents;
Dries Buytaert's avatar
 
Dries Buytaert committed
826
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
827
828
829
830
  else {
    return array();
  }
}
Dries Buytaert's avatar
 
Dries Buytaert committed
831

Dries Buytaert's avatar
   
Dries Buytaert committed
832
833
834
835
/**
 * Find all ancestors of a given term ID.
 */
function taxonomy_get_parents_all($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
836
837
838
839
840
841
842
843
844
845
846
847
  $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 Buytaert's avatar
   
Dries Buytaert committed
848
849
850
851
/**
 * Find all children of a term ID.
 */
function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
852
  if ($vid) {
853
    $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 Buytaert's avatar
 
Dries Buytaert committed
854
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
855
  else {
856
    $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 Mannes's avatar
Kjartan Mannes committed
857
858
859
860
861
862
863
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
864

Dries Buytaert's avatar
   
Dries Buytaert committed
865
866
867
868
869
870
871
872
873
874
875
876
877
/**
 * 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 Buytaert's avatar
   
Dries Buytaert committed
878
879
880
 * @param $max_depth
 *   The number of levels of the tree to return. Leave NULL to return all levels.
 *
Dries Buytaert's avatar
   
Dries Buytaert committed
881
882
883
884
885
 * @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 Buytaert's avatar
   
Dries Buytaert committed
886
  static $children, $parents, $terms;
Dries Buytaert's avatar
   
Dries Buytaert committed
887

Kjartan Mannes's avatar
Kjartan Mannes committed
888
  $depth++;
Dries Buytaert's avatar
   
Dries Buytaert committed
889

Dries Buytaert's avatar
   
Dries Buytaert committed
890
891
892
893
  // 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 Buytaert's avatar
   
Dries Buytaert committed
894

895
    $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 Buytaert's avatar
   
Dries Buytaert committed
896
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
897
898
899
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
900
901
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
902

Dries Buytaert's avatar
   
Dries Buytaert committed
903
  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
Dries Buytaert's avatar
Dries Buytaert committed
904
905
906
907
908
909
910
911
912
913
914
915
  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 Buytaert's avatar
   
Dries Buytaert committed
916
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
917
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
918
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
919

Dries Buytaert's avatar
   
Dries Buytaert committed
920
  return $tree ? $tree : array();
Kjartan Mannes's avatar
Kjartan Mannes committed
921
}
Dries Buytaert's avatar
 
Dries Buytaert committed
922

Dries Buytaert's avatar
   
Dries Buytaert committed
923
924
925
/**
 * Return an array of synonyms of the given term ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
926
927
function taxonomy_get_synonyms($tid) {
  if ($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
928
    $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid);
Kjartan Mannes's avatar
Kjartan Mannes committed
929
    while ($synonym = db_fetch_array($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
930
      $synonyms[] = $synonym['name'];
Dries Buytaert's avatar
 
Dries Buytaert committed
931
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
932
    return $synonyms ? $synonyms : array();
Dries Buytaert's avatar
 
Dries Buytaert committed
933
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
934
935
  else {
    return array();
Dries Buytaert's avatar
   
Dries Buytaert committed
936
  }
Kjartan Mannes's avatar