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

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

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

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

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

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

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

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

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    $items[] = array('path' => 'admin/taxonomy/edit/vocabulary', 'title' => t('edit vocabulary'),
      'callback' => 'taxonomy_admin',
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);

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

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

Dries's avatar
   
Dries committed
79
80
81
82
    $items[] = array('path' => 'taxonomy/term', 'title' => t('taxonomy term'),
      'callback' => 'taxonomy_term_page',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
Steven Wittens's avatar
Steven Wittens committed
83
84
85
86
87

    $items[] = array('path' => 'taxonomy/autocomplete', 'title' => t('autocomplete taxonomy'),
      'callback' => 'taxonomy_autocomplete',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
Dries's avatar
   
Dries committed
88
  }
Dries's avatar
Dries committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  else {
    if (is_numeric(arg(2))) {
      $items[] = array('path' => 'admin/taxonomy/' . arg(2), 'title' => t('add term'),
        'callback' => 'taxonomy_admin',
        'access' => user_access('administer taxonomy'),
        'type' => MENU_CALLBACK);

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

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

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

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

  $form['description'] = array(type => 'textarea', title => t('Description'), default_value => $edit['description'], cols => 60, rows => 5, description => t('Description of the vocabulary; can be used by modules.'));
  $form['help'] = array(type => 'textfield', title => t('Help text'), default_value => $edit['help'], size => 60, maxlength => 255, description => t('Instructions to present to the user when choosing a term.'));
  $form['nodes'] = array(type => 'checkboxes', title => t('Types'), default_value => $edit['nodes'], options => node_get_types(), description => t('A list of node types you want to associate with this vocabulary.'), required => TRUE);
  $form['hierarchy'] = array(type => 'radios', title => t('Hierarchy'), default_value => $edit['hierarchy'], options => array(t('Disabled'), t('Single'), t('Multiple')), description => t('Allows <a href="%help-url">a tree-like hierarchy</a> between terms of this vocabulary.', array('%help-url' => url('admin/help/taxonomy', NULL, NULL, 'hierarchy'))));
  $form['relations'] = array(type => 'checkbox', title => t('Related terms'), default_value => $edit['relations'], return_value => 1, description => t('Allows <a href="%help-url">related terms</a> in this vocabulary.', array('%help-url' => url('admin/help/taxonomy', NULL, NULL, 'related-terms'))));
  $form['tags'] = array(type => 'checkbox', title => t('Free tagging'), default_value => $edit['tags'], return_value => 1, description => t('Content is categorized by typing terms instead of choosing from a list.'));
  $form['multiple'] = array(type => 'checkbox', title => t('Multiple select'), default_value => $edit['multiple'], return_value => 1, description => t('Allows nodes to have more than one term from this vocabulary (always true for free tagging).'));
  $form['required'] = array(type => 'checkbox', title => t('Required'), default_value => $edit['required'], return_value => 1, description => t('If enabled, every node <strong>must</strong> have at least one term in this vocabulary.'));
  $form['weight'] = array(type => 'weight', title => t('Weight'), default_value => $edit['weight'], delta => 10, description => t('In listings, the heavier vocabularies will sink and the lighter vocabularies will be positioned nearer the top.'));
 
  // Add extra vocabulary form elements.
  $extra = module_invoke_all('taxonomy', 'form', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
      $extra[$key][weight] = isset($extra[$key][weight]) ? $nodeapi[$key][weight] : -18;
    }
    $form = array_merge($form, $extra);
  }
Dries's avatar
   
Dries committed
130

131
  $form['submit'] = array(type => 'submit', value => t('Submit'));
Dries's avatar
   
Dries committed
132
  if ($edit['vid']) {
133
134
    $form['delete'] = array(type => 'submit', value => t('Delete'));
    $form['vid'] = array(type => 'hidden', value => $edit['vid']);
Dries's avatar
   
Dries committed
135
  }
136
  return drupal_get_form('taxonomy_form_vocabulary', $form);
Kjartan's avatar
Kjartan committed
137
}
Kjartan's avatar
Kjartan committed
138

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

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

  cache_clear_all();
Dries's avatar
   
Dries committed
168

169
  return $status;
Kjartan's avatar
Kjartan committed
170
}
Dries's avatar
   
