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

4
function taxonomy_system($field){
Kjartan's avatar
Kjartan committed
5
  $system["description"] = t("Enables the organization of content into categories.");
6
7
8
  return $system[$field];
}

Kjartan's avatar
Kjartan committed
9
10
function taxonomy_feed() {
  global $id, $or, $and, $type;
Dries's avatar
   
Dries committed
11

Kjartan's avatar
Kjartan committed
12
13
14
15
16
17
18
  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
19
      }
Kjartan's avatar
Kjartan committed
20
21
22
23
24
25
      $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
26
      }
27
      $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
28
29
30
31
32
      $term = taxonomy_get_term($and);
    }
    else {
      return node_feed();
    }
Dries's avatar
   
Dries committed
33

Kjartan's avatar
Kjartan committed
34
35
36
    $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
37

Kjartan's avatar
Kjartan committed
38
    node_feed($result, $channel);
Dries's avatar
   
Dries committed
39
  }
Kjartan's avatar
Kjartan committed
40
}
Dries's avatar
   
Dries committed
41

Kjartan's avatar
Kjartan committed
42
43
44
function taxonomy_perm() {
  return array("administer taxonomy");
}
Dries's avatar
 
Dries committed
45

Kjartan's avatar
Kjartan committed
46
47
48
function taxonomy_link($type) {
  if ($type == "admin" && user_access("administer taxonomy")) {
    $links[] = la(t("taxonomy"), array("mod" => "taxonomy"));
Dries's avatar
 
Dries committed
49
50
  }

Kjartan's avatar
Kjartan committed
51
52
53
  return $links ? $links : array();
}

Dries's avatar
 
Dries committed
54
55
56
57
/*
** admin pages (form, save, overview)
*/

Kjartan's avatar
Kjartan committed
58
59
60
61
function taxonomy_form_vocabulary($edit = array()) {
  foreach (module_list() as $name) {
    if (module_hook($name, "node")) {
      $nodetypes[$name] = $name;
Dries's avatar
 
Dries committed
62
    }
Kjartan's avatar
Kjartan committed
63
  }
Dries's avatar
   
Dries committed
64

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

Kjartan's avatar
Kjartan committed
75
  if ($edit["vid"]) {
Dries's avatar
   
Dries committed
76
    $form .= form_submit(t("Delete"));
Kjartan's avatar
Kjartan committed
77
    $form .= form_hidden("vid", $edit["vid"]);
Dries's avatar
 
Dries committed
78
79
  }

Kjartan's avatar
Kjartan committed
80
81
  return form($form);
}
Kjartan's avatar
Kjartan committed
82

Kjartan's avatar
Kjartan committed
83
84
85
86
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"]) {
Dries's avatar
   
Dries committed
87
    db_query("UPDATE vocabulary SET ". _prepare_update($data) ." WHERE vid = '". check_query($edit["vid"]) ."'");
Dries's avatar
   
Dries committed
88
    return t("update vocabulary '%name'.", array("%name" => $edit["name"]));
Dries's avatar
 
Dries committed
89
  }
Kjartan's avatar
Kjartan committed
90
  else if ($edit["vid"]) {
Dries's avatar
   
Dries committed
91
    return taxonomy_del_vocabulary($edit["vid"]);
Kjartan's avatar
Kjartan committed
92
93
94
  }
  else {
    db_query("INSERT INTO vocabulary ". _prepare_insert($data, 1) ." VALUES ". _prepare_insert($data, 2));
Dries's avatar
   
Dries committed
95
    return t("created new vocabulary '%name'.", array("%name" => $edit["name"]));
Kjartan's avatar
Kjartan committed
96
97
  }
}
Dries's avatar
 
Dries committed
98

Kjartan's avatar
Kjartan committed
99
function taxonomy_del_vocabulary($vid) {
Dries's avatar
   
Dries committed
100
101
  $vocabulary = taxonomy_get_vocabulary($vid);

Dries's avatar
   
Dries committed
102
103
  db_query("DELETE FROM vocabulary WHERE vid = '%d'", $vid);
  $result = db_query("SELECT tid FROM term_data WHERE vid = '%d'", $vid);
Kjartan's avatar
Kjartan committed
104
105
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries's avatar
 
Dries committed
106
  }
Dries's avatar
   
Dries committed
107
108
109
110
111
112
113
114
115
116
117
118
119
120

  return t("deleted vocabulary '%name'.", array("%name" => $vocabulary->name));
}

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

  $form .= form_hidden("confirm", 1);
  $form .= form_hidden("type", "vocabulary");
  $form .= form_hidden("vid", $vid);
  $form .= form_submit(t("Delete"));
  $form .= form_submit(t("Cancel"));

  return form(form_item(t("Delete vocabulary '%name'", array("%name" => $vocabulary->name)), $form, t("Are you sure you want to delete the vocabulary and all its terms?")));
