taxonomy.module 26.8 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

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

Kjartan's avatar
Kjartan committed
75 76 77
  if ($edit["vid"]) {
    $form .= form_submit("Delete");
    $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 87
function taxonomy_save_vocabulary($edit) {
  $data = array("name" => $edit["name"], "types" => @implode(",", $edit["types"]), "description" => $edit["description"], "multiple" => $edit["multiple"], "required" => $edit["required"], "hierarchy" => $edit["hierarchy"], "relations" => $edit["relations"], "weight" => $edit["weight"]);

  if ($edit["vid"] && $edit["name"]) {
    db_query("UPDATE vocabulary SET ". _prepare_update($data) ." WHERE vid = '". check_input($edit["vid"]) ."'");
Dries's avatar
 
Dries committed
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 98 99 100 101
function taxonomy_del_vocabulary($vid) {
  db_query("DELETE FROM vocabulary WHERE vid = '%s'", $vid);
  $result = db_query("SELECT tid FROM term_data WHERE vid = '%s'", $vid);
  while ($term = db_fetch_object($result)) {
    taxonomy_del_term($term->tid);
Dries's avatar
 
Dries committed
102
  }
Kjartan's avatar
Kjartan committed
103
}
Dries's avatar
 
Dries committed
104

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

Kjartan's avatar
Kjartan committed
114 115 116
  if ($vocabulary->relations) {
    $form .= _taxonomy_term_select("Related terms", "relations", array_keys(taxonomy_get_related($edit["tid"])), $vocabulary_id, "Optional.", 1, "<none>", array($edit["tid"]));
  }
Dries's avatar
 
Dries committed
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) {
Kjartan's avatar
Kjartan committed
126
      $form .= _taxonomy_term_select("Parent", "parent", $parent, $vocabulary_id, "Required. ". la("Parent term", array("mod" => "taxonomy", "op" => "help"), "parent") .".", 0, "<root>", $exclude);
Dries's avatar
 
Dries committed
127
    }
Kjartan's avatar
Kjartan committed
128
    elseif ($vocabulary->hierarchy == 2) {
Kjartan's avatar
Kjartan committed
129
      $form .= _taxonomy_term_select("Parents", "parent", $parent, $vocabulary_id, "Required. ". la("Parent terms", array("mod" => "taxonomy", "op" => "help"), "parent") .".", 1, "<root>", $exclude);
Dries's avatar
 
Dries committed
130
    }
Kjartan's avatar
Kjartan committed
131
  }
Dries's avatar
 
Dries committed
132

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

  if ($edit["tid"]) {
    $form .= form_submit("Delete");
    $form .= form_hidden("tid", $edit["tid"]);
Dries's avatar
 
Dries committed
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

Kjartan's avatar
Kjartan committed
150 151 152 153 154 155 156 157 158 159
    db_query("UPDATE term_data SET ". _prepare_update($data) ." WHERE tid = '%s'", $edit["tid"]);
  }
  else if ($edit["tid"]) {
    taxonomy_del_term($edit["tid"]);
  }
  else {
    $edit["tid"] = db_result(db_query("SELECT MAX(tid) + 1 FROM term_data"));
    if (!$edit["tid"]) {
      // first term
      $edit["tid"] = 1;
Dries's avatar
 
Dries committed
160
    }
Kjartan's avatar
Kjartan committed
161 162 163
    $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
164

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

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

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

Kjartan's avatar
Kjartan committed
205 206 207 208 209 210 211
function taxonomy_del_term($tid) {
  db_query("DELETE FROM term_data WHERE tid = '%s'", $tid);
  db_query("DELETE FROM term_hierarchy WHERE tid = '%s'", $tid);
  db_query("DELETE FROM term_relation WHERE tid1 = '%s' OR tid2 = '%s'", $tid, $tid);
  db_query("DELETE FROM term_synonym WHERE tid = '%s'", $tid);
  db_query("DELETE FROM term_node WHERE tid = '%s'", $tid);
}
Dries's avatar
 
Dries committed
212

Kjartan's avatar
Kjartan committed
213 214
function taxonomy_overview() {
  global $tree;
Dries's avatar
 
Dries committed
215

Dries's avatar
 
Dries committed
216
  $output .= "<h3>Vocabularies overview</h3>";
Kjartan's avatar
Kjartan committed
217 218
  $output .= "<table border=\"1\" cellpadding=\"2\" cellspacing=\"2\">\n";
  $output .= " <tr><th>name</th><th>node types</th><th>operations</th></tr>\n";
Dries's avatar
 
Dries committed
219

Kjartan's avatar
Kjartan committed
220 221 222 223 224 225
  $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
226

Kjartan's avatar
Kjartan committed
227 228 229 230 231 232 233 234
    $output .= " <tr><td>". check_output($vocabulary->name) ."</td><td>". check_output($vocabulary->types) ."</td><td>". implode(" | ", $links) ."</td></tr>\n";

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

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

function taxonomy_form($vocabulary_id, $value = 0) {
  $vocabulary = taxonomy_get_vocabulary($vocabulary_id);
  if ($vocabulary->required) {
    $verb = "must";
    $blank = 0;
  }
  else {
    $verb = "can";
Dries's avatar
 
Dries committed
252
    $blank = t("<none>");
Kjartan's avatar
Kjartan committed
253
  }
Dries's avatar
 
Dries committed
254

Kjartan's avatar
Kjartan committed
255
  if ($vocabulary->multiple) {
Dries's avatar
 
Dries committed
256
    $description = t("You $verb choose one or more terms for this node.");
Kjartan's avatar
Kjartan committed
257
    $multiple = 1;
Dries's avatar
 
Dries committed
258
  }
Kjartan's avatar
Kjartan committed
259
  else {
Dries's avatar
 
Dries committed
260
    $description = t("You $verb choose one term for this node.");
Kjartan's avatar
Kjartan committed
261 262 263 264
    $multiple = 0;
  }
  return _taxonomy_term_select($vocabulary->name, "taxonomy", $value, $vocabulary_id, $description, $multiple, $blank);
}
Dries's avatar
 
Dries committed
265 266 267 268 269

/*
** API functions
*/

Kjartan's avatar
Kjartan committed
270 271 272 273 274 275 276 277 278 279 280
// 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
281
  }
Kjartan's avatar
Kjartan committed
282 283
  return $vocabularies;
}
Dries's avatar
 
Dries committed
284

Kjartan's avatar
Kjartan committed
285 286 287 288 289
// 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
290 291
    }
    else {
Kjartan's avatar
Kjartan committed
292
      $terms = 0;
Dries's avatar
 
Dries committed
293
    }
