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

Kjartan's avatar
Kjartan committed
4
5
function taxonomy_feed() {
  global $id, $or, $and, $type;
Dries's avatar
   
Dries committed
6

Kjartan's avatar
Kjartan committed
7
8
9
10
11
12
13
  if ($type == "voc") {
    //TODO - vocabulary feed. How to represent an outline in XML?
  }
  else {
    if ($or) {
      foreach ((explode(",", $or)) as $t) {
        $terms[] = "'". check_query($t) ."'";
Dries's avatar
   
Dries committed
14
      }
Kjartan's avatar
Kjartan committed
15
16
17
18
19
20
      $result = db_query("SELECT DISTINCT(n.nid), type FROM node n LEFT JOIN term_node r ON n.nid = r.nid WHERE tid IN (". implode(",", $terms) .") AND status = '1' ORDER BY static DESC, created DESC LIMIT 15");
      $term = taxonomy_get_term($or);
    }
    else if ($and) {
      foreach ((explode(",", $and)) as $t) {
        $terms[] = "'". check_query($t) ."'";
Dries's avatar
   
Dries committed
21
      }
22
      $result = db_query("SELECT n.nid, type, count(*) AS c FROM node n LEFT JOIN term_node r ON n.nid = r.nid WHERE tid IN (". implode(",", $terms) .") AND status = '1' GROUP BY n.nid, n.type HAVING c = ". count($terms) ." ORDER BY static DESC, created DESC LIMIT 15");
Kjartan's avatar
Kjartan committed
23
24
25
26
27
      $term = taxonomy_get_term($and);
    }
    else {
      return node_feed();
    }
Dries's avatar
   
Dries committed
28

Kjartan's avatar
Kjartan committed
29
30
31
    $channel["title"] = variable_get("site_name", "drupal") ." - ". $term->name;
    $channel["link"] = path_uri() ."index.php?or=$or";
    $channel["description"] = $term->description;
Dries's avatar
   
Dries committed
32

Kjartan's avatar
Kjartan committed
33
    node_feed($result, $channel);
Dries's avatar
   
Dries committed
34
  }
Kjartan's avatar
Kjartan committed
35
}
Dries's avatar
   
Dries committed
36

Kjartan's avatar
Kjartan committed
37
38
39
function taxonomy_perm() {
  return array("administer taxonomy");
}
Dries's avatar
 
Dries committed
40

Kjartan's avatar
Kjartan committed
41
42
43
function taxonomy_link($type) {
  if ($type == "admin" && user_access("administer taxonomy")) {
    $links[] = la(t("taxonomy"), array("mod" => "taxonomy"));
Dries's avatar
 
Dries committed
44
45
  }

Kjartan's avatar
Kjartan committed
46
47
48
  return $links ? $links : array();
}

Dries's avatar
 
Dries committed
49
50
51
52
/*
** admin pages (form, save, overview)
*/

Kjartan's avatar
Kjartan committed
53
54
55
56
function taxonomy_form_vocabulary($edit = array()) {
  foreach (module_list() as $name) {
    if (module_hook($name, "node")) {
      $nodetypes[$name] = $name;
Dries's avatar
 
Dries committed
57
    }
Kjartan's avatar
Kjartan committed
58
  }
59
  
Kjartan's avatar
Kjartan committed
60
61
62
  $form .= form_textfield("Vocabulary name", "name", $edit["name"], 50, 64, "Required.  The name for this vocabulary.  Example: 'Topic'.");
  $form .= form_textarea("Description", "description", $edit["description"], 60, 5, "Optional.  Description of the vocabulary, can be used by modules.");
  $form .= form_select("Types", "types", explode(",", $edit["types"]), $nodetypes, "Required.  A list of node types you want to associate this vocabulary with.", "", 1);
Kjartan's avatar
Kjartan committed
63
64
  $form .= form_checkbox("Related terms", "relations", 1, $edit["relations"], "Optional. Allows ". la("related terms", array("mod" => "taxonomy", "op" => "help"), "relatedterms") ." in this vocabulary.");
  $form .= form_select("Hierarchy", "hierarchy", $edit["hierarchy"], array("Disabled", "Single", "Multiple"), "Optional. Allows ". la("a tree-like hierarchy", array("mod" => "taxonomy", "op" => "help"), "hierarchy") ." between terms of this vocabulary.", "", 0);
Kjartan's avatar
Kjartan committed
65
66
67
68
  $form .= form_checkbox("Multiple select", "multiple", 1, $edit["multiple"], "Optional. Allows nodes to have more than one term in this vocabulary.");
  $form .= form_checkbox("Required", "required", 1, $edit["required"], "If enabled every node <b>must</b> have at least one term in this vocabulary");
  $form .= form_textfield("Weight", "weight", $edit["weight"], 3, 3, "Optional. In listings, the heavier vocabularies will sink and the lighter vocabularies will be positioned nearer the top.");
  $form .= form_submit("Submit");
Dries's avatar
 
Dries committed
69

Kjartan's avatar
Kjartan committed
70
71
72
  if ($edit["vid"]) {
    $form .= form_submit("Delete");
    $form .= form_hidden("vid", $edit["vid"]);
Dries's avatar
 
Dries committed
73
74
  }

Kjartan's avatar
Kjartan committed
75
76
  return form($form);
}
Kjartan's avatar
Kjartan committed
77

