taxonomy.module 52.1 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[] = l($term->name, taxonomy_term_path($term), array('rel' => 'tag', 'title' => strip_tags($term->description)));
Dries Buytaert's avatar
   
Dries Buytaert committed
33
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
34
35
36
    }
    return $links;
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
37
38
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/**
 * 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';
}

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

Dries Buytaert's avatar
   
Dries Buytaert committed
304
  if ($edit['vid'] && $edit['name']) {
305
    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
306
    db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']);
307
    foreach ($edit['nodes'] as $type => $selected) {
Dries Buytaert's avatar
   
Dries Buytaert committed
308
309
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
310
    module_invoke_all('taxonomy', 'update', 'vocabulary', $edit);
311
    $status = SAVED_UPDATED;
Dries Buytaert's avatar
 
Dries Buytaert committed
312
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
313
  else if ($edit['vid']) {
314
    $status = taxonomy_del_vocabulary($edit['vid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
315
316
  }
  else {
317
318
    $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');
319
    foreach ($edit['nodes'] as $type => $selected) {
Dries Buytaert's avatar
   
Dries Buytaert committed
320
321
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
322
    module_invoke_all('taxonomy', 'insert', 'vocabulary', $edit);
323
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
324
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
325
326

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

328
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
329
}
Dries Buytaert's avatar
 
Dries Buytaert committed
330

Kjartan Mannes's avatar
Kjartan Mannes committed
331
function taxonomy_del_vocabulary($vid) {
332
  $vocabulary = (array) taxonomy_get_vocabulary($vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
333

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

Dries Buytaert's avatar
   
Dries Buytaert committed
341
  module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
Dries Buytaert's avatar
   
Dries Buytaert committed
342

Dries Buytaert's avatar
   
Dries Buytaert committed
343
344
  cache_clear_all();

345
  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert committed
346
347
348
349
350
}

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

351
352
353
354
  $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,
355
356
357
                  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.'),
358
                  t('Delete'),
359
                  t('Cancel'));
Kjartan Mannes's avatar
Kjartan Mannes committed
360
}
Dries Buytaert's avatar
 
Dries Buytaert committed
361

362
363
364
365
366
367
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
368
function taxonomy_form_term($edit = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
369
  $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
Kjartan Mannes's avatar
Kjartan Mannes committed
370
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries Buytaert's avatar
   
Dries Buytaert committed
371

372
  $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);
373

374
  $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
375

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

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

Kjartan Mannes's avatar
Kjartan Mannes committed
386
    if ($vocabulary->hierarchy == 1) {
387
      $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
388
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
389
    elseif ($vocabulary->hierarchy == 2) {
390
      $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
391
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
392
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
393

394
  if ($vocabulary->relations) {
395
    $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']));
396
397
  }

398
399
  $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.'));
400
401
402
403
404

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


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

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

422
  return drupal_get_form('taxonomy_form_term', $form);
Kjartan Mannes's avatar
Kjartan Mannes committed
423
}
Dries Buytaert's avatar
 
Dries Buytaert committed
424

425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
/**
 * 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';
}

440
function taxonomy_save_term(&$edit) {
Dries Buytaert's avatar
   
Dries Buytaert committed
441
  if ($edit['tid'] && $edit['name']) {
442
    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
443
    module_invoke_all('taxonomy', 'update', 'term', $edit);
444
    $status = SAVED_UPDATED;
Kjartan Mannes's avatar
Kjartan Mannes committed
445
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
446
447
  else if ($edit['tid']) {
    return taxonomy_del_term($edit['tid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
448
449
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
450
    $edit['tid'] = db_next_id('{term_data}_tid');
451
    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
452
    module_invoke_all('taxonomy', 'insert', 'term', $edit);
453
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
454
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
455

Dries Buytaert's avatar
   
Dries Buytaert committed
456
457
458
  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
459
      if ($related_id != 0) {
Dries Buytaert's avatar
   
Dries Buytaert committed
460
        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $edit['tid'], $related_id);
Dries Buytaert's avatar
 
Dries Buytaert committed
461
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
462
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
463
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
464

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

Dries Buytaert's avatar
   
Dries Buytaert committed
485
486
487
  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
488
      if ($synonym) {
Dries Buytaert's avatar
   
Dries Buytaert committed
489
        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $edit['tid'], chop($synonym));
Dries Buytaert's avatar
   
Dries Buytaert committed
490
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
491
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
492
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
493

Dries Buytaert's avatar
   
Dries Buytaert committed
494
495
  cache_clear_all();

496
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
497
}
Dries Buytaert's avatar
 
Dries Buytaert committed
498

Kjartan Mannes's avatar
Kjartan Mannes committed
499
function taxonomy_del_term($tid) {
500
501
502
503
504
505
506
507
508
509
510
511
512
513
  $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
514

515
      $term = (array) taxonomy_get_term($tid);
Dries Buytaert's avatar
   
Dries Buytaert committed
516

517
518
519
520
521
      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
522

523
524
      module_invoke_all('taxonomy', 'delete', 'term', $term);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
525

526
527
    $tids = $orphans;
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
528

Dries Buytaert's avatar
   
Dries Buytaert committed
529
  cache_clear_all();
530
531

  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert committed
532
533
534
535
536
}

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

537
538
539
540
  $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,
541
542
                  t('Are you sure you want to delete the term %title?',
                  array('%title' => theme('placeholder', $term->name))),
543
544
545
                  'admin/taxonomy',
                  t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
                  t('Delete'),
546
                  t('Cancel'));
Kjartan Mannes's avatar
Kjartan Mannes committed
547
}
Dries Buytaert's avatar
 
Dries Buytaert committed
548

549
550
551
552
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
553
554
}

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

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

571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
/**
 * 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
591
592
593
594
595
596
/**
 * 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
597
function taxonomy_get_vocabularies($type = NULL) {
Kjartan Mannes's avatar
Kjartan Mannes committed
598
  if ($type) {
599
    $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
600
601
  }
  else {
602
    $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
603
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
604

Kjartan Mannes's avatar
Kjartan Mannes committed
605
  $vocabularies = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
606
  $node_types = array();
Kjartan Mannes's avatar
Kjartan Mannes committed
607
  while ($voc = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
608
609
610
611
    $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
612
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
613

Kjartan Mannes's avatar
Kjartan Mannes committed
614
615
  return $vocabularies;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
616

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

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

636
    $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);
637

638
639
640
641
    while ($vocabulary = db_fetch_object($c)) {
      if ($vocabulary->tags) {
        $typed_terms = array();
        foreach ($terms as $term) {
642
          // Extract terms belonging to the vocabulary in question.
643
          if ($term->vid == $vocabulary->vid) {
644

645
646
647
648
649
650
651
            // 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;
          }
652
        }
653
        $typed_string = implode(', ', $typed_terms) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
654

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

Dries Buytaert's avatar
   
Dries Buytaert committed
690
691
692
693
/**
 * Find all terms associated to the given node, within one vocabulary.
 */
