taxonomy.module 27.8 KB
Newer Older
Dries's avatar
 
Dries committed
1
<?php
Dries's avatar
 
Dries committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

  function taxonomy_feed() {
    global $id, $or, $and, $type;

    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)."'";
        }
        $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)."'";
        }
        $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 HAVING c = ".count($terms)." ORDER BY static DESC, created DESC LIMIT 15");
        $term = taxonomy_get_term($and);
      }
      else {
        return node_feed();
      }

      $channel["title"] = variable_get("site_name", "drupal") . " - " . $term->name;
      $channel["link"] = path_uri() . "index.php?or=$or";
      $channel["description"] = $term->description;

      node_feed($result, $channel);
    }
  }

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

  function taxonomy_link($type) {
    if ($type == "admin" && user_access("administer taxonomy")) {
Dries's avatar
 
Dries committed
42
      $links[] = la(t("taxonomy"), array("mod" => "taxonomy"));
Dries's avatar
 
Dries committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
    }

    return $links ? $links : array();
  }

/*
** admin pages (form, save, overview)
*/

  function taxonomy_form_vocabulary($edit = array()) {
    foreach (module_list() as $name) {
      if (module_hook($name, "node")) {
        $nodetypes[$name] = $name;
      }
    }

    $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);
Dries's avatar
 
Dries committed
62 63
    $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);
Dries's avatar
 
Dries committed
64
    $form .= form_checkbox("Multiple select", "multiple", 1, $edit[multiple], "Optional. Allows nodes to have more than one term in this vocabulary.");
Dries's avatar
 
Dries committed
65
    $form .= form_checkbox("Required", "required", 1, $edit[required], "If enabled every node <b>must</b> have at least one term in this vocabulary");
Dries's avatar
 
Dries committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    $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");

    if ($edit[vid]) {
      $form .= form_submit("Delete");
      $form .= form_hidden("vid", $edit[vid]);
    }

    return form($form);
  }

  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"])."'");
    }
    else if ($edit["vid"]) {
      taxonomy_del_vocabulary($edit["vid"]);
    }
    else {
      db_query("INSERT INTO vocabulary "._prepare_insert($data, 1)." VALUES "._prepare_insert($data, 2));
    }
  }

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

  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.");

    if ($vocabulary->relations) {
Dries's avatar
 
Dries committed
117
      $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
118 119 120 121 122 123
    }


    if ($vocabulary->hierarchy) {
      $parent = array_keys(taxonomy_get_parents($edit["tid"]));
      taxonomy_get_tree($vocabulary_id, $children, $edit["tid"]);
Dries's avatar
 
Dries committed
124
      // you can't be son of yourself or your children
Dries's avatar
 
Dries committed
125 126 127
      $exclude = array_keys($children);
      $exclude[] = $edit["tid"];
      if ($vocabulary->hierarchy == 1) {
Dries's avatar
 
Dries committed
128
        $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
129
      } elseif ($vocabulary->hierarchy == 2) {
Dries's avatar
 
Dries committed
130
        $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
131 132 133
      }
    }

Dries's avatar
 
Dries committed
134
    $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.");
Dries's avatar
 
Dries committed
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    $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"]);
    }

    return form($form);
  }

  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
155
      db_query("UPDATE term_data SET "._prepare_update($data)." WHERE tid = '%s'", $edit["tid"]);
Dries's avatar
 
Dries committed
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    }
    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;
      }
      $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));
    }

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

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

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

  function taxonomy_del_term($tid) {
Dries's avatar
 
Dries committed
216 217 218 219 220
    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
221 222 223 224 225 226 227 228 229 230 231
  }

  function taxonomy_overview() {
    global $tree;

    $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";

    $vocabularies = taxonomy_get_vocabularies();
    foreach ($vocabularies as $vocabulary) {
Dries's avatar
 
Dries committed
232 233 234 235 236
      $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));

      $output .= " <tr><td>". check_output($vocabulary->name) ."</td><td>". check_output($vocabulary->types) ."</td><td>".implode(" | ", $links)."</td></tr>\n";
Dries's avatar
 
Dries committed
237 238 239 240 241 242

      unset($tree);
      taxonomy_get_tree($vocabulary->vid, $tree);
      if ($tree) {
        $output .= "<tr><td colspan=\"3\"><table><tr><td>";
        foreach ($tree as $term) {
Dries's avatar
 
Dries committed
243
          $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
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
        }
        $output .= "</td></tr></table></td></tr>\n";
      }
    }
    $output .= "</table>\n";

    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;
    } 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);
  }