Kjartan's avatar
Kjartan committed
78
79
80
81
82
function taxonomy_save_vocabulary($edit) {
  $data = array("name" => $edit["name"], "types" => @implode(",", $edit["types"]), "description" => $edit["description"], "multiple" => $edit["multiple"], "required" => $edit["required"], "hierarchy" => $edit["hierarchy"], "relations" => $edit["relations"], "weight" => $edit["weight"]);

  if ($edit["vid"] && $edit["name"]) {
    db_query("UPDATE vocabulary SET ". _prepare_update($data) ." WHERE vid = '". check_input($edit["vid"]) ."'");
Dries's avatar
 
Dries committed
83
  }
Kjartan's avatar
Kjartan committed
84
85
86
87
88
89
90
  else if ($edit["vid"]) {
    taxonomy_del_vocabulary($edit["vid"]);
  }
  else {
    db_query("INSERT INTO vocabulary ". _prepare_insert($data, 1) ." VALUES ". _prepare_insert($data, 2));
  }
}
Dries's avatar
 
Dries committed
91

Kjartan's avatar
Kjartan committed
92
93
94
95
96
function taxonomy_del_vocabulary($vid) {
  db_query("DELETE FROM vocabulary WHERE vid = '%s'", $vid);
  $result = db_query("SELECT tid FROM term_data WHERE vid = '%s'", $vid);
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries's avatar
 
Dries committed
97
  }
Kjartan's avatar
Kjartan committed
98
}
Dries's avatar
 
Dries committed
99

Kjartan's avatar
Kjartan committed
100
101
102
103
104
105
106
107
function taxonomy_form_term($edit = array()) {
  global $vocabulary_id;
  if (!$vocabulary_id) {
    $vocabulary_id = $edit["vid"];
  }
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
  $form = form_textfield("Term name", "name", $edit["name"], 50, 64, "Required.  The name for this term.  Example: 'Linux'.");
  $form .= form_textarea("Description", "description", $edit["description"], 60, 5, "Optional.  Description of the term, can be used by modules.");
Dries's avatar
 
Dries committed
108

Kjartan's avatar
Kjartan committed
109
110
111
  if ($vocabulary->relations) {
    $form .= _taxonomy_term_select("Related terms", "relations", array_keys(taxonomy_get_related($edit["tid"])), $vocabulary_id, "Optional.", 1, "<none>", array($edit["tid"]));
  }
Dries's avatar
 
Dries committed
112
113


Kjartan's avatar
Kjartan committed
114
115
116
117
118
119
120
  if ($vocabulary->hierarchy) {
    $parent = array_keys(taxonomy_get_parents($edit["tid"]));
    taxonomy_get_tree($vocabulary_id, $children, $edit["tid"]);
    // you can't be son of yourself or your children
    $exclude = array_keys($children);
    $exclude[] = $edit["tid"];
    if ($vocabulary->hierarchy == 1) {
Kjartan's avatar
Kjartan committed
121
      $form .= _taxonomy_term_select("Parent", "parent", $parent, $vocabulary_id, "Required. ". la("Parent term", array("mod" => "taxonomy", "op" => "help"), "parent") .".", 0, "<root>", $exclude);
Dries's avatar
 
Dries committed
122
    }
Kjartan's avatar
Kjartan committed
123
    elseif ($vocabulary->hierarchy == 2) {
Kjartan's avatar
Kjartan committed
124
      $form .= _taxonomy_term_select("Parents", "parent", $parent, $vocabulary_id, "Required. ". la("Parent terms", array("mod" => "taxonomy", "op" => "help"), "parent") .".", 1, "<root>", $exclude);
Dries's avatar
 
Dries committed
125
    }
Kjartan's avatar
Kjartan committed
126
  }
Dries's avatar
 
Dries committed
127

Kjartan's avatar
Kjartan committed
128
  $form .= form_textarea("Synonyms", "synonyms", implode("\n", taxonomy_get_synonyms($edit["tid"])), 30, 5, "Optional. ". la("Synonyms", array("mod" => "taxonomy", "op" => "help"), "synonyms") ." of this term, one synonym per line.");
Kjartan's avatar
Kjartan committed
129
130
131
132
133
134
135
  $form .= form_textfield("Weight", "weight", $edit["weight"], 3, 3, "Optional. In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top.");
  $form .= form_hidden("vid", $vocabulary->vid);
  $form .= form_submit("Submit");

  if ($edit["tid"]) {
    $form .= form_submit("Delete");
    $form .= form_hidden("tid", $edit["tid"]);
Dries's avatar
 
Dries committed
136
137
  }

Kjartan's avatar
Kjartan committed
138
139
  return form($form);
}
Dries's avatar
 
Dries committed
140