Kjartan's avatar
Kjartan committed
121
}
Dries's avatar
 
Dries committed
122

Kjartan's avatar
Kjartan committed
123
124
125
126
127
128
function taxonomy_form_term($edit = array()) {
  global $vocabulary_id;
  if (!$vocabulary_id) {
    $vocabulary_id = $edit["vid"];
  }
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
Dries's avatar
   
Dries committed
129
130
  $form = form_textfield(t("Term name"), "name", $edit["name"], 50, 64, t("Required") . ". " . t("The name for this term.  Example: 'Linux'."));
  $form .= form_textarea(t("Description"), "description", $edit["description"], 60, 5, t("Optional") . ". " . t("A description of the term."));
Dries's avatar
 
Dries committed
131

Kjartan's avatar
Kjartan committed
132
  if ($vocabulary->relations) {
Dries's avatar
   
Dries committed
133
    $form .= _taxonomy_term_select(t("Related terms"), "relations", array_keys(taxonomy_get_related($edit["tid"])), $vocabulary_id, t("Optional") . ". ", 1, "<" . t("none") . ">", array($edit["tid"]));
Kjartan's avatar
Kjartan committed
134
  }
Dries's avatar
 
Dries committed
135
136


Kjartan's avatar
Kjartan committed
137
138
  if ($vocabulary->hierarchy) {
    $parent = array_keys(taxonomy_get_parents($edit["tid"]));
Dries's avatar
   
Dries committed
139
    $children = taxonomy_get_tree($vocabulary_id, $edit["tid"]);
Kjartan's avatar
Kjartan committed
140
141
142
143
    // you can't be son of yourself or your children
    $exclude = array_keys($children);
    $exclude[] = $edit["tid"];
    if ($vocabulary->hierarchy == 1) {
Dries's avatar
   
Dries committed
144
      $form .= _taxonomy_term_select(t("Parent"), "parent", $parent, $vocabulary_id, t("Required") . ". " . la(t("Parent term"), array("mod" => "taxonomy", "op" => "help"), "parent") .".", 0, "<" . t("root") . ">", $exclude);
Dries's avatar
 
Dries committed
145
    }
Kjartan's avatar
Kjartan committed
146
    elseif ($vocabulary->hierarchy == 2) {
Dries's avatar
   
Dries committed
147
      $form .= _taxonomy_term_select(t("Parents"), "parent", $parent, $vocabulary_id, t("Required") . ". ". la(t("Parent terms"), array("mod" => "taxonomy", "op" => "help"), "parent") .".", 1, "<" . t("root") . ">", $exclude);
Dries's avatar
 
Dries committed
148
    }
Kjartan's avatar
Kjartan committed
149
  }
Dries's avatar
 
Dries committed
150

Dries's avatar
   
Dries committed
151
152
  $form .= form_textarea(t("Synonyms"), "synonyms", implode("\n", taxonomy_get_synonyms($edit["tid"])), 30, 5, t("Optional") . ". ". t(la("Synonyms", array("mod" => "taxonomy", "op" => "help"), "synonyms") ." of this term, one synonym per line."));
  $form .= form_weight(t("Weight"), "weight", $edit["weight"], 10, t("Optional. In listings, the heavier terms will sink and the lighter terms will be positioned nearer the top."));
Kjartan's avatar
Kjartan committed
153
  $form .= form_hidden("vid", $vocabulary->vid);
Dries's avatar
   
Dries committed
154
  $form .= form_submit(t("Submit"));
Kjartan's avatar
Kjartan committed
155
156

  if ($edit["tid"]) {
Dries's avatar
   
Dries committed
157
    $form .= form_submit(t("Delete"));
Kjartan's avatar
Kjartan committed
158
    $form .= form_hidden("tid", $edit["tid"]);
Dries's avatar
 
Dries committed
159
160
  }

Kjartan's avatar
Kjartan committed
161
162
  return form($form);
}
Dries's avatar
 
Dries committed
163

Kjartan's avatar
Kjartan committed
164
165
166
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
167

Dries's avatar
   
Dries committed
168
    db_query("UPDATE term_data SET ". _prepare_update($data) ." WHERE tid = '%d'", $edit["tid"]);
Dries's avatar
   
Dries committed
169
    $message = t("Term <b>%a</b> updated.", array("%a" => $edit["name"]));
