taxonomy.module 51.8 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
66
67
    $items[] = array('path' => 'admin/taxonomy',
      'title' => t('categories'),
      'callback' => 'taxonomy_overview_vocabularies',
Dries Buytaert's avatar
   
Dries Buytaert committed
68
      'access' => user_access('administer taxonomy'));
69

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

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

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

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

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

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

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

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

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

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

155
  return theme('table', $header, $rows, array('id' => 'taxonomy'));
156
157
158
159
160
161
162
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
}

/**
 * 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 Mannes's avatar
Kjartan Mannes committed
199
function taxonomy_form_vocabulary($edit = array()) {
200
201
202
203
  $form['name'] = array('#type' => 'textfield',
    '#title' => t('Vocabulary name'),
    '#default_value' => $edit['name'],
    '#maxlength' => 64,
204
    '#description' => t('The name for this vocabulary. Example: "Topic".'),
205
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
    '#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.'),
  );
255

256
257
258
259
  // Add extra vocabulary form elements.
  $extra = module_invoke_all('taxonomy', 'form', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
260
      $extra[$key]['#weight'] = isset($extra[$key]['#weight']) ? $extra[$key]['#weight'] : -18;
261
262
263
    }
    $form = array_merge($form, $extra);
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
264

265
  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
Dries Buytaert's avatar
   
Dries Buytaert committed
266
  if ($edit['vid']) {
267
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
268
    $form['vid'] = array('#type' => 'value', '#value' => $edit['vid']);
269
    $form['module'] = array('#type' => 'value', '#value' => $edit['module']);
Dries Buytaert's avatar
 
Dries Buytaert committed
270
  }
271
  return drupal_get_form('taxonomy_form_vocabulary', $form);
Kjartan Mannes's avatar
Kjartan Mannes committed
272
}
Kjartan Mannes's avatar
Kjartan Mannes committed
273

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/**
 * 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';
}

291
function taxonomy_save_vocabulary(&$edit) {
292
  $edit['nodes'] = empty($edit['nodes']) ? array() : $edit['nodes'];
Dries Buytaert's avatar
   
Dries Buytaert committed
293

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

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

318
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
319
}
Dries Buytaert's avatar
 
Dries Buytaert committed
320

Kjartan Mannes's avatar
Kjartan Mannes committed
321
function taxonomy_del_vocabulary($vid) {
322
  $vocabulary = (array) taxonomy_get_vocabulary($vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
323

Dries Buytaert's avatar
   
Dries Buytaert committed
324
  db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
325
  db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
326
  $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
Kjartan Mannes's avatar
Kjartan Mannes committed
327
328
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries Buytaert's avatar
 
Dries Buytaert committed
329
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
330

Dries Buytaert's avatar
   
Dries Buytaert committed
331
  module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
Dries Buytaert's avatar
   
Dries Buytaert committed
332

Dries Buytaert's avatar
   
Dries Buytaert committed
333
334
  cache_clear_all();

335
  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert committed
336
337
338
339
340
}

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

341
342
343
344
  $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,
345
346
347
                  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.'),
348
                  t('Delete'),
349
                  t('Cancel'));
Kjartan Mannes's avatar
Kjartan Mannes committed
350
}
Dries Buytaert's avatar
 
Dries Buytaert committed
351

352
353
354
355
356
357
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 Mannes's avatar
Kjartan Mannes committed
358
function taxonomy_form_term($edit = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
359
  $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
Kjartan Mannes's avatar
Kjartan Mannes committed
360
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries Buytaert's avatar
   
Dries Buytaert committed
361

362
  $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);
363

364
  $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
365

Kjartan Mannes's avatar
Kjartan Mannes committed
366
  if ($vocabulary->hierarchy) {
Dries Buytaert's avatar
   
Dries Buytaert committed
367
368
    $parent = array_keys(taxonomy_get_parents($edit['tid']));
    $children = taxonomy_get_tree($vocabulary_id, $edit['tid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
369

Dries Buytaert's avatar
   
Dries Buytaert committed
370
    // A term can't be the child of itself, nor of its children.
Dries Buytaert's avatar
   
Dries Buytaert committed
371
372
373
    foreach ($children as $child) {
      $exclude[] = $child->tid;
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
374
    $exclude[] = $edit['tid'];
Dries Buytaert's avatar
   
Dries Buytaert committed
375

Kjartan Mannes's avatar
Kjartan Mannes committed
376
    if ($vocabulary->hierarchy == 1) {
377
      $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
378
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
379
    elseif ($vocabulary->hierarchy == 2) {
380
      $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
381
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
382
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
383

384
  if ($vocabulary->relations) {
385
    $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']));
386
387
  }

388
389
  $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.'));
390
391
392
393
394

  // Add extra term form elements.
  $extra = module_invoke_all('taxonomy', 'term', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
395
      $extra[$key]['#weight'] = isset($extra[$key]['#weight']) ? $extra[$key]['#weight'] : -18;
396
397
398
399
400
    }
    $form = array_merge($form, $extra);
  }


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

Dries Buytaert's avatar
   
Dries Buytaert committed
404
  if ($edit['tid']) {
405
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
406
    $form['tid'] = array('#type' => 'value', '#value' => $edit['tid']);
Dries Buytaert's avatar
 
Dries Buytaert committed
407
  }
408
  else {
409
    $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
410
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
411

412
  return drupal_get_form('taxonomy_form_term', $form);
Kjartan Mannes's avatar
Kjartan Mannes committed
413
}
Dries Buytaert's avatar
 
Dries Buytaert committed
414

415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
/**
 * 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';
}

430
function taxonomy_save_term(&$edit) {
Dries Buytaert's avatar
   
Dries Buytaert committed
431
  if ($edit['tid'] && $edit['name']) {
432
    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
433
    module_invoke_all('taxonomy', 'update', 'term', $edit);
434
    $status = SAVED_UPDATED;
Kjartan Mannes's avatar
Kjartan Mannes committed
435
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
436
437
  else if ($edit['tid']) {
    return taxonomy_del_term($edit['tid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
438
439
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
440
    $edit['tid'] = db_next_id('{term_data}_tid');
441
    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
442
    module_invoke_all('taxonomy', 'insert', 'term', $edit);
443
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
444
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
445

Dries Buytaert's avatar
   
Dries Buytaert committed
446
447
448
  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
449
      if ($related_id != 0) {
Dries Buytaert's avatar
   
Dries Buytaert committed
450
        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $edit['tid'], $related_id);
Dries Buytaert's avatar
 
Dries Buytaert committed
451
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
452
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
453
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
454

Dries Buytaert's avatar
   
Dries Buytaert committed
455
  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $edit['tid']);
456
  if (!isset($edit['parent']) || empty($edit['parent'])) {
457
    $edit['parent'] = array(0);
Kjartan Mannes's avatar
Kjartan Mannes committed
458
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
459
460
  if (is_array($edit['parent'])) {
    foreach ($edit['parent'] as $parent) {
461
462
463
464
465
466
467
468
      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
469
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
470
  }
471
472
473
  else {
    db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $edit['tid'], $edit['parent']);
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
474

Dries Buytaert's avatar
   
Dries Buytaert committed
475
476
477
  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
478
      if ($synonym) {
Dries Buytaert's avatar
   
Dries Buytaert committed
479
        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $edit['tid'], chop($synonym));
Dries Buytaert's avatar
   
Dries Buytaert committed
480
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
481
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
482
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
483

Dries Buytaert's avatar
   
Dries Buytaert committed
484
485
  cache_clear_all();

486
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
487
}
Dries Buytaert's avatar
 
Dries Buytaert committed
488

Kjartan Mannes's avatar
Kjartan Mannes committed
489
function taxonomy_del_term($tid) {
490
491
492
493
494
495
496
497
498
499
500
501
502
503
  $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
504

505
      $term = (array) taxonomy_get_term($tid);
Dries Buytaert's avatar
   
Dries Buytaert committed
506

507
508
509
510
511
      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
512

513
514
      module_invoke_all('taxonomy', 'delete', 'term', $term);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
515

516
517
    $tids = $orphans;
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
518

Dries Buytaert's avatar
   
Dries Buytaert committed
519
  cache_clear_all();
520
521

  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert committed
522
523
524
525
526
}

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

527
528
529
530
  $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,
531
532
                  t('Are you sure you want to delete the term %title?',
                  array('%title' => theme('placeholder', $term->name))),
533
534
535
                  'admin/taxonomy',
                  t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
                  t('Delete'),
536
                  t('Cancel'));
Kjartan Mannes's avatar
Kjartan Mannes committed
537
}
Dries Buytaert's avatar
 
Dries Buytaert committed
538

539
540
541
542
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 Mannes's avatar
Kjartan Mannes committed
543
544
}

Dries Buytaert's avatar
   
Dries Buytaert committed
545
546
547
/**
 * Generate a form element for selecting terms from a vocabulary.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
548
function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
Dries Buytaert's avatar
   
Dries Buytaert committed
549
  $vocabulary = taxonomy_get_vocabulary($vid);
550
  $help = ($help) ? $help : $vocabulary->help;
Kjartan Mannes's avatar
Kjartan Mannes committed
551
552
553
554
  if ($vocabulary->required) {
    $blank = 0;
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
555
    $blank = '<'. t('none') .'>';
Kjartan Mannes's avatar
Kjartan Mannes committed
556
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
557

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

561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
/**
 * 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
581
582
583
584
585
586
/**
 * 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
587
function taxonomy_get_vocabularies($type = NULL) {
Kjartan Mannes's avatar
Kjartan Mannes committed
588
  if ($type) {
589
    $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
590
591
  }
  else {
592
    $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
593
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
594

Kjartan Mannes's avatar
Kjartan Mannes committed
595
  $vocabularies = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
596
  $node_types = array();
Kjartan Mannes's avatar
Kjartan Mannes committed
597
  while ($voc = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
598
599
600
601
    $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
602
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
603

Kjartan Mannes's avatar
Kjartan Mannes committed
604
605
  return $vocabularies;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
606

Dries Buytaert's avatar
   
Dries Buytaert committed
607
608
609
/**
 * Generate a form for selecting terms to associate with a node.
 */
