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

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

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

Dries's avatar
   
Dries 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's avatar
   
Dries committed
27
function taxonomy_link($type, $node = NULL) {
Dries's avatar
   
Dries committed
28
  if ($type == 'taxonomy terms' && $node != NULL) {
Kjartan's avatar
   
Kjartan committed
29
    $links = array();
Dries's avatar
   
Dries committed
30
    if (array_key_exists('taxonomy', $node)) {
Dries's avatar
   
Dries committed
31
32
      foreach ($node->taxonomy as $tid) {
        $term = taxonomy_get_term($tid);
Dries's avatar
   
Dries committed
33
        $links[] = l($term->name, 'taxonomy/term/'. $term->tid);
Dries's avatar
   
Dries committed
34
35
36
37
      }
    }
    else {
      foreach (taxonomy_node_get_terms($node->nid) as $term) {
Dries's avatar
   
Dries committed
38
        $links[] = l($term->name, 'taxonomy/term/'. $term->tid);
Dries's avatar
   
Dries committed
39
      }
Dries's avatar
   
Dries committed
40
41
42
    }
    return $links;
  }
Kjartan's avatar
Kjartan committed
43
44
}

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

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

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

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

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    $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's avatar
   
Dries committed
79
80
81
82
    $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
83
84
85
86
87

    $items[] = array('path' => 'taxonomy/autocomplete', 'title' => t('autocomplete taxonomy'),
      'callback' => 'taxonomy_autocomplete',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
Dries's avatar
   
Dries committed
88
  }
Dries's avatar
Dries committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  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's avatar
   
Dries committed
105

Dries's avatar
   
Dries committed
106
107
  return $items;
}
Dries's avatar
   
Dries committed
108

Kjartan's avatar
Kjartan committed
109
function taxonomy_form_vocabulary($edit = array()) {
110
  foreach (node_list() as $type => $module) {
Dries's avatar
   
Dries committed
111
112
    $node_type = node_invoke($type, 'node_name');
    $nodes[$type] = $node_type ? $node_type : $type;
Kjartan's avatar
Kjartan committed
113
  }
Dries's avatar
   
Dries committed
114

115
  $form .= form_textfield(t('Vocabulary name'), 'name', $edit['name'], 60, 64, t('The name for this vocabulary.  Example: "Topic".'), NULL, TRUE);
116
117
  // Prepend extra vocabulary form elements.
  $form .= implode('', module_invoke_all('taxonomy', 'form pre', 'vocabulary', $edit));
Dries's avatar
   
Dries committed
118
  $form .= form_textarea(t('Description'), 'description', $edit['description'], 60, 5, t('Description of the vocabulary; can be used by modules.'));
119
  $form .= form_textfield(t('Help text'), 'help', $edit['help'], 60, 255, t('Instructions to present to the user when choosing a term.'));
Dries's avatar
   
Dries committed
120
  $form .= form_checkboxes(t('Types'), 'nodes', $edit['nodes'], $nodes, t('A list of node types you want to associate with this vocabulary.'), NULL, TRUE);
121
  $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'))));
Dries's avatar
Dries committed
122
123
124
  $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's avatar
   
Dries committed
125
  $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's avatar
   
Dries committed
126
  $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.'));
127
128
  // Append extra vocabulary form elements.
  $form .= implode('', module_invoke_all('taxonomy', 'form post', 'vocabulary', $edit));
Dries's avatar
   
Dries committed
129
  $form .= form_submit(t('Submit'));
Dries's avatar
   
Dries committed
130

Dries's avatar
   
Dries committed
131
132
133
  if ($edit['vid']) {
    $form .= form_submit(t('Delete'));
    $form .= form_hidden('vid', $edit['vid']);
Dries's avatar
   
Dries committed
134
135
  }

Kjartan's avatar
Kjartan committed
136
137
  return form($form);
}
Kjartan's avatar
Kjartan committed
138

Kjartan's avatar
Kjartan committed
139
function taxonomy_save_vocabulary($edit) {
Dries's avatar
Dries committed
140
141
  $edit['nodes'] = ($edit['nodes']) ? $edit['nodes'] : array();
  $edit['weight'] = ($edit['weight']) ? $edit['weight'] : 0;
Dries's avatar
   
Dries committed
142

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

  cache_clear_all();
Dries's avatar
   
Dries committed
167

168
  return array('status' => $status, 'object' => $edit);
Kjartan's avatar
Kjartan committed
169
}
Dries's avatar
   
Dries committed
170

