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

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
72
73
  $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"));
  $form .= form_textfield(t("Weight"), "weight", $edit["weight"], 3, 3, t("Optional") . ". " . t("In listings, the heavier vocabularies will sink and the lighter vocabularies will be positioned nearer the top."));
  $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
  }
Kjartan's avatar
Kjartan committed
89
90
91
92
93
94
95
  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
96

Kjartan's avatar
Kjartan committed
97
function taxonomy_del_vocabulary($vid) {
Dries's avatar
   
Dries committed
98
99
  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
100
101
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries's avatar
 
Dries committed
102
  }
Kjartan's avatar
Kjartan committed
103
}
Dries's avatar
 
Dries committed
104

Kjartan's avatar
Kjartan committed
105
106
107
108
109
110
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
111
112
  $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
113

Kjartan's avatar
Kjartan committed
114
  if ($vocabulary->relations) {
Dries's avatar
   
Dries committed
115
    $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
116
  }
Dries's avatar
 
Dries committed
117
118


Kjartan's avatar
Kjartan committed
119
120
121
122
123
124
125
  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) {
Dries's avatar
   
Dries committed
126
      $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
127
    }
Kjartan's avatar
Kjartan committed
128
    elseif ($vocabulary->hierarchy == 2) {
Dries's avatar
   
Dries committed
129
      $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
130
    }
Kjartan's avatar
Kjartan committed
131
  }
Dries's avatar
 
Dries committed
132

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

  if ($edit["tid"]) {
Dries's avatar
   
Dries committed
139
    $form .= form_submit(t("Delete"));
Kjartan's avatar
Kjartan committed
140
    $form .= form_hidden("tid", $edit["tid"]);
Dries's avatar
 
Dries committed
141
142
  }

Kjartan's avatar
Kjartan committed
143
144
  return form($form);
}
Dries's avatar
 
Dries committed
145

Kjartan's avatar
Kjartan committed
146
147
148
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
149

Dries's avatar
   
Dries committed
150
    db_query("UPDATE term_data SET ". _prepare_update($data) ." WHERE tid = '%d'", $edit["tid"]);
Kjartan's avatar
Kjartan committed
151
152
153
154
155
  }
  else if ($edit["tid"]) {
    taxonomy_del_term($edit["tid"]);
  }
  else {
Dries's avatar
   
Dries committed
156
    $edit["tid"] = db_next_id("term_data");
Kjartan's avatar
Kjartan committed
157
158
159
    $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
160

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

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

Kjartan's avatar
Kjartan committed
185
  // synonyms (very cool idea indeed)
Dries's avatar
   
Dries committed
186
  db_query("DELETE FROM term_synonym WHERE tid = '%d'", $edit["tid"]);
Kjartan's avatar
Kjartan committed
187
188
  if ($edit["synonyms"]) {
    foreach (explode ("\n", $edit["synonyms"]) as $synonym) {
Dries's avatar
   
Dries committed
189
      db_query("INSERT INTO term_synonym (tid, name) VALUES ('%d', '%s')", $edit["tid"], chop($synonym));
Kjartan's avatar
Kjartan committed
190
    }
Dries's avatar
 
Dries committed
191
  }
Kjartan's avatar
Kjartan committed
192
}
Dries's avatar
 
Dries committed
193

Kjartan's avatar
Kjartan committed
194
function taxonomy_del_term($tid) {
Dries's avatar
   
Dries committed
195
196
197
198
199
  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);
Kjartan's avatar
Kjartan committed
200
}
Dries's avatar
 
Dries committed
201

Kjartan's avatar
Kjartan committed
202
203
function taxonomy_overview() {
  global $tree;
Dries's avatar
 
Dries committed
204

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

Kjartan's avatar
Kjartan committed
209
210
211
212
213
214
  $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
215

Dries's avatar
   
Dries committed
216
    $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
217
218
219
220
221
222
223

    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
224
      }