Kjartan's avatar
Kjartan committed
170
171
  }
  else if ($edit["tid"]) {
Dries's avatar
   
Dries committed
172
    return taxonomy_del_term($edit["tid"]);
Kjartan's avatar
Kjartan committed
173
174
  }
  else {
Dries's avatar
   
Dries committed
175
    $edit["tid"] = db_next_id("term_data");
Kjartan's avatar
Kjartan committed
176
177
    $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
178
179

    $message = t("created new term '%name'.", array("%name" => $edit["name"]));
Kjartan's avatar
Kjartan committed
180
  }
Dries's avatar
 
Dries committed
181

Kjartan's avatar
Kjartan committed
182
  // relations (seem very powerful, but I have to understand it completely)
Dries's avatar
   
Dries committed
183
  db_query("DELETE FROM term_relation WHERE tid1 = '%d' OR tid2 = '%d'", $edit["tid"], $edit["tid"]);
Kjartan's avatar
Kjartan committed
184
185
186
  if ($edit["relations"]) {
    foreach ($edit["relations"] as $related_id) {
      if ($related_id != 0) {
Dries's avatar
   
Dries committed
187
        db_query("INSERT INTO term_relation (tid1, tid2) VALUES ('%d', '%d')", $edit["tid"], $related_id);
Dries's avatar
 
Dries committed
188
      }
Kjartan's avatar
Kjartan committed
189
    }
Kjartan's avatar
Kjartan committed
190
  }
Dries's avatar
 
Dries committed
191

Kjartan's avatar
Kjartan committed
192
  // hierarchy
Dries's avatar
   
Dries committed
193
  db_query("DELETE FROM term_hierarchy WHERE tid = '%d'", $edit["tid"]);
Kjartan's avatar
Kjartan committed
194
195
196
197
198
  if (!isset($edit["parent"])) {
    $edit["parent"] = 0;
  }
  if (is_array($edit["parent"])) {
    foreach ($edit["parent"] as $parent) {
Dries's avatar
   
Dries committed
199
      db_query("INSERT INTO term_hierarchy (tid, parent) VALUES ('%d', '%d')", $edit["tid"], $parent);
Dries's avatar
 
Dries committed
200
    }
Kjartan's avatar
Kjartan committed
201
202
  }
  else {
Dries's avatar
   
Dries committed
203
    db_query("INSERT INTO term_hierarchy (tid, parent) VALUES ('%d', '%d')", $edit["tid"], $edit["parent"][0]);
Dries's avatar
 
Dries committed
204
205
  }

Kjartan's avatar
Kjartan committed
206
  // synonyms (very cool idea indeed)
Dries's avatar
   
Dries committed
207
  db_query("DELETE FROM term_synonym WHERE tid = '%d'", $edit["tid"]);
Kjartan's avatar
Kjartan committed
208
209
  if ($edit["synonyms"]) {
    foreach (explode ("\n", $edit["synonyms"]) as $synonym) {
Dries's avatar
   
Dries committed
210
      db_query("INSERT INTO term_synonym (tid, name) VALUES ('%d', '%s')", $edit["tid"], chop($synonym));
Kjartan's avatar
Kjartan committed
211
    }
Dries's avatar
 
Dries committed
212
  }
Dries's avatar
   
Dries committed
213
214

  return $message;
Kjartan's avatar
Kjartan committed
215
}
Dries's avatar
 
Dries committed
216

Kjartan's avatar
Kjartan committed
217
function taxonomy_del_term($tid) {
Dries's avatar
   
Dries committed
218
219
  $term = taxonomy_get_term($tid);

Dries's avatar
   
Dries committed
220
221
222
223
224
  db_query("DELETE FROM term_data WHERE tid = '%d'", $tid);
  db_query("DELETE FROM term_hierarchy WHERE tid = '%d'", $tid);
  db_query("DELETE FROM term_relation WHERE tid1 = '%d' OR tid2 = '%d'", $tid, $tid);
  db_query("DELETE FROM term_synonym WHERE tid = '%d'", $tid);
  db_query("DELETE FROM term_node WHERE tid = '%d'", $tid);
Dries's avatar
   
Dries committed
225
226
227
228
229
230
231
232
233
234
235
236
237
238

  return t("deleted term '%name'.", array("%name" => $term->name));
}

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

  $form .= form_hidden("confirm", 1);
  $form .= form_hidden("type", "term");
  $form .= form_hidden("tid", $tid);
  $form .= form_submit(t("Delete"));
  $form .= form_submit(t("Cancel"));

  return form(form_item(t("Delete term '%name'", array("%name" => $term->name)), $form, t("Are you sure you want to delete the term?")));