Dries committed
171

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

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

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

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

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

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

192
193
194
195
196
197
198
  $form['type'] = array(type => 'hidden', value => 'vocabulary');
  $form['vid'] = array(type => 'hidden', value => $vid);
  $form['name'] = array(type => 'hidden', value => $vocabulary->name);
  return confirm_form('vocabulary_confirm_delete', $form,
                  t('Are you sure you want to delete the vocabulary %title?',
                  array('%title' => theme('placeholder', $vocabulary->name))),
                  'admin/taxonomy', t('Deleting a vocabulary will delete all the terms in it. This action cannot be undone.'),
199
                  t('Delete'),
200
                  t('Cancel'));
Kjartan's avatar
Kjartan committed
201
}
Dries's avatar
   
Dries committed
202

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

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

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

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

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

Kjartan's avatar
Kjartan committed
221
    if ($vocabulary->hierarchy == 1) {
222
      $form['parent'] = _taxonomy_term_select(t('Parent'), 'parent', $parent, $vocabulary_id, l(t('Parent term'), 'admin/help/taxonomy', NULL, NULL, 'parent') .'.', 0, '<'. t('root') .'>', $exclude);
Dries's avatar
   
Dries committed
223
    }
Kjartan's avatar
Kjartan committed
224
    elseif ($vocabulary->hierarchy == 2) {
225
      $form['parent'] = _taxonomy_term_select(t('Parents'), 'parent', $parent, $vocabulary_id, l(t('Parent terms'), 'admin/help/taxonomy', NULL, NULL, 'parent') .'.', 1, '<'. t('root') .'>', $exclude);
Dries's avatar
   
Dries committed
226
    }
Kjartan's avatar
Kjartan committed
227
  }
Dries's avatar
   
Dries committed
228

229
  if ($vocabulary->relations) {
230
    $form['relations'] = _taxonomy_term_select(t('Related terms'), 'relations', array_keys(taxonomy_get_related($edit['tid'])), $vocabulary_id, NULL, 1, '<'. t('none') .'>', array($edit['tid']));
231
232
  }

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  $form['synonyms'] = array(type => 'textarea', title => t('Synonyms'), default_value => implode("\n", taxonomy_get_synonyms($edit['tid'])), cols => 60, rows => 5, description => t('<a href="%help-url">Synonyms</a> of this term, one synonym per line.', array('%help-url' => url('admin/help/taxonomy', NULL, NULL, 'synonyms'))));
  $form['weight'] = array(type => 'weight', title => t('Weight'), default_value => $edit['weight'], delta => 10, description => t('In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top.'));

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


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

Dries's avatar
   
Dries committed
249
  if ($edit['tid']) {
250
251
    $form['delete'] = array(type => 'submit', value => t('Delete'));
    $form['tid'] = array(type => 'hidden', value => $edit['tid']);
Dries's avatar
   
Dries committed
252
  }
253
  else {
254
    $form['destination'] = array(type => 'hidden', value => $_GET['q']);
255
  }
Dries's avatar
   
Dries committed
256

257
  return drupal_get_form('taxonomy_form_term', $form);
Kjartan's avatar
Kjartan committed
258
}
Dries's avatar
   
Dries committed
259

260
function taxonomy_save_term(&$edit) {
Dries's avatar
   
Dries committed
261
262
  if ($edit['tid'] && $edit['name']) {
    $data = array('name' => $edit['name'], 'description' => $edit['description'], 'weight' => $edit['weight']);
Dries's avatar
   
Dries committed
263

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

Dries's avatar
   
Dries committed
279
280
281
  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
282
      if ($related_id != 0) {
Dries's avatar
   
Dries committed
283
        db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $edit['tid'], $related_id);
Dries's avatar
   
Dries committed
284
      }
Kjartan's avatar
Kjartan committed
285
    }
Kjartan's avatar
Kjartan committed
286
  }
Dries's avatar
   
Dries committed
287

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

Dries's avatar
   