Kjartan's avatar
Kjartan committed
141
142
143
function taxonomy_save_term($edit) {
 if ($edit["tid"] && $edit["name"]) {
    $data = array("name" => $edit["name"], "description" => $edit["description"], "weight" => $edit["weight"]);
Dries's avatar
 
Dries committed
144

Kjartan's avatar
Kjartan committed
145
146
147
148
149
150
151
152
153
154
    db_query("UPDATE term_data SET ". _prepare_update($data) ." WHERE tid = '%s'", $edit["tid"]);
  }
  else if ($edit["tid"]) {
    taxonomy_del_term($edit["tid"]);
  }
  else {
    $edit["tid"] = db_result(db_query("SELECT MAX(tid) + 1 FROM term_data"));
    if (!$edit["tid"]) {
      // first term
      $edit["tid"] = 1;
Dries's avatar
 
Dries committed
155
    }
Kjartan's avatar
Kjartan committed
156
157
158
    $data = array("tid" => $edit["tid"], "name" => $edit["name"], "description" => $edit["description"], "vid" => $edit["vid"], "weight" => $edit["weight"]);
    db_query("INSERT INTO term_data ". _prepare_insert($data, 1) ." VALUES ". _prepare_insert($data, 2));
  }
Dries's avatar
 
Dries committed
159

Kjartan's avatar
Kjartan committed
160
161
162
163
164
165
  // relations (seem very powerful, but I have to understand it completely)
  db_query("DELETE FROM term_relation WHERE tid1 = '%s' OR tid2 = '%s'", $edit["tid"], $edit["tid"]);
  if ($edit["relations"]) {
    foreach ($edit["relations"] as $related_id) {
      if ($related_id != 0) {
        $rel_q[] = "('". check_query($edit["tid"]) ."', '". check_query($related_id) ."')";
Dries's avatar
 
Dries committed
166
      }
Kjartan's avatar
Kjartan committed
167
    }
Kjartan's avatar
Kjartan committed
168
169
170
    if ($rel_q) {
      $related_query = implode(", ", $rel_q);
      db_query("INSERT INTO term_relation (tid1, tid2) VALUES $related_query");
Dries's avatar
 
Dries committed
171
    }
Kjartan's avatar
Kjartan committed
172
  }
Dries's avatar
 
Dries committed
173

Kjartan's avatar
Kjartan committed
174
175
176
177
178
179
180
181
  // hierarchy
  db_query("DELETE FROM term_hierarchy WHERE tid = '%s'", $edit["tid"]);
  if (!isset($edit["parent"])) {
    $edit["parent"] = 0;
  }
  if (is_array($edit["parent"])) {
    foreach ($edit["parent"] as $parent) {
      $sql[] = "('". check_query($edit["tid"]) ."', '". check_query($parent) ."')";
Dries's avatar
 
Dries committed
182
    }
Kjartan's avatar
Kjartan committed
183
184
185
186
    db_query("INSERT INTO term_hierarchy (tid, parent) VALUES ". implode(", ", $sql));
  }
  else {
    db_query("INSERT INTO term_hierarchy (tid, parent) VALUES ('%s', '%s')", $edit["tid"], $edit["parent"][0]);
Dries's avatar
 
Dries committed
187
188
  }

Kjartan's avatar
Kjartan committed
189
190
191
192
193
194
195
196
  // synonyms (very cool idea indeed)
  db_query("DELETE FROM term_synonym WHERE tid = '%s'", $edit["tid"]);
  if ($edit["synonyms"]) {
    foreach (explode ("\n", $edit["synonyms"]) as $synonym) {
      $syn_q[] = "('". check_query($edit["tid"]) ."', '". check_query(chop($synonym)) ."')";
    }
    $synonyms_query = implode(", ", $syn_q);
    db_query("INSERT INTO term_synonym (tid, name) VALUES $synonyms_query");
Dries's avatar
 
Dries committed
197
  }
Kjartan's avatar
Kjartan committed
198
}
Dries's avatar
 
Dries committed
199

Kjartan's avatar
Kjartan committed
200
201
202
203
204
205
206
function taxonomy_del_term($tid) {
  db_query("DELETE FROM term_data WHERE tid = '%s'", $tid);
  db_query("DELETE FROM term_hierarchy WHERE tid = '%s'", $tid);
  db_query("DELETE FROM term_relation WHERE tid1 = '%s' OR tid2 = '%s'", $tid, $tid);
  db_query("DELETE FROM term_synonym WHERE tid = '%s'", $tid);
  db_query("DELETE FROM term_node WHERE tid = '%s'", $tid);
}
Dries's avatar
 
Dries committed
207

Kjartan's avatar
Kjartan committed
208
209
function taxonomy_overview() {
  global $tree;
Dries's avatar
 
Dries committed
210

Kjartan's avatar
Kjartan committed
211
212
213
  $output .= "<h3>vocabularies overview</h3>";
  $output .= "<table border=\"1\" cellpadding=\"2\" cellspacing=\"2\">\n";
  $output .= " <tr><th>name</th><th>node types</th><th>operations</th></tr>\n";
Dries's avatar
   
Dries committed
214

Kjartan's avatar
Kjartan committed
215
216
217
218
219
220
  $vocabularies = taxonomy_get_vocabularies();
  foreach ($vocabularies as $vocabulary) {
    $links = array();
    $links[] = la(t("edit vocabulary"), array("mod" => "taxonomy", "type" => "vocabulary", "op" => "edit", "id" => $vocabulary->vid));
    $links[] = la(t("add term"), array("mod" => "taxonomy", "op" => "add", "type" => "leaf", "vocabulary_id" => $vocabulary->vid));
    $links[] = la(t("preview form"), array("mod" => "taxonomy", "type" => "vocabulary", "op" => "preview", "id" => $vocabulary->vid));
Dries's avatar
 
Dries committed
221

Kjartan's avatar
Kjartan committed
222
223
224
225
226
227
228
229
    $output .= " <tr><td>". check_output($vocabulary->name) ."</td><td>". check_output($vocabulary->types) ."</td><td>". implode(" | ", $links) ."</td></tr>\n";

    unset($tree);
    taxonomy_get_tree($vocabulary->vid, $tree);
    if ($tree) {
      $output .= "<tr><td colspan=\"3\"><table><tr><td>";
      foreach ($tree as $term) {
        $output .= "<tr><td>". la(_taxonomy_depth($term->depth) . check_output($term->name), array("mod" => "taxonomy", "op" => "edit", "type" => "term", "id" => check_output($term->tid))) ."</td></tr>";
Dries's avatar
 
Dries committed
230
      }
Kjartan's avatar
Kjartan committed
231
      $output .= "</td></tr></table></td></tr>\n";
Dries's avatar
 
Dries committed
232
233
    }
  }
Kjartan's avatar
Kjartan committed
234
  $output .= "</table>\n";
Dries's avatar
 
Dries committed
235

Kjartan's avatar
Kjartan committed
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  return $output;
}