Kjartan's avatar
Kjartan committed
239
}
Dries's avatar
 
Dries committed
240

Kjartan's avatar
Kjartan committed
241
242
function taxonomy_overview() {
  global $tree;
Dries's avatar
 
Dries committed
243

Dries's avatar
   
Dries committed
244
  $output .= "<h3>" . t("Vocabularies overview") . "</h3>";
Kjartan's avatar
Kjartan committed
245
  $output .= "<table border=\"1\" cellpadding=\"2\" cellspacing=\"2\">\n";
Dries's avatar
   
Dries committed
246
  $output .= " <tr><th>" . t("name") . "</th><th>" . t("node types") . "</th><th>" . t("operations") . "</th></tr>\n";
Dries's avatar
   
Dries committed
247

Kjartan's avatar
Kjartan committed
248
249
250
251
252
253
  $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
254

Dries's avatar
   
Dries committed
255
    $output .= " <tr><td>". check_output($vocabulary->name) ."</td><td align=\"center\">". check_output($vocabulary->types) ."</td><td>". implode(" | ", $links) ."</td></tr>\n";
Kjartan's avatar
Kjartan committed
256

Dries's avatar
   
Dries committed
257
    $tree = taxonomy_get_tree($vocabulary->vid);
Kjartan's avatar
Kjartan committed
258
259
260
261
    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
262
      }
Kjartan's avatar
Kjartan committed
263
      $output .= "</td></tr></table></td></tr>\n";
Dries's avatar
 
Dries committed
264
265
    }
  }
Kjartan's avatar
Kjartan committed
266
  $output .= "</table>\n";
Dries's avatar
 
Dries committed
267

Kjartan's avatar
Kjartan committed
268
269
270
271
272
273
274
275
276
277
278
  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";
Dries's avatar
   
Dries committed
279
    $blank = "<" . t("none") . ">";
Kjartan's avatar
Kjartan committed
280
  }
Dries's avatar
   
Dries committed
281

Kjartan's avatar
Kjartan committed
282
  if ($vocabulary->multiple) {
Dries's avatar
   
Dries committed
283
    $description = t("You $verb choose one or more terms for this node.");
Kjartan's avatar
Kjartan committed
284
    $multiple = 1;
Dries's avatar
 
Dries committed
285
  }
Kjartan's avatar
Kjartan committed
286
  else {
Dries's avatar
   
Dries committed
287
    $description = t("You $verb choose one term for this node.");
Kjartan's avatar
Kjartan committed
288
289
290
291
    $multiple = 0;
  }
  return _taxonomy_term_select($vocabulary->name, "taxonomy", $value, $vocabulary_id, $description, $multiple, $blank);
}
Dries's avatar
 
Dries committed
292
293
294
295
296

/*
** API functions
*/

Kjartan's avatar
Kjartan committed
297
298
299
300
301
302
303
304
305
306
307
// 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
308
  }
Kjartan's avatar
Kjartan committed
309
310
  return $vocabularies;
}
Dries's avatar
 
Dries committed
311

Kjartan's avatar
Kjartan committed
312
313
314
315
316
// 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
317
318
    }
    else {
Kjartan's avatar
Kjartan committed
319
      $terms = 0;
Dries's avatar
 
Dries committed
320
    }
Kjartan's avatar
Kjartan committed
321
322
323
324
  }
  else {
    $terms = $node->taxonomy;
  }
Dries's avatar
 
Dries committed
325

Kjartan's avatar
Kjartan committed
326
327
328
  $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
329
  }
Kjartan's avatar
Kjartan committed
330
331
  return $result ? $result : array();
}
Dries's avatar
 
Dries committed
332

Kjartan's avatar
Kjartan committed
333
334
335
// 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
336

Dries's avatar
   
Dries committed
337
  return db_result(db_query("SELECT COUNT(n.nid) FROM node n WHERE n.nid = '%d' AND ((n.body LIKE '%%%s%%') OR (n.body LIKE '%%%s%%'))", $nid, $term_name, $term_name));
Kjartan's avatar
Kjartan committed
338
339
340
341
}

// 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") {
Dries's avatar
   
Dries committed
342
  $result = db_query("SELECT t.* FROM term_data t, term_node r WHERE t.tid = r.tid AND t.vid = '%d' AND r.nid = '%d' ORDER BY weight", $vid, $nid);
Kjartan's avatar
Kjartan committed
343
344
345
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries's avatar
 
Dries committed
346
  }
Kjartan's avatar
Kjartan committed
347
348
349
350
351
352
  return $terms;
}

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

Dries's avatar
   