Kjartan's avatar
Kjartan committed
294 295 296 297
  }
  else {
    $terms = $node->taxonomy;
  }
Dries's avatar
 
Dries committed
298

Kjartan's avatar
Kjartan committed
299 300 301
  $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
302
  }
Kjartan's avatar
Kjartan committed
303 304
  return $result ? $result : array();
}
Dries's avatar
 
Dries committed
305

Kjartan's avatar
Kjartan committed
306 307 308
// 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
309

Kjartan's avatar
Kjartan committed
310 311 312 313 314 315 316 317 318
  return db_result(db_query("SELECT COUNT(n.nid) FROM node n WHERE n.nid = '%s' AND ((n.body LIKE '%%%s%%') OR (n.body LIKE '%%%s%%'))", $nid, $term_name, $term_name));
}

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

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

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

Kjartan's avatar
Kjartan committed
337 338 339
// save terms of a node
function taxonomy_node_save($nid, $terms) {
  taxonomy_node_delete($nid);
Dries's avatar
 
Dries committed
340

Kjartan's avatar
Kjartan committed
341 342 343
  if ($terms) {
    foreach ($terms as $t) {
      $query[] = "('". check_query($nid) ."', '". check_query($t) ."')";
Dries's avatar
 
Dries committed
344
    }
Kjartan's avatar
Kjartan committed
345
    db_query("INSERT INTO term_node (nid, tid) VALUES ". implode(", ", $query));
Dries's avatar
 
Dries committed
346
  }
Kjartan's avatar
Kjartan committed
347
}
Dries's avatar
 
Dries committed
348

Kjartan's avatar
Kjartan committed
349 350 351 352
// clean up terms
function taxonomy_node_delete($nid) {
  db_query("DELETE FROM term_node WHERE nid = '%s'", $nid);
}
Dries's avatar
 
Dries committed
353

