taxonomy.module 50 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)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
31
32
      foreach ($node->taxonomy as $tid) {
        $term = taxonomy_get_term($tid);
33
        $links[] = l($term->name, taxonomy_term_path($term), array('rel' => 'tag', 'title' => $term->description));
Dries Buytaert's avatar
   
Dries Buytaert committed
34
35
36
37
      }
    }
    else {
      foreach (taxonomy_node_get_terms($node->nid) as $term) {
38
        $links[] = l($term->name, taxonomy_term_path($term), array('rel' => 'tag', 'title' => $term->description));
Dries Buytaert's avatar
   
Dries Buytaert committed
39
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
40
41
42
    }
    return $links;
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
43
44
}

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

Dries Buytaert's avatar
   
Dries Buytaert committed
59
60
61
62
  if ($may_cache) {
    $items[] = array('path' => 'admin/taxonomy', 'title' => t('categories'),
      'callback' => 'taxonomy_admin',
      'access' => user_access('administer taxonomy'));
63

Dries Buytaert's avatar
   
Dries Buytaert committed
64
65
    $items[] = array('path' => 'admin/taxonomy/list', 'title' => t('list'),
      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
66

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

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    $items[] = array('path' => 'admin/taxonomy/edit/vocabulary', 'title' => t('edit vocabulary'),
      'callback' => 'taxonomy_admin',
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);

    $items[] = array('path' => 'admin/taxonomy/add/term', 'title' => t('add term'),
      'callback' => 'taxonomy_admin',
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);

    $items[] = array('path' => 'admin/taxonomy/edit/term', 'title' => t('edit term'),
      'callback' => 'taxonomy_admin',
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);

Dries Buytaert's avatar
   
Dries Buytaert committed
87
88
89
90
    $items[] = array('path' => 'taxonomy/term', 'title' => t('taxonomy term'),
      'callback' => 'taxonomy_term_page',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
Steven Wittens's avatar
Steven Wittens committed
91
92
93
94
95

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

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

      $items[] = array('path' => 'admin/taxonomy/' . arg(2) . '/add/term', 'title' => t('add term'),
        'callback' => 'taxonomy_admin',
        'access' => user_access('administer taxonomy'),
        'type' => MENU_LOCAL_TASK);
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
113

Dries Buytaert's avatar
   
Dries Buytaert committed
114
115
  return $items;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
116

Kjartan Mannes's avatar
Kjartan Mannes committed
117
function taxonomy_form_vocabulary($edit = array()) {
118
119
120
121
122
123
124
125
126
127
128
  $form['name'] = array('#type' => 'textfield', '#title' => t('Vocabulary name'), '#default_value' => $edit['name'], '#size' => 60, '#maxlength' => 64, '#description' => t('The name for this vocabulary.  Example: "Topic".'), '#required' => TRUE);

  $form['description'] = array('#type' => 'textarea', '#title' => t('Description'), '#default_value' => $edit['description'], '#cols' => 60, '#rows' => 5, '#description' => t('Description of the vocabulary; can be used by modules.'));
  $form['help'] = array('#type' => 'textfield', '#title' => t('Help text'), '#default_value' => $edit['help'], '#size' => 60, '#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'], '#return_value' => 1, '#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'], '#return_value' => 1, '#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'], '#return_value' => 1, '#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'], '#return_value' => 1, '#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'], '#delta' => 10, '#description' => t('In listings, the heavier vocabularies will sink and the lighter vocabularies will be positioned nearer the top.'));
129

130
131
132
133
  // Add extra vocabulary form elements.
  $extra = module_invoke_all('taxonomy', 'form', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
134
      $extra[$key]['#weight'] = isset($extra[$key]['#weight']) ? $nodeapi[$key]['#weight'] : -18;
135
136
137
    }
    $form = array_merge($form, $extra);
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
138

139
  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
Dries Buytaert's avatar
   
Dries Buytaert committed
140
  if ($edit['vid']) {
141
142
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
    $form['vid'] = array('#type' => 'hidden', '#value' => $edit['vid']);
Dries Buytaert's avatar
 
Dries Buytaert committed
143
  }
144
  return drupal_get_form('taxonomy_form_vocabulary', $form);
Kjartan Mannes's avatar
Kjartan Mannes committed
145
}
Kjartan Mannes's avatar
Kjartan Mannes committed
146