Dries committed
354
  if (!isset($terms[$nid])) {
Dries's avatar
   
Dries committed
355
    $result = db_query("SELECT t.* FROM term_data t, term_node r WHERE r.tid = t.tid AND r.nid = '%d' ORDER BY weight", $nid);
Kjartan's avatar
Kjartan committed
356
    $terms[$nid] = array();
Dries's avatar
 
Dries committed
357
    while ($term = db_fetch_object($result)) {
Kjartan's avatar
Kjartan committed
358
      $terms[$nid][$term->$key] = $term;
Dries's avatar
 
Dries committed
359
360
    }
  }
Kjartan's avatar
Kjartan committed
361
362
  return $terms[$nid];
}
Dries's avatar
 
Dries committed
363

Kjartan's avatar
Kjartan committed
364
365
366
// save terms of a node
function taxonomy_node_save($nid, $terms) {
  taxonomy_node_delete($nid);
Dries's avatar
 
Dries committed
367

Kjartan's avatar
Kjartan committed
368
  if ($terms) {
Dries's avatar
   
Dries committed
369
370
    foreach ($terms as $term) {
      db_query("INSERT INTO term_node (nid, tid) VALUES ('%d', '%d')", $nid, $term);
Dries's avatar
 
Dries committed
371
372
    }
  }
Kjartan's avatar
Kjartan committed
373
}
Dries's avatar
 
Dries committed
374

Kjartan's avatar
Kjartan committed
375
376
// clean up terms
function taxonomy_node_delete($nid) {
Dries's avatar
   
Dries committed
377
  db_query("DELETE FROM term_node WHERE nid = '%d'", $nid);
Kjartan's avatar
Kjartan committed
378
}
Dries's avatar
 
Dries committed
379

Kjartan's avatar
Kjartan committed
380
381
382
// relations: return array of related terms
function taxonomy_get_related($tid, $key = "tid") {
  if ($tid) {
383
    $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", $tid, $tid, $tid);
Kjartan's avatar
Kjartan committed
384
385
386
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries's avatar
 
Dries committed
387
    }
Kjartan's avatar
Kjartan committed
388
    return $related;
Dries's avatar
 
Dries committed
389
  }
Kjartan's avatar
Kjartan committed
390
391
  else {
    return array();
Dries's avatar
 
Dries committed
392
  }
Kjartan's avatar
Kjartan committed
393
}
Dries's avatar
 
Dries committed
394

Kjartan's avatar
Kjartan committed
395
396
397
// hierarchy: get parent terms
function taxonomy_get_parents($tid, $key = "tid") {
  if ($tid) {
Dries's avatar
   
Dries committed
398
    $result = db_query("SELECT t.* FROM term_hierarchy h, term_data t WHERE h.parent = t.tid AND h.tid = '%d' ORDER BY weight, name", $tid);
Kjartan's avatar
Kjartan committed
399
400
401
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries's avatar
   
Dries committed
402
    }
Kjartan's avatar
Kjartan committed
403
    return $parents;
Dries's avatar
 
Dries committed
404
  }
Kjartan's avatar
Kjartan committed
405
406
407
408
  else {
    return array();
  }
}
Dries's avatar
 
Dries committed
409