Kjartan's avatar
Kjartan committed
225
      $output .= "</td></tr></table></td></tr>\n";
Dries's avatar
 
Dries committed
226
227
    }
  }
Kjartan's avatar
Kjartan committed
228
  $output .= "</table>\n";
Dries's avatar
 
Dries committed
229

Kjartan's avatar
Kjartan committed
230
231
232
233
234
235
236
237
238
239
240
  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
241
    $blank = "<" . t("none") . ">";
Kjartan's avatar
Kjartan committed
242
  }
Dries's avatar
   
Dries committed
243

Kjartan's avatar
Kjartan committed
244
  if ($vocabulary->multiple) {
Dries's avatar
   
Dries committed
245
    $description = t("You $verb choose one or more terms for this node.");
Kjartan's avatar
Kjartan committed
246
    $multiple = 1;
Dries's avatar
 
Dries committed
247
  }
Kjartan's avatar
Kjartan committed
248
  else {
Dries's avatar
   
Dries committed
249
    $description = t("You $verb choose one term for this node.");
Kjartan's avatar
Kjartan committed
250
251
252
253
    $multiple = 0;
  }
  return _taxonomy_term_select($vocabulary->name, "taxonomy", $value, $vocabulary_id, $description, $multiple, $blank);
}
Dries's avatar
 
Dries committed
254
255
256
257
258

/*
** API functions
*/

Kjartan's avatar
Kjartan committed
259
260
261
262
263
264
265
266
267
268
269
// 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
270
  }
Kjartan's avatar
Kjartan committed
271
272
  return $vocabularies;
}
Dries's avatar
 
Dries committed
273

Kjartan's avatar
Kjartan committed
274
275
276
277
278
// 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
279
280
    }
    else {
Kjartan's avatar
Kjartan committed
281
      $terms = 0;
Dries's avatar
 
Dries committed
282
    }
Kjartan's avatar
Kjartan committed
283
284
285
286
  }
  else {
    $terms = $node->taxonomy;
  }
Dries's avatar
 
Dries committed
287

Kjartan's avatar
Kjartan committed
288
289
290
  $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
291
  }
Kjartan's avatar
Kjartan committed
292
293
  return $result ? $result : array();
}
Dries's avatar
 
Dries committed
294

Kjartan's avatar
Kjartan committed
295
296
297
// 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
298

Dries's avatar
   
Dries committed
299
  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
300
301
302
303
}

// 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
304
  $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
305
306
307
  $terms = array();
  while ($term = db_fetch_object($result)) {
    $terms[$term->$key] = $term;
Dries's avatar
 
Dries committed
308
  }
Kjartan's avatar
Kjartan committed
309
310
311
312
313
314
  return $terms;
}

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

Kjartan's avatar
Kjartan committed
316
  if (!$terms[$nid]) {
Dries's avatar
   
Dries committed
317
    $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
318
    $terms[$nid] = array();
Dries's avatar
 
Dries committed
319
    while ($term = db_fetch_object($result)) {
Kjartan's avatar
Kjartan committed
320
      $terms[$nid][$term->$key] = $term;
Dries's avatar
 
Dries committed
321
322
    }
  }
Kjartan's avatar
Kjartan committed
323
324
  return $terms[$nid];
}
Dries's avatar
 
Dries committed
325

Kjartan's avatar
Kjartan committed
326
327
328
// save terms of a node
function taxonomy_node_save($nid, $terms) {
  taxonomy_node_delete($nid);
Dries's avatar
 
Dries committed
329

Kjartan's avatar
Kjartan committed
330
  if ($terms) {
Dries's avatar
   
Dries committed
331
332
    foreach ($terms as $term) {
      db_query("INSERT INTO term_node (nid, tid) VALUES ('%d', '%d')", $nid, $term);
Dries's avatar
 
Dries committed
333
334
    }
  }
Kjartan's avatar
Kjartan committed
335
}
Dries's avatar
 
Dries committed
336

Kjartan's avatar
Kjartan committed
337
338
// clean up terms
function taxonomy_node_delete($nid) {
Dries's avatar
   
Dries committed
339
  db_query("DELETE FROM term_node WHERE nid = '%d'", $nid);
Kjartan's avatar
Kjartan committed
340
}
Dries's avatar
 
Dries committed
341

Kjartan's avatar
Kjartan committed
342
343
344
// relations: return array of related terms
function taxonomy_get_related($tid, $key = "tid") {
  if ($tid) {
345
    $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
346
347
348
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries's avatar
 
Dries committed
349
    }
Kjartan's avatar
Kjartan committed
350
    return $related;
Dries's avatar
 
Dries committed
351
  }
Kjartan's avatar
Kjartan committed
352
353
  else {
    return array();
Dries's avatar
 
Dries committed
354
  }
Kjartan's avatar
Kjartan committed
355
}
Dries's avatar
 
Dries committed
356

Kjartan's avatar
Kjartan committed
357
358
359
// hierarchy: get parent terms
function taxonomy_get_parents($tid, $key = "tid") {
  if ($tid) {
Dries's avatar
   
Dries committed
360
    $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
361
362
363
    $parents = array();
    while ($parent = db_fetch_object($result)) {
      $parents[$parent->$key] = $parent;
Dries's avatar
   
Dries committed
364
    }
Kjartan's avatar
Kjartan committed
365
    return $parents;
Dries's avatar
 
Dries committed
366
  }
Kjartan's avatar
Kjartan committed
367
368
369
370
  else {
    return array();
  }
}
Dries's avatar
 
Dries committed
371

Kjartan's avatar
Kjartan committed
372
373
374
// hierarchy: get children
function taxonomy_get_children($tid, $vid = 0, $key = "tid") {
  if ($vid) {
Dries's avatar
   
Dries committed
375
    $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
376
  }
Kjartan's avatar
Kjartan committed
377
  else {
Dries's avatar
   
Dries committed
378
    $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
379
380
381
382
383
384
385
  }
  $children = array();
  while ($term = db_fetch_object($result)) {
    $children[$term->$key] = $term;
  }
  return $children;
}
Dries's avatar
 
Dries committed
386

Kjartan's avatar
Kjartan committed
387
388
389
390
// 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
391
    $children = array();
Kjartan's avatar
Kjartan committed
392
393
    $tree = array();
    // $terms = array();   // we should be able to safely do this
Dries's avatar
 
Dries committed
394
  }
Kjartan's avatar
Kjartan committed
395
396
397
  $depth++;
  if ($vocabulary_id) {
    if (!$children) {
Dries's avatar
   
Dries committed
398
      $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);
Kjartan's avatar
Kjartan committed
399
400
401
      while ($term = db_fetch_object($result)) {
        $children[$term->parent][] = $term->tid;
        $terms[$term->tid] = $term;
Dries's avatar
 
Dries committed
402
      }
Kjartan's avatar
Kjartan committed
403
    }
Kjartan's avatar
Kjartan committed
404
405
406
407
408
409
    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
410
411
    }
  }
Kjartan's avatar
Kjartan committed
412
413
414
415
  else {
    return 0;
  }
}
Dries's avatar
 
Dries committed
416

Kjartan's avatar
Kjartan committed
417
418
419
// synonyms: return array of synonyms
function taxonomy_get_synonyms($tid) {
  if ($tid) {
Dries's avatar
   
Dries committed
420
    $result = db_query("SELECT name FROM term_synonym WHERE tid = '%d'", $tid);
Kjartan's avatar
Kjartan committed
421
422
    while ($synonym = db_fetch_array($result)) {
      $synonyms[] = $synonym["name"];
Dries's avatar
 
Dries committed
423
    }
Kjartan's avatar
Kjartan committed
424
    return $synonyms ? $synonyms : array();
Dries's avatar
 
Dries committed
425
  }
Kjartan's avatar
Kjartan committed
426
427
  else {
    return array();
Dries's avatar
   
Dries committed
428
  }
Kjartan's avatar
Kjartan committed
429
}
Dries's avatar
   
Dries committed
430

Kjartan's avatar
Kjartan committed
431
432
433
434
// 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
435

Kjartan's avatar
Kjartan committed
436
437
438
// given a term id, count number of nodes in it
function taxonomy_term_count_nodes($tid) {
  static $count;
Dries's avatar
   
Dries committed
439

Kjartan's avatar
Kjartan committed
440
441
442
443
  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
444
445
446
    }
  }

Kjartan's avatar
Kjartan committed
447
448
449
450
451
452
453
454
455
  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
456

Kjartan's avatar
Kjartan committed
457
458
459
460
  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
461
    }
Dries's avatar
 
Dries committed
462
  }
Kjartan's avatar
Kjartan committed
463
464
  return $children[$tid] ? $children[$tid] : array();
}
Dries's avatar
 
Dries committed
465

Kjartan's avatar
Kjartan committed
466
467
function taxonomy_get_vocabulary($vid) {
  // simple cache using a static var?
Dries's avatar
   
Dries committed
468
  return db_fetch_object(db_query("SELECT * FROM vocabulary WHERE vid = '%d'", $vid));
Kjartan's avatar
Kjartan committed
469
}
Dries's avatar
 
Dries committed
470

Kjartan's avatar
Kjartan committed
471
472
function taxonomy_get_term($tid) {
  // simple cache using a static var?
Dries's avatar
   
Dries committed
473
  return db_fetch_object(db_query("SELECT * FROM term_data WHERE tid = '%d'", $tid));
Kjartan's avatar
Kjartan committed
474
}
Dries's avatar
 
Dries committed
475
476
477
478
479

/*
** service functions
*/

Kjartan's avatar
Kjartan committed
480
481
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
482

Kjartan's avatar
Kjartan committed
483
484
485
  if ($blank) {
    $options[0] = $blank;
  }
Dries's avatar
 
Dries committed
486

Kjartan's avatar
Kjartan committed
487
488
489
490
  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
491
492
      }
    }
Kjartan's avatar
Kjartan committed
493
494
495
496
497
    if (!$blank && !$value) {
      // required but without a predefined value, so set first as predefined
      $value = $tree[0]->tid;
    }
  }
Dries's avatar
 
Dries committed
498

Kjartan's avatar
Kjartan committed
499
500
501
502
  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
503

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

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

Kjartan's avatar
Kjartan committed
510
511
512
function _taxonomy_depth($depth, $graphic = '--') {
  for ($n = 0; $n < $depth; $n++) {
    $result .= $graphic;
Dries's avatar
 
Dries committed
513
  }
Kjartan's avatar
Kjartan committed
514
515
  return $result;
}
Dries's avatar
 
Dries committed
516

Kjartan's avatar
Kjartan committed
517
518
519
function _prepare_update($data) {
  foreach ($data as $key => $value) {
    $q[] = "$key = '". check_query($value) ."'";
Dries's avatar
 
Dries committed
520
  }
Kjartan's avatar
Kjartan committed
521
522
523
  $result = implode(", ", $q);
  return $result;
}
Dries's avatar
 
Dries committed
524

Kjartan's avatar
Kjartan committed
525
526
527
528
529
530
531
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
532
    }
Kjartan's avatar
Kjartan committed
533
    $result = implode(", ", $q);
Dries's avatar
 
Dries committed
534
  }
Kjartan's avatar
Kjartan committed
535
536
  return "($result)";
}
Dries's avatar
   
Dries committed
537

Kjartan's avatar
Kjartan committed
538
539
function taxonomy_page() {
  global $op;
Dries's avatar
   
Dries committed
540

Kjartan's avatar
Kjartan committed
541
542
543
544
545
546
  switch ($op) {
    case "feed":
      taxonomy_feed();
      break;
    default:
       // TODO: pretty display of all vocabularies
Dries's avatar
   
Dries committed
547
  }
Kjartan's avatar
Kjartan committed
548
}
Dries's avatar
   
Dries committed
549

Kjartan's avatar
Kjartan committed
550
/*
Dries's avatar
 
Dries committed
551
552
553
** admin
*/

Kjartan's avatar
Kjartan committed
554
555
556
557
558
559
560
561
562
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
563

Kjartan's avatar
Kjartan committed
564
565
    switch ($op) {
      case "add":
Kjartan's avatar
Kjartan committed
566
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
567
          print taxonomy_form_vocabulary();
Kjartan's avatar
Kjartan committed
568
569
        }
        else {
Kjartan's avatar
Kjartan committed
570
          print taxonomy_form_term();
Kjartan's avatar
Kjartan committed
571
        }
Kjartan's avatar
Kjartan committed
572
573
        break;
      case "edit":
Kjartan's avatar
Kjartan committed
574
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
575
          print taxonomy_form_vocabulary(object2array(taxonomy_get_vocabulary($id)));
Kjartan's avatar
Kjartan committed
576
577
        }
        else {
Kjartan's avatar
Kjartan committed
578
          print taxonomy_form_term(object2array(taxonomy_get_term($id)));
Kjartan's avatar
Kjartan committed
579
        }
Kjartan's avatar
Kjartan committed
580
581
        break;
      case "preview":
Kjartan's avatar
Kjartan committed
582
        print taxonomy_form($id);
Kjartan's avatar
Kjartan committed
583
584
585
586
        break;
      case "help":
        print taxonomy_help();
        break;
Dries's avatar
   
Dries committed
587
      case t("Delete"):
Kjartan's avatar
Kjartan committed
588
589
        $edit["name"] = 0;
        // fall through:
Dries's avatar
   
Dries committed
590
      case t("Submit"):
Kjartan's avatar
Kjartan committed
591
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
592
          print status(taxonomy_save_vocabulary($edit));
Kjartan's avatar
Kjartan committed
593
594
        }
        else {
Kjartan's avatar
Kjartan committed
595
          print status(taxonomy_save_term($edit));
Kjartan's avatar
Kjartan committed
596
        }
Kjartan's avatar
Kjartan committed
597
598
599
600
601
602
603
604
605
606
607
608
        // fall through:
      default:
        print taxonomy_overview();
    }
  }
  else {
    print message_access();
  }
}

function taxonomy_help() {
  ?>
Dries's avatar
   
Dries committed
609
610
611
612
613
614

  <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
615
616
617
618
619
620
621
622
623
624
625
  --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
626
627
628
629
630
631
  <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
632
  <h3>Vocabularies</h3>
Dries's avatar
   
Dries committed
633
  <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
634
635

  <h4>Setting up a vocabulary</h4>
Dries's avatar
   
Dries committed
636
637
638
  <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
639
  <br />
Dries's avatar
   
Dries committed
640
  <i>Description</i><br />Optional. Description of the vocabulary, can be used by modules and feeds.<br />
Kjartan's avatar
Kjartan committed
641
  <br />
Dries's avatar
   
Dries committed
642
  <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
643
  <br />
Dries's avatar
   
Dries committed
644
  <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
645
  <br />
Dries's avatar
   
Dries committed
646
  <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
647
  <br />
Dries's avatar
   
Dries committed
648
649
  <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
650
  <h4>Adding terms to a vocabulary</h4>
Dries's avatar
   
Dries committed
651
652
653
<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
654
  <br />
Dries's avatar
   
Dries committed
655
  <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
656
  <br />
Dries's avatar
   
Dries committed
657
  <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
658
  <br />
Dries's avatar
   
Dries committed
659
660
661
662
663
664
665
666
  <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 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 <? 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>

  <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
667
}
Dries's avatar
 
Dries committed
668
?>