function taxonomy_form($vocabulary_id, $value = 0) {
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
  if ($vocabulary->required) {
    $verb = "must";
    $blank = 0;
  }
  else {
    $verb = "can";
    $blank = "<none>";
  }
  if ($vocabulary->multiple) {
    $description = "You $verb choose one or more terms for this node";
    $multiple = 1;
Dries's avatar
 
Dries committed
252
  }
Kjartan's avatar
Kjartan committed
253
254
255
256
257
258
  else {
    $description = "You $verb choose one term for this node";
    $multiple = 0;
  }
  return _taxonomy_term_select($vocabulary->name, "taxonomy", $value, $vocabulary_id, $description, $multiple, $blank);
}
Dries's avatar
 
Dries committed
259
260
261
262
263

/*
** API functions
*/

Kjartan's avatar
Kjartan committed
264
265
266
267
268
269
270
271
272
273
274
// return array of vocabularies, as objects
function taxonomy_get_vocabularies($type = '', $key = "vid") {
  if ($type) {
    $result = db_query("SELECT * FROM vocabulary WHERE types LIKE '%%%s%%' ORDER BY weight, name", $type);
  }
  else {
    $result = db_query("SELECT * FROM vocabulary ORDER BY weight, name");
  }
  $vocabularies = array();
  while ($voc = db_fetch_object($result)) {
    $vocabularies[$voc->$key] = $voc;
Dries's avatar
 
Dries committed
275
  }
Kjartan's avatar
Kjartan committed
276
277
  return $vocabularies;
}
Dries's avatar
 
Dries committed
278

Kjartan's avatar
Kjartan committed
279
280
281
282
283
// return form with current term
function taxonomy_node_form($type, $node = '') {
  if (!$node->taxonomy) {
    if ($node->nid) {
      $terms = array_keys(taxonomy_node_get_terms($node->nid));
Kjartan's avatar
Kjartan committed
284
285
    }
    else {
Kjartan's avatar
Kjartan committed
286
      $terms = 0;
Dries's avatar
 
Dries committed
287
    }
Kjartan's avatar
Kjartan committed
288
289
290
291
  }
  else {
    $terms = $node->taxonomy;
  }
Dries's avatar
 
Dries committed
292

Kjartan's avatar
Kjartan committed
293
294
295
  $c = db_query("SELECT * FROM vocabulary WHERE types LIKE '%%%s%%' ORDER BY weight, name", $type);
  while ($vocabulary = db_fetch_object($c)) {
    $result[] .= taxonomy_form($vocabulary->vid, $terms);
Dries's avatar
 
Dries committed
296
  }
Kjartan's avatar
Kjartan committed
297
298
  return $result ? $result : array();
}
Dries's avatar
 
Dries committed
299

Kjartan's avatar
Kjartan committed
300
301
302
// return 1 if node identified by $nid contains a taxonomy term identified by $tid in his body or title
function taxonomy_node_has_term($nid, $tid) {
  $term_name = db_result(db_query("SELECT name FROM term_data WHERE tid = '%s'", $tid));
Dries's avatar
 
Dries committed
303

Kjartan's avatar
Kjartan committed
304
305
306
307
308
309
310
311
312
  return db_result(db_query("SELECT COUNT(n.nid) FROM node n WHERE n.nid = '%s' AND ((n.body LIKE '%%%s%%') OR (n.body LIKE '%%%s%%'))", $nid, $term_name, $term_name));
}

// return array of terms of a node beloging to a particular vocabulary identified by $vid
function taxonomy_node_get_terms_by_vocabulary($nid, $vid, $key = "tid") {
  $result = db_query("SELECT t.* FROM term_data t, term_node r WHERE t.tid = r.tid AND t.vid = '%s' AND r.nid = '%s' ORDER BY weight", $vid, $nid);
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries's avatar
 
Dries committed
313
  }
Kjartan's avatar
Kjartan committed
314
315
316
317
318
319
  return $terms;
}

// return array of terms of a node
function taxonomy_node_get_terms($nid, $key = "tid") {
  static $terms;
Dries's avatar
 
Dries committed
320

Kjartan's avatar
Kjartan committed
321
322
323
  if (!$terms[$nid]) {
    $result = db_query("SELECT t.* FROM term_data t, term_node r WHERE r.tid = t.tid AND r.nid = '%s' ORDER BY weight", $nid);
    $terms[$nid] = array();
Dries's avatar
 
Dries committed
324
    while ($term = db_fetch_object($result)) {
Kjartan's avatar
Kjartan committed
325
      $terms[$nid][$term->$key] = $term;
Dries's avatar
 
Dries committed
326
327
    }
  }
Kjartan's avatar
Kjartan committed
328
329
  return $terms[$nid];
}
Dries's avatar
 