Kjartan's avatar
Kjartan committed
410
411
412
// hierarchy: get children
function taxonomy_get_children($tid, $vid = 0, $key = "tid") {
  if ($vid) {
Dries's avatar
   
Dries committed
413
    $result = db_query("SELECT t.* FROM term_hierarchy h, term_data t WHERE t.vid = '%d' AND h.tid = t.tid AND h.parent = '%d' ORDER BY weight, name", $vid, $tid);
Dries's avatar
 
Dries committed
414
  }
Kjartan's avatar
Kjartan committed
415
  else {
Dries's avatar
   
Dries committed
416
    $result = db_query("SELECT t.* FROM term_hierarchy h, term_data t WHERE h.tid = t.tid AND parent = '%d' ORDER BY weight", $tid);
Kjartan's avatar
Kjartan committed
417
418
419
420
421
422
423
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries's avatar
 
Dries committed
424

Kjartan's avatar
Kjartan committed
425
// hierarchy: get whole family, with tid, parent and depth; useful to show
Dries's avatar
   
Dries committed
426
427
function taxonomy_get_tree($vocabulary_id, $parent = 0, $depth = -1, $key = "tid") {
  static $children, $parents, $terms, $tree;
Kjartan's avatar
Kjartan committed
428
  if ($depth == -1) {
Dries's avatar
 
Dries committed
429
    $children = array();
Dries's avatar
   
Dries committed
430
431
    $parents = array();
    $terms = array();
Kjartan's avatar
Kjartan committed
432
    $tree = array();
Dries's avatar
 
Dries committed
433
  }
Dries's avatar
   
Dries committed
434

Kjartan's avatar
Kjartan committed
435
  $depth++;
Dries's avatar
   
Dries committed
436
437
438
439
440
441
442

  if (!count($children)) {
    $result = db_query("SELECT t.*, parent FROM term_data t, term_hierarchy h WHERE t.tid = h.tid AND t.vid = '%d' ORDER BY weight, name", $vocabulary_id);
    while ($term = db_fetch_object($result)) {
      $children[$term->parent][] = $term->tid;
      $parents[$term->tid][] = $term->parent;
      $terms[$term->tid] = $term;
Dries's avatar
 
Dries committed
443
444
    }
  }
Dries's avatar
   
Dries committed
445
446
447
448
449
450
451
452
453
454

  if ($children[$parent]) {
    foreach ($children[$parent] as $child) {
      $terms[$child]->depth = $depth;
      unset($terms[$child]->parent); // this would show just one parent
      $terms[$child]->parents = $parents[$child];
      $tree[] = $terms[$child];

      taxonomy_get_tree($vocabulary_id, $child, $depth, $key);
    }
Kjartan's avatar
Kjartan committed
455
  }
Dries's avatar
   
Dries committed
456
457

  return $tree;
Kjartan's avatar
Kjartan committed
458
}
Dries's avatar
 
Dries committed
459

Kjartan's avatar
Kjartan committed
460
461
462
// synonyms: return array of synonyms
function taxonomy_get_synonyms($tid) {
  if ($tid) {
Dries's avatar
   
Dries committed
463
    $result = db_query("SELECT name FROM term_synonym WHERE tid = '%d'", $tid);
Kjartan's avatar
Kjartan committed
464
465
    while ($synonym = db_fetch_array($result)) {
      $synonyms[] = $synonym["name"];
Dries's avatar
 
Dries committed
466
    }
Kjartan's avatar
Kjartan committed
467
    return $synonyms ? $synonyms : array();
Dries's avatar
 
Dries committed
468
  }
Kjartan's avatar
Kjartan committed
469
470
  else {
    return array();
Dries's avatar
   
Dries committed
471
  }
Kjartan's avatar
Kjartan committed
472
}
Dries's avatar
   
Dries committed
473

Kjartan's avatar
Kjartan committed
474
475
476
477
// 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
478

Kjartan's avatar
Kjartan committed
479
// given a term id, count number of nodes in it
Dries's avatar
   
Dries committed
480
function taxonomy_term_count_nodes($tid, $type = 0) {
Kjartan's avatar
Kjartan committed
481
  static $count;
Dries's avatar
   
Dries committed
482

Dries's avatar
   
Dries committed
483
484
485
  if (!isset($count[$type])) {
    // $type == 0 always evaluates true is $type is a string
    if (is_numeric($type)) {
Dries's avatar
   
Dries committed
486
      $result = db_query("SELECT t.tid, COUNT(*) AS c FROM term_node t GROUP BY t.tid");
Dries's avatar
   
Dries committed
487
488
    }
    else {
Dries's avatar
   
Dries committed
489
      $result = db_query("SELECT t.tid, COUNT(*) AS c FROM term_node t, node n WHERE t.nid = n.nid AND n.type = '%s' GROUP BY t.tid", $type);
Dries's avatar
   
Dries committed
490
    }
Kjartan's avatar
Kjartan committed
491
    while ($term = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
492
      $count[$type][$term->tid] = $term->c;
Dries's avatar
   
Dries committed
493
494
495
    }
  }

Kjartan's avatar
Kjartan committed
496
  foreach (_taxonomy_term_children($tid) as $c) {
Dries's avatar
   
Dries committed
497
    $children_count += taxonomy_term_count_nodes($c, $type);
Kjartan's avatar
Kjartan committed
498
  }
Dries's avatar
   
Dries committed
499
  return $count[$type][$tid] + $children_count;
Kjartan's avatar
Kjartan committed
500
501
502
503
504
}

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

Dries's avatar
   
Dries committed
506
  if (!isset($children)) {
Kjartan's avatar
Kjartan committed
507
508
509
    $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
510
    }
Dries's avatar
 
Dries committed
511
  }
Kjartan's avatar
Kjartan committed
512
513
  return $children[$tid] ? $children[$tid] : array();
}
Dries's avatar
 
Dries committed
514

