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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/*
** API functions
*/

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/*
** service functions
*/

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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