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

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

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

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

    }
    return $links;
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
45
46
}

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
54
55
56
  if ($may_cache) {
    $items[] = array('path' => 'admin/taxonomy', 'title' => t('categories'),
      'callback' => 'taxonomy_admin',
      'access' => user_access('administer taxonomy'));
57

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

Dries Buytaert's avatar
   
Dries Buytaert committed
61
62
63
64
65
    $items[] = array('path' => 'admin/taxonomy/add/vocabulary', 'title' => t('add vocabulary'),
      'callback' => 'taxonomy_admin',
      'access' => user_access('administer taxonomy'),
      'type' => MENU_LOCAL_TASK);

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
    $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
81
82
83
84
85
    $items[] = array('path' => 'taxonomy/term', 'title' => t('taxonomy term'),
      'callback' => 'taxonomy_term_page',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
  }
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
  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
102

Dries Buytaert's avatar
   
Dries Buytaert committed
103
104
  return $items;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
105

Kjartan Mannes's avatar
Kjartan Mannes committed
106
function taxonomy_form_vocabulary($edit = array()) {
Dries Buytaert's avatar
Dries Buytaert committed
107
  foreach (node_list() as $type) {
Dries Buytaert's avatar
   
Dries Buytaert committed
108
109
    $node_type = node_invoke($type, 'node_name');
    $nodes[$type] = $node_type ? $node_type : $type;
Kjartan Mannes's avatar
Kjartan Mannes committed
110
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
111

Dries Buytaert's avatar
   
Dries Buytaert committed
112
113
114
  $form .= form_textfield(t('Vocabulary name'), 'name', $edit['name'], 50, 64, t('The name for this vocabulary.  Example: "Topic".'), NULL, TRUE);
  $form .= form_textarea(t('Description'), 'description', $edit['description'], 60, 5, t('Description of the vocabulary; can be used by modules.'));
  $form .= form_textfield(t('Help text'), 'help', $edit['help'], 50, 255, t('Instructions to present to the user when choosing a term.'));
Dries Buytaert's avatar
   
Dries Buytaert committed
115
  $form .= form_checkboxes(t('Types'), 'nodes', $edit['nodes'], $nodes, t('A list of node types you want to associate with this vocabulary.'), NULL, TRUE);
116
  $form .= form_radios(t('Hierarchy'), 'hierarchy', $edit['hierarchy'], array(t('Disabled'), t('Single'), t('Multiple')), 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'))));
117
118
119
  $form .= form_checkbox(t('Related terms'), 'relations', 1, $edit['relations'], t('Allows <a href="%help-url">related terms</a> in this vocabulary.', array('%help-url' => url('admin/help/taxonomy', NULL, NULL, 'related-terms'))));
  $form .= form_checkbox(t('Free tagging'), 'tags', 1, $edit['tags'], t('Content is categorized by typing terms instead of choosing from a list.'));
  $form .= form_checkbox(t('Multiple select'), 'multiple', 1, $edit['multiple'], t('Allows nodes to have more than one term from this vocabulary (always true for free tagging).'));
Dries Buytaert's avatar
   
Dries Buytaert committed
120
  $form .= form_checkbox(t('Required'), 'required', 1, $edit['required'], t('If enabled, every node <strong>must</strong> have at least one term in this vocabulary.'));
Dries Buytaert's avatar
   
Dries Buytaert committed
121
  $form .= form_weight(t('Weight'), 'weight', $edit['weight'], 10, t('In listings, the heavier vocabularies will sink and the lighter vocabularies will be positioned nearer the top.'));
Dries Buytaert's avatar
   
Dries Buytaert committed
122
  $form .= form_submit(t('Submit'));
Dries Buytaert's avatar
 
Dries Buytaert committed
123

Dries Buytaert's avatar
   
Dries Buytaert committed
124
125
126
  if ($edit['vid']) {
    $form .= form_submit(t('Delete'));
    $form .= form_hidden('vid', $edit['vid']);
Dries Buytaert's avatar
 
Dries Buytaert committed
127
128
  }

Kjartan Mannes's avatar
Kjartan Mannes committed
129
130
  return form($form);
}
Kjartan Mannes's avatar
Kjartan Mannes committed
131

Kjartan Mannes's avatar
Kjartan Mannes committed
132
function taxonomy_save_vocabulary($edit) {
133
134
  $edit['nodes'] = ($edit['nodes']) ? $edit['nodes'] : array();
  $edit['weight'] = ($edit['weight']) ? $edit['weight'] : 0;
Dries Buytaert's avatar
   
Dries Buytaert committed
135

136
  $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
137
  if ($edit['vid'] && $edit['name']) {
Dries Buytaert's avatar
   
Dries Buytaert committed
138
    db_query('UPDATE {vocabulary} SET '. _taxonomy_prepare_update($data) .' WHERE vid = %d', $edit['vid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
139
140
141
142
    db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d", $edit['vid']);
    foreach ($edit['nodes'] as $type) {
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
143
    module_invoke_all('taxonomy', 'update', 'vocabulary', $edit);
144
    $status = SAVED_UPDATED;
Dries Buytaert's avatar
 
Dries Buytaert committed
145
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
146
  else if ($edit['vid']) {
147
    $status = taxonomy_del_vocabulary($edit['vid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
148
149
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
150
    $data['vid'] = $edit['vid'] = db_next_id('{vocabulary}_vid');
Dries Buytaert's avatar
   
Dries Buytaert committed
151
    db_query('INSERT INTO {vocabulary} '. _taxonomy_prepare_insert($data, 1) .' VALUES '. _taxonomy_prepare_insert($data, 2));
Dries Buytaert's avatar
   
Dries Buytaert committed
152
153
154
    foreach ($edit['nodes'] as $type) {
      db_query("INSERT INTO {vocabulary_node_types} (vid, type) VALUES (%d, '%s')", $edit['vid'], $type);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
155
    module_invoke_all('taxonomy', 'insert', 'vocabulary', $edit);
156
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
157
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
158
159

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

161
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
162
}
Dries Buytaert's avatar
 
Dries Buytaert committed
163

Kjartan Mannes's avatar
Kjartan Mannes committed
164
function taxonomy_del_vocabulary($vid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
165
166
  $vocabulary = taxonomy_get_vocabulary($vid);

Dries Buytaert's avatar
   
Dries Buytaert committed
167
  db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
168
  db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
169
  $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
Kjartan Mannes's avatar
Kjartan Mannes committed
170
171
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries Buytaert's avatar
 
Dries Buytaert committed
172
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
173

Dries Buytaert's avatar
   
Dries Buytaert committed
174
  module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
Dries Buytaert's avatar
   
Dries Buytaert committed
175

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

178
  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert committed
179
180
181
182
183
}

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

184
185
  $extra  = form_hidden('type', 'vocabulary');
  $extra .= form_hidden('vid', $vid);
186
  $extra .= form_hidden('name', $vocabulary->name);
187
188

  $output = theme('confirm',
189
                  t('Are you sure you want to delete the vocabulary %title?', array('%title' => theme('placeholder', $vocabulary->name))),
190
191
192
193
194
195
                  'admin/taxonomy',
                  t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'),
                  t('Delete'),
                  t('Cancel'),
                  $extra);
  return $output;
Kjartan Mannes's avatar
Kjartan Mannes committed
196
}
Dries Buytaert's avatar
 
Dries Buytaert committed
197

Kjartan Mannes's avatar
Kjartan Mannes committed
198
function taxonomy_form_term($edit = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
199
  $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
Kjartan Mannes's avatar
Kjartan Mannes committed
200
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries Buytaert's avatar
   
Dries Buytaert committed
201

Dries Buytaert's avatar
   
Dries Buytaert committed
202
203
  $form = form_textfield(t('Term name'), 'name', $edit['name'], 50, 64, t('The name for this term.  Example: "Linux".'), NULL, TRUE);
  $form .= form_textarea(t('Description'), 'description', $edit['description'], 60, 5, t('A description of the term.'));
Dries Buytaert's avatar
 
Dries Buytaert committed
204

Kjartan Mannes's avatar
Kjartan Mannes committed
205
  if ($vocabulary->hierarchy) {
Dries Buytaert's avatar
   
Dries Buytaert committed
206
207
    $parent = array_keys(taxonomy_get_parents($edit['tid']));
    $children = taxonomy_get_tree($vocabulary_id, $edit['tid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
208

Dries Buytaert's avatar
   
Dries Buytaert committed
209
    // A term can't be the child of itself, nor of its children.
Dries Buytaert's avatar
   
Dries Buytaert committed
210
211
212
    foreach ($children as $child) {
      $exclude[] = $child->tid;
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
213
    $exclude[] = $edit['tid'];
Dries Buytaert's avatar
   
Dries Buytaert committed
214

Kjartan Mannes's avatar
Kjartan Mannes committed
215
    if ($vocabulary->hierarchy == 1) {
216
      $form .= _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
217
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
218
    elseif ($vocabulary->hierarchy == 2) {
219
      $form .= _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
220
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
221
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
222

223
  if ($vocabulary->relations) {
Dries Buytaert's avatar
   
Dries Buytaert committed
224
    $form .= _taxonomy_term_select(t('Related terms'), 'relations', array_keys(taxonomy_get_related($edit['tid'])), $vocabulary_id, NULL, 1, '<'. t('none') .'>', array($edit['tid']));
225
226
  }

227
  $form .= form_textarea(t('Synonyms'), 'synonyms', implode("\n", taxonomy_get_synonyms($edit['tid'])), 30, 5, t('<a href="%help-url">Synonyms</a> of this term, one synonym per line.', array('%help-url' => url('admin/help/taxonomy', NULL, NULL, 'synonyms'))));
Dries Buytaert's avatar
   
Dries Buytaert committed
228
  $form .= form_weight(t('Weight'), 'weight', $edit['weight'], 10, t('In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top.'));
Dries Buytaert's avatar
   
Dries Buytaert committed
229
230
  $form .= form_hidden('vid', $vocabulary->vid);
  $form .= form_submit(t('Submit'));
Kjartan Mannes's avatar
Kjartan Mannes committed
231

Dries Buytaert's avatar
   
Dries Buytaert committed
232
233
234
  if ($edit['tid']) {
    $form .= form_submit(t('Delete'));
    $form .= form_hidden('tid', $edit['tid']);
Dries Buytaert's avatar
 
Dries Buytaert committed
235
  }
236
237
238
  else {
    $form .= form_hidden('destination', $_GET['q']);
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
239

Kjartan Mannes's avatar
Kjartan Mannes committed
240
241
  return form($form);
}
Dries Buytaert's avatar
 
Dries Buytaert committed
242

Kjartan Mannes's avatar
Kjartan Mannes committed
243
function taxonomy_save_term($edit) {
Dries Buytaert's avatar
   
Dries Buytaert committed
244
245
  if ($edit['tid'] && $edit['name']) {
    $data = array('name' => $edit['name'], 'description' => $edit['description'], 'weight' => $edit['weight']);
Dries Buytaert's avatar
 
Dries Buytaert committed
246

Dries Buytaert's avatar
   
Dries Buytaert committed
247
    db_query('UPDATE {term_data} SET '. _taxonomy_prepare_update($data) .' WHERE tid = %d', $edit['tid']);
Dries Buytaert's avatar
   
Dries Buytaert committed
248
    module_invoke_all('taxonomy', 'update', 'term', $edit);
249
    $status = SAVED_UPDATED;
Kjartan Mannes's avatar
Kjartan Mannes committed
250
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
251
252
  else if ($edit['tid']) {
    return taxonomy_del_term($edit['tid']);
Kjartan Mannes's avatar
Kjartan Mannes committed
253
254
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
255
256
    $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
257
    db_query('INSERT INTO {term_data} '. _taxonomy_prepare_insert($data, 1) .' VALUES '. _taxonomy_prepare_insert($data, 2));
Dries Buytaert's avatar
   
Dries Buytaert committed
258
    module_invoke_all('taxonomy', 'insert', 'term', $edit);
259
    $status = SAVED_NEW;
Kjartan Mannes's avatar
Kjartan Mannes committed
260
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
261

Dries Buytaert's avatar
   
Dries Buytaert committed
262
263
264
  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
265
      if ($related_id != 0) {
Dries Buytaert's avatar
   
Dries Buytaert committed
266
        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $edit['tid'], $related_id);
Dries Buytaert's avatar
 
Dries Buytaert committed
267
      }
Kjartan Mannes's avatar
Kjartan Mannes committed
268
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
269
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
270

Dries Buytaert's avatar
   
Dries Buytaert committed
271
272
  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $edit['tid']);
  if (!isset($edit['parent'])) {
273
    $edit['parent'] = array(0);
Kjartan Mannes's avatar
Kjartan Mannes committed
274
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
275
276
  if (is_array($edit['parent'])) {
    foreach ($edit['parent'] as $parent) {
277
278
279
280
281
282
283
284
      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
285
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
286
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
287

Dries Buytaert's avatar
   
Dries Buytaert committed
288
289
290
  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
291
      if ($synonym) {
Dries Buytaert's avatar
   
Dries Buytaert committed
292
        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $edit['tid'], chop($synonym));
Dries Buytaert's avatar
   
Dries Buytaert 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

Dries Buytaert's avatar
   
Dries Buytaert committed
297
298
  cache_clear_all();

299
  return $status;
Kjartan Mannes's avatar
Kjartan Mannes committed
300
}
Dries Buytaert's avatar
 
Dries Buytaert committed
301

Kjartan Mannes's avatar
Kjartan Mannes committed
302
function taxonomy_del_term($tid) {
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  $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
317

318
      $term = taxonomy_get_term($tid);
Dries Buytaert's avatar
   
Dries Buytaert committed
319

320
321
322
323
324
      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
325

326
      module_invoke_all('taxonomy', 'delete', 'term', $term);
327
      drupal_set_message(t('Deleted term %name.', array('%name' => theme('placeholder', $term->name))));
328
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
329

330
331
    $tids = $orphans;
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
332

Dries Buytaert's avatar
   
Dries Buytaert committed
333
  cache_clear_all();
Dries Buytaert's avatar
   
Dries Buytaert committed
334
335
336
337
338
}

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

339
340
  $extra  = form_hidden('type', 'term');
  $extra .= form_hidden('tid', $tid);
Dries Buytaert's avatar
   
Dries Buytaert committed
341

342
  $output = theme('confirm',
343
                  t('Are you sure you want to delete the term %title?', array('%title' => theme('placeholder', $term->name))),
344
345
346
347
348
                  'admin/taxonomy',
                  t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
                  t('Delete'),
                  t('Cancel'),
                  $extra);
349
350

  return $output;
Kjartan Mannes's avatar
Kjartan Mannes committed
351
}
Dries Buytaert's avatar
 
Dries Buytaert committed
352

Dries Buytaert's avatar
   
Dries Buytaert committed
353
354
355
/**
 * Generate a tabular listing of administrative functions for vocabularies.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
356
function taxonomy_overview() {
Dries Buytaert's avatar
   
Dries Buytaert committed
357

358
359
360
361
362
363
364
365
  $vid = arg(2);

  // Show all vocabularies and their terms. if vocabulary is "Free tagging",
  // don't show terms here, but instruct user to "view terms" instead.
  if (!$vid) {
    $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '3'));
    $vocabularies = taxonomy_get_vocabularies();
    foreach ($vocabularies as $vocabulary) {
366
367
368
369
370
371
372
373
374
375
376
377
      $types = array();
      foreach ($vocabulary->nodes as $type) {
        $node_type = node_invoke($type, 'node_name');
        $types[] = $node_type ? $node_type : $type;
      }
      $rows[] = array('<strong>'.check_plain($vocabulary->name).'</strong>', implode(', ', $types), l(t('edit vocabulary'), "admin/taxonomy/edit/vocabulary/$vocabulary->vid"), l(t('add term'), "admin/taxonomy/add/term/$vocabulary->vid"), l(t('view terms'), "admin/taxonomy/$vocabulary->vid"));

      // Show terms if non-free.
      if (!$vocabulary->tags) {
        $tree = taxonomy_get_tree($vocabulary->vid);
        if ($tree) {
          foreach ($tree as $term) {
378
            $rows[] = array(array('data' => _taxonomy_depth($term->depth) . ' ' . l($term->name, "taxonomy/term/$term->tid"), 'class' => 'term'), NULL, NULL, NULL, l(t('edit term'), "admin/taxonomy/edit/term/$term->tid"));
379
380
          }
        }
381
382
        else {
          $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '5', 'class' => 'message'));
383
        }
384
      }
385
386
387
      elseif ($vocabulary->tags) {
        $rows[] = array(array('data' => t('This is a free tagging vocabulary:') . ' ' . l(t('view terms'), "admin/taxonomy/$vocabulary->vid") . '.', 'colspan' => '5', 'class' => 'message'));
      }
388
389
390
391
392
393
    }

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

395
396
397
398
399
400
401
402
403
404
405
  // Free tagging vocabularies get their own terms pager
  // since there is a greater chance of 1000+ terms.
  else {
    $header = array(t('Name'), array('data' => t('Operations'), 'width' => '60'));
    $vocabulary = taxonomy_get_vocabulary($vid);
    if ($vocabulary->module == 'taxonomy') {
      drupal_set_title(check_plain($vocabulary->name));
      $start_from      = $_GET['from'] ? $_GET['from'] : 0;
      $total_entries   = 0;  // total count for pager
      $page_increment  = 25; // number of tids per page
      $displayed_count = 0;  // number of tids shown
Kjartan Mannes's avatar
Kjartan Mannes committed
406

407
      $tree = taxonomy_get_tree($vocabulary->vid);
Kjartan Mannes's avatar
Kjartan Mannes committed
408
      foreach ($tree as $term) {
409
410
411
412
        $total_entries++; // we're counting all-totals, not displayed
        if (($start_from && $start_from > $total_entries) || ($displayed_count == $page_increment)) { continue; }
        $rows[] = array(_taxonomy_depth($term->depth) . ' ' . check_plain($term->name), l(t('edit term'), "admin/taxonomy/edit/term/$term->tid"));
        $displayed_count++; // we're counting tids displayed
Dries Buytaert's avatar
 
Dries Buytaert committed
413
      }
414
415
416

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

419
420
421
422
      $GLOBALS['pager_from_array'][] = $start_from;
      $GLOBALS['pager_total'][]      = $total_entries;
      $rows[] = array(array('data' => theme('pager', NULL, $page_increment), 'colspan' => '2'));
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
423
424
  }

425
  return theme('table', $header, $rows, array('id' => 'taxonomy'));
Kjartan Mannes's avatar
Kjartan Mannes committed
426
427
}

Dries Buytaert's avatar
   
Dries Buytaert committed
428
429
430
/**
 * Generate a form element for selecting terms from a vocabulary.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
431
function taxonomy_form($vid, $value = 0, $help = NULL, $name = 'taxonomy') {
Dries Buytaert's avatar
   
Dries Buytaert committed
432
  $vocabulary = taxonomy_get_vocabulary($vid);
433
  $help = ($help) ? $help : $vocabulary->help;
Kjartan Mannes's avatar
Kjartan Mannes committed
434
435
436
437
  if ($vocabulary->required) {
    $blank = 0;
  }
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
438
    $blank = '<'. t('none') .'>';
Kjartan Mannes's avatar
Kjartan Mannes committed
439
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
440

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

444
/**
445
446
447
 * Generate a set of options for selecting a term from all vocabularies. Can be
 * passed to form_select.
 */
448
function taxonomy_form_all($free_tags = 0) {
449
450
451
  $vocabularies = taxonomy_get_vocabularies();
  $options = array();
  foreach ($vocabularies as $vid => $vocabulary) {
452
    if ($vocabulary->tags && !$free_tags) { continue; }
453
454
455
456
457
458
459
460
461
462
463
    $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
464
465
466
467
468
469
/**
 * 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
470
function taxonomy_get_vocabularies($type = NULL) {
Kjartan Mannes's avatar
Kjartan Mannes committed
471
  if ($type) {
472
    $result = db_query("SELECT 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", $type);
Kjartan Mannes's avatar
Kjartan Mannes committed
473
474
  }
  else {
475
    $result = db_query('SELECT v.*, n.type FROM {vocabulary} v LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid ORDER BY v.weight, v.name');
Kjartan Mannes's avatar
Kjartan Mannes committed
476
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
477

Kjartan Mannes's avatar
Kjartan Mannes committed
478
  $vocabularies = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
479
  $node_types = array();
Kjartan Mannes's avatar
Kjartan Mannes committed
480
  while ($voc = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
481
482
483
484
    $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
485
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
486

Kjartan Mannes's avatar
Kjartan Mannes committed
487
488
  return $vocabularies;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
489

Dries Buytaert's avatar
   
Dries Buytaert committed
490
491
492
/**
 * Generate a form for selecting terms to associate with a node.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
493
function taxonomy_node_form($type, $node = '', $help = NULL, $name = 'taxonomy') {
494
  if (!array_key_exists('taxonomy', $node)) {
Kjartan Mannes's avatar
Kjartan Mannes committed
495
    if ($node->nid) {
496
      $terms = taxonomy_node_get_terms($node->nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
497
498
    }
    else {
499
      $terms = array();
Dries Buytaert's avatar
 
Dries Buytaert committed
500
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
501
502
503
504
  }
  else {
    $terms = $node->taxonomy;
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
505

Dries Buytaert's avatar
   
Dries Buytaert committed
506
  $c = db_query("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", $type);
Kjartan Mannes's avatar
Kjartan Mannes committed
507
  while ($vocabulary = db_fetch_object($c)) {
508
509
510
511
512
513
514
515
516
517
518
519
520
521
    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);
522
      $result[] = form_textfield($vocabulary->name, "$name][tags][". $vocabulary->vid, $typed_string, 50, 100, t('A comma-separated list of terms describing this content (Example: funny, bungie jumping, "Company, Inc.").'), NULL, ($vocabulary->required ? TRUE : FALSE));
523
524
525
526
527
    }
    else {
      $ntterms = array_key_exists('taxonomy', $node) ? $terms : array_keys($terms);
      $result[] = taxonomy_form($vocabulary->vid, $ntterms, $help, $name);
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
528
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
529
530
  return $result ? $result : array();
}
Dries Buytaert's avatar
 
Dries Buytaert committed
531

Dries Buytaert's avatar
   
Dries Buytaert committed
532
533
534
535
536
/**
 * Find all terms associated to the given node, within one vocabulary.
 */
function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') {
  $result = db_query('SELECT t.* FROM {term_data} t, {term_node} r WHERE t.tid = r.tid AND t.vid = %d AND r.nid = %d ORDER BY weight', $vid, $nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
537
538
539
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
540
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
541
542
543
  return $terms;
}

Dries Buytaert's avatar
   
Dries Buytaert committed
544
545
546
547
/**
 * Find all terms associated to the given node.
 */
function taxonomy_node_get_terms($nid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
548
  static $terms;
Dries Buytaert's avatar
 
Dries Buytaert committed
549

Dries Buytaert's avatar
   
Dries Buytaert committed
550
  if (!isset($terms[$nid])) {
Dries Buytaert's avatar
   
Dries Buytaert committed
551
    $result = db_query('SELECT t.* FROM {term_data} t, {term_node} r WHERE r.tid = t.tid AND r.nid = %d ORDER BY weight, name', $nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
552
    $terms[$nid] = array();
Dries Buytaert's avatar
 
Dries Buytaert committed
553
    while ($term = db_fetch_object($result)) {
Kjartan Mannes's avatar
Kjartan Mannes committed
554
      $terms[$nid][$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
555
556
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
557
558
  return $terms[$nid];
}
Dries Buytaert's avatar
 
Dries Buytaert committed
559

560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
/**
 * 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
577
578
579
/**
 * Save term associations for a given node.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
580
function taxonomy_node_save($nid, $terms) {
581
  taxonomy_node_delete($nid);
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622

  // 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) {
          $new_term = taxonomy_save_term(array('vid' => $vid, 'name' => $typed_term));
          $typed_term_tid = $new_term['tid'];
        }

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

624
  if (is_array($terms)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
625
    foreach ($terms as $term) {
626
627
628
629
630
631
632
      if (is_array($term)) {
        foreach ($term as $tid) {
          if ($tid) {
            db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $tid);
          }
        }
      }
633
      else if ($term) {
Dries Buytaert's avatar
   
Dries Buytaert committed
634
        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term);
635
      }
Dries Buytaert's avatar
 
Dries Buytaert committed
636
637
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
638
}
Dries Buytaert's avatar
 
Dries Buytaert committed
639

Dries Buytaert's avatar
   
Dries Buytaert committed
640
641
642
/**
 * Remove associations of a node to its terms.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
643
function taxonomy_node_delete($nid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
644
  db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
645
}
Dries Buytaert's avatar
 
Dries Buytaert committed
646

Dries Buytaert's avatar
   
Dries Buytaert committed
647
648
649
650
/**
 * Find all term objects related to a given term ID.
 */
function taxonomy_get_related($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
651
  if ($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
652
    $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
653
654
655
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
656
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
657
    return $related;
Dries Buytaert's avatar
 
Dries Buytaert committed
658
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
659
660
  else {
    return array();
Dries Buytaert's avatar
 
Dries Buytaert committed
661
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
662
}
Dries Buytaert's avatar
 
Dries Buytaert committed
663

Dries Buytaert's avatar
   
Dries Buytaert committed
664
665
666
667
/**
 * Find all parents of a given term ID.
 */
function taxonomy_get_parents($tid, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
668
  if ($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
669
    $result = db_query('SELECT t.* FROM {term_hierarchy} h, {term_data} t WHERE h.parent = t.tid AND h.tid = %d ORDER BY weight, name', $tid);
Kjartan Mannes's avatar
Kjartan Mannes committed
670
671
672
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries Buytaert's avatar
   
Dries Buytaert committed
673
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
674
    return $parents;
Dries Buytaert's avatar
 
Dries Buytaert committed
675
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
676
677
678
679
  else {
    return array();
  }
}
Dries Buytaert's avatar
 
Dries Buytaert committed
680

Dries Buytaert's avatar
   
Dries Buytaert committed
681
682
683
684
/**
 * Find all ancestors of a given term ID.
 */
function taxonomy_get_parents_all($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
685
686
687
688
689
690
691
692
693
694
695
696
  $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
697
698
699
700
/**
 * Find all children of a term ID.
 */
function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes committed
701
  if ($vid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
702
    $result = db_query('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', $vid, $tid);
Dries Buytaert's avatar
 
Dries Buytaert committed
703
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
704
  else {
Dries Buytaert's avatar
   
Dries Buytaert committed
705
    $result = db_query('SELECT t.* FROM {term_hierarchy} h, {term_data} t WHERE h.tid = t.tid AND parent = %d ORDER BY weight', $tid);
Kjartan Mannes's avatar
Kjartan Mannes committed
706
707
708
709
710
711
712
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
713

Dries Buytaert's avatar
   
Dries Buytaert committed
714
715
716
717
718
719
720
721
722
723
724
725
726
/**
 * 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
727
728
729
 * @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
730
731
732
733
734
 * @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
735
  static $children, $parents, $terms;
Dries Buytaert's avatar
   
Dries Buytaert committed
736

Kjartan Mannes's avatar
Kjartan Mannes committed
737
  $depth++;
Dries Buytaert's avatar
   
Dries Buytaert committed
738

Dries Buytaert's avatar
   
Dries Buytaert committed
739
740
741
742
  // 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
743

Dries Buytaert's avatar
   
Dries Buytaert committed
744
    $result = db_query('SELECT t.*, parent FROM {term_data} t, {term_hierarchy} h WHERE t.tid = h.tid AND t.vid = %d ORDER BY weight, name', $vid);
Dries Buytaert's avatar
   
Dries Buytaert committed
745
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
746
747
748
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries Buytaert's avatar
 
Dries Buytaert committed
749
750
    }
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
751

Dries Buytaert's avatar
   
Dries Buytaert committed
752
  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
Dries Buytaert's avatar
Dries Buytaert committed
753
754
755
756
757
758
759
760
761
762
763
764
  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
765
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
766
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
767
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
768

Dries Buytaert's avatar
   
Dries Buytaert committed
769
  return $tree ? $tree : array();
Kjartan Mannes's avatar
Kjartan Mannes committed
770
}
Dries Buytaert's avatar
 
Dries Buytaert committed
771

Dries Buytaert's avatar
   
Dries Buytaert committed
772
773
774
/**
 * Return an array of synonyms of the given term ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
775
776
function taxonomy_get_synonyms($tid) {
  if ($tid) {
Dries Buytaert's avatar
   
Dries Buytaert committed
777
    $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid);
Kjartan Mannes's avatar
Kjartan Mannes committed
778
    while ($synonym = db_fetch_array($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
779
      $synonyms[] = $synonym['name'];
Dries Buytaert's avatar
 
Dries Buytaert committed
780
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
781
    return $synonyms ? $synonyms : array();
Dries Buytaert's avatar
 
Dries Buytaert committed
782
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
783
784
  else {
    return array();
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
789
790
791
792
/**
 * 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
793
}
Dries Buytaert's avatar
   
Dries Buytaert committed
794

Dries Buytaert's avatar
   
Dries Buytaert committed
795
796
797
/**
 * Given a term id, count the number of published nodes in it.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
798
function taxonomy_term_count_nodes($tid, $type = 0) {
Kjartan Mannes's avatar
Kjartan Mannes committed
799
  static $count;
Dries Buytaert's avatar
   
Dries Buytaert committed
800

Dries Buytaert's avatar
   
Dries Buytaert committed
801
802
803
  if (!isset($count[$type])) {
    // $type == 0 always evaluates true is $type is a string
    if (is_numeric($type)) {
804
      $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
805
806
    }
    else {
807
      $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
808
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
809
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
810
      $count[$type][$term->tid] = $term->c;
Dries Buytaert's avatar
   
Dries Buytaert committed
811
812
813
    }
  }

Kjartan Mannes's avatar
Kjartan Mannes committed
814
  foreach (_taxonomy_term_children($tid) as $c) {
Dries Buytaert's avatar
   
Dries Buytaert committed
815
    $children_count += taxonomy_term_count_nodes($c, $type);
Kjartan Mannes's avatar
Kjartan Mannes committed
816
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
817
  return $count[$type][$tid] + $children_count;
Kjartan Mannes's avatar
Kjartan Mannes committed
818
819
}

Dries Buytaert's avatar
   
Dries Buytaert committed
820
821
822
/**
 * Helper for taxonomy_term_count_nodes().
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
823
824
function _taxonomy_term_children($tid) {
  static $children;
Dries Buytaert's avatar
   
Dries Buytaert committed
825

Dries Buytaert's avatar
   
Dries Buytaert committed
826
  if (!isset($children)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
827
    $result = db_query('SELECT tid, parent FROM {term_hierarchy}');
Kjartan Mannes's avatar
Kjartan Mannes committed
828
829
    while ($term = db_fetch_object($result)) {
      $children[$term->parent][] = $term->tid;
Dries Buytaert's avatar
   
Dries Buytaert committed
830
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
831
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
832
833
  return $children[$tid] ? $children[$tid] : array();
}
Dries Buytaert's avatar
 
Dries Buytaert committed
834

Dries Buytaert's avatar
   
Dries Buytaert committed
835
/**
Dries Buytaert's avatar
   
Dries Buytaert committed
836
 * Try to map a string to an existing term, as for glossary use.
Dries Buytaert's avatar
   
Dries Buytaert committed
837
 *
Dries Buytaert's avatar
   
Dries Buytaert committed
838
839
840
841
842
843
844
845
 * 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
846
847
 */
function taxonomy_get_term_by_name($name) {
Dries Buytaert's avatar
   
Dries Buytaert committed
848
  $db_result = db_query("SELECT * FROM {term_data} WHERE LOWER('%s') LIKE LOWER(name)", trim($name));
Dries Buytaert's avatar
   
Dries Buytaert committed
849
850
851
852
853
854
855
856
  $result = array();
  while ($term = db_fetch_object($db_result)) {
    $result[] = $term;
  }

  return $result;
}

Dries Buytaert's avatar
   
Dries Buytaert committed
857
858
859
/**
 * Return the vocabulary object matching a vocabulary ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
860
function taxonomy_get_vocabulary($vid) {
861
  $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);
Dries Buytaert's avatar
   
Dries Buytaert committed
862
863
864
865
866
867
868
869
870
  $node_types = array();
  while ($voc = db_fetch_object($result)) {
    $node_types[] = $voc->type;
    unset($voc->type);
    $voc->nodes = $node_types;
    $vocabulary = $voc;
  }

  return $vocabulary;
Kjartan Mannes's avatar
Kjartan Mannes committed
871
}
Dries Buytaert's avatar
 
Dries Buytaert committed
872

Dries Buytaert's avatar
   
Dries Buytaert committed
873
874
875
/**
 * Return the term object matching a term ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
876
877
function taxonomy_get_term($tid) {
  // simple cache using a static var?
Dries Buytaert's avatar
   
Dries Buytaert committed
878
  return db_fetch_object(db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid));
Kjartan Mannes's avatar
Kjartan Mannes committed
879
}
Dries Buytaert's avatar
 
Dries Buytaert committed
880

Kjartan Mannes's avatar
Kjartan Mannes committed
881
function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
882
  $tree = taxonomy_get_tree($vocabulary_id);
883
884
  $options = array();

Dries Buytaert's avatar
Dries Buytaert committed
885
  if ($blank) {
886
    $options[0] = $blank;
Dries Buytaert's avatar
Dries Buytaert committed
887
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
888
889
890
  if ($tree) {
    foreach ($tree as $term) {
      if (!in_array($term->tid, $exclude)) {
891
        $options[$term->tid] = _taxonomy_depth($term->depth, '-') . $term->name;
Dries Buytaert's avatar
 
Dries Buytaert committed
892
893
      }
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
894
895
896
897
898
    if (!$blank && !$value) {
      // required but without a predefined value, so set first as predefined
      $value = $tree[0]->tid;
    }
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
899

900
  return form_select($title, $name . ($multiple ? '' : ']['), $value, $options, $description, $multiple ? 'size="'. min(12, count($options)) .'"' : 0, $multiple);
Kjartan Mannes's avatar
Kjartan Mannes committed
901
}
Dries Buytaert's avatar
 
Dries Buytaert committed
902

Kjartan Mannes's avatar
Kjartan Mannes committed
903
904
905
function _taxonomy_depth($depth, $graphic = '--') {
  for ($n = 0; $n < $depth; $n++) {
    $result .= $graphic;
Dries Buytaert's avatar
 
Dries Buytaert committed
906
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
907
908
  return $result;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
909

Dries Buytaert's avatar
   
Dries Buytaert committed
910
function _taxonomy_prepare_update($data) {
Kjartan Mannes's avatar
Kjartan Mannes committed
911
  foreach ($data as $key => $value) {
Dries Buytaert's avatar
   
Dries Buytaert committed
912
    $q[] = "$key = '". str_replace('%', '%%', db_escape_string($value)) ."'";
Dries Buytaert's avatar
 
Dries Buytaert committed
913
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
914
  $result = implode(', ', $q);
Kjartan Mannes's avatar
Kjartan Mannes committed
915
916
  return $result;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
917

Dries Buytaert's avatar
   
Dries Buytaert committed
918
function _taxonomy_prepare_insert($data, $stage) {
Kjartan Mannes's avatar
Kjartan Mannes committed
919
  if ($stage == 1) {
Dries Buytaert's avatar
   
Dries Buytaert committed
920
    $result = implode(', ', array_keys($data));
Kjartan Mannes's avatar
Kjartan Mannes committed
921
922
923
  }
  else {
    foreach (array_values($data) as $value) {
924
925
926
927
928
929
930
      //Escape strings, but not integers
      if (is_int($value)) {
        $q[] = $value;
      }
      else {
        $q[] = "'". str_replace('%', '%%', db_escape_string($value)) ."'";
      }
Dries Buytaert's avatar
 
Dries Buytaert committed
931
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
932
    $result = implode(', ', $q);
Dries Buytaert's avatar
 
Dries Buytaert committed
933
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
934
935
  return "($result)";
}
Dries Buytaert's avatar
   
Dries Buytaert committed
936

Dries Buytaert's avatar
   
Dries Buytaert committed
937
938
939
/**
 * Finds all nodes that match selected taxonomy conditions.
 *
Dries Buytaert's avatar
   
Dries Buytaert committed