taxonomy.module 53.6 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
  $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.'));
121

122
123
124
125
  // Add extra vocabulary form elements.
  $extra = module_invoke_all('taxonomy', 'form', 'vocabulary');
  if (is_array($extra)) {
    foreach ($extra as $key => $element) {
126
      $extra[$key]['#weight'] = isset($extra[$key]['#weight']) ? $nodeapi[$key]['#weight'] : -18;
127
128
129
    }
    $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
  $form['type'] = array('#type' => 'hidden', '#value' => 'vocabulary');
  $form['vid'] = array('#type' => 'hidden', '#value' => $vid);
  $form['name'] = array('#type' => 'hidden', '#value' => $vocabulary->name);
195
196
197
198
  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
  $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);
208

209
  $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
  $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.'));
235
236
237
238
239

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


246
247
  $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
  $form['type'] = array('#type' => 'hidden', '#value' => 'term');
  $form['tid'] = array('#type' => 'hidden', '#value' => $tid);
358
359
360
  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.
 */
492
function taxonomy_node_form($node) {
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'), $node->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
      $form['taxonomy']['tags'][$vocabulary->vid] = array('#type' => 'textfield', '#default_value' => $typed_string, '#size' => 60, '#maxlength' => 100, '#autocomplete_path' => 'taxonomy/autocomplete/'. $vocabulary->vid, '#required' => $vocabulary->required, '#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
523
524
525
    }
    else {
      $ntterms = array_key_exists('taxonomy', $node) ? $terms : array_keys($terms);
526
      $form['taxonomy'][$vocabulary->vid] = taxonomy_form($vocabulary->vid, $ntterms, $help, 'taxonomy');
Dries's avatar
Dries committed
527
    }
Dries's avatar
   
Dries committed
528
  }
529
530
531
532
533
534
535
536
  if ($form) {
    $form['taxonomy']['#tree'] = TRUE;
    $form['taxonomy']['#weight'] = -15;
    return $form;
  }
  else {
    return array();
  }
Kjartan's avatar
Kjartan committed
537
}
Dries's avatar
   
Dries committed
538

Dries's avatar
   
Dries committed
539
540
541
542
/**
 * Find all terms associated to the given node, within one vocabulary.
 */
function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = 'tid') {
543
  $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
544
545
546
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries's avatar
   
Dries committed
547
  }
Kjartan's avatar
Kjartan committed
548
549
550
  return $terms;
}

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

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

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

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

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

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

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

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

Dries's avatar
   
Dries committed
672
673
674
675
/**
 * Find all parents of a given term ID.
 */
function taxonomy_get_parents($tid, $key = 'tid') {
Kjartan's avatar
Kjartan committed
676
  if ($tid) {
677
    $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
678
679
680
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries's avatar
   
Dries committed
681
    }
Kjartan's avatar
Kjartan committed
682
    return $parents;
Dries's avatar
   
Dries committed
683
  }
Kjartan's avatar
Kjartan committed
684
685
686
687
  else {
    return array();
  }
}
Dries's avatar
   
Dries committed
688

Dries's avatar
   
Dries committed
689
690
691
692
/**
 * Find all ancestors of a given term ID.
 */
function taxonomy_get_parents_all($tid) {
Dries's avatar
   
Dries committed
693
694
695
696
697
698
699
700
701
702
703
704
  $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
705
706
707
708
/**
 * Find all children of a term ID.
 */
function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
Kjartan's avatar
Kjartan committed
709
  if ($vid) {
710
    $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
711
  }
Kjartan's avatar
Kjartan committed
712
  else {
713
    $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
714
715
716
717
718
719
720
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries's avatar
   
Dries committed
721

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

Kjartan's avatar
Kjartan committed
745
  $depth++;
Dries's avatar
   
Dries committed
746

Dries's avatar
   
Dries committed
747
748
749
750
  // 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
751

752
    $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
753
    while ($term = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
754
755
756
      $children[$vid][$term->parent][] = $term->tid;
      $parents[$vid][$term->tid][] = $term->parent;
      $terms[$vid][$term->tid] = $term;
Dries's avatar
   
Dries committed
757
758
    }
  }
Dries's avatar
   
Dries committed
759

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

Dries's avatar
   
Dries committed
777
  return $tree ? $tree : array();
Kjartan's avatar
Kjartan committed
778
}
Dries's avatar
   
Dries committed
779

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

Dries's avatar
   
Dries committed
796
797
798
799
800
/**
 * 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
801
}
Dries's avatar
   
Dries committed
802

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

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

Kjartan's avatar
Kjartan committed
822
  foreach (_taxonomy_term_children($tid) as $c) {
Dries's avatar
   
Dries committed
823
    $children_count += taxonomy_term_count_nodes($c, $type);
Kjartan's avatar
Kjartan committed
824
  }
Dries's avatar
   
Dries committed
825
  return $count[$type][$tid] + $children_count;
Kjartan's avatar
Kjartan committed
826
827
}

Dries's avatar
   
Dries committed
828
829
830
/**
 * Helper for taxonomy_term_count_nodes().
 */
Kjartan's avatar
Kjartan committed
831
832
function _taxonomy_term_children($tid) {
  static $children;
Dries's avatar
   
Dries committed
833

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

Dries's avatar
   
Dries committed
843
/**
Dries's avatar
   
Dries committed
844
 * Try to map a string to an existing term, as for glossary use.
Dries's avatar
   
Dries committed
845
 *
Dries's avatar
   
Dries committed
846
847
848
849
850
851
852
853
 * 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
854
855
 */
function taxonomy_get_term_by_name($name) {
856
  $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
857
858
859
860
861
862
863
864
  $result = array();
  while ($term = db_fetch_object($db_result)) {
    $result[] = $term;
  }

  return $result;
}

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

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

Kjartan's avatar
Kjartan committed
889
function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
Dries's avatar
   
Dries committed
890
  $tree = taxonomy_get_tree($vocabulary_id);
891
892
  $options = array();

Dries's avatar
Dries committed
893
  if ($blank) {
894
    $options[0] = $blank;
Dries's avatar
Dries committed
895
  }
Kjartan's avatar
Kjartan committed
896
897
898
  if ($tree) {
    foreach ($tree as $term) {
      if (!in_array($term->tid, $exclude)) {
899
        $options[$term->tid] = _taxonomy_depth($term->depth, '-') . $term->name;
Dries's avatar
   
Dries committed
900
901
      }
    }
Kjartan's avatar
Kjartan committed
902
903
904
905
906
    if (!$blank && !$value) {
      // required but without a predefined value, so set first as predefined
      $value = $tree[0]->tid;
    }
  }
907
  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
908
}
Dries's avatar
   
Dries committed
909

Kjartan's avatar
Kjartan committed
910
911
912
function _taxonomy_depth($depth, $graphic = '--') {
  for ($n = 0; $n < $depth; $n++) {
    $result .= $graphic;
Dries's avatar
   
Dries committed
913
  }
Kjartan's avatar
Kjartan committed
914
915
  return $result;
}
Dries's avatar
   
Dries committed
916

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

Dries's avatar
   
Dries committed