taxonomy.module 49.9 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
38
      }
    }
    else {
      $links = array();
      foreach (taxonomy_node_get_terms($node->nid) as $term) {
Dries's avatar
   
Dries committed
39
        $links[] = l($term->name, 'taxonomy/term/'. $term->tid);
Dries's avatar
   
Dries committed
40
      }
Dries's avatar
   
Dries committed
41
42
43
44

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

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

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

Dries's avatar
   
Dries 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's avatar
   
Dries 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);
  }
Dries's avatar
Dries committed
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's avatar
   
Dries committed
102

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

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

Dries's avatar
   
Dries 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's avatar
   
Dries 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'))));
Dries's avatar
Dries committed
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's avatar
   
Dries 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's avatar
   
Dries 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's avatar
   
Dries committed
122
  $form .= form_submit(t('Submit'));
Dries's avatar
   
Dries committed
123

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

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

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

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

  cache_clear_all();
Dries's avatar
   
Dries committed
160

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

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

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

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

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

178
  return SAVED_DELETED;
Dries's avatar
   
Dries 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's avatar
Kjartan committed
196
}
Dries's avatar
   
Dries committed
197

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

Dries's avatar
   
Dries 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's avatar
   
Dries committed
204

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

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

Kjartan's avatar
Kjartan 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's avatar
   
Dries committed
217
    }
Kjartan's avatar
Kjartan 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's avatar
   
Dries committed
220
    }
Kjartan's avatar
Kjartan committed
221
  }
Dries's avatar
   
Dries committed
222

223
  if ($vocabulary->relations) {
Dries's avatar
   
Dries 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's avatar
   
Dries 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's avatar
   
Dries committed
229
230
  $form .= form_hidden('vid', $vocabulary->vid);
  $form .= form_submit(t('Submit'));
Kjartan's avatar
Kjartan committed
231

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

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

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

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

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

Dries's avatar
   
Dries committed
271
272
  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $edit['tid']);
  if (!isset($edit['parent'])) {
273
    $edit['parent'] = array(0);
Kjartan's avatar
Kjartan committed
274
  }
Dries's avatar
   
Dries 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's avatar
   
Dries committed
285
    }
Kjartan's avatar
Kjartan committed
286
  }
Dries's avatar
   
Dries committed
287

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

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

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

Kjartan's avatar
Kjartan 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's avatar
   
Dries committed
317

318
      $term = taxonomy_get_term($tid);
Dries's avatar
   
Dries 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's avatar
   
Dries 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's avatar
   
Dries committed
329

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

Dries's avatar
   
Dries committed
333
  cache_clear_all();
Dries's avatar
   
Dries 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's avatar
   
Dries 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's avatar
Kjartan committed
351
}
Dries's avatar
   
Dries committed
352

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

Dries's avatar
Dries committed
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"));
Dries's avatar
Dries committed
379
380
          }
        }
381
382
        else {
          $rows[] = array(array('data' => t('No terms available.'), 'colspan' => '5', 'class' => 'message'));
Dries's avatar
Dries committed
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'));
      }
Dries's avatar
Dries committed
388
389
390
391
392
393
    }

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

Dries's avatar
Dries committed
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's avatar
Kjartan committed
406

407
      $tree = taxonomy_get_tree($vocabulary->vid);
Kjartan's avatar
Kjartan committed
408
      foreach ($tree as $term) {
Dries's avatar
Dries committed
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's avatar
   
Dries committed
413
      }
Dries's avatar
Dries committed
414
415
416

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

Dries's avatar
Dries committed
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's avatar
   
Dries committed
423
424
  }

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

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

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

444
/**
445
446
447
 * Generate a set of options for selecting a term from all vocabularies. Can be
 * passed to form_select.
 */
Dries's avatar
Dries committed
448
function taxonomy_form_all($free_tags = 0) {
449
450
451
  $vocabularies = taxonomy_get_vocabularies();
  $options = array();
  foreach ($vocabularies as $vid => $vocabulary) {
Dries's avatar
Dries committed
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's avatar
   
Dries 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's avatar
   
Dries committed
470
function taxonomy_get_vocabularies($type = NULL) {
Kjartan's avatar
Kjartan 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's avatar
Kjartan 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's avatar
Kjartan committed
476
  }
Dries's avatar
   
Dries committed
477

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

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

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

Dries's avatar
   
Dries 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's avatar
Kjartan committed
507
  while ($vocabulary = db_fetch_object($c)) {
Dries's avatar
Dries committed
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));
Dries's avatar
Dries committed
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's avatar
   
Dries committed
528
  }
Kjartan's avatar
Kjartan committed
529
530
  return $result ? $result : array();
}
Dries's avatar
   
Dries committed
531

Dries's avatar
   
Dries 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's avatar
Kjartan committed
537
538
539
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries's avatar
   
Dries committed
540
  }
Kjartan's avatar
Kjartan committed
541
542
543
  return $terms;
}

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

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

Dries's avatar
Dries committed
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's avatar
   
Dries committed
577
578
579
/**
 * Save term associations for a given node.
 */