Dries committed
330

Kjartan's avatar
Kjartan committed
331
332
333
// save terms of a node
function taxonomy_node_save($nid, $terms) {
  taxonomy_node_delete($nid);
Dries's avatar
 
Dries committed
334

Kjartan's avatar
Kjartan committed
335
336
337
  if ($terms) {
    foreach ($terms as $t) {
      $query[] = "('". check_query($nid) ."', '". check_query($t) ."')";
Dries's avatar
 
Dries committed
338
    }
Kjartan's avatar
Kjartan committed
339
    db_query("INSERT INTO term_node (nid, tid) VALUES ". implode(", ", $query));
Dries's avatar
 
Dries committed
340
  }
Kjartan's avatar
Kjartan committed
341
}
Dries's avatar
 
Dries committed
342

Kjartan's avatar
Kjartan committed
343
344
345
346
// clean up terms
function taxonomy_node_delete($nid) {
  db_query("DELETE FROM term_node WHERE nid = '%s'", $nid);
}
Dries's avatar
 
Dries committed
347

Kjartan's avatar
Kjartan committed
348
349
350
351
352
353
354
// relations: return array of related terms
function taxonomy_get_related($tid, $key = "tid") {
  if ($tid) {
    $result = db_query("SELECT t.*, tid1, tid2 FROM term_relation, term_data t WHERE (t.tid = tid1 OR t.tid = tid2) AND (tid1 = '%s' OR tid2 = '%s') ORDER BY weight", $tid, $tid);
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries's avatar
 
Dries committed
355
    }
Kjartan's avatar
Kjartan committed
356
    return $related;
Dries's avatar
 
Dries committed
357
  }
Kjartan's avatar
Kjartan committed
358
359
  else {
    return array();
Dries's avatar
 
Dries committed
360
  }
Kjartan's avatar
Kjartan committed
361
}
Dries's avatar
 
Dries committed
362

Kjartan's avatar
Kjartan committed
363
364
365
366
367
368
369
// hierarchy: get parent terms
function taxonomy_get_parents($tid, $key = "tid") {
  if ($tid) {
    $result = db_query("SELECT t.* FROM term_hierarchy h, term_data t WHERE h.parent = t.tid AND h.tid = '%s' ORDER BY weight, name", $tid);
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries's avatar
   
Dries committed
370
    }
Kjartan's avatar
Kjartan committed
371
    return $parents;
Dries's avatar
 
Dries committed
372
  }
Kjartan's avatar
Kjartan committed
373
374
375
376
  else {
    return array();
  }
}
Dries's avatar
 
Dries committed
377