/*
** API functions
*/

  // return array of vocabularies, as objects
  function taxonomy_get_vocabularies($type = '', $key = "vid") {
    if ($type) {
Dries's avatar
 
Dries committed
279
      $result = db_query("SELECT * FROM vocabulary WHERE types LIKE '%%%s%%' ORDER BY weight, name", $type);
Dries's avatar
 
Dries committed
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
    } else {
      $result = db_query("SELECT * FROM vocabulary ORDER BY weight, name");
    }
    $vocabularies = array();
    while ($voc = db_fetch_object($result)) {
      $vocabularies[$voc->$key] = $voc;
    }
    return $vocabularies;
  }

  // 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));
      } else {
        $terms = 0;
      }
    } else {
      $terms = $node->taxonomy;
    }

Dries's avatar
 
Dries committed
302
    $c = db_query("SELECT * FROM vocabulary WHERE types LIKE '%%%s%%' ORDER BY weight, name", $type);
Dries's avatar
 
Dries committed
303 304 305
    while ($vocabulary = db_fetch_object($c)) {
      $result[] .= taxonomy_form($vocabulary->vid, $terms);
    }
306
    return $result ? $result : array();
Dries's avatar
 
Dries committed
307 308 309 310
  }

  // 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) {
Dries's avatar
 
Dries committed
311
    $term_name = db_result(db_query("SELECT name FROM term_data WHERE tid = '%s'", $tid));
Dries's avatar
 
Dries committed
312

Dries's avatar
 
Dries committed
313
    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));
Dries's avatar
 
Dries committed
314 315 316 317
  }

  // 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
318
    $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);
Dries's avatar
 
Dries committed
319 320 321 322 323 324 325 326 327 328 329 330
    $terms = array();
    while ($term = db_fetch_object($result)) {
      $terms[$term->$key] = $term;
    }
    return $terms;
  }

  // return array of terms of a node
  function taxonomy_node_get_terms($nid, $key = "tid") {
    static $terms;

    if (!$terms[$nid]) {
Dries's avatar
 
Dries committed
331
      $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);
Dries's avatar
 
Dries committed
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
      $terms[$nid] = array();
      while ($term = db_fetch_object($result)) {
        $terms[$nid][$term->$key] = $term;
      }
    }
    return $terms[$nid];
  }

  // save terms of a node
  function taxonomy_node_save($nid, $terms) {
    taxonomy_node_delete($nid);

    if ($terms) {
      foreach ($terms as $t) {
        $query[] = "('".check_query($nid)."', '".check_query($t)."')";
      }
      db_query("INSERT INTO term_node (nid, tid) VALUES ".implode(", ", $query));
    }
  }

  // clean up terms
  function taxonomy_node_delete($nid) {
Dries's avatar
 
Dries committed
354
    db_query("DELETE FROM term_node WHERE nid = '%s'", $nid);
Dries's avatar
 
Dries committed
355 356 357 358 359
  }

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

Dries's avatar
 
Dries committed
369
  // hierarchy: get parent terms
Dries's avatar
 
Dries committed
370 371
  function taxonomy_get_parents($tid, $key = "tid") {
    if ($tid) {
Dries's avatar
 
Dries committed
372
      $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);
Dries's avatar
 
Dries committed
373 374 375 376 377 378 379 380 381 382 383 384 385
      $parents = array();
      while ($parent = db_fetch_object($result)) {
        $parents[$parent->$key] = $parent;
      }
      return $parents;
    } else {
      return array();
    }
  }

  // hierarchy: get children
  function taxonomy_get_children($tid, $vid = 0, $key = "tid") {
    if ($vid) {
Dries's avatar
 
Dries committed
386
      $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
387
    } else {
Dries's avatar
 
Dries committed
388
      $result = db_query("SELECT t.* FROM term_hierarchy h, term_data t WHERE h.tid = t.tid AND parent = '%s' ORDER BY weight", $tid);
Dries's avatar
 
Dries committed
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
    }
    $children = array();
    while ($term = db_fetch_object($result)) {
      $children[$term->$key] = $term;
    }
    return $children;
  }

  // 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) {
      $children = array();
      $tree = array();
      // $terms = array();   // we should be able to safely do this
    }
    $depth++;
    if ($vocabulary_id) {
      if (!$children) {
Dries's avatar
 
Dries committed
408
        $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);
Dries's avatar
 
Dries committed
409 410 411 412 413 414 415 416
        while ($term = db_fetch_object($result)) {
          $children[$term->parent][] = $term->tid;
          $terms[$term->tid] = $term;
        }
      }
      if ($children[$parent]) {
        foreach ($children[$parent] as $child) {
          $terms[$child]->depth = $depth;
Dries's avatar
 
Dries committed
417
          $tree[] = $terms[$child];
Dries's avatar
 
Dries committed
418 419 420 421 422 423 424 425 426 427 428
          taxonomy_get_tree($vocabulary_id, $tree, $child, $depth, $key);
        }
      }
    } else {
      return 0;
    }
  }

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

  // synonyms: return original term
  function taxonomy_get_synonym_root($term) {
Dries's avatar
 
Dries committed
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
    return db_fetch_object(db_query("SELECT * FROM term_synonym s, term_data t WHERE t.tid = s.tid AND s.name = '%s'", $term));
  }

  // given a term id, count number of nodes in it
  function taxonomy_term_count_nodes($tid) {
    static $count;

    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;
      }
    }

    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;

    if (!$children) {
      $result = db_query("SELECT tid, parent FROM term_hierarchy");
      while ($term = db_fetch_object($result)) {
        $children[$term->parent][] = $term->tid;
      }
    }
    return $children[$tid] ? $children[$tid] : array();