Kjartan's avatar
Kjartan committed
354 355 356
// relations: return array of related terms
function taxonomy_get_related($tid, $key = "tid") {
  if ($tid) {
357
    $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
358 359 360
    $related = array();
    while ($term = db_fetch_object($result)) {
      $related[$term->$key] = $term;
Dries's avatar
 
Dries committed
361
    }
Kjartan's avatar
Kjartan committed
362
    return $related;
Dries's avatar
 
Dries committed
363
  }
Kjartan's avatar
Kjartan committed
364 365
  else {
    return array();
Dries's avatar
 
Dries committed
366
  }
Kjartan's avatar
Kjartan committed
367
}
Dries's avatar
 
Dries committed
368

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

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

Kjartan's avatar
Kjartan committed
399 400 401 402
// 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
403
    $children = array();
Kjartan's avatar
Kjartan committed
404 405
    $tree = array();
    // $terms = array();   // we should be able to safely do this
Dries's avatar
 
Dries committed
406
  }
Kjartan's avatar
Kjartan committed
407 408 409 410 411 412 413
  $depth++;
  if ($vocabulary_id) {
    if (!$children) {
      $result = db_query("SELECT t.*, parent FROM term_data t, term_hierarchy h WHERE t.tid = h.tid AND t.vid = '%s' ORDER BY weight, name", $vocabulary_id);
      while ($term = db_fetch_object($result)) {
        $children[$term->parent][] = $term->tid;
        $terms[$term->tid] = $term;
Dries's avatar
 
Dries committed
414
      }
Kjartan's avatar
Kjartan committed
415
    }
Kjartan's avatar
Kjartan committed
416 417 418 419 420 421
    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
422 423
    }
  }
Kjartan's avatar
Kjartan committed
424 425 426 427
  else {
    return 0;
  }
}
Dries's avatar
 
Dries committed
428

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

Kjartan's avatar
Kjartan committed
443 444 445 446
// 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
447

Kjartan's avatar
Kjartan committed
448 449 450
// given a term id, count number of nodes in it
function taxonomy_term_count_nodes($tid) {
  static $count;
Dries's avatar
 
Dries committed
451

Kjartan's avatar
Kjartan committed
452 453 454 455
  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
456 457 458
    }
  }

Kjartan's avatar
Kjartan committed
459 460 461 462 463 464 465 466 467
  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
468

Kjartan's avatar
Kjartan committed
469 470 471 472
  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
473
    }
Dries's avatar
 
Dries committed
474
  }
Kjartan's avatar
Kjartan committed
475 476
  return $children[$tid] ? $children[$tid] : array();
}
Dries's avatar
 
Dries committed
477

Kjartan's avatar
Kjartan committed
478 479 480 481
function taxonomy_get_vocabulary($vid) {
  // simple cache using a static var?
  return db_fetch_object(db_query("SELECT * FROM vocabulary WHERE vid = '%s'", $vid));
}
Dries's avatar
 
Dries committed
482

Kjartan's avatar
Kjartan committed
483 484 485 486
function taxonomy_get_term($tid) {
  // simple cache using a static var?
  return db_fetch_object(db_query("SELECT * FROM term_data WHERE tid = '%s'", $tid));
}
Dries's avatar
 
Dries committed
487 488 489 490 491

/*
** service functions
*/

Kjartan's avatar
Kjartan committed
492 493
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
494

Kjartan's avatar
Kjartan committed
495 496 497
  if ($blank) {
    $options[0] = $blank;
  }
Dries's avatar
 
Dries committed
498

Kjartan's avatar
Kjartan committed
499 500 501 502
  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
503 504
      }
    }
Kjartan's avatar
Kjartan committed
505 506 507 508 509
    if (!$blank && !$value) {
      // required but without a predefined value, so set first as predefined
      $value = $tree[0]->tid;
    }
  }
Dries's avatar
 
Dries committed
510

Kjartan's avatar
Kjartan committed
511 512 513 514
  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
515

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

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

Kjartan's avatar
Kjartan committed
522 523 524
function _taxonomy_depth($depth, $graphic = '--') {
  for ($n = 0; $n < $depth; $n++) {
    $result .= $graphic;
Dries's avatar
 
Dries committed
525
  }
Kjartan's avatar
Kjartan committed
526 527
  return $result;
}
Dries's avatar
 
Dries committed
528

Kjartan's avatar
Kjartan committed
529 530 531
function _prepare_update($data) {
  foreach ($data as $key => $value) {
    $q[] = "$key = '". check_query($value) ."'";
Dries's avatar
 
Dries committed
532
  }
Kjartan's avatar
Kjartan committed
533 534 535
  $result = implode(", ", $q);
  return $result;
}
Dries's avatar
 
Dries committed
536