Kjartan's avatar
Kjartan committed
378
379
380
381
// hierarchy: get children
function taxonomy_get_children($tid, $vid = 0, $key = "tid") {
  if ($vid) {
    $result = db_query("SELECT t.* FROM term_hierarchy h, term_data t WHERE t.vid = '%s' AND h.tid = t.tid AND h.parent = '%s' ORDER BY weight, name", $vid, $tid);
Dries's avatar
 
Dries committed
382
  }
Kjartan's avatar
Kjartan committed
383
384
385
386
387
388
389
390
391
  else {
    $result = db_query("SELECT t.* FROM term_hierarchy h, term_data t WHERE h.tid = t.tid AND parent = '%s' ORDER BY weight", $tid);
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries's avatar
 
Dries committed
392

Kjartan's avatar
Kjartan committed
393
394
395
396
// hierarchy: get whole family, with tid, parent and depth; useful to show
function taxonomy_get_tree($vocabulary_id, &$tree, $parent = 0, $depth = -1, $key = "tid") {
  static $children, $terms;
  if ($depth == -1) {
Dries's avatar
 
Dries committed
397
    $children = array();
Kjartan's avatar
Kjartan committed
398
399
    $tree = array();
    // $terms = array();   // we should be able to safely do this
Dries's avatar
 
Dries committed
400
  }
Kjartan's avatar
Kjartan committed
401
402
403
404
405
406
407
  $depth++;
  if ($vocabulary_id) {
    if (!$children) {
      $result = db_query("SELECT t.*, parent FROM term_data t, term_hierarchy h WHERE t.tid = h.tid AND t.vid = '%s' ORDER BY weight, name", $vocabulary_id);
      while ($term = db_fetch_object($result)) {
        $children[$term->parent][] = $term->tid;
        $terms[$term->tid] = $term;
Dries's avatar
 
Dries committed
408
      }
Kjartan's avatar
Kjartan committed
409
    }
Kjartan's avatar
Kjartan committed
410
411
412
413
414
415
    if ($children[$parent]) {
      foreach ($children[$parent] as $child) {
        $terms[$child]->depth = $depth;
        $tree[] = $terms[$child];
        taxonomy_get_tree($vocabulary_id, $tree, $child, $depth, $key);
      }
Dries's avatar
 
Dries committed
416
417
    }
  }
Kjartan's avatar
Kjartan committed
418
419
420
421
  else {
    return 0;
  }
}
Dries's avatar
 
Dries committed
422

Kjartan's avatar
Kjartan committed
423
424
425
426
427
428
// synonyms: return array of synonyms
function taxonomy_get_synonyms($tid) {
  if ($tid) {
    $result = db_query("SELECT name FROM term_synonym WHERE tid = '%s'", $tid);
    while ($synonym = db_fetch_array($result)) {
      $synonyms[] = $synonym["name"];
Dries's avatar
 
Dries committed
429
    }
Kjartan's avatar
Kjartan committed
430
    return $synonyms ? $synonyms : array();
Dries's avatar
 
Dries committed
431
  }
Kjartan's avatar
Kjartan committed
432
433
  else {
    return array();
Dries's avatar
   
Dries committed
434
  }
Kjartan's avatar
Kjartan committed
435
}
Dries's avatar
   
Dries committed
436

Kjartan's avatar
Kjartan committed
437
438
439
440
// synonyms: return original term
function taxonomy_get_synonym_root($term) {
  return db_fetch_object(db_query("SELECT * FROM term_synonym s, term_data t WHERE t.tid = s.tid AND s.name = '%s'", $term));
}
Dries's avatar
   
Dries committed
441

Kjartan's avatar
Kjartan committed
442
443
444
// given a term id, count number of nodes in it
function taxonomy_term_count_nodes($tid) {
  static $count;
Dries's avatar
   
Dries committed
445

Kjartan's avatar
Kjartan committed
446
447
448
449
  if (!$count) {
    $result = db_query("SELECT tid, COUNT(*) AS c FROM term_node GROUP BY tid");
    while ($term = db_fetch_object($result)) {
      $count[$term->tid] = $term->c;
Dries's avatar
   
Dries committed
450
451
452
    }
  }

Kjartan's avatar
Kjartan committed
453
454
455
456
457
458
459
460
461
  foreach (_taxonomy_term_children($tid) as $c) {
    $children_count += taxonomy_term_count_nodes($c);
  }
  return $count[$tid] + $children_count;
}

// helper for above function
function _taxonomy_term_children($tid) {
  static $children;
Dries's avatar
   
Dries committed
462

Kjartan's avatar
Kjartan committed
463
464
465
466
  if (!$children) {
    $result = db_query("SELECT tid, parent FROM term_hierarchy");
    while ($term = db_fetch_object($result)) {
      $children[$term->parent][] = $term->tid;
Dries's avatar
   
Dries committed
467
    }
Dries's avatar
 
Dries committed
468
  }
Kjartan's avatar
Kjartan committed
469
470
  return $children[$tid] ? $children[$tid] : array();
}
Dries's avatar
 
Dries committed
471

Kjartan's avatar
Kjartan committed
472
473
474
475
function taxonomy_get_vocabulary($vid) {
  // simple cache using a static var?
  return db_fetch_object(db_query("SELECT * FROM vocabulary WHERE vid = '%s'", $vid));
}
Dries's avatar
 
Dries committed
476

Kjartan's avatar
Kjartan committed
477
478
479
480
function taxonomy_get_term($tid) {
  // simple cache using a static var?
  return db_fetch_object(db_query("SELECT * FROM term_data WHERE tid = '%s'", $tid));
}
Dries's avatar
 
Dries committed
481
482
483
484
485

/*
** service functions
*/

Kjartan's avatar
Kjartan committed
486
487
function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
  taxonomy_get_tree($vocabulary_id, $tree);
Dries's avatar
 
Dries committed
488

Kjartan's avatar
Kjartan committed
489
490
491
  if ($blank) {
    $options[0] = $blank;
  }
Dries's avatar
 
Dries committed
492

Kjartan's avatar
Kjartan committed
493
494
495
496
  if ($tree) {
    foreach ($tree as $term) {
      if (!in_array($term->tid, $exclude)) {
        $options[$term->tid] = _taxonomy_depth($term->depth, '-').$term->name;
Dries's avatar
 
Dries committed
497
498
      }
    }
Kjartan's avatar
Kjartan committed
499
500
501
502
503
    if (!$blank && !$value) {
      // required but without a predefined value, so set first as predefined
      $value = $tree[0]->tid;
    }
  }
Dries's avatar
 
Dries committed
504

Kjartan's avatar
Kjartan committed
505
506
507
508
  if (count($options) > 0) {
    foreach ($options as $key=>$choice) {
      $select .= "<option value=\"$key\"". (is_array($value) ? (in_array($key, $value) ? " selected=\"selected\"" : "") : ($key == $value ? " selected=\"selected\"" : "")) .">". check_form($choice) ."</option>";
    }
Dries's avatar
 
Dries committed
509

Kjartan's avatar
Kjartan committed
510
511
    // min 8, possibly options/3 (set max too?)
    $size = max(8, round(count($options)) / 3);
Dries's avatar
 
Dries committed
512

Kjartan's avatar
Kjartan committed
513
    return form_item($title, "<select name=\"edit[$name][]\"". ($multiple ? " multiple size=\"$size\"" : "") . ($extra ? " $extra" : "") .">$select</select>", $description);
Dries's avatar
 
Dries committed
514
  }
Kjartan's avatar
Kjartan committed
515
}
Dries's avatar
 
Dries committed
516

Kjartan's avatar
Kjartan committed
517
518
519
function _taxonomy_depth($depth, $graphic = '--') {
  for ($n = 0; $n < $depth; $n++) {
    $result .= $graphic;
Dries's avatar
 
Dries committed
520
  }
Kjartan's avatar
Kjartan committed
521
522
  return $result;
}
Dries's avatar
 
Dries committed
523

Kjartan's avatar
Kjartan committed
524
525
526
function _prepare_update($data) {
  foreach ($data as $key => $value) {
    $q[] = "$key = '". check_query($value) ."'";
Dries's avatar
 
Dries committed
527
  }
Kjartan's avatar
Kjartan committed
528
529
530
  $result = implode(", ", $q);
  return $result;
}
Dries's avatar
 
Dries committed
531

Kjartan's avatar
Kjartan committed
532
533
534
535
536
537
538
function _prepare_insert($data, $stage) {
  if ($stage == 1) {
    $result = implode(", ", array_keys($data));
  }
  else {
    foreach (array_values($data) as $value) {
      $q[] = "'". check_query($value) ."'";
Dries's avatar
 
Dries committed
539
    }
Kjartan's avatar
Kjartan committed
540
    $result = implode(", ", $q);
Dries's avatar
 
Dries committed
541
  }
Kjartan's avatar
Kjartan committed
542
543
  return "($result)";
}
Dries's avatar
   
Dries committed
544

Kjartan's avatar
Kjartan committed
545
546
function taxonomy_page() {
  global $op;
Dries's avatar
   
Dries committed
547

Kjartan's avatar
Kjartan committed
548
549
550
551
552
553
  switch ($op) {
    case "feed":
      taxonomy_feed();
      break;
    default:
       // TODO: pretty display of all vocabularies
Dries's avatar
   
Dries committed
554
  }
Kjartan's avatar
Kjartan committed
555
}
Dries's avatar
   
Dries committed
556

Kjartan's avatar
Kjartan committed
557
/*
Dries's avatar
 
Dries committed
558
559
560
** admin
*/

Kjartan's avatar
Kjartan committed
561
562
563
564
565
566
567
568
569
function taxonomy_admin() {
  global $edit, $type, $op, $id, $tree;

  if (user_access("administer taxonomy")) {
    $links[] = la(t("add new vocabulary"), array("mod" => "taxonomy", "op" => "add", "type" => "vocabulary"));
    $links[] = la(t("overview"), array("mod" => "taxonomy"));
    $links[] = la(t("help"), array("mod" => "taxonomy", "op" => "help"));

    print "<small>". implode(" | ", $links) ."</small><hr>\n";
Dries's avatar
 
Dries committed
570

Kjartan's avatar
Kjartan committed
571
572
    switch ($op) {
      case "add":
Kjartan's avatar
Kjartan committed
573
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
574
          print taxonomy_form_vocabulary();
Kjartan's avatar
Kjartan committed
575
576
        }
        else {
Kjartan's avatar
Kjartan committed
577
          print taxonomy_form_term();
Kjartan's avatar
Kjartan committed
578
        }
Kjartan's avatar
Kjartan committed
579
580
        break;
      case "edit":
Kjartan's avatar
Kjartan committed
581
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
582
          print taxonomy_form_vocabulary(object2array(taxonomy_get_vocabulary($id)));
Kjartan's avatar
Kjartan committed
583
584
        }
        else {
Kjartan's avatar
Kjartan committed
585
          print taxonomy_form_term(object2array(taxonomy_get_term($id)));
Kjartan's avatar
Kjartan committed
586
        }
Kjartan's avatar
Kjartan committed
587
588
        break;
      case "preview":
Kjartan's avatar
Kjartan committed
589
        print taxonomy_form($id);
Kjartan's avatar
Kjartan committed
590
591
592
593
594
595
596
597
        break;
      case "help":
        print taxonomy_help();
        break;
      case "Delete":
        $edit["name"] = 0;
        // fall through:
      case "Submit":
Kjartan's avatar
Kjartan committed
598
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
599
          print status(taxonomy_save_vocabulary($edit));
Kjartan's avatar
Kjartan committed
600
601
        }
        else {
Kjartan's avatar
Kjartan committed
602
          print status(taxonomy_save_term($edit));
Kjartan's avatar
Kjartan committed
603
        }
Kjartan's avatar
Kjartan committed
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
        // fall through:
      default:
        print taxonomy_overview();
    }
  }
  else {
    print message_access();
  }
}