Dries committed
305
306
307
  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
308
      if ($synonym) {
Dries's avatar
   
Dries committed
309
        db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $edit['tid'], chop($synonym));
Dries's avatar
   
Dries committed
310
      }
Kjartan's avatar
Kjartan committed
311
    }
Dries's avatar
   
Dries committed
312
  }
Dries's avatar
   
Dries committed
313

Dries's avatar
   
Dries committed
314
315
  cache_clear_all();

316
  return $status;
Kjartan's avatar
Kjartan committed
317
}
Dries's avatar
   
Dries committed
318

Kjartan's avatar
Kjartan committed
319
function taxonomy_del_term($tid) {
320
321
322
323
324
325
326
327
328
329
330
331
332
333
  $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
334

335
      $term = taxonomy_get_term($tid);
Dries's avatar
   
Dries committed
336

337
338
339
340
341
      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
342

343
      module_invoke_all('taxonomy', 'delete', 'term', $term);
344
      drupal_set_message(t('Deleted term %name.', array('%name' => theme('placeholder', $term->name))));
345
    }
Dries's avatar
   
Dries committed
346

347
348
    $tids = $orphans;
  }
Dries's avatar
   
Dries committed
349

Dries's avatar
   
Dries committed
350
  cache_clear_all();
Dries's avatar
   
Dries committed
351
352
353
354
355
}

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

356
357
358
359
360
  $form['type'] = array(type => 'hidden', value => 'term');
  $form['tid'] = array(type => 'hidden', value => $tid);
  return confirm_form('term_confirm_delete', $form,
                  t('Are you sure you want to delete the term %title?',
                  array('%title' => theme('placeholder', $term->name))),
361
362
363
                  'admin/taxonomy',
                  t('Deleting a term will delete all its children if there are any. This action cannot be undone.'),
                  t('Delete'),
364
                  t('Cancel'));
Kjartan's avatar
Kjartan committed
365
}
Dries's avatar
   
Dries committed
366

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

373
  // Show all vocabularies, and a "view terms" link to the pagers.
Dries's avatar
Dries committed
374
  if (!$vid) {
375
    $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '2'));
Dries's avatar
Dries committed
376
377
    $vocabularies = taxonomy_get_vocabularies();
    foreach ($vocabularies as $vocabulary) {
378
379
      $types = array();
      foreach ($vocabulary->nodes as $type) {
380
        $node_type = node_get_name($type);
381
382
        $types[] = $node_type ? $node_type : $type;
      }
Dries's avatar
Dries committed
383
      $rows[] = array(check_plain($vocabulary->name), implode(', ', $types), l(t('edit vocabulary'), "admin/taxonomy/edit/vocabulary/$vocabulary->vid"), l(t('edit terms'), "admin/taxonomy/$vocabulary->vid"));
Dries's avatar
Dries committed
384
385
386
    }

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

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

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

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

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

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

Dries's avatar
Dries committed
416
417
    $GLOBALS['pager_page_array'][] = $start_from;  // FIXME
    $GLOBALS['pager_total'][] = intval($total_entries / $page_increment) + 1; // FIXME
418

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

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

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

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

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

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

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

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

505
  $c = db_query(db_rewrite_sql("SELECT v.*, n.type FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name", 'v', 'vid'), $type);
Kjartan's avatar
Kjartan committed
506
  while ($vocabulary = db_fetch_object($c)) {
Dries's avatar
Dries committed
507
508
509
510
511
512
513
514
515
516
517
518
519
520
    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);
521
522
523
524
525

      
      $form[$name]['tags'][$vocabulary->vid] = array( type => textfield, default_value => $typed_string, size => 60, maxlength => 100,
            autocomplete_path => 'taxonomy/autocomplete/'. $vocabulary->vid, required => ($vocabulary->required ? TRUE : FALSE), title => $vocabulary->name,
            description => t('A comma-separated list of terms describing this content (Example: funny, bungie jumping, "Company, Inc.").') );