function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') {
694
  $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
695
696
697
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
698
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
699
700
701
  return $terms;
}

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

Dries Buytaert's avatar
   
Dries Buytaert committed
708
  if (!isset($terms[$nid])) {
709
    $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
710
    $terms[$nid] = array();
Dries Buytaert's avatar
 
Dries Buytaert committed
711
    while ($term = db_fetch_object($result)) {
Kjartan Mannes's avatar
Kjartan Mannes committed
712
      $terms[$nid][$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
713
714
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
715
716
  return $terms[$nid];
}
Dries Buytaert's avatar
 
Dries Buytaert committed
717

718
719
720
721
722
723
724
725
726
727
/**
 * 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) {
728
729
730
          // 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))));
731
732
733
734
735
736
        }
      }
    }
  }
}

Dries Buytaert's avatar
   
Dries Buytaert committed
737
738
739
/**
 * Save term associations for a given node.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
740
function taxonomy_node_save($nid, $terms) {
741
  taxonomy_node_delete($nid);
742
743
744

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

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

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

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

        if (!$typed_term_tid) {
775
776
777
          $edit = array('vid' => $vid, 'name' => $typed_term);
          $status = taxonomy_save_term($edit);
          $typed_term_tid = $edit['tid'];
778
779
780
781
782
783
        }

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
804
805
806
/**
 * Remove associations of a node to its terms.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
807
function taxonomy_node_delete($nid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
808
  db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
809
}
Dries Buytaert's avatar
 
Dries Buytaert committed
810

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

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

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

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

Kjartan Mannes's avatar
Kjartan Mannes committed
901
  $depth++;
Dries Buytaert's avatar
   
Dries Buytaert committed
902

Dries Buytaert's avatar
   
Dries Buytaert committed
903
904
905
906
  // 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
907

908
    $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
909
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
910
911
912
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
913
914
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
915

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

Dries Buytaert's avatar
   
Dries Buytaert committed
933
  return $tree ? $tree : array();
Kjartan Mannes's avatar
Kjartan Mannes committed
934
}
Dries Buytaert's avatar
 
Dries Buytaert committed
935

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

Dries Buytaert's avatar
   
Dries Buytaert committed
952
953
954
955
956
/**
 * 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
957
}
Dries Buytaert's avatar
   
Dries Buytaert committed
958

Dries Buytaert's avatar
   
Dries Buytaert committed
959
960
961
/**
 * Given a term id, count the number of published nodes in it.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
962
function taxonomy_term_count_nodes($tid, $type = 0) {
Kjartan Mannes's avatar
Kjartan Mannes committed
963
  static $count;
Dries Buytaert's avatar
   
Dries Buytaert committed
964

Dries Buytaert's avatar
   
Dries Buytaert committed
965
966
967
  if (!isset($count[$type])) {
    // $type == 0 always evaluates true is $type is a string
    if (is_numeric($type)) {
968
      $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
969
970
    }
    else {
971
      $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
972
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
973
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
974
      $count[$type][$term->tid] = $term->c;
Dries Buytaert's avatar
   
Dries Buytaert committed
975
976
977
    }
  }

Kjartan Mannes's avatar
Kjartan Mannes committed
978
  foreach (_taxonomy_term_children($tid) as $c) {
Dries Buytaert's avatar
   
Dries Buytaert committed