Kjartan's avatar
Kjartan committed
171
function taxonomy_del_vocabulary($vid) {
Dries's avatar
   
Dries committed
172
173
  $vocabulary = taxonomy_get_vocabulary($vid);

Dries's avatar
   
Dries committed
174
  db_query('DELETE FROM {vocabulary} WHERE vid = %d', $vid);
Dries's avatar
   
Dries committed
175
  db_query('DELETE FROM {vocabulary_node_types} WHERE vid = %d', $vid);
Dries's avatar
   
Dries committed
176
  $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $vid);
Kjartan's avatar
Kjartan committed
177
178
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries's avatar
   
Dries committed
179
  }
Dries's avatar
   
Dries committed
180

Dries's avatar
   
Dries committed
181
  module_invoke_all('taxonomy', 'delete', 'vocabulary', $vocabulary);
Dries's avatar
   
Dries committed
182

Dries's avatar
   
Dries committed
183
184
  cache_clear_all();

185
  return SAVED_DELETED;
Dries's avatar
   
Dries committed
186
187
188
189
190
}

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

191
192
  $extra  = form_hidden('type', 'vocabulary');
  $extra .= form_hidden('vid', $vid);
193
  $extra .= form_hidden('name', $vocabulary->name);
194
195

  $output = theme('confirm',
196
                  t('Are you sure you want to delete the vocabulary %title?', array('%title' => theme('placeholder', $vocabulary->name))),
197
198
199
200
201
202
                  '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's avatar
Kjartan committed
203
}
Dries's avatar
   
Dries committed
204

Kjartan's avatar
Kjartan committed
205
function taxonomy_form_term($edit = array()) {
Dries's avatar
   
Dries committed
206
  $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
Kjartan's avatar
Kjartan committed
207
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries's avatar
   
Dries committed
208

209
  $form = form_textfield(t('Term name'), 'name', $edit['name'], 60, 64, t('The name for this term.  Example: "Linux".'), NULL, TRUE);
210
211
  // Prepend extra term form elements.
  $form .= implode('', module_invoke_all('taxonomy', 'form pre', 'term', $edit));
Dries's avatar
   
Dries committed
212
  $form .= form_textarea(t('Description'), 'description', $edit['description'], 60, 5, t('A description of the term.'));
Dries's avatar
   
Dries committed
213

Kjartan's avatar
Kjartan committed
214
  if ($vocabulary->hierarchy) {
Dries's avatar
   
Dries committed
215
216
    $parent = array_keys(taxonomy_get_parents($edit['tid']));
    $children = taxonomy_get_tree($vocabulary_id, $edit['tid']);
Dries's avatar
   
Dries committed
217

Dries's avatar
   
Dries committed
218
    // A term can't be the child of itself, nor of its children.
Dries's avatar
   
Dries committed
219
220
221
    foreach ($children as $child) {
      $exclude[] = $child->tid;
    }
Dries's avatar
   
Dries committed
222
    $exclude[] = $edit['tid'];
Dries's avatar
   
Dries committed
223

Kjartan's avatar
Kjartan committed
224
    if ($vocabulary->hierarchy == 1) {
225
      $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's avatar
   
Dries committed
226
    }
Kjartan's avatar
Kjartan committed
227
    elseif ($vocabulary->hierarchy == 2) {
228
      $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's avatar
   
Dries committed
229
    }
Kjartan's avatar
Kjartan committed
230
  }
Dries's avatar
   
Dries committed
231

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

236
  $form .= form_textarea(t('Synonyms'), 'synonyms', implode("\n", taxonomy_get_synonyms($edit['tid'])), 60, 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's avatar
   
Dries committed
237
  $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.'));
238
239
  // Append extra term form elements.
  $form .= implode('', module_invoke_all('taxonomy', 'form post', 'term', $edit));
Dries's avatar
   
Dries committed
240
241
  $form .= form_hidden('vid', $vocabulary->vid);
  $form .= form_submit(t('Submit'));
Kjartan's avatar
Kjartan committed
242

Dries's avatar
   
Dries committed
243
244
245
  if ($edit['tid']) {
    $form .= form_submit(t('Delete'));
    $form .= form_hidden('tid', $edit['tid']);
Dries's avatar
   
Dries committed
246
  }
247
248
249
  else {
    $form .= form_hidden('destination', $_GET['q']);
  }
Dries's avatar
   
Dries committed
250

Kjartan's avatar
Kjartan committed
251
252
  return form($form);
}
Dries's avatar
   
Dries committed
253

Kjartan's avatar
Kjartan committed
254
function taxonomy_save_term($edit) {
Dries's avatar
   
Dries committed
255
256
  if ($edit['tid'] && $edit['name']) {
    $data = array('name' => $edit['name'], 'description' => $edit['description'], 'weight' => $edit['weight']);
Dries's avatar
   
Dries committed
257

Dries's avatar
   
Dries committed
258
    db_query('UPDATE {term_data} SET '. _taxonomy_prepare_update($data) .' WHERE tid = %d', $edit['tid']);
Dries's avatar
   
Dries committed
259
    module_invoke_all('taxonomy', 'update', 'term', $edit);
260
    $status = SAVED_UPDATED;
Kjartan's avatar
Kjartan committed
261
  }
Dries's avatar
   
Dries committed
262
263
  else if ($edit['tid']) {
    return taxonomy_del_term($edit['tid']);
Kjartan's avatar
Kjartan committed
264
265
  }
  else {
Dries's avatar
   
Dries committed
266
267
    $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's avatar
   
Dries committed
268
    db_query('INSERT INTO {term_data} '. _taxonomy_prepare_insert($data, 1) .' VALUES '. _taxonomy_prepare_insert($data, 2));
Dries's avatar
   
Dries committed
269
    module_invoke_all('taxonomy', 'insert', 'term', $edit);
270
    $status = SAVED_NEW;
Kjartan's avatar
Kjartan committed
271
  }
Dries's avatar
   
Dries committed
272

Dries's avatar
   
Dries committed
273
274
275
  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's avatar
Kjartan committed
276
      if ($related_id != 0) {
Dries's avatar
   
Dries committed
277
        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $edit['tid'], $related_id);
Dries's avatar
   
Dries committed
278
      }
Kjartan's avatar
Kjartan committed
279
    }