Kjartan's avatar
Kjartan committed
515
516
function taxonomy_get_vocabulary($vid) {
  // simple cache using a static var?
Dries's avatar
   
Dries committed
517
  return db_fetch_object(db_query("SELECT * FROM vocabulary WHERE vid = '%d'", $vid));
Kjartan's avatar
Kjartan committed
518
}
Dries's avatar
 
Dries committed
519

Kjartan's avatar
Kjartan committed
520
521
function taxonomy_get_term($tid) {
  // simple cache using a static var?
Dries's avatar
   
Dries committed
522
  return db_fetch_object(db_query("SELECT * FROM term_data WHERE tid = '%d'", $tid));
Kjartan's avatar
Kjartan committed
523
}
Dries's avatar
 
Dries committed
524
525
526
527
528

/*
** service functions
*/

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

Kjartan's avatar
Kjartan committed
532
533
534
  if ($blank) {
    $options[0] = $blank;
  }
Dries's avatar
 
Dries committed
535

Kjartan's avatar
Kjartan committed
536
537
538
539
  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
540
541
      }
    }
Kjartan's avatar
Kjartan committed
542
543
544
545
546
    if (!$blank && !$value) {
      // required but without a predefined value, so set first as predefined
      $value = $tree[0]->tid;
    }
  }
Dries's avatar
 
Dries committed
547

Kjartan's avatar
Kjartan committed
548
549
550
551
  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
552

Dries's avatar
   
Dries committed
553
    $size = min(12, count($options));
Dries's avatar
 
Dries committed
554

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

Kjartan's avatar
Kjartan committed
559
560
561
function _taxonomy_depth($depth, $graphic = '--') {
  for ($n = 0; $n < $depth; $n++) {
    $result .= $graphic;
Dries's avatar
 
Dries committed
562
  }
Kjartan's avatar
Kjartan committed
563
564
  return $result;
}
Dries's avatar
 
Dries committed
565

Kjartan's avatar
Kjartan committed
566
567
568
function _prepare_update($data) {
  foreach ($data as $key => $value) {
    $q[] = "$key = '". check_query($value) ."'";
Dries's avatar
 
Dries committed
569
  }
Kjartan's avatar
Kjartan committed
570
571
572
  $result = implode(", ", $q);
  return $result;
}
Dries's avatar
 
Dries committed
573

Kjartan's avatar
Kjartan committed
574
575
576
577
578
579
580
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
581
    }
Kjartan's avatar
Kjartan committed
582
    $result = implode(", ", $q);
Dries's avatar
 
Dries committed
583
  }
Kjartan's avatar
Kjartan committed
584
585
  return "($result)";
}
Dries's avatar
   
Dries committed
586

Kjartan's avatar
Kjartan committed
587
588
function taxonomy_page() {
  global $op;
Dries's avatar
   
Dries committed
589

Kjartan's avatar
Kjartan committed
590
591
592
593
594
595
  switch ($op) {
    case "feed":
      taxonomy_feed();
      break;
    default:
       // TODO: pretty display of all vocabularies
Dries's avatar
   
Dries committed
596
  }
Kjartan's avatar
Kjartan committed
597
}
Dries's avatar
   
Dries committed
598

Kjartan's avatar
Kjartan committed
599
/*
Dries's avatar
 
Dries committed
600
601
602
** admin
*/

Kjartan's avatar
Kjartan committed
603
function taxonomy_admin() {
Dries's avatar
   
Dries committed
604
  global $edit, $type, $op, $id, $theme;
Kjartan's avatar
Kjartan committed
605
606
607
608
609
610
611

  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
612

Kjartan's avatar
Kjartan committed
613
614
    switch ($op) {
      case "add":
Kjartan's avatar
Kjartan committed
615
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
616
          print taxonomy_form_vocabulary();
Kjartan's avatar
Kjartan committed
617
618
        }
        else {
Kjartan's avatar
Kjartan committed
619
          print taxonomy_form_term();
Kjartan's avatar
Kjartan committed
620
        }
Kjartan's avatar
Kjartan committed
621
622
        break;
      case "edit":
Kjartan's avatar
Kjartan committed
623
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
624
          print taxonomy_form_vocabulary(object2array(taxonomy_get_vocabulary($id)));
Kjartan's avatar
Kjartan committed
625
626
        }
        else {
Kjartan's avatar
Kjartan committed
627
          print taxonomy_form_term(object2array(taxonomy_get_term($id)));
Kjartan's avatar
Kjartan committed
628
        }
Kjartan's avatar
Kjartan committed
629
630
        break;
      case "preview":
Kjartan's avatar
Kjartan committed
631
        print taxonomy_form($id);
Kjartan's avatar
Kjartan committed
632
633
634
635
        break;
      case "help":
        print taxonomy_help();
        break;
