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