function taxonomy_help() {
  ?>
<h3>Background</h3>
  Classifying nodes allows for the organization of content into categories and
  subcategories of description. These categories can be used to organize and retrieve
  similarly described content. Drupal's <i>taxonomy.module</i> is an extremely flexible
  classification system that allows for multiple lists of categories for classification
  (controlled vocabularies) and offers the possibility of creating thesauri (controlled
  vocabularies that indicate the relationship of terms) and taxonomies (controlled
  vocabularies where relationships are indicated hierarchically). For details about
<a href="http://www.eleganthack.com/archives/002165.html#002165">classification
types</a> and insight into the development of <i>taxonomy.module</i>, see this
<a href="http://www.drupal.org/node.php?id=55">drupal.org discussion</a>.<br />
<h3>An Example Taxonomy - Foods</h3>
<p>Dairy <br />
  --Milk <br />
  Drink <br />
  --Alchohol <br />
  --Pop <br />
  --Milk<br />
  Meat <br />
  --Beef <br />
  --Chicken <br />
  --Lamb <br />
  Spices <br />
  --Sugar</p>
<p><b>Notes</b></p>
<ul>
  <li>The term <i>Milk</i> appears within both <i>Dairy</i> and <i>Drink</i>.
    This is an example of <i>Multiple Parents</i> for a term.</li>
  <li>The order of siblings (e.g. <i>Beef</i>, <i>Chicken</i>, <i>Lamb</i>) in
    the taxonomy may be controlled with the <i>Weight</i> parameter. </li>
</ul>
<h4></h4>
  <h3>Vocabularies</h3>
  When you create a controlled vocabulary you are creating a set of terms to use
  for describing content (known as descriptors in indexing lingo). Drupal allows
  you to describe each node of content (blog, story, etc.)
  using one or many of these terms. For simple implementations, you might
  create a set of categories without subcategories, similar to <a href="http://www.slashdot.com">Slashdot's</a> "Sections".
  For more complex implementations, you might create a hierarchical list of categories
  such as the example <i>Food</i> taxonomy above.

  <h4>Setting up a vocabulary</h4>
  <p>When you set up a controlled vocabulary, you will be asked to enter some descriptive
data and define the attributes of this vocabulary. For example, if you select
the <i>Hierarchy </i>option, you will be defining a taxonomy or a thesaurus. If
you select <i>Related Terms</i> option, you are allowing the definition of related
terms as in a thesaurus. Selecting <i>Multiple Select</i> will allow you to describe
a node using more than one term. That node will then appear in each term's page,
thus increasing the chance that a user will find it.</p>
  <i>Vocabulary name</i><br />
  Required. The name for this vocabulary. Example: <i>Dairy</i>.<br />
  <br />
Dries's avatar
   
Dries committed
668
  <i>Description</i><br />
Kjartan's avatar
Kjartan committed
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
  Optional. Description of the vocabulary, can be used by modules and feeds.<br />
  <br />
  <i>Types</i><br />
  Required. The list of node types you want to associate this vocabulary
  with. Some available types are: blog, book, forum, page, story.<br />
  <br />
  <i><a name="relatedterms"></a>Related Terms</i><br />
  Allows relationships between terms within this vocabulary. Think of these as
  <i>See also</i> references.<br />
  <br />
  <i><a name="hierarchy"></a>Hierarchy</i><br />
  Allows a tree-like taxonomy, as in our <i>Foods</i> example above<br />
  <br />
  <i>Multiple Select</i><br />
Allows nodes to be described using more than one term. Nodes may then appear on
multiple taxonomy pages.<br />
  <h4>Adding terms to a vocabulary</h4>
The options you see when adding a term to a vocabulary will depend on what you
selected for <i>Related Terms</i>, <i>Hierarchy </i>and <i>Multiple Select</i>
when you created the corrosponding vocabulary.<br />
  <br />
<i>Term name</i><br />
  Required. The name for this term. Example: <i>Milk</i><br />
  <br />
<i>Description</i><br />
Optional. Description of the term that may be used by modules and RSS feeds.
This is synonymous with a 'Scope note'.<br />
  <br />
<i><a name="parent"></a>Parent</i><br />
  Required. Select the term under which this term is a subset -- the branch of the hierarchy
  that this term belongs under. This is also known as the "Broader term" indicator
  used in thesauri.<br />
  <br />
<i><a name="synonyms"></a>Synonyms</i><br />
  Optional. Enter synonyms for this term, one synonym per line. Synonyms can be used for
  variant spellings, acronyms, and other terms that have the same meaning as the
  added term, but which are not explicitly listed in this thesaurus (i.e. <i>unauthorized
  terms</i>).<br />

<h3>Displaying Nodes Organized by Term(s)</h3>
<p>In order to view the nodes associated with a term or a collection of terms, you
  should browse to a properly formed URL. For example, see
  <a href="<?php print path_uri().drupal_url(array("mod" => "node", "or" => "1,2"), "module"); ?>"><?php print path_uri().drupal_url(array("mod" => "node", "or" => "1,2"), "module"); ?></a>.
  Taxonomy URLs always contain  a termID or list of termIDs at the end of the URL (aka <i>querystring</i>).
   You may learn the termID for a given term by hovering over that term in the <? echo la("Taxonomy Overview", array("mod" => "taxonomy")) ?> page in the Admin and noting the number after the querystring parameter called <i>tid</i>.
   If you wish to see nodes from a collection of termIDs, separate each termID with a comma.
   Also, the name of the querystring parameter may be <i>or</i> or <i>and</i>.
   <i>or</i> shows nodes which appear in <b>any</b> of the termIDs while <i>and</i> shows nodes in <b>all</b> the specified termIDs.
    Thus, <i>or</i> is less specific than <i>and</i>.
</p>

<h3>RSS Feeds</h3>
<p>Every term, or collection of terms, provides an <a href="http://backend.userland.com/stories/rss091">RSS</a> feed to which interested
  users may subscribe. The URL format for an sample RSS feed is
    <a href="<?php print path_uri().drupal_url(array("mod" => "node", "op" => "feed", "or" => "1,2"), "module"); ?>"><?php print path_uri().drupal_url(array("mod" => "node", "op" => "feed", "or" => "1,2"), "module"); ?></a>.</p>
    <?php
}
Dries's avatar
 
Dries committed
726
?>