Kjartan's avatar
Kjartan committed
537 538 539 540 541 542 543
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
544
    }
Kjartan's avatar
Kjartan committed
545
    $result = implode(", ", $q);
Dries's avatar
 
Dries committed
546
  }
Kjartan's avatar
Kjartan committed
547 548
  return "($result)";
}
Dries's avatar
 
Dries committed
549

Kjartan's avatar
Kjartan committed
550 551
function taxonomy_page() {
  global $op;
Dries's avatar
 
Dries committed
552

Kjartan's avatar
Kjartan committed
553 554 555 556 557 558
  switch ($op) {
    case "feed":
      taxonomy_feed();
      break;
    default:
       // TODO: pretty display of all vocabularies
Dries's avatar
 
Dries committed
559
  }
Kjartan's avatar
Kjartan committed
560
}
Dries's avatar
 
Dries committed
561

Kjartan's avatar
Kjartan committed
562
/*
Dries's avatar
 
Dries committed
563 564 565
** admin
*/

Kjartan's avatar
Kjartan committed
566 567 568 569 570 571 572 573 574
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
575

Kjartan's avatar
Kjartan committed
576 577
    switch ($op) {
      case "add":
Kjartan's avatar
Kjartan committed
578
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
579
          print taxonomy_form_vocabulary();
Kjartan's avatar
Kjartan committed
580 581
        }
        else {
Kjartan's avatar
Kjartan committed
582
          print taxonomy_form_term();
Kjartan's avatar
Kjartan committed
583
        }
Kjartan's avatar
Kjartan committed
584 585
        break;
      case "edit":
Kjartan's avatar
Kjartan committed
586
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
587
          print taxonomy_form_vocabulary(object2array(taxonomy_get_vocabulary($id)));
Kjartan's avatar
Kjartan committed
588 589
        }
        else {
Kjartan's avatar
Kjartan committed
590
          print taxonomy_form_term(object2array(taxonomy_get_term($id)));
Kjartan's avatar
Kjartan committed
591
        }
Kjartan's avatar
Kjartan committed
592 593
        break;
      case "preview":
Kjartan's avatar
Kjartan committed
594
        print taxonomy_form($id);
Kjartan's avatar
Kjartan committed
595 596 597 598 599 600 601 602
        break;
      case "help":
        print taxonomy_help();
        break;
      case "Delete":
        $edit["name"] = 0;
        // fall through:
      case "Submit":
Kjartan's avatar
Kjartan committed
603
        if ($type == "vocabulary") {
Kjartan's avatar
Kjartan committed
604
          print status(taxonomy_save_vocabulary($edit));
Kjartan's avatar
Kjartan committed
605 606
        }
        else {
Kjartan's avatar
Kjartan committed
607
          print status(taxonomy_save_term($edit));
Kjartan's avatar
Kjartan committed
608
        }
Kjartan's avatar
Kjartan committed
609 610 611 612 613 614 615 616 617 618 619 620
        // fall through:
      default:
        print taxonomy_overview();
    }
  }
  else {
    print message_access();
  }
}

function taxonomy_help() {
  ?>
Dries's avatar
 
Dries committed
621 622 623 624 625 626

  <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
627 628 629 630 631 632 633 634 635 636 637
  --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
638 639 640 641 642 643
  <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
644
  <h3>Vocabularies</h3>
Dries's avatar
 
Dries committed
645
  <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
646 647

  <h4>Setting up a vocabulary</h4>
Dries's avatar
 
Dries committed
648 649 650
  <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
651
  <br />
Dries's avatar
 
Dries committed
652
  <i>Description</i><br />Optional. Description of the vocabulary, can be used by modules and feeds.<br />
Kjartan's avatar
Kjartan committed
653
  <br />
Dries's avatar
 
Dries committed
654
  <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
655
  <br />
Dries's avatar
 
Dries committed
656
  <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
657
  <br />
Dries's avatar
 
Dries committed
658
  <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
659
  <br />
Dries's avatar
 
Dries committed
660 661
  <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
662
  <h4>Adding terms to a vocabulary</h4>
Dries's avatar
 
Dries committed
663 664 665
<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
666
  <br />
Dries's avatar
 
Dries committed
667
  <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
668
  <br />
Dries's avatar
 
Dries committed
669
  <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
670
  <br />
Dries's avatar
 
Dries committed
671 672 673 674 675 676 677 678
  <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
679
}
Dries's avatar
 
Dries committed
680
?>