Dries's avatar
 
Dries committed
472 473 474 475
  }

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

  function taxonomy_get_term($tid) {
    // simple cache using a static var?
Dries's avatar
 
Dries committed
481
    return db_fetch_object(db_query("SELECT * FROM term_data WHERE tid = '%s'", $tid));
Dries's avatar
 
Dries committed
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
  }

/*
** service functions
*/

  function _taxonomy_term_select($title, $name, $value, $vocabulary_id, $description, $multiple, $blank, $exclude = array()) {
    taxonomy_get_tree($vocabulary_id, $tree);

    if ($blank) {
      $options[0] = $blank;
    }

    if ($tree) {
      foreach ($tree as $term) {
        if (!in_array($term->tid, $exclude)) {
          $options[$term->tid] = _taxonomy_depth($term->depth, '-').$term->name;
        }
      }
      if (!$blank && !$value) {
        // required but without a predefined value, so set first as predefined
        $value = $tree[0]->tid;
      }
    }

    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>";
      }

      // min 8, possibly options/3 (set max too?)
      $size = max(8, round(count($options)) / 3);

      return form_item($title, "<select name=\"edit[$name][]\"" .($multiple ? " multiple size=\"$size\"" : "").($extra ? " $extra" : "") .">$select</select>", $description);
    }
  }

  function _taxonomy_depth($depth, $graphic = '--') {
    for ($n=0; $n<$depth; $n++) {
      $result .= $graphic;
    }
    return $result;
  }

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

  function _prepare_insert($data, $stage) {
    if ($stage == 1) {
      $result = implode(", ", array_keys($data));
    } else {
      foreach (array_values($data) as $value) {
Dries's avatar
 
Dries committed
539
        $q[] = "'". check_query($value) ."'";
Dries's avatar
 
Dries committed
540 541 542 543 544
      }
      $result = implode(", ", $q);
    }
    return "($result)";
  }
Dries's avatar
 
Dries committed
545 546 547 548 549 550 551 552 553 554 555 556 557 558

  function taxonomy_page() {
    global $op;

    switch ($op) {
      case "feed":
        taxonomy_feed();
        break;
      default:
         // TODO: pretty display of all vocabularies
    }
  }

 /*
Dries's avatar
 
Dries committed
559 560 561 562 563 564 565
** admin
*/

  function taxonomy_admin() {
    global $edit, $type, $op, $id, $tree;

    if (user_access("administer taxonomy")) {
Dries's avatar
 
Dries committed
566 567 568
      $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"));
Dries's avatar
 
Dries committed
569

Dries's avatar
 
Dries committed
570
      print "<small>".implode(" | ", $links)."</small><hr>\n";
Dries's avatar
 
Dries committed
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608

      switch ($op) {
        case "add":
          if ($type == "vocabulary")
            print taxonomy_form_vocabulary();
          else
            print taxonomy_form_term();
          break;
        case "edit":
          if ($type == "vocabulary")
            print taxonomy_form_vocabulary(object2array(taxonomy_get_vocabulary($id)));
          else
            print taxonomy_form_term(object2array(taxonomy_get_term($id)));
          break;
        case "preview":
            print taxonomy_form($id);
          break;
        case "help":
          print taxonomy_help();
          break;
        case "Delete":
          $edit[name] = 0;
          // fall through:
        case "Submit":
          if ($type == "vocabulary")
            print status(taxonomy_save_vocabulary($edit));
          else
            print status(taxonomy_save_term($edit));
          // fall through:
        default:
          print taxonomy_overview();
      }
    }
    else {
      print message_access();
    }
  }

Dries's avatar
 
Dries committed
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 668 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
  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 />
    <i>Description</i><br />
    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
721
?>