Dries's avatar
   
Dries committed
636
      case t("Delete"):
Dries's avatar
   
Dries committed
637
638
639
640
641
642
643
644
645
646
647
648
649
        if (!$edit["confirm"]) {
          if ($type == "vocabulary") {
            echo _taxonomy_confirm_del_vocabulary($edit["vid"]);
          }
          else {
            echo _taxonomy_confirm_del_term($edit["tid"]);
          }
          break;
        }
        else {
          $edit["name"] = 0;
          // fall through:
        }
Dries's avatar
   
Dries committed
650
      case t("Submit"):
Kjartan's avatar
Kjartan committed
651
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
652
          print status(taxonomy_save_vocabulary($edit));
Kjartan's avatar
Kjartan committed
653
654
        }
        else {
Kjartan's avatar
Kjartan committed
655
          print status(taxonomy_save_term($edit));
Dries's avatar
   
Dries committed
656
657
658
659
660
661
          if (!$edit["tid"]) {
            // if INSERT show form again
            print taxonomy_form_term();
            break;
          }
          // else (UPDATE or DELETE) fall through
Kjartan's avatar
Kjartan committed
662
        }
Kjartan's avatar
Kjartan committed
663
664
665
666
667
668
669
670
671
672
673
674
        // fall through:
      default:
        print taxonomy_overview();
    }
  }
  else {
    print message_access();
  }
}

function taxonomy_help() {
  ?>
Dries's avatar
   
Dries committed
675
676
677
678
679
680

  <h3>Background</h3>
  <p>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>.</p>

  <h3>An example taxonomy: food</h3>
  <p>Dairy <br />
Kjartan's avatar
Kjartan committed
681
682
683
684
685
686
687
688
689
690
691
  --Milk <br />
  Drink <br />
  --Alchohol <br />
  --Pop <br />
  --Milk<br />
  Meat <br />
  --Beef <br />
  --Chicken <br />
  --Lamb <br />
  Spices <br />
  --Sugar</p>
Dries's avatar
   
Dries committed
692
693
694
695
696
697
  <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>nmultiple 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>

Kjartan's avatar
Kjartan committed
698
  <h3>Vocabularies</h3>
Dries's avatar
   
Dries committed
699
  <p>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.</p>
Kjartan's avatar
Kjartan committed
700
701

  <h4>Setting up a vocabulary</h4>
Dries's avatar
   
Dries committed
702
703
704
  <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 />
Kjartan's avatar
Kjartan committed
705
  <br />
Dries's avatar
   
Dries committed
706
  <i>Description</i><br />Optional. Description of the vocabulary, can be used by modules and feeds.<br />
Kjartan's avatar
Kjartan committed
707
  <br />
Dries's avatar
   
Dries committed
708
  <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 />
Kjartan's avatar
Kjartan committed
709
  <br />
Dries's avatar
   
Dries committed
710
  <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 />
Kjartan's avatar
Kjartan committed
711
  <br />
Dries's avatar
   
Dries committed
712
  <i><a name="hierarchy"></a>Hierarchy</i><br />Allows a tree-like taxonomy, as in our <i>Foods</i> example above<br />
Kjartan's avatar
Kjartan committed
713
  <br />
Dries's avatar
   
Dries committed
714
715
  <i>Multiple select</i><br />Allows nodes to be described using more than one term. Nodes may then appear on multiple taxonomy pages.<br />

Kjartan's avatar
Kjartan committed
716
  <h4>Adding terms to a vocabulary</h4>
Dries's avatar
   
Dries committed
717
718
719
<p>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.</p>

  <i>Term name</i><br />Required. The name for this term. Example: <i>Milk</i><br />
Kjartan's avatar
Kjartan committed
720
  <br />
Dries's avatar
   
Dries committed
721
  <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 />
Kjartan's avatar
Kjartan committed
722
  <br />
Dries's avatar
   
Dries committed
723
  <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 />
Kjartan's avatar
Kjartan committed
724
  <br />
Dries's avatar
   
Dries committed
725
726
727
  <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>
Dries's avatar
   
Dries committed
728
  <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 term ID or list of term IDs at the end of the URL (aka <i>querystring</i>). You may learn the term ID for a given term by hovering over that term in the <?php 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 term IDs, separate each term ID 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 term IDs while <i>and</i> shows nodes in <b>all</b> the specified term IDs.  Thus, <i>or</i> is less specific than <i>and</i>.</p>
Dries's avatar
   
Dries committed
729
730
731
732

  <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
Kjartan's avatar
Kjartan committed
733
}
Dries's avatar
   
Dries committed
734
?>