610
611
612
613
function taxonomy_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
    $node = $form['#node'];

614
    if (!isset($node->taxonomy)) {
615
616
617
618
619
620
      if ($node->nid) {
        $terms = taxonomy_node_get_terms($node->nid);
      }
      else {
        $terms = array();
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
621
622
    }
    else {
623
      $terms = $node->taxonomy;
Dries Buytaert's avatar
 
Dries Buytaert committed
624
625
    }

626
    $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);
627

628
629
630
631
    while ($vocabulary = db_fetch_object($c)) {
      if ($vocabulary->tags) {
        $typed_terms = array();
        foreach ($terms as $term) {
632
          // Extract terms belonging to the vocabulary in question.
633
          if ($term->vid == $vocabulary->vid) {
634

635
636
637
638
639
640
641
            // 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;
          }
642
        }
643
        $typed_string = implode(', ', $typed_terms) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
644

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

Dries Buytaert's avatar
   
Dries Buytaert committed
680
681
682
683
/**
 * Find all terms associated to the given node, within one vocabulary.
 */
function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') {
684
  $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
685
686
687
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
688
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
689
690
691
  return $terms;
}

Dries Buytaert's avatar
   
Dries Buytaert committed
692
/**
693
 * Find all terms associated to the given node, ordered by vocabulary and term weight.
Dries Buytaert's avatar
   
Dries Buytaert committed
694
695
 */
function taxonomy_node_get_terms($nid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
696
  static $terms;
Dries Buytaert's avatar
 
Dries Buytaert committed
697

Dries Buytaert's avatar
   
Dries Buytaert committed
698
  if (!isset($terms[$nid])) {
699
    $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
700
    $terms[$nid] = array();
Dries Buytaert's avatar
 
Dries Buytaert committed
701
    while ($term = db_fetch_object($result)) {
Kjartan Mannes's avatar
Kjartan Mannes committed
702
      $terms[$nid][$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
703
704
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
705
706
  return $terms[$nid];
}
Dries Buytaert's avatar
 
Dries Buytaert committed
707

708
709
710
711
712
713
714
715
716
717
/**
 * 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) {
718
719
720
          // 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))));
721
722
723
724
725
726
        }
      }
    }
  }
}

Dries Buytaert's avatar
   
Dries Buytaert committed
727
728
729
/**
 * Save term associations for a given node.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
730
function taxonomy_node_save($nid, $terms) {
731
  taxonomy_node_delete($nid);
732
733
734

  // Free tagging vocabularies do not send their tids in the form,
  // so we'll detect them here and process them independently.
735
  if (isset($terms['tags'])) {
736
737
738
739
740
741
742
743
    $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);
744
      $typed_terms = array_unique($matches[1]);
745

746
      $inserted = array();
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
      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) {
766
767
768
          $edit = array('vid' => $vid, 'name' => $typed_term);
          $status = taxonomy_save_term($edit);
          $typed_term_tid = $edit['tid'];
769
770
        }

771
772
773
774
775
        // 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;
        }
776
777
778
      }
    }
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
779

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
806
807
808
809
/**
 * Find all term objects related to a given term ID.
 */
function taxonomy_get_related($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
810
  if ($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
811
    $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
812
813
814
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
815
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
816
    return $related;
Dries Buytaert's avatar
 
Dries Buytaert committed
817
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
818
819
  else {
    return array();
Dries Buytaert's avatar
 
Dries Buytaert committed
820
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
821
}
Dries Buytaert's avatar
 
Dries Buytaert committed
822

Dries Buytaert's avatar
   
Dries Buytaert committed
823
824
825
826
/**
 * Find all parents of a given term ID.
 */
function taxonomy_get_parents($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
827
  if ($tid) {
828
    $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
829
830
831
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries Buytaert's avatar
   
Dries Buytaert committed
832
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
833
    return $parents;
Dries Buytaert's avatar
 
Dries Buytaert committed
834
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
835
836
837
838
  else {
    return array();
  }
}
Dries Buytaert's avatar
 
Dries Buytaert committed
839

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

Dries Buytaert's avatar
   
Dries Buytaert committed
873
874
875
876
877
878
879
880
881
882
883
884
885
/**
 * 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
886
887
888
 * @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
889
890
891
892
893
 * @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
894
  static $children, $parents, $terms;
Dries Buytaert's avatar
   
Dries Buytaert committed
895

Kjartan Mannes's avatar
Kjartan Mannes committed
896
  $depth++;
Dries Buytaert's avatar
   
Dries Buytaert committed
897

Dries Buytaert's avatar
   
Dries Buytaert committed
898
899
900
901
  // 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
902

903
    $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
904
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
905
906
907
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
908
909
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
910

Dries Buytaert's avatar
   
Dries Buytaert committed
911
  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
Dries Buytaert's avatar
Dries Buytaert committed
912
913
914
915
916
917
918
919
920
921
922
923
  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
924
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
925
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
926
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
927

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
947
948
949
950
951
/**
 * 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 Mannes's avatar
Kjartan Mannes committed
952
}
Dries Buytaert's avatar
   
Dries Buytaert committed
953

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

Dries Buytaert's avatar
   
Dries Buytaert committed
960
  if (!isset($count[$type])) {
961
    // $type == 0 always evaluates TRUE is $type is a string
Dries Buytaert's avatar
   
Dries Buytaert committed
962
    if (is_numeric($type)) {
963
      $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 Buytaert's avatar
   
Dries Buytaert committed
964
965
    }
    else {
966
      $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 Buytaert's avatar
   
Dries Buytaert committed
967
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
968
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar