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
?>