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

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

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

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

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

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

Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
88
  }
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 Buytaert's avatar
   
Dries Buytaert committed
105

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

Kjartan Mannes's avatar
Kjartan Mannes 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 Buytaert's avatar
 
Dries Buytaert committed
130

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

139
function taxonomy_save_vocabulary(&$edit) {
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 Buytaert's avatar
   
Dries Buytaert committed
143

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

  cache_clear_all();
Dries Buytaert's avatar
   
Dries Buytaert committed
168

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

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

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

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

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

186
  return SAVED_DELETED;
Dries Buytaert's avatar
   
Dries Buytaert 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 Mannes's avatar
Kjartan Mannes committed
201
}
Dries Buytaert's avatar
 
Dries Buytaert committed
202

Kjartan Mannes's avatar
Kjartan Mannes committed
203
function taxonomy_form_term($edit = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
204
  $vocabulary_id = isset($edit['vid']) ? $edit['vid'] : arg(4);
Kjartan Mannes's avatar
Kjartan Mannes committed
205
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
 
Dries Buytaert committed
210

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

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

Kjartan Mannes's avatar
Kjartan Mannes 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 Buytaert's avatar
 
Dries Buytaert committed
223
    }
Kjartan Mannes's avatar
Kjartan Mannes 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 Buytaert's avatar
 
Dries Buytaert committed
226
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
227
  }
Dries Buytaert's avatar
 
Dries Buytaert 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 Mannes's avatar
Kjartan Mannes committed
248

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

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

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

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
288
289
  db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $edit['tid']);
  if (!isset($edit['parent'])) {
290
    $edit['parent'] = array(0);
Kjartan Mannes's avatar
Kjartan Mannes committed
291
  }
Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
 
Dries Buytaert committed
302
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
303
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
304

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

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

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

Kjartan Mannes's avatar
Kjartan Mannes 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 Buytaert's avatar
   
Dries Buytaert committed
334

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
350
  cache_clear_all();
Dries Buytaert's avatar
   
Dries Buytaert 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 Mannes's avatar
Kjartan Mannes committed
365
}
Dries Buytaert's avatar
 
Dries Buytaert committed
366

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

373
  // Show all vocabularies, and a "view terms" link to the pagers.
374
  if (!$vid) {
375
    $header = array(t('Name'), t('Type'), array('data' => t('Operations'), 'colspan' => '2'));
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 Buytaert's avatar
Dries Buytaert 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"));
384
385
386
    }

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

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

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

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

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

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

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

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

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

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

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
489
490
491
/**
 * Generate a form for selecting terms to associate with a node.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
492
function taxonomy_node_form($type, $node = '', $help = NULL, $name = 'taxonomy') {
493
  if (!array_key_exists('taxonomy', $node)) {
Kjartan Mannes's avatar
Kjartan Mannes committed
494
    if ($node->nid) {
495
      $terms = taxonomy_node_get_terms($node->nid);
Kjartan Mannes's avatar
Kjartan Mannes committed
496
497
    }
    else {
498
      $terms = array();
Dries Buytaert's avatar
 
Dries Buytaert committed
499
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
500
501
502
503
  }
  else {
    $terms = $node->taxonomy;
  }
Dries Buytaert's avatar
 
Dries Buytaert 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 Mannes's avatar
Kjartan Mannes committed
506
  while ($vocabulary = db_fetch_object($c)) {
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.").') );
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);
530
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
531
  }
532
  return $form ? $form : array();
Kjartan Mannes's avatar
Kjartan Mannes committed
533
}
Dries Buytaert's avatar
 
Dries Buytaert committed
534

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

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

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

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 Buytaert's avatar
   
Dries Buytaert committed
580
581
582
/**
 * Save term associations for a given node.
 */
Kjartan Mannes's avatar
Kjartan Mannes committed
583
function taxonomy_node_save($nid, $terms) {
584
  taxonomy_node_delete($nid);
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'];
621
622
623
624
625
626
        }

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

628
  if (is_array($terms)) {
Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
638
        db_query('INSERT INTO {term_node} (nid, tid) VALUES (%d, %d)', $nid, $term);
639
      }
Dries Buytaert's avatar
 
Dries Buytaert committed
640
641
    }
  }
Kjartan Mannes's avatar
Kjartan Mannes committed
642
}
Dries Buytaert's avatar
 
Dries Buytaert committed
643

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

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
685
686
687
688
/**
 * Find all ancestors of a given term ID.
 */
function taxonomy_get_parents_all($tid) {
Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
701
702
703
704
/**
 * Find all children of a term ID.
 */
function taxonomy_get_children($tid, $vid = 0, $key = 'tid') {
Kjartan Mannes's avatar
Kjartan Mannes 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 Buytaert's avatar
 
Dries Buytaert committed
707
  }
Kjartan Mannes's avatar
Kjartan Mannes 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 Mannes's avatar
Kjartan Mannes committed
710
711
712
713
714
715
716
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries Buytaert's avatar
 
Dries Buytaert committed
717

Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
731
732
733
 * @param $max_depth
 *   The number of levels of the tree to return. Leave NULL to return all levels.
 *
Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
739
  static $children, $parents, $terms;
Dries Buytaert's avatar
   
Dries Buytaert committed
740

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
756
  $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
Dries Buytaert's avatar
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
769
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
770
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
771
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
772

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

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

Dries Buytaert's avatar
   
Dries Buytaert 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 Mannes's avatar
Kjartan Mannes committed
797
}
Dries Buytaert's avatar
   
Dries Buytaert committed
798

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

Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
812
    }
Kjartan Mannes's avatar
Kjartan Mannes committed
813
    while ($term = db_fetch_object($result)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
814
      $count[$type][$term->tid] = $term->c;
Dries Buytaert's avatar
   
Dries Buytaert committed
815
816
817
    }
  }

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

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

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

Dries Buytaert's avatar
   
Dries Buytaert committed
839
/**
Dries Buytaert's avatar
   
Dries Buytaert committed
840
 * Try to map a string to an existing term, as for glossary use.
Dries Buytaert's avatar
   
Dries Buytaert committed
841
 *
Dries Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert 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 Buytaert's avatar
   
Dries Buytaert committed
853
854
855
856
857
858
859
860
  $result = array();
  while ($term = db_fetch_object($db_result)) {
    $result[] = $term;
  }

  return $result;
}

Dries Buytaert's avatar
   
Dries Buytaert committed
861
862
863
/**
 * Return the vocabulary object matching a vocabulary ID.
 */
Kjartan Mannes's avatar
Kjartan Mannes 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 Buytaert's avatar
   
Dries Buytaert 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 Mannes's avatar
Kjartan Mannes committed
875
}
Dries Buytaert's avatar
 
Dries Buytaert committed
876

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

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

Dries Buytaert's avatar
Dries Buytaert committed
889
  if ($blank) {
890
    $options[0] = $blank;
Dries Buytaert's avatar
Dries Buytaert committed
891
  }
Kjartan Mannes's avatar
Kjartan Mannes 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 Buytaert's avatar
 
Dries Buytaert committed
896
897
      }
    }
Kjartan Mannes's avatar
Kjartan Mannes 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 Mannes's avatar
Kjartan Mannes committed
904
}
Dries Buytaert's avatar
 
Dries Buytaert committed
905

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

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