Kjartan's avatar
Kjartan committed
280
  }
Dries's avatar
   
Dries committed
281

Dries's avatar
   
Dries committed
282
283
  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $edit['tid']);
  if (!isset($edit['parent'])) {
284
    $edit['parent'] = array(0);
Kjartan's avatar
Kjartan committed
285
  }
Dries's avatar
   
Dries committed
286
287
  if (is_array($edit['parent'])) {
    foreach ($edit['parent'] as $parent) {
288
289
290
291
292
293
294
295
      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's avatar
   
Dries committed
296
    }
Kjartan's avatar
Kjartan committed
297
  }
Dries's avatar
   
Dries committed
298

Dries's avatar
   
Dries committed
299
300
301
  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's avatar
   
Dries committed
302
      if ($synonym) {
Dries's avatar
   
Dries committed
303
        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $edit['tid'], chop($synonym));
Dries's avatar
   
Dries committed
304
      }
Kjartan's avatar
Kjartan committed
305
    }
Dries's avatar
   
Dries committed
306
  }
Dries's avatar
   
Dries committed
307

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

310
  return array('status' => $status, 'object' => $edit);
Kjartan's avatar
Kjartan committed
311
}
Dries's avatar
   
Dries committed
312

Kjartan's avatar
Kjartan committed
313
function taxonomy_del_term($tid) {
314
315
316
317
318
319
320
321
322
323
324
325
326
327
  $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's avatar
   
Dries committed
328

329
      $term = taxonomy_get_term($tid);
Dries's avatar
   
Dries committed
330

331
332
333
334
335
      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's avatar
   
Dries committed
336

337
      module_invoke_all('taxonomy', 'delete', 'term', $term);
338
      drupal_set_message(t('Deleted term %name.', array('%name' => theme('placeholder', $term->name))));
339
    }
Dries's avatar
   
Dries committed
340

341
342
    $tids = $orphans;
  }
Dries's avatar
   
Dries committed
343

Dries's avatar
   
Dries committed
344
  cache_clear_all();
Dries's avatar
   
Dries committed
345
346
347
348
349
}

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

350
351
  $extra  = form_hidden('type', 'term');
  $extra .= form_hidden('tid', $tid);
Dries's avatar
   
Dries committed
352

353
  $output = theme('confirm',
354
                  t('Are you sure you want to delete the term %title?', array('%title' => theme('placeholder', $term->name))),
355
356
357
358
359
                  '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);
360
361

  return $output;
Kjartan's avatar
Kjartan committed
362
}
Dries's avatar
   
Dries committed
363

Dries's avatar
   
Dries committed
364
365
366
/**
 * Generate a tabular listing of administrative functions for vocabularies.
 */