Kjartan's avatar
Kjartan committed
580
function taxonomy_node_save($nid, $terms) {
581
  taxonomy_node_delete($nid);
Dries's avatar
Dries committed
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's avatar
   
Dries committed
623

624
  if (is_array($terms)) {
Dries's avatar
   
Dries 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's avatar
   
Dries committed
634
        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term);
635
      }
Dries's avatar
   
Dries committed
636
637
    }
  }
Kjartan's avatar
Kjartan committed
638
}
Dries's avatar
   
Dries committed
639

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

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

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

Dries's avatar
   
Dries committed
681
682
683
684
/**
 * Find all ancestors of a given term ID.
 */
function taxonomy_get_parents_all($tid) {
Dries's avatar
   
Dries 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's avatar
   
Dries committed
697
698
699
700
/**
 * Find all children of a term ID.
 */
function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
Kjartan's avatar
Kjartan committed
701
  if ($vid) {
Dries's avatar
   
Dries 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's avatar
   
Dries committed
703
  }
Kjartan's avatar
Kjartan committed
704
  else {
Dries's avatar
   
Dries 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's avatar
Kjartan committed
706
707
708
709
710
711
712
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries's avatar
   
Dries committed
713

Dries's avatar
   
Dries 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's avatar
   
Dries committed
727
728
729
 * @param $max_depth
 *   The number of levels of the tree to return. Leave NULL to return all levels.
 *
Dries's avatar
   
Dries 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's avatar
   
Dries committed
735
  static $children, $parents, $terms;
Dries's avatar
   
Dries committed
736

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

Dries's avatar
   
Dries 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's avatar
   
Dries committed
743

Dries's avatar
   
Dries 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's avatar
   
Dries committed
745
    while ($term = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
746
747
748
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries's avatar
   
Dries committed
749
750
    }
  }
Dries's avatar
   
Dries committed
751

Dries's avatar
   
Dries committed
752
  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
Dries's avatar
Dries 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's avatar
   
Dries committed
765
      }
Dries's avatar
   
Dries committed
766
    }
Kjartan's avatar
Kjartan committed
767
  }
Dries's avatar
   
Dries committed
768

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

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

Dries's avatar
   
Dries 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's avatar
Kjartan committed
793
}
Dries's avatar
   
Dries committed
794

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

Dries's avatar
   
Dries 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's avatar
   
Dries 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's avatar
   
Dries committed
808
    }
Kjartan's avatar
Kjartan committed
809
    while ($term = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
810
      $count[$type][$term->tid] = $term->c;
Dries's avatar
   
Dries committed
811
812
813
    }
  }

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

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

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

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

  return $result;
}

Dries's avatar
   
Dries committed
857
858
859
/**
 * Return the vocabulary object matching a vocabulary ID.
 */
Kjartan's avatar
Kjartan 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's avatar
   
Dries 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's avatar
Kjartan committed
871
}
Dries's avatar
   
Dries committed
872

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

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

Dries's avatar
Dries committed
885
  if ($blank) {
886
    $options[0] = $blank;
Dries's avatar
Dries committed
887
  }
Kjartan's avatar
Kjartan 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's avatar
   
Dries committed
892
893
      }
    }
Kjartan's avatar
Kjartan 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's avatar
   
Dries committed
899

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

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

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

Dries's avatar
   
Dries committed
918
function _taxonomy_prepare_insert($data, $stage) {
Kjartan's avatar
Kjartan committed
919
  if ($stage == 1) {
Dries's avatar
   
Dries committed
920
    $result = implode(', ', array_keys($data));
Kjartan's avatar
Kjartan 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's avatar
   
Dries committed
931
    }
Dries's avatar
   
Dries committed
932
    $result = implode(', ', $q);
Dries's avatar
   
Dries committed
933
  }
Kjartan's avatar
Kjartan committed
934
935
  return "($result)";
}
Dries's avatar
   
Dries committed
936

Dries's avatar
   
Dries committed
937
938
939
/**
 * Finds all nodes that match selected taxonomy conditions.
 *
Dries's avatar
   
Dries committed
940
941
942
943
944
945
946
 * @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
947
948
949
950
951
952
 * @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).
 * @return
 *   A resource identifier pointing to the query results.
 */
Dries's avatar
   
Dries committed
953
954
955
956
957
958
959
960
961
962
function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE) {
  if (count($tids) > 0) {
    // For each term ID, generate an array of descendant term IDs to the right depth.
    $descendant_tids = array();
    if ($depth === 'all') {
      $depth = NULL;
    }
    foreach ($tids as $index => $tid) {
      $term = taxonomy_get_term($tid);
      $tree = taxonomy_get_tree($term->vid, $tid, -1, $depth);
Steven Wittens's avatar
Steven Wittens committed
963
      $descendant_tids[] = array_merge(array($tid), array_map('_taxonomy_get_tid_from_term', $tree));
964
    }
Dries's avatar
   
Dries committed
965

Dries's avatar
   
Dries committed
966
967
    if ($operator == 'or') {
      $str_tids = implode(',', call_user_func_array('array_merge', $descendant_tids));
968
969
      $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid WHERE tn.tid IN ('. $str_tids .') AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC';<