147
function taxonomy_save_vocabulary(&$edit) {
148
149
  $edit['nodes'] = ($edit['nodes']) ? $edit['nodes'] : array();
  $edit['weight'] = ($edit['weight']) ? $edit['weight'] : 0;
150
  $edit['tags'] = ($edit['tags']) ? $edit['tags'] : 0;
Dries Buytaert's avatar
   
Dries Buytaert committed
151

152
  $data = array('name' => $edit['name'], 'description' => $edit['description'], 'help' => $edit['help'], 'multiple' => $edit['multiple'], 'required' => $edit['required'], 'hierarchy' => $edit['hierarchy'], 'relations' => $edit['relations'], 'tags' => $edit['tags'], 'weight' => $edit['weight'],  'module' => isset($edit['module']) ? $edit['module'] : 'taxonomy');
Dries Buytaert's avatar
   
Dries Buytaert committed
153
  if ($edit['vid'] && $edit['name']) {
Dries Buytaert's avatar
   
Dries Buytaert committed
154
    db_query('UPDATE {vocabulary} SET '. _taxonomy_prepare_update($data) .' WHERE vid = %d', $edit['vid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
155
    db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']);
156
    foreach ($edit['nodes'] as $type => $selected) {
Dries Buytaert's avatar
   
Dries Buytaert committed
157
158
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
159
    module_invoke_all('taxonomy', 'update', 'vocabulary', $edit);
160
    $status = SAVED_UPDATED;
Dries Buytaert's avatar
 
Dries Buytaert committed
161
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
162
  else if ($edit['vid']) {
163
    $status = taxonomy_del_vocabulary($edit['vid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
164
165
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
166
    $data['vid'] = $edit['vid'] = db_next_id('{vocabulary}_vid');
Dries Buytaert's avatar
   
Dries Buytaert committed
167
    db_query('INSERT INTO {vocabulary} '. _taxonomy_prepare_insert($data, 1) .' VALUES '. _taxonomy_prepare_insert($data, 2));
168
    foreach ($edit['nodes'] as $type => $selected) {
Dries Buytaert's avatar
   
Dries Buytaert committed
169
170
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
171
    module_invoke_all('taxonomy', 'insert', 'vocabulary', $edit);
172
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
173
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
174
175

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

177
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
178
}
Dries Buytaert's avatar
 
Dries Buytaert committed
179

Kjartan Mannes's avatar
Kjartan Mannes committed
180
function taxonomy_del_vocabulary($vid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
181
182
  $vocabulary = taxonomy_get_vocabulary($vid);

Dries Buytaert's avatar
   
Dries Buytaert committed
183
  db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
184
  db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
185
  $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
Kjartan Mannes's avatar
Kjartan Mannes committed
186
187
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries Buytaert's avatar
 
Dries Buytaert committed
188
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
189

Dries Buytaert's avatar
   
Dries Buytaert committed
190
  module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
Dries Buytaert's avatar
   
Dries Buytaert committed
191

Dries Buytaert's avatar
   
Dries Buytaert committed
192
193
  cache_clear_all();

194
  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert committed
195
196
197
198
199
}

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

200
201
202
  $form['type'] = array('#type' => 'hidden', '#value' => 'vocabulary');
  $form['vid'] = array('#type' => 'hidden', '#value' => $vid);
  $form['name'] = array('#type' => 'hidden', '#value' => $vocabulary->name);
203
204
205
206
  return confirm_form('vocabulary_confirm_delete', $form,
                  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.'),
207
                  t('Delete'),
208
                  t('Cancel'));
Kjartan Mannes's avatar
Kjartan Mannes committed
209
}
Dries Buytaert's avatar
 
Dries Buytaert committed
210

Kjartan Mannes's avatar
Kjartan Mannes committed
211
function taxonomy_form_term($edit = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
212
  $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
Kjartan Mannes's avatar
Kjartan Mannes committed
213
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries Buytaert's avatar
   
Dries Buytaert committed
214

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

217
  $form['description'] = array('#type' => 'textarea', '#title' => t('Description'), '#default_value' => $edit['description'], '#cols' => 60, '#rows' => 5, '#description' => t('A description of the term.'));
Dries Buytaert's avatar
 
Dries Buytaert committed
218

Kjartan Mannes's avatar
Kjartan Mannes committed
219
  if ($vocabulary->hierarchy) {
Dries Buytaert's avatar
   
Dries Buytaert committed
220
221
    $parent = array_keys(taxonomy_get_parents($edit['tid']));
    $children = taxonomy_get_tree($vocabulary_id, $edit['tid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
222

Dries Buytaert's avatar
   
Dries Buytaert committed
223
    // A term can't be the child of itself, nor of its children.
Dries Buytaert's avatar
   
Dries Buytaert committed
224
225
226
    foreach ($children as $child) {
      $exclude[] = $child->tid;
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
227
    $exclude[] = $edit['tid'];
Dries Buytaert's avatar
   
Dries Buytaert committed
228

Kjartan Mannes's avatar
Kjartan Mannes committed
229
    if ($vocabulary->hierarchy == 1) {
230
      $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
231
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
232
    elseif ($vocabulary->hierarchy == 2) {
233
      $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
234
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
235
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
236

237
  if ($vocabulary->relations) {
238
    $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']));
239
240
  }

241
242
  $form['synonyms'] = array('#type' => 'textarea', '#title' => t('Synonyms'), '#default_value' => implode("\n", taxonomy_get_synonyms($edit['tid'])), '#cols' => 60, '#rows' => 5, '#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'], '#delta' => 10, '#description' => t('In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top.'));
243
244
245
246
247

  // Add extra term form elements.
  $extra = module_invoke_all('taxonomy', 'term', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
248
      $extra[$key]['#weight'] = isset($extra[$key]['#weight']) ? $nodeapi[$key]['#weight'] : -18;
249
250
251
252
253
    }
    $form = array_merge($form, $extra);
  }


254
255
  $form['vid'] = array('#type' => 'hidden', '#value' => $vocabulary->vid);
  $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
Kjartan Mannes's avatar
Kjartan Mannes committed
256

Dries Buytaert's avatar
   
Dries Buytaert committed
257
  if ($edit['tid']) {
258
259
    $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
    $form['tid'] = array('#type' => 'hidden', '#value' => $edit['tid']);
Dries Buytaert's avatar
 
Dries Buytaert committed
260
  }
261
  else {
262
    $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
263
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
264

265
  return drupal_get_form('taxonomy_form_term', $form);
Kjartan Mannes's avatar
Kjartan Mannes committed
266
}
Dries Buytaert's avatar
 
Dries Buytaert committed
267

268
function taxonomy_save_term(&$edit) {
Dries Buytaert's avatar
   
Dries Buytaert committed
269
270
  if ($edit['tid'] && $edit['name']) {
    $data = array('name' => $edit['name'], 'description' => $edit['description'], 'weight' => $edit['weight']);
Dries Buytaert's avatar
 
Dries Buytaert committed
271

Dries Buytaert's avatar
   
Dries Buytaert committed
272
    db_query('UPDATE {term_data} SET '. _taxonomy_prepare_update($data) .' WHERE tid = %d', $edit['tid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
273
    module_invoke_all('taxonomy', 'update', 'term', $edit);
274
    $status = SAVED_UPDATED;
Kjartan Mannes's avatar
Kjartan Mannes committed
275
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
276
277
  else if ($edit['tid']) {
    return taxonomy_del_term($edit['tid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
278
279
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
280
281
    $edit['tid'] = db_next_id('{term_data}_tid');
    $data = array('tid' => $edit['tid'], 'name' => $edit['name'], 'description' => $edit['description'], 'vid' => $edit['vid'], 'weight' => $edit['weight']);
Dries Buytaert's avatar
   
Dries Buytaert committed
282
    db_query('INSERT INTO {term_data} '. _taxonomy_prepare_insert($data, 1) .' VALUES '. _taxonomy_prepare_insert($data, 2));
Dries Buytaert's avatar
   
Dries Buytaert committed
283
    module_invoke_all('taxonomy', 'insert', 'term', $edit);
284
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
285
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
286

Dries Buytaert's avatar
   
Dries Buytaert committed
287
288
289
  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
290
      if ($related_id != 0) {
Dries Buytaert's avatar
   
Dries Buytaert committed
291
        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $edit['tid'], $related_id);
Dries Buytaert's avatar
 
Dries Buytaert committed
292
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
293
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
294
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
295

Dries Buytaert's avatar
   
Dries Buytaert committed
296
297
  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $edit['tid']);
  if (!isset($edit['parent'])) {
298
    $edit['parent'] = array(0);
Kjartan Mannes's avatar
Kjartan Mannes committed
299
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
300
301
  if (is_array($edit['parent'])) {
    foreach ($edit['parent'] as $parent) {
302
303
304
305
306
307
308
309
      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
310
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
311
  }
312
313
314
  else {
    db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $edit['tid'], $edit['parent']);
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
315

Dries Buytaert's avatar
   
Dries Buytaert committed
316
317
318
  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
319
      if ($synonym) {
Dries Buytaert's avatar
   
Dries Buytaert committed
320
        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $edit['tid'], chop($synonym));
Dries Buytaert's avatar
   
Dries Buytaert committed
321
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
322
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
323
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
324

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

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

Kjartan Mannes's avatar
Kjartan Mannes committed
330
function taxonomy_del_term($tid) {
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  $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
345

346
      $term = taxonomy_get_term($tid);
Dries Buytaert's avatar
   
Dries Buytaert committed
347

348
349
350
351
352
      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
353

354
      module_invoke_all('taxonomy', 'delete', 'term', $term);
355
      drupal_set_message(t('Deleted term %name.', array('%name' => theme('placeholder', $term->name))));
356
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
357

358
359
    $tids = $orphans;
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
360

Dries Buytaert's avatar
   
Dries Buytaert committed
361
  cache_clear_all();
Dries Buytaert's avatar
   
Dries Buytaert committed
362
363
364
365
366
}

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

367
368
  $form['type'] = array('#type' => 'hidden', '#value' => 'term');
  $form['tid'] = array('#type' => 'hidden', '#value' => $tid);
369
370
371
  return confirm_form('term_confirm_delete', $form,
                  t('Are you sure you want to delete the term %title?',
                  array('%title' => theme('placeholder', $term->name))),
372
373
374
                  'admin/taxonomy',
                  t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
                  t('Delete'),
375
                  t('Cancel'));
Kjartan Mannes's avatar
Kjartan Mannes committed
376
}
Dries Buytaert's avatar
 
Dries Buytaert committed
377

Dries Buytaert's avatar
   
Dries Buytaert committed
378
379
380
/**
 * Generate a tabular listing of administrative functions for vocabularies.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
381
function taxonomy_overview() {
382
383
  $vid = arg(2);

384
  // Show all vocabularies, and a "view terms" link to the pagers.
385
  if (!$vid) {
386
    $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '2'));
387
388
    $vocabularies = taxonomy_get_vocabularies();
    foreach ($vocabularies as $vocabulary) {
389
390
      $types = array();
      foreach ($vocabulary->nodes as $type) {
391
        $node_type = node_get_name($type);
392
393
        $types[] = $node_type ? $node_type : $type;
      }
Dries Buytaert's avatar
Dries Buytaert committed
394
      $rows[] = array(check_plain($vocabulary->name), implode(', ', $types), l(t('edit vocabulary'), "admin/taxonomy/edit/vocabulary/$vocabulary->vid"), l(t('edit terms'), "admin/taxonomy/$vocabulary->vid"));
395
396
397
    }

    if (!$rows) {
398
      $rows[] = array(array('data' => t('No categories available.'), 'colspan' => '4', 'class' => 'message'));
399
400
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
401

402
  // Show the vocabulary's terms with a pager.
403
  else {
404
405
406
    $destination = drupal_get_destination();

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

Dries Buytaert's avatar
Dries Buytaert committed
409
410
411
412
413
414
415
416
417
418
    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; }
419
      $rows[] = array(_taxonomy_depth($term->depth) . ' ' . l(check_plain($term->name), "taxonomy/term/$term->tid"), l(t('edit'), "admin/taxonomy/edit/term/$term->tid", array(), $destination));
Dries Buytaert's avatar
Dries Buytaert committed
420
421
422
423
424
425
      $displayed_count++; // we're counting tids displayed
    }

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

Dries Buytaert's avatar
Dries Buytaert committed
427
428
    $GLOBALS['pager_page_array'][] = $start_from;  // FIXME
    $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME
429

Dries Buytaert's avatar
Dries Buytaert committed
430
431
    if ($total_entries >= $page_increment) {
      $rows[] = array(array('data' => theme('pager', NULL, $page_increment), 'colspan' => '2'));
432
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
433
434
  }

435
  return theme('table', $header, $rows, array('id' => 'taxonomy'));
Kjartan Mannes's avatar
Kjartan Mannes committed
436
437
}

Dries Buytaert's avatar
   
Dries Buytaert committed
438
439
440
/**
 * Generate a form element for selecting terms from a vocabulary.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
441
function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
Dries Buytaert's avatar
   
Dries Buytaert committed
442
  $vocabulary = taxonomy_get_vocabulary($vid);
443
  $help = ($help) ? $help : $vocabulary->help;
Kjartan Mannes's avatar
Kjartan Mannes committed
444
445
446
447
  if ($vocabulary->required) {
    $blank = 0;
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
448
    $blank = '<'. t('none') .'>';
Kjartan Mannes's avatar
Kjartan Mannes committed
449
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
450

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

454
/**
455
456
457
 * Generate a set of options for selecting a term from all vocabularies. Can be
 * passed to form_select.
 */
458
function taxonomy_form_all($free_tags = 0) {
459
460
461
  $vocabularies = taxonomy_get_vocabularies();
  $options = array();
  foreach ($vocabularies as $vid => $vocabulary) {
462
    if ($vocabulary->tags && !$free_tags) { continue; }
463
464
465
466
467
468
469
470
471
472
473
    $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
474
475
476
477
478
479
/**
 * 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
480
function taxonomy_get_vocabularies($type = NULL) {
Kjartan Mannes's avatar
Kjartan Mannes committed
481
  if ($type) {
482
    $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
483
484
  }
  else {
485
    $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
486
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
487

Kjartan Mannes's avatar
Kjartan Mannes committed
488
  $vocabularies = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
489
  $node_types = array();
Kjartan Mannes's avatar
Kjartan Mannes committed
490
  while ($voc = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
491
492
493
494
    $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
495
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
496

Kjartan Mannes's avatar
Kjartan Mannes committed
497
498
  return $vocabularies;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
499

Dries Buytaert's avatar
   
Dries Buytaert committed
500
501
502
/**
 * Generate a form for selecting terms to associate with a node.
 */
503
function taxonomy_node_form($node) {
504
  if (!array_key_exists('taxonomy', $node)) {
Kjartan Mannes's avatar
Kjartan Mannes committed
505
    if ($node->nid) {
506
      $terms = taxonomy_node_get_terms($node->nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
507
508
    }
    else {
509
      $terms = array();
Dries Buytaert's avatar
 
Dries Buytaert committed
510
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
511
512
513
514
  }
  else {
    $terms = $node->taxonomy;
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
515

516
  $c = db_query(db_rewrite_sql("SELECT v.*, n.type 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);
Kjartan Mannes's avatar
Kjartan Mannes committed
517
  while ($vocabulary = db_fetch_object($c)) {
518
519
520
521
522
523
524
525
526
527
528
529
530
531
    if ($vocabulary->tags) {
      $typed_terms = array();
      foreach ($terms as $term) {
        if ($term->vid == $vocabulary->vid) {

          // 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;
        }
      }
      $typed_string = implode(', ', $typed_terms) . (array_key_exists('tags', $terms) ? $terms['tags'][$vocabulary->vid] : NULL);
532

533
      $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield', '#default_value' => $typed_string, '#size' => 60, '#maxlength' => 100, '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid, '#required' => $vocabulary->required, '#title' => $vocabulary->name, '#description' => t('A comma-separated list of terms describing this content (Example: funny, bungie jumping, "Company, Inc.").'));
534
535
536
    }
    else {
      $ntterms = array_key_exists('taxonomy', $node) ? $terms : array_keys($terms);
537
      $form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, $ntterms, $help, 'taxonomy');
538
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
539
  }
540
541
542
543
544
545
546
547
  if ($form) {
    $form['taxonomy']['#tree'] = TRUE;
    $form['taxonomy']['#weight'] = -15;
    return $form;
  }
  else {
    return array();
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
548
}
Dries Buytaert's avatar
 
Dries Buytaert committed
549

Dries Buytaert's avatar
   
Dries Buytaert committed
550
551
552
553
/**
 * Find all terms associated to the given node, within one vocabulary.
 */
function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') {
554
  $result = db_query(db_rewrite_sql('SELECT t.tid, t.* FROM {term_data} t, {term_node} r WHERE t.tid = r.tid AND t.vid = %d AND r.nid = %d ORDER BY weight', 't', 'tid'), $vid, $nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
555
556
557
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
558
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
559
560
561
  return $terms;
}

Dries Buytaert's avatar
   
Dries Buytaert committed
562
/**
563
 * Find all terms associated to the given node, ordered by vocabulary and term weight.
Dries Buytaert's avatar
   
Dries Buytaert committed
564
565
 */
function taxonomy_node_get_terms($nid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
566
  static $terms;
Dries Buytaert's avatar
 
Dries Buytaert committed
567

Dries Buytaert's avatar
   
Dries Buytaert committed
568
  if (!isset($terms[$nid])) {
569
    $result = db_query('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', $nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
570
    $terms[$nid] = array();
Dries Buytaert's avatar
 
Dries Buytaert committed
571
    while ($term = db_fetch_object($result)) {
Kjartan Mannes's avatar
Kjartan Mannes committed
572
      $terms[$nid][$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
573
574
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
575
576
  return $terms[$nid];
}
Dries Buytaert's avatar
 
Dries Buytaert committed
577

578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
/**
 * Make sure incoming vids are free tagging enabled.
 */
function taxonomy_node_validate(&$node) {
  if ($node->taxonomy) {
    $terms = $node->taxonomy;
    if ($terms['tags']) {
      foreach ($terms['tags'] as $vid => $vid_value) {
        $vocabulary = taxonomy_get_vocabulary($vid);
        if (!$vocabulary->tags) {
          form_set_error("taxonomy[tags][$vid", t('The %name vocabulary can not be modified in this way.', array('%name' => theme('placeholder', $vocabulary->name))));
        }
      }
    }
  }
}

Dries Buytaert's avatar
   
Dries Buytaert committed
595
596
597
/**
 * Save term associations for a given node.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
598
function taxonomy_node_save($nid, $terms) {
599
  taxonomy_node_delete($nid);
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632

  // Free tagging vocabularies do not send their tids in the form,
  // so we'll detect them here and process them independently.
  if ($terms['tags']) {
    $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) {
633
634
635
          $edit = array('vid' => $vid, 'name' => $typed_term);
          $status = taxonomy_save_term($edit);
          $typed_term_tid = $edit['tid'];
636
637
638
639
640
641
        }

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

643
  if (is_array($terms)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
644
    foreach ($terms as $term) {
645
646
647
648
649
650
651
      if (is_array($term)) {
        foreach ($term as $tid) {
          if ($tid) {
            db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $tid);
          }
        }
      }
652
      else if ($term) {
Dries Buytaert's avatar
   
Dries Buytaert committed
653
        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term);
654
      }
Dries Buytaert's avatar
 
Dries Buytaert committed
655
656
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
657
}
Dries Buytaert's avatar
 
Dries Buytaert committed
658

Dries Buytaert's avatar
   
Dries Buytaert committed
659
660
661
/**
 * Remove associations of a node to its terms.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
662
function taxonomy_node_delete($nid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
663
  db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
664
}
Dries Buytaert's avatar
 
Dries Buytaert committed
665

Dries Buytaert's avatar
   
Dries Buytaert committed
666
667
668
669
/**
 * Find all term objects related to a given term ID.
 */
function taxonomy_get_related($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
670
  if ($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
671
    $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
672
673
674
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
675
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
676
    return $related;
Dries Buytaert's avatar
 
Dries Buytaert committed
677
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
678
679
  else {
    return array();
Dries Buytaert's avatar
 
Dries Buytaert committed
680
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
681
}
Dries Buytaert's avatar
 
Dries Buytaert committed
682

Dries Buytaert's avatar
   
Dries Buytaert committed
683
684
685
686
/**
 * Find all parents of a given term ID.
 */
function taxonomy_get_parents($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
687
  if ($tid) {
688
    $result = db_query(db_rewrite_sql('SELECT t.tid, t.* FROM {term_hierarchy} h, {term_data} t WHERE h.parent = t.tid AND h.tid = %d ORDER BY weight, name', 't', 'tid'), $tid);
Kjartan Mannes's avatar
Kjartan Mannes committed
689
690
691
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries Buytaert's avatar
   
Dries Buytaert committed
692
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
693
    return $parents;
Dries Buytaert's avatar
 
Dries Buytaert committed
694
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
695
696
697
698
  else {
    return array();
  }
}
Dries Buytaert's avatar
 
Dries Buytaert committed
699

Dries Buytaert's avatar
   
Dries Buytaert committed
700
701
702
703
/**
 * Find all ancestors of a given term ID.
 */
function taxonomy_get_parents_all($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
704
705
706
707
708
709
710
711
712
713
714
715
  $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
716
717
718
719
/**
 * Find all children of a term ID.
 */
function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
720
  if ($vid) {
721
    $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_hierarchy} h, {term_data} t WHERE t.vid = %d AND h.tid = t.tid AND h.parent = %d ORDER BY weight, name', 't', 'tid'), $vid, $tid);
Dries Buytaert's avatar
 
Dries Buytaert committed
722
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
723
  else {
724
    $result = db_query(db_rewrite_sql('SELECT t.* FROM {term_hierarchy} h, {term_data} t WHERE h.tid = t.tid AND parent = %d ORDER BY weight, name', 't', 'tid'), $tid);
Kjartan Mannes's avatar
Kjartan Mannes committed
725
726
727
728
729
730
731
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
732

Dries Buytaert's avatar
   
Dries Buytaert committed
733
734
735
736
737
738
739
740
741
742
743
744
745
/**
 * 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
746
747
748
 * @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
749
750
751
752
753
 * @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
754
  static $children, $parents, $terms;
Dries Buytaert's avatar
   
Dries Buytaert committed
755

Kjartan Mannes's avatar
Kjartan Mannes committed
756
  $depth++;
Dries Buytaert's avatar
   
Dries Buytaert committed
757

Dries Buytaert's avatar
   
Dries Buytaert committed
758
759
760
761
  // 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
762

763
    $result = db_query(db_rewrite_sql('SELECT t.tid, t.*, parent FROM {term_data} t, {term_hierarchy} h WHERE t.tid = h.tid AND t.vid = %d ORDER BY weight, name', 't', 'tid'), $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
764
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
765
766
767
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
768
769
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
770

Dries Buytaert's avatar
   
Dries Buytaert committed
771
  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
Dries Buytaert's avatar
Dries Buytaert committed
772
773
774
775
776
777
778
779
780
781
782
783
  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
784
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
785
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
786
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
787

Dries Buytaert's avatar
   
Dries Buytaert committed
788
  return $tree ? $tree : array();
Kjartan Mannes's avatar
Kjartan Mannes committed
789
}
Dries Buytaert's avatar
 
Dries Buytaert committed
790

Dries Buytaert's avatar
   
Dries Buytaert committed
791
792
793
/**
 * Return an array of synonyms of the given term ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
794
795
function taxonomy_get_synonyms($tid) {
  if ($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
796
    $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid);
Kjartan Mannes's avatar
Kjartan Mannes committed
797
    while ($synonym = db_fetch_array($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
798
      $synonyms[] = $synonym['name'];
Dries Buytaert's avatar
 
Dries Buytaert committed
799
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
800
    return $synonyms ? $synonyms : array();
Dries Buytaert's avatar
 
Dries Buytaert committed
801
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
802
803
  else {
    return array();
Dries Buytaert's avatar
   
Dries Buytaert committed
804
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
805
}
Dries Buytaert's avatar
   
Dries Buytaert committed
806

Dries Buytaert's avatar
   
Dries Buytaert committed
807
808
809
810
811
/**
 * 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
812
}
Dries Buytaert's avatar
   
Dries Buytaert committed
813

Dries Buytaert's avatar
   
Dries Buytaert committed
814
815
816
/**
 * Given a term id, count the number of published nodes in it.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
817
function taxonomy_term_count_nodes($tid, $type = 0) {
Kjartan Mannes's avatar
Kjartan Mannes committed
818
  static $count;
Dries Buytaert's avatar
   
Dries Buytaert committed
819

Dries Buytaert's avatar
   
Dries Buytaert committed
820
821
822
  if (!isset($count[$type])) {
    // $type == 0 always evaluates true is $type is a string
    if (is_numeric($type)) {
823
      $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
824
825
    }
    else {
826
      $result = db_query(db_rewrite_sql("SELECT t.tid, COUNT(n.nid) AS c FROM {term_node} t, {node} n WHERE t.nid = n.nid AND n.status = 1 AND n.type = '%s' GROUP BY t.tid"), $type);
Dries Buytaert's avatar
   
Dries Buytaert committed
827
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
828
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
829
      $count[$type][$term->tid] = $term->c;
Dries Buytaert's avatar
   
Dries Buytaert committed
830
831
832
    }
  }

Kjartan Mannes's avatar
Kjartan Mannes committed
833
  foreach (_taxonomy_term_children($tid) as $c) {
Dries Buytaert's avatar
   
Dries Buytaert committed
834
    $children_count += taxonomy_term_count_nodes($c, $type);
Kjartan Mannes's avatar
Kjartan Mannes committed
835
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
836
  return $count[$type][$tid] + $children_count;
Kjartan Mannes's avatar
Kjartan Mannes committed
837
838
}

Dries Buytaert's avatar
   
Dries Buytaert committed
839
840
841
/**
 * Helper for taxonomy_term_count_nodes().
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
842
843
function _taxonomy_term_children($tid) {
  static $children;
Dries Buytaert's avatar
   
Dries Buytaert committed
844

Dries Buytaert's avatar
   
Dries Buytaert committed
845
  if (!isset($children)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
846
    $result = db_query('SELECT tid, parent FROM {term_hierarchy}');
Kjartan Mannes's avatar
Kjartan Mannes committed
847
848
    while ($term = db_fetch_object($result)) {
      $children[$term->parent][] = $term->tid;
Dries Buytaert's avatar
   
Dries Buytaert committed
849
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
850
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
851
852
  return $children[$tid] ? $children[$tid] : array();
}
Dries Buytaert's avatar
 
Dries Buytaert committed
853

Dries Buytaert's avatar
   
Dries Buytaert committed
854
/**
Dries Buytaert's avatar
   
Dries Buytaert committed
855
 * Try to map a string to an existing term, as for glossary use.
Dries Buytaert's avatar
   
Dries Buytaert committed
856
 *
Dries Buytaert's avatar
   
Dries Buytaert committed
857
858
859
860
861
862
863
864
 * Provides a case-insensitive and trimmed mapping, to maximize the
 * likelihood of a successful match.
 *
 * @param name
 *   Name of the term to search for.
 *
 * @return
 *   An array of matching term objects.
Dries Buytaert's avatar
   
Dries Buytaert committed
865
866
 */
function taxonomy_get_term_by_name($name) {
867
  $db_result = db_query(db_rewrite_sql("SELECT t.tid, t.* FROM {term_data} t WHERE LOWER('%s') LIKE LOWER(name)", 't', 'tid'), trim($name));
Dries Buytaert's avatar
   
Dries Buytaert committed
868
869
870
871
872
873
874
875
  $result = array();
  while ($term = db_fetch_object($db_result)) {
    $result[] = $term;
  }

  return $result;
}

Dries Buytaert's avatar
   
Dries Buytaert committed
876
877
878
/**
 * Return the vocabulary object matching a vocabulary ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
879
function taxonomy_get_vocabulary($vid) {
880
881
882
883
884
885
886
887
888
889
890
  static $vocabularies = array();

  if (!array_key_exists($vid, $vocabularies)) {
    $result = db_query('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE v.vid = %d ORDER BY v.weight, v.name', $vid);
    $node_types = array();
    while ($voc = db_fetch_object($result)) {
      $node_types[] = $voc->type;
      unset($voc->type);
      $voc->nodes = $node_types;
      $vocabularies[$vid] = $voc;
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
891
892
  }

893
  return $vocabularies[$vid];
Kjartan Mannes's avatar
Kjartan Mannes committed
894
}
Dries Buytaert's avatar
 
Dries Buytaert committed
895

Dries Buytaert's avatar
   
Dries Buytaert committed
896
897
898
/**
 * Return the term object matching a term ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
899
900
function taxonomy_get_term($tid) {
  // simple cache using a static var?
Dries Buytaert's avatar
   
Dries Buytaert committed
901
  return db_fetch_object(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid));
Kjartan Mannes's avatar
Kjartan Mannes committed
902
}
Dries Buytaert's avatar
 
Dries Buytaert committed
903

Kjartan Mannes's avatar
Kjartan Mannes committed
904
function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
905
  $tree = taxonomy_get_tree($vocabulary_id);
906
907
  $options = array();

Dries Buytaert's avatar
Dries Buytaert committed
908
  if ($blank) {
909
    $options[0] = $blank;
Dries Buytaert's avatar
Dries Buytaert committed
910
  }