Kjartan's avatar
Kjartan committed
367
function taxonomy_overview() {
Dries's avatar
Dries committed
368
369
  $vid = arg(2);

370
  // Show all vocabularies, and a "view terms" link to the pagers.
Dries's avatar
Dries committed
371
  if (!$vid) {
372
    $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '2'));
Dries's avatar
Dries committed
373
374
    $vocabularies = taxonomy_get_vocabularies();
    foreach ($vocabularies as $vocabulary) {
375
376
377
378
379
      $types = array();
      foreach ($vocabulary->nodes as $type) {
        $node_type = node_invoke($type, 'node_name');
        $types[] = $node_type ? $node_type : $type;
      }
Dries's avatar
Dries committed
380
      $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"));
Dries's avatar
Dries committed
381
382
383
    }

    if (!$rows) {
384
      $rows[] = array(array('data' => t('No categories available.'), 'colspan' => '4', 'class' => 'message'));
Dries's avatar
Dries committed
385
386
    }
  }
Dries's avatar
   
Dries committed
387

388
  // Show the vocabulary's terms with a pager.
Dries's avatar
Dries committed
389
  else {
390
391
392
    $destination = drupal_get_destination();

    $header = array(t('Name'), t('Operations'));
Dries's avatar
Dries committed
393
    $vocabulary = taxonomy_get_vocabulary($vid);
394

Dries's avatar
Dries committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
    drupal_set_title(check_plain($vocabulary->name));
    $start_from      = $_GET['page'] ? $_GET['page'] : 0;
    $total_entries   = 0;  // total count for pager
    $page_increment  = 25; // number of tids per page
    $displayed_count = 0;  // number of tids shown

    $tree = taxonomy_get_tree($vocabulary->vid);
    foreach ($tree as $term) {
      $total_entries++; // we're counting all-totals, not displayed
      if (($start_from && ($start_from * $page_increment) >= $total_entries) || ($displayed_count == $page_increment)) { continue; }
      $rows[] = array(_taxonomy_depth($term->depth) . ' ' . check_plain($term->name), l(t('edit'), "admin/taxonomy/edit/term/$term->tid", array(), $destination));
      $displayed_count++; // we're counting tids displayed
    }

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

Dries's avatar
Dries committed
413
414
    $GLOBALS['pager_page_array'][] = $start_from;  // FIXME
    $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME
415

Dries's avatar
Dries committed
416
417
    if ($total_entries >= $page_increment) {
      $rows[] = array(array('data' => theme('pager', NULL, $page_increment), 'colspan' => '2'));
Dries's avatar
Dries committed
418
    }
Dries's avatar
   
Dries committed
419
420
  }

Dries's avatar
Dries committed
421
  return theme('table', $header, $rows, array('id' => 'taxonomy'));
Kjartan's avatar
Kjartan committed
422
423
}

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

437
  return _taxonomy_term_select(check_plain($vocabulary->name), $name, $value, $vid, $help, intval($vocabulary->multiple), $blank);
Kjartan's avatar
Kjartan committed
438
}
Dries's avatar
   
Dries committed
439

440
/**
441
442
443
 * Generate a set of options for selecting a term from all vocabularies. Can be
 * passed to form_select.
 */