Dries's avatar
Dries committed
526
527
528
    }
    else {
      $ntterms = array_key_exists('taxonomy', $node) ? $terms : array_keys($terms);
529
      $form[$name][$vocabulary->vid] = taxonomy_form($vocabulary->vid, $ntterms, $help, $name);
Dries's avatar
Dries committed
530
    }
Dries's avatar
   
Dries committed
531
  }
532
  return $form ? $form : array();
Kjartan's avatar
Kjartan committed
533
}
Dries's avatar
   
Dries committed
534

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

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

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

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

  // 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) {
618
619
620
          $edit = array('vid' => $vid, 'name' => $typed_term);
          $status = taxonomy_save_term($edit);
          $typed_term_tid = $edit['tid'];
Dries's avatar
Dries committed
621
622
623
624
625
626
        }

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

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

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

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

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

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

Dries's avatar
   
Dries committed
718
719
720
721
722
723
724
725
726
727
728
729
730
/**
 * 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
731
732
733
 * @param $max_depth
 *   The number of levels of the tree to return. Leave NULL to return all levels.
 *
Dries's avatar
   
Dries committed
734
735
736
737
738
 * @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
739
  static $children, $parents, $terms;
Dries's avatar
   
Dries committed
740

Kjartan's avatar
Kjartan committed
741
  $depth++;
Dries's avatar
   
Dries committed
742

Dries's avatar
   
Dries committed
743
744
745
746
  // 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
747

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

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

Dries's avatar
   
Dries committed
773
  return $tree ? $tree : array();
Kjartan's avatar
Kjartan committed
774
}
Dries's avatar
   
Dries committed
775

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

Dries's avatar
   
Dries committed
792
793
794
795
796
/**
 * 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
797
}
Dries's avatar
   
Dries committed
798

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

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

Kjartan's avatar
Kjartan committed
818
  foreach (_taxonomy_term_children($tid) as $c) {
Dries's avatar
   
Dries committed
819
    $children_count += taxonomy_term_count_nodes($c, $type);
Kjartan's avatar
Kjartan committed
820
  }
Dries's avatar
   
Dries committed
821
  return $count[$type][$tid] + $children_count;
Kjartan's avatar
Kjartan committed
822
823
}

Dries's avatar
   
Dries committed
824
825
826
/**
 * Helper for taxonomy_term_count_nodes().
 */
Kjartan's avatar
Kjartan committed
827
828
function _taxonomy_term_children($tid) {
  static $children;
Dries's avatar
   
Dries committed
829

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

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

  return $result;
}

Dries's avatar
   
Dries committed
861
862
863
/**
 * Return the vocabulary object matching a vocabulary ID.
 */
Kjartan's avatar
Kjartan committed
864
function taxonomy_get_vocabulary($vid) {
865
  $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
866
867
868
869
870
871
872
873
874
  $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
875
}
Dries's avatar
   
Dries committed
876

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

Kjartan's avatar
Kjartan committed
885
function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
Dries's avatar
   
Dries committed
886
  $tree = taxonomy_get_tree($vocabulary_id);
887
888
  $options = array();

Dries's avatar
Dries committed
889
  if ($blank) {
890
    $options[0] = $blank;
Dries's avatar
Dries committed
891
  }
Kjartan's avatar
Kjartan committed
892
893
894
  if ($tree) {
    foreach ($tree as $term) {
      if (!in_array($term->tid, $exclude)) {
895
        $options[$term->tid] = _taxonomy_depth($term->depth, '-') . $term->name;
Dries's avatar
   
Dries committed
896
897
      }
    }
Kjartan's avatar
Kjartan committed
898
899
900
901
902
    if (!$blank && !$value) {
      // required but without a predefined value, so set first as predefined
      $value = $tree[0]->tid;
    }
  }
903
  return array(type => 'select', title => $title, default_value => $value, options => $options, description => $description, multiple => $multiple, size => $multiple ? 'size="'. min(12, count($options)) .'"' : 0, weight => -15);
Kjartan's avatar
Kjartan committed
904
}
Dries's avatar
   
Dries committed
905

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

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

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

Dries's avatar
   
Dries committed
940
941
942
/**
 * Finds all nodes that match selected taxonomy conditions.
 *
Dries's avatar