Dries's avatar
Dries committed
444
function taxonomy_form_all($free_tags = 0) {
445
446
447
  $vocabularies = taxonomy_get_vocabularies();
  $options = array();
  foreach ($vocabularies as $vid => $vocabulary) {
Dries's avatar
Dries committed
448
    if ($vocabulary->tags && !$free_tags) { continue; }
449
450
451
452
453
454
455
456
457
458
459
    $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's avatar
   
Dries committed
460
461
462
463
464
465
/**
 * Return an array of all vocabulary objects.
 *
 * @param $type
 *   If set, return only those vocabularies associated with this node type.
 */
Dries's avatar
   
Dries committed
466
function taxonomy_get_vocabularies($type = NULL) {
Kjartan's avatar
Kjartan committed
467
  if ($type) {
468
    $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's avatar
Kjartan committed
469
470
  }
  else {
471
    $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's avatar
Kjartan committed
472
  }
Dries's avatar
   
Dries committed
473

Kjartan's avatar
Kjartan committed
474
  $vocabularies = array();
Dries's avatar
   
Dries committed
475
  $node_types = array();
Kjartan's avatar
Kjartan committed
476
  while ($voc = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
477
478
479
480
    $node_types[$voc->vid][] = $voc->type;
    unset($voc->type);
    $voc->nodes = $node_types[$voc->vid];
    $vocabularies[$voc->vid] = $voc;
Dries's avatar
   
Dries committed
481
  }
Dries's avatar
   
Dries committed
482

Kjartan's avatar
Kjartan committed
483
484
  return $vocabularies;
}
Dries's avatar
   
Dries committed
485

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

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

Dries's avatar
   
Dries committed
528
529
530
531
532
/**
 * 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's avatar
Kjartan committed
533
534
535
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries's avatar
   
Dries committed
536
  }
Kjartan's avatar
Kjartan committed
537
538
539
  return $terms;
}

Dries's avatar
   
Dries committed
540
/**
541
 * Find all terms associated to the given node, ordered by vocabulary and term weight.
Dries's avatar
   
Dries committed
542
543
 */
function taxonomy_node_get_terms($nid, $key = 'tid') {
Kjartan's avatar
Kjartan committed
544
  static $terms;
Dries's avatar
   
Dries committed
545

Dries's avatar
   
Dries committed
546
  if (!isset($terms[$nid])) {
547
    $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's avatar
Kjartan committed
548
    $terms[$nid] = array();
Dries's avatar
   
Dries committed
549
    while ($term = db_fetch_object($result)) {
Kjartan's avatar
Kjartan committed
550
      $terms[$nid][$term->$key] = $term;
Dries's avatar
   
Dries committed
551
552
    }
  }
Kjartan's avatar
Kjartan committed
553
554
  return $terms[$nid];
}
Dries's avatar
   
Dries committed
555

Dries's avatar
Dries committed
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
/**
 * 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's avatar
   
Dries committed
573
574
575
/**
 * Save term associations for a given node.
 */
Kjartan's avatar
Kjartan committed
576
function taxonomy_node_save($nid, $terms) {
577
  taxonomy_node_delete($nid);
Dries's avatar
Dries committed
578
579
580
581
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

  // 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) {
611
612
          list($status, $object) = array_values(taxonomy_save_term(array('vid' => $vid, 'name' => $typed_term)));
          $typed_term_tid = $object['tid'];
Dries's avatar
Dries committed
613
614
615
616
617
618
        }

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

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

Dries's avatar
   
Dries committed
636
637
638
/**
 * Remove associations of a node to its terms.
 */
Kjartan's avatar
Kjartan committed
639
function taxonomy_node_delete($nid) {
Dries's avatar
   
Dries committed
640
  db_query('DELETE FROM {term_node} WHERE nid = %d', $nid);
Kjartan's avatar
Kjartan committed
641
}
Dries's avatar
   
Dries committed
642

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

Dries's avatar
   
Dries committed
660
661
662
663
/**
 * Find all parents of a given term ID.
 */
function taxonomy_get_parents($tid, $key = 'tid') {
Kjartan's avatar
Kjartan committed
664
  if ($tid) {
Dries's avatar
   
Dries committed
665
    $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's avatar
Kjartan committed
666
667
668
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries's avatar
   
Dries committed
669
    }
Kjartan's avatar
Kjartan committed
670
    return $parents;
Dries's avatar
   
Dries committed
671
  }
Kjartan's avatar
Kjartan committed
672
673
674
675
  else {
    return array();
  }
}
Dries's avatar
   
Dries committed
676

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

Dries's avatar
   
Dries committed
710
711
712
713
714
715
716
717
718
719
720
721
722
/**
 * 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's avatar
   
Dries committed
723
724
725
 * @param $max_depth
 *   The number of levels of the tree to return. Leave NULL to return all levels.
 *
Dries's avatar
   
Dries committed
726
727
728
729
730
 * @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's avatar
   
Dries committed
731
  static $children, $parents, $terms;
Dries's avatar
   
Dries committed
732

Kjartan's avatar
Kjartan committed
733
  $depth++;
Dries's avatar
   
Dries committed
734

Dries's avatar
   
Dries committed
735
736
737
738
  // 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's avatar
   
Dries committed
739

740
    $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's avatar
   
Dries committed
741
    while ($term = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
742
743
744
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries's avatar
   
Dries committed
745
746
    }
  }
Dries's avatar
   
Dries committed
747

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

Dries's avatar
   
Dries committed
765
  return $tree ? $tree : array();
Kjartan's avatar
Kjartan committed
766
}
Dries's avatar
   
Dries committed
767

Dries's avatar
   
Dries committed
768
769
770
/**
 * Return an array of synonyms of the given term ID.
 */
Kjartan's avatar
Kjartan committed
771
772
function taxonomy_get_synonyms($tid) {
  if ($tid) {
Dries's avatar
   
Dries committed
773
    $result = db_query('SELECT name FROM {term_synonym} WHERE tid = %d', $tid);
Kjartan's avatar
Kjartan committed
774
    while ($synonym = db_fetch_array($result)) {
Dries's avatar
   
Dries committed
775
      $synonyms[] = $synonym['name'];
Dries's avatar
   
Dries committed
776
    }
Kjartan's avatar
Kjartan committed
777
    return $synonyms ? $synonyms : array();
Dries's avatar
   
Dries committed
778
  }
Kjartan's avatar
Kjartan committed
779
780
  else {
    return array();
Dries's avatar
   
Dries committed
781
  }
Kjartan's avatar
Kjartan committed
782
}
Dries's avatar
   
Dries committed
783

Dries's avatar
   
Dries committed
784
785
786
787
788
/**
 * 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's avatar
Kjartan committed
789
}
Dries's avatar
   
Dries committed
790

Dries's avatar
   
Dries committed
791
792
793
/**
 * Given a term id, count the number of published nodes in it.
 */
Dries's avatar
   
Dries committed
794
function taxonomy_term_count_nodes($tid, $type = 0) {
Kjartan's avatar
Kjartan committed
795
  static $count;
Dries's avatar
   
Dries committed
796

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

Kjartan's avatar
Kjartan committed
810
  foreach (_taxonomy_term_children($tid) as $c) {
Dries's avatar
   
Dries committed
811
    $children_count += taxonomy_term_count_nodes($c, $type);
Kjartan's avatar
Kjartan committed
812
  }
Dries's avatar
   
Dries committed
813
  return $count[$type][$tid] + $children_count;
Kjartan's avatar
Kjartan committed
814
815
}

Dries's avatar
   
Dries committed
816
817
818
/**
 * Helper for taxonomy_term_count_nodes().
 */
Kjartan's avatar
Kjartan committed
819
820
function _taxonomy_term_children($tid) {
  static $children;
Dries's avatar
   
Dries committed
821

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

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

  return $result;
}

Dries's avatar
   
Dries committed
853
854
855
/**
 * Return the vocabulary object matching a vocabulary ID.
 */
Kjartan's avatar
Kjartan committed
856
function taxonomy_get_vocabulary($vid) {
857
  $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's avatar
   
Dries committed
858
859
860
861
862
863
864
865
866
  $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's avatar
Kjartan committed
867
}
Dries's avatar
   
Dries committed
868

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

Kjartan's avatar
Kjartan committed
877
function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
Dries's avatar
   
Dries committed
878
  $tree = taxonomy_get_tree($vocabulary_id);
879
880
  $options = array();

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

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

Kjartan's avatar
Kjartan committed
899
900
901
function _taxonomy_depth($depth, $graphic = '--') {
  for ($n = 0; $n < $depth; $n++) {
    $result .= $graphic;
Dries's avatar
   
Dries committed
902
  }
Kjartan's avatar
Kjartan committed
903
904
  return $result;
}
Dries's avatar
   
Dries committed
905

Dries's avatar
   
Dries committed
906
function _taxonomy_prepare_update($data) {
Kjartan's avatar
Kjartan committed
907
  foreach ($data as $key => $value) {
Dries's avatar
   
Dries committed
908
    $q[] = "$key = '". str_replace('%', '%%', db_escape_string($value)) ."'";
Dries's avatar
   
Dries committed
909
  }
Dries's avatar
   
Dries committed
910
  $result = implode(', ', $q);
Kjartan's avatar
Kjartan committed
911
912
  return $result;
}
Dries's avatar
   
Dries committed
913

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

Dries's avatar
   
Dries committed
933
934
935
/**
 * Finds all nodes that match selected taxonomy conditions.
 *
Dries's avatar
   
Dries committed
936
937
938
939
940
941
942
 * @param $tids
 *   An array of term IDs to match.
 * @param $operator
 *   How to interpret multiple IDs in the array. Can be "or" or "and".
 * @param $depth
 *   How many levels deep to traverse the taxonomy tree. Can be a nonnegative
 *   integer or "all".
Dries's avatar
   
Dries committed
943
944
945
 * @param $pager
 *   Whether the nodes are to be used with a pager (the case on most Drupal
 *   pages) or not (in an XML feed, for example).