/[drupal]/contributions/modules/cmt/cmt.module
ViewVC logotype

Contents of /contributions/modules/cmt/cmt.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.33 - (show annotations) (download) (as text)
Tue Aug 21 03:40:53 2007 UTC (2 years, 3 months ago) by agaric
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-5
Changes since 1.32: +19 -1 lines
File MIME type: text/x-php
minor, nothing that breaks it I hope.  Just updating so we can tag it D5
1 <?php
2 // $Id: cmt.module,v 1.32 2007/08/20 18:59:47 agaric Exp $
3 /**
4 * @file
5 * Community-managed taxonomy enables users to collaborate on categorization decisions.
6 */
7
8 /**
9 * Display modes (if any). I also want to make it as available to custom theming as possible
10 */
11 define('CMT_NODE_FORM_INLINE', 2);
12 define('CMT_NODE_FORM_TAB', 1);
13 define('CMT_NODE_FORM_BLOCK', 0);
14 // follows community_tags' numbering convention, but inline will be default (possibly only)
15
16 // include admin functions; look up if there's a Drupal-standard way to prevent this from loading unless it needs to
17 include_once './'. drupal_get_path('module', 'cmt') .'/cmt_admin.inc';
18
19 /**
20 * Implementation of hook_perm().
21 */
22 function cmt_perm() {
23 return array(
24 'access community managed taxonomy', // aka manage community categories
25 'administer community managed taxonomy',
26 );
27 }
28
29 /**
30 * Implementation of hook_menu().
31 */
32 function cmt_menu($may_cache) {
33 $items = array();
34 if ($may_cache) {
35 /* -- administrative menu items ----------------------------------------- */
36 $items[] = array(
37 'access' => user_access('administer community managed taxonomy'),
38 'callback' => 'cmt_overview_vocabularies',
39 'description' => t('Edit settings for community-managed vocabularies (taxonomy enhanced by the cmt module).'),
40 'path' => 'admin/content/cmt',
41 'title' => t('Community-managed Categories'),
42 );
43 $items[] = array(
44 'access' => user_access('administer community managed taxonomy'),
45 'path' => 'admin/content/cmt/list',
46 // optional? 'callback' => 'cmt_overview_vocabularies',
47 'type' => MENU_DEFAULT_LOCAL_TASK,
48 'title' => t('List vocabularies'),
49 'weight' => -10,
50 );
51 $items[] = array(
52 'path' => 'admin/content/cmt/edit/vocabulary',
53 'title' => t('Edit community-managed vocabulary'),
54 'callback' => 'cmt_admin_vocabulary_edit',
55 'access' => user_access('administer community managed taxonomy'),
56 'type' => MENU_CALLBACK,
57 );
58 $items[] = array(
59 'access' => user_access('administer community managed taxonomy'),
60 'callback' => 'drupal_get_form',
61 'callback arguments' => array('cmt_settings'),
62 'description' => t('Configure Community-managed Taxonomy options that apply to all vocabularies with these settings.'),
63 'path' => 'admin/content/cmt/settings',
64 'title' => t('Settings'),
65 'type' => MENU_LOCAL_TASK,
66 );
67 /* -- other menu items ---------------------------------------------- */
68 $items[] = array(
69 'access' => user_access('access community managed taxonomy'),
70 'callback' => 'cmt_autocomplete',
71 'path' => 'cmt/autocomplete',
72 'title' => t('Community-managed taxonomy autocomplete'),
73 'type' => MENU_CALLBACK,
74 );
75 }
76 else { // no cache
77 /* ADD later from community_tags ?
78 global $user;
79 */
80 }
81 return $items;
82 }
83
84 /**
85 * Implementation of hook_form_alter().
86 *
87 * An important hook for CMT.
88 * First, to add vocabularies to community management by making it an option
89 * on the create and edit vocabulary forms.
90 */
91 function cmt_form_alter($form_id, &$form) {
92 if ($form_id == 'taxonomy_form_vocabulary') {
93 if (!$form['vid']['#value'] || !cmt_is_vid_cmt($form['vid']['#value'])) {
94 $cmt_vocabulary['cmt_vocabulary'] = array(
95 '#type' => 'radios',
96 '#title' => t('Community management'),
97 '#default_value' => 0,
98 '#options' => array(
99 0 => t('Disabled'),
100 1 => t('No threshold'),
101 2 => t('Default threshold'),
102 3 => t('Default threshold (with related terms derived)'),
103 4 => t('High threshold (with related terms derived)'),
104 ),
105 '#description' => t('Allows <a href="@help-url">community managing</a> of terms in this vocabulary. Users can influence both what terms nodes are tagged with and how these terms are themselves organized, named, and described.', array('@help-url' => url('admin/help/cmt'))),
106 );
107 }
108 else {
109 $cmt_vocabulary['cmt_vocabulary'] = array(
110 '#type' => 'markup',
111 '#title' => t('Community management'),
112 '#value' => '<h2>' . t('Community management:') . '</h2> <div>' . t('This vocabulary is open to <a href="@help-url">community managing</a> of its terms. Users can influence both what terms nodes are tagged with and how these terms are themselves organized, named, and described. To edit community-management settings for this vocabulary (including disabling it) please see its <a href="@cmtsettings">Community managed Categories administration page</a>.', array('@help-url' => url('admin/help/cmt'), '@cmtsettings' => url('admin/content/cmt/edit/vocabulary/' . $form['vid']['#value']))) . '</div>',
113 );
114 }
115 $pos = array_search('submit', array_keys($form));
116 $form = array_merge(array_slice($form, 0, $pos), $cmt_vocabulary, array_slice($form, $pos));
117 }
118 elseif ($form_id == 'taxonomy_vocabulary_confirm_delete') {
119 $form['cmt_deleete'] = array(
120 '#type' => 'markup',
121 '#value' => '<div>' . t('<em>Deleting the community-managed vocabulary</em> @name <em>will also delete all community suggestions and choices about term position and information that was associated with it.</em>', array('@name' => $form['name']['#value'])) . '</div>',
122 '#weight' => 10,
123 );
124 $form['actions']['#weight'] = 11;
125 }
126 }
127
128 /**
129 * Implementation of hook_taxonomy()
130 *
131 * This could be moved to an admin-only section that is loaded for *every*
132 * administration page. It stays here until that is determined.
133 */
134 function cmt_taxonomy($op, $type, $array) {
135 switch ($type) {
136 case 'vocabulary':
137 // we're only interested if it's newly community-managed
138 if ($array['cmt_vocabulary']) {
139 // would it be good, bad, or neutral practice to put this line in the if condition?
140 // initial threshold key, making 'reset' feature possible. Not after disabling though.
141 $array['cmt_enabled'] = $array['cmt_vocabulary']['#value'];
142 // we would require_once('cmt_admin.inc') here but you can't require functions inside a function. So right now we save no bandwidth, but we get the admin stuff out of MY way
143 // this file assigns default thresholds directly to the $array variable:
144 require_once('default_thresholds.inc');
145 $msg_end = t('You may want to review its <a href="@cmtsettings">community-management settings</a>.', array('@cmtsettings' => url('admin/content/cmt/edit/vocabulary/' . $array['vid'])));
146 switch($op) {
147 case 'update':
148 if (!cmt_is_vid_cmt($array['vid'])) $op = 'insert';
149 if (cmt_save_vocabulary($array, $op)) {
150 $msg = t('The vocabulary %name is now community-managed. ', array('%name' => $array['name']));
151 drupal_set_message($msg . $msg_end);
152 }
153 else drupal_set_message('<strong>Making this vocabulary community-managed failed somehow.</strong>');
154 break;
155 case 'insert':
156 $msg = t('Your new vocabulary is community-managed. ');
157 if(cmt_save_vocabulary($array, $op)) {
158 drupal_set_message($msg . $msg_end);
159 }
160 else drupal_set_message('<strong>Making your new vocabulary community-managed failed somehow.</strong>');
161 break;
162 case 'delete':
163 if(_cmt_del_vocabulary($array['vid'])) {
164 // there's no reason to say 'you just deleted a CMT' - drupal_set_message('');
165 // we do insert an extra notice in the delete confirmation form
166 }
167 break;
168 } // end switch on op
169 break;
170 } // end if cmt_vocabulary set
171 } // end switch on type
172 }
173
174 /**
175 * Implementation of hook_nodeapi().
176 */
177 function cmt_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
178 switch ($op) {
179
180 case 'insert':
181 case 'update':
182 // Form alter to remove the community managed taxonomy vocabularies from the edit form?
183 break;
184
185 case 'load':
186
187 // This may be wrong, but since I don't want to put any data in the node load---
188 // if some other module wants community tags information, we'll have a function for that
189 // Therefore we shall save all logic for node view
190 /*
191 // modeled on community_tags
192 // compare cmt_node_form_display option with inline constant, store TRUE if match
193 $node->cmt_node_form_display = variable_get('cmt_node_form_display', CMT_NODE_FORM_INLINE) == CMT_NODE_FORM_INLINE;
194 */
195 break;
196
197 case 'view':
198 /* for deciding which nodes we even have to mess with, we have two options:
199 * 1. use an array of node->types affected by CMT vocabularies saved as a variable
200 if (in_array($node->type, variable_get('casetracker_case_node_types', array('casetracker_basic_case')), TRUE)) {
201 }
202 * 2. select directly from the cmt_vocabularies and vocabulary_node_types tables
203 vocabulary_node_types: vid, type
204 we'll go with the second version, outsourced to a static variable function,
205 cmt_node_types
206 */
207 if (in_array($node->type, cmt_node_types()) && !$teaser) // make teasers an option here
208 {
209 $node->content['body']['#value'] .= $output;
210 // modeled on community_tags
211 $node->content['cmt_terms'] = array(
212 '#value' => cmt_terms_node_view($node, TRUE),
213 '#weight' => 15,
214 );
215 }
216 break;
217 }
218 }
219
220 /**
221 * List all node types affected by any CMT vocabulary.
222 */
223 function cmt_node_types() {
224 static $cmt_node_types;
225 if (!$cmt_node_types) {
226 $result = db_query('SELECT DISTINCT(v.type) AS type FROM {cmt_vocabulary} c LEFT JOIN {vocabulary_node_types} v ON c.vid = v.vid WHERE c.cmt_enabled = 1');
227 $cmt_node_types = array();
228 while ($data = db_fetch_object($result)) {
229 $cmt_node_types[] = $data->type;
230 }
231 }
232 return $cmt_node_types;
233 }
234
235 /**
236 * Delete all CMT information associated with a node.
237 *
238 * @param nid
239 * The nid of the node that is being deleted.
240 */
241 function cmt_node_delete($nid) {
242 /* I don't think there's any percentage in wrapping in a function like this:
243 if (in_array($node->type, variable_get('cmt_node_types', array('default')), TRUE)) {
244 */
245 db_query('DELETE FROM {cmt_term_node} AS t, {votingapi_vote} AS v, {votingapi_cache} AS c USING v LEFT JOIN c LEFT JOIN t WHERE v.content_id = c.content_id AND c.content_id = t.content_id AND c.content_type = "cmt_term" AND nid = %d', $nid);
246 }
247
248 /**
249 * Find all exposed community-managed terms associated with the given node,
250 * ordered by vocabulary and term weight.
251 *
252 * Like taxonomy_node_get_terms, but for cmt vocabs only.
253 * This loads ONLY exposed terms, not suggested cmt terms (aka official not proposed)
254 */
255 function cmt_taxonomy_node_get_terms($nid, $key = 'tid') {
256 // this is all encased in a function and so will be happily inefficient for now
257 $cmt_taxonomy_terms = array();
258 // taxonomy_node_get_terms should already be loaded, so we'll just use it
259 $terms = taxonomy_node_get_terms($nid);
260 $cmt_vocab_ids = array_keys(cmt_get_vocabularies_by_node($nid));
261 // in_array could be replaced by cmt_is_vid_cmt, but this may be more efficient
262 $c = count($terms);
263 // for($i=0;$i<$c;$i++) - won't work- terms key is tid
264 foreach($terms as $term) {
265 if (in_array($term->vid, $cmt_vocab_ids)) {
266 $cmt_taxonomy_terms[$term->$key] = $term;
267 }
268 }
269 return $cmt_taxonomy_terms;
270 }
271
272 /*
273 * Find all community-managed terms associated with the given node, ordered by
274 * vocabulary and term weight.
275 *
276 * Both exposed (official taxonomy) and proposed (below-threshold CMT) terms
277 * are returned.
278 */
279 function cmt_node_get_terms($nid, $key = 'tid') {
280 static $cmt_terms;
281 if (!isset($cmt_terms[$nid])) { // do we need any {vocabulary} info?
282 $result = db_query('SELECT t.*, tv.vid FROM {cmt_term_node} r INNER JOIN {cmt_term_data} t ON r.tid = t.tid INNER JOIN {cmt_term_vocab} tv ON t.tid = tv.tid INNER JOIN {cmt_vocabulary} cv ON tv.vid = cv.vid INNER JOIN {vocabulary} v ON v.vid = cv.vid WHERE r.nid = %d AND cv.cmt_enabled > 0 ORDER BY v.weight, r.vote DESC, t.weight, t.name', $nid);
283 $cmt_terms[$nid] = array();
284 while ($term = db_fetch_object($result)) {
285 $cmt_terms[$nid][$term->$key] = $term;
286 }
287 }
288 return $cmt_terms[$nid];
289 }
290
291 /**
292 * Community-managed taxonomy callback for node view.
293 */
294 function cmt_terms_node_view($node, $inline = TRUE) {
295 if (!$inline) {
296 drupal_set_title(check_plain($node->title));
297 }
298 if (user_access('access community managed taxonomy') && count($cmt_vocabs = cmt_get_vocabularies_by_node($node->nid))) {
299 $module_path = drupal_get_path('module', 'cmt') .'/';
300 drupal_add_css($module_path .'cmt.css');
301 drupal_add_js($module_path .'js/cmt_terms_node_view.js');
302 $output = '<div class="cmt-terms-node-view">';
303
304 // array('nid' => $node->nid, 'vid' => $vid, 'inline' => $inline)
305 // we only need to include this: 'tags' => $tags,
306 // if the form doesn't track that itself for errors
307
308 /* so I need to get the real taxonomy metrics and compare them to the community taxonomy variables. This way I can hide access-controlled terms. This will require me to also figure out thresholds on my own, and exclude those that are above the threshold (or, simpler, include only those below the threshold). Uh, actually, I can't fathom what the implications of restricted terms would be for a community-managed vocabulary. So I'm not going to worry about it for now.
309
310 I'm also trying to think what I can expose here for testing, 'cause this would be a great place for very hard-to-find bugs to hide
311 */
312 $cmt_terms = cmt_node_get_terms($node->nid);
313 $tax_terms = cmt_taxonomy_node_get_terms($node->nid);
314 $exposed_terms = array_intersect_key($cmt_terms, $tax_terms);
315 // official terms first
316 foreach ($exposed_terms as $tid => $term) {
317 // we don't want to run the same term twice, so remove the official from the proposed
318 unset($cmt_terms[$tid]);
319 }
320 // now, remaining proposed terms
321 foreach ($cmt_terms as $tid => $term) {
322 $output .= '<h4><a href="#">' . $term->name . '</a></h4>';
323 $output .= '<div class="term">';
324 $hierarchies = cmt_get_hierarchies($tid);
325 // drupal_set_message("tid ". $tid . " with vid " . $term->vid);
326 // $tree = cmt_get_tree($term->vid, 1);
327 // agaric_a($hierarchies);
328 // drupal_set_message("And now my way: ");
329 // $ancestors = array();
330 // cmt_get_ancestors($tid, $term->vid, $ancestors);
331 // agaric_a($ancestors);
332 $edit = array(
333 'nid' => $node->nid,
334 'tid' => $tid,
335 'vid' => $term->vid,
336 // 'term_hierarchy' => implode(' > ', $hierarchies[0]),
337 );
338 $output .= drupal_get_form('cmt_term_hierarchy_form', $edit);
339 $output .= '</div>';
340 }
341 foreach ($cmt_vocabs AS $vid => $vocab) {
342 $edit = array(
343 'nid' => $node->nid,
344 'vid' => $vid,
345 );
346 if (count($cmt_vocabs) > 1) $edit['for'] = ' '.t('for').' '. $vocab->name;
347 else $edit['for'] = '';
348 $output .= drupal_get_form('cmt_new_term_form', $edit);
349 }
350 $output .= '</div>';
351 }
352 return $output;
353 }
354
355 /**
356 * Community-managed taxonomy form for managing terms from a node
357 *
358 * Called by cmt_terms_node_view
359 */
360 function cmt_term_hierarchy_form($edit) { // $edit, $title = NULL
361 // redundant check... should be removed somewhere? Should come before anything?
362 if (!user_access('access community managed taxonomy')) {
363 return;
364 }
365 $form['term_hierarchy'] = array(
366 '#type' => 'markup',
367 // '#title' => t('Hierarchy'),
368 '#prefix' => '<div>',
369 '#value' => $edit['term_hierarchy'],
370 '#suffix' => '</div>',
371 );
372 $form['submit'] = array(
373 '#type' => 'submit',
374 '#value' => t('Endorse term at this location'),
375 );
376 $form['content_id'] = array(
377 '#value' => $edit['content_id'],
378 );
379 $form['nid'] = array(
380 '#type' => 'value',
381 '#value' => $edit['nid'],
382 );
383 $form['vid'] = array(
384 '#type' => 'value',
385 '#value' => $edit['vid'],
386 );
387 return $form;
388 }
389
390 /**
391 * Form for node view that allows adding of a new term suggestion.
392 *
393 * Look into making this a multi-form to gather initial description and weight suggestions.
394 */
395 function cmt_new_term_form($edit) {
396 $form['cmt_terms'] = array(
397 '#type' => 'textfield',
398 '#title' => t('New term') . $edit['for'],
399 '#maxlength' => 265,
400 '#size' => 40,
401 // '#autocomplete_path' => 'cmt/autocomplete/'. $edit['vid'],
402 '#attributes' => array('class' => 'cmt-new-term'),
403 '#description' => t('Text for a term name (optionally preceded by parent terms delimited by ">" to put the term in a hierarchy, which need not already be created).<span class="no-js"></span> Example: "Horn of Plenty", Vegetables > rutabaga'),
404 // it would be polite to insert the help text each vocabulary provides here
405 );
406 $form['submit'] = array(
407 '#type' => 'submit',
408 '#value' => t('Create term'),
409 );
410 $form['nid'] = array(
411 '#type' => 'value',
412 '#value' => $edit['nid'],
413 );
414 $form['vid'] = array(
415 '#type' => 'value',
416 '#value' => $edit['vid'],
417 );
418 return $form;
419 }
420
421 /**
422 * Validate new term form
423 */
424 function cmt_new_term_form_validate($form_id, $form_values) {
425 if ($form_values['cmt_terms'] == '') {
426 form_set_error('cmt_terms', t('To suggest a new term to associate with this content, you must enter some text for a term name (optionally preceded by parent terms delimited by ">" to put the term in a hierarchy, which need not already be created).'));
427 }
428 }
429
430 /**
431 * Submit callback for new term form
432 */
433 function cmt_new_term_form_submit($form_id, $form_values) {
434 // Add parsing for hierarchical, even comma-separated multiple hierarchical vocabularies, later
435 // this will technically be "cmt_new_terms_form_submit" but I think name stays
436
437 // parsing function below. Save called as many times as necessary.
438 // OR do the initial parsing in the validate command, if there's any such thing as invalid input
439
440 // first split on commas
441 $typed_value = $form_values['cmt_terms'];
442 unset($form_values['cmt_terms']);
443 $vid = $form_values['vid'];
444 // lifted from taxonomy_node_save()
445 // This regexp allows the following types of user input:
446 // this, "somecmpany, llc", "and ""this"" w,o.rks", foo bar
447 // (and, I hope, lot > of > tags)
448 $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x';
449 preg_match_all($regexp, $typed_value, $set_matches);
450 $typed_termsets = array_unique($set_matches[1]);
451 foreach ($typed_termsets as $typed_termset) {
452 // next, split each section into components of the hierarchical path, if any
453 // the regular expression here needs serious attention
454 // it is supposed to separate terms by ">"
455 $regexp = '%(?:^|>\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^">]*))%x';
456 preg_match_all($regexp, $typed_termset, $term_matches);
457 $typed_terms = array_unique($term_matches[1]);
458 $count = count($typed_terms);
459 $i = 1;
460 $pid = 0;
461 foreach ($typed_terms as $typed_term) {
462 // If a user has escaped a term (to demonstrate that it is a group,
463 // or includes a comma or quote character), we remove the escape
464 // formatting so to save the term into the database as the user intends.
465 // $typed_term = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $typed_term));
466 $typed_term = trim($typed_term);
467 $terms = cmt_get_term_by_name($typed_term, $vid);
468 if (count($terms)) { // we found a match
469 if (count($terms) > 1) drupal_set_message("There is more than one term by the name '$typed_term'. We will use the first one (ok, we're choosing one at random as far as you're concerned), but hope to provide an interface for choosing the one you want to use soon.", 'status');
470 // @TODO: do what the status message above says
471 $term = $terms[0];
472 $tid = $term->tid;
473 /*
474 if (!strcmp($term->name, $typed_term)) {
475 }
476 // actually we want to cast a vote for the typed name, new or not
477 // and that's what the function below does. We need to get the content_id
478 // for the cmt_term_name table entry, and cmt_get_term_by_name uses
479 // cmt_term_data. Performance could surely be improved here.
480 */
481 // create new name for term honoring user's exact capitalization
482 $tid_array = array(
483 'value' => $tid,
484 );
485 $name_array = array(
486 'value' => $typed_term,
487 'field' => 'name',
488 'type' => "'%s'",
489 );
490 cmt_term_attribute_set($tid_array, $name_array, 'cmt_term_name', $vid);
491 }
492 else { // no match
493 // get a term ID, insert a new term name, and join to a vocabulary
494 $cmt_term = cmt_term_create($typed_term, $vid);
495 $tid = $cmt_term['tid'];
496 }
497 if ($i == $count) { // last term in set
498 // needs to be associated with the node in question
499 $nid_array = array(
500 'value' => $form_values['nid'],
501 'field' => 'nid',
502 );
503 $tid_array = array(
504 'value' => $tid,
505 'field' => 'tid',
506 );
507 cmt_term_attribute_set($nid_array, $tid_array, 'cmt_term_node', $vid);
508 }
509 // set hierarchy (parent)
510 $tid_array = array(
511 'value' => $tid,
512 );
513 $pid_array = array(
514 'value' => $pid,
515 'field' => 'parent',
516 );
517 cmt_term_attribute_set($tid_array, $pid_array, 'cmt_term_hierarchy', $vid);
518 $pid = $tid;
519 $i++;
520 }
521 }
522 // return not needed unless you're going somewhere else
523 // return array('node/'. $form_values['nid']);
524 }
525
526 function cmt_new_term_full_form_submit($form_id, $form_values) {
527 $vid = $form_values['vid'];
528 $tid = array(
529 'value' => $form_values['tid'],
530 );
531 $description = array(
532 'value' => $form_values['description'],
533 'field' => 'description',
534 'type' => "'%s'",
535 );
536 cmt_term_attribute_set($tid, $description, 'cmt_term_description', $vid);
537 }
538
539 /**
540 * Create a CMT term with an initial name and acquire a tid, and vote for that
541 * name and assign the term to a vocabulary
542 *
543 * This does NOT check if it already exists. You have to have done that
544 * already, and you'll have to add the parent afterward.
545 *
546 * @param $name
547 * Name of the term to create.
548 * @param $vid
549 * A vocabulary vid to assign the term to.
550 *
551 * @return
552 * An array with the term ID (tid)
553 * (The content_id has already been used to vote and isn't needed,
554 * but an array gives us easier future expansion.)
555 */
556 function cmt_term_create($name, $vid, $vote = 1, $uid = NULL) {
557 // in which cmt_term_data becomes much more than a denormalised table
558 // and becomes the keeper of the CMT tid sequence
559 $result = db_query("INSERT INTO {cmt_term_data} SET name = '%s'", $name);
560 if (!$result) {
561 drupal_set_error("Creating new term (inserting int cmt_term_data to get a tid) failed.");
562 return FALSE;
563 }
564 $tid = cmt_db_last_insert_id('cmt_term_data', 'tid');
565 drupal_set_message("TID: " . $tid, 'status');
566 $tid_array = array(
567 'value' => $tid,
568 );
569 $name_array = array(
570 'value' => $name,
571 'field' => 'name',
572 'type' => "'%s'",
573 );
574 // 4th parameter "TRUE" to turn off a redundant SQL check
575 cmt_term_attribute_set($tid_array, $name_array, 'cmt_term_name', $vid, TRUE);
576
577 // all new terms get attached to a vocabulary, so we'll put that in here and ignore it forevermore
578 cmt_term_vocab_set($tid, $vid, TRUE); // set $new to TRUE: we just created the tid
579 $cmt_term = array(
580 'tid' => $tid,
581 );
582 return $cmt_term;
583 }
584
585 /**
586 * Saves a community-managed term's relationship to a vocabulary.
587 *
588 * cmt_term_vocab_set is different from the cmt_term_attribute_set function
589 * because we do not use votingapi. Users cannot vote on what vocabulary a
590 * term belongs in at this time! We still need to tie a term to a vocab.
591 *
592 * @param $tid
593 * A CMT term ID.
594 * @param $vid
595 * A vocabulary ID of a vocabulary that is community-managed.
596 * @param $new
597 * An optional flag to skip the if-existing check
598 *
599 * @return
600 * A database query result resource or TRUE if the term is already assigned
601 * to the vocabulary; FALSE if the query was not executed correctly.
602 */
603 function cmt_term_vocab_set($tid, $vid, $new = FALSE) {
604 // we won't need this to fetch the content_id, but we will for the others
605 if ($new || !$result = db_query("SELECT * FROM {cmt_term_vocab} WHERE tid=%d AND vid=%d", $tid, $vid)) {
606 // returns a resource ID or FALSE - a resource ID is as good as TRUE, right?
607 return db_query("INSERT INTO {cmt_term_vocab} (tid, vid) VALUES (%d, %d)", $tid, $vid);
608 // no reason to check against a term being in multiple vocabularies
609 // not that this should be able to happen in the current setup
610 }
611 else return TRUE; // it's already in there
612 }
613
614 /**
615 * Saves a community-managed term's attribute and updates votingAPI.
616 *
617 * First it checks to make sure the attribute doesn't already exist for
618 * this term (as unlikely as that might be), and if it does, uses that
619 * content_id rather than inserting a new row.
620 *
621 * @param $id
622 * An array with a 'value' set to the CMT term ID or node ID, and
623 * optionally a field name other than tid
624 * @param $attribute
625 * An array with the attribute for that CMT term, including:
626 * 'value' which can be a numeric type, longtext
627 * 'field' the field name that will be saved
628 * 'type' (optional) if not %d, define how db_query should take this
629 * @param $content_type
630 * A string of the content_type, also the cmt_term_* table name
631 * @param $new
632 * Optional parameter to assert a new insert, skip the SQL select
633 * @param $value
634 * Optional vote value if more than 1
635 * @param $uid
636 * Optional uid if not active user
637 *
638 * @return
639 * INSERT if a new description, UPDATE if the description was already
640 * proposed for the term, and FALSE if a query failed.
641 */
642 function cmt_term_attribute_set($id, $attribute, $content_type, $vid, $new = FALSE, $value = 1, $uid = NULL) {
643 // set defaults
644 if (!$id['field']) $id['field'] = 'tid';
645 if (!$attribute['type']) $attribute['type'] = '%d';
646 $return = 'INSERT'; // if not overridden, insert new id-attribute pair
647 if (!$new) {
648 $result = db_query("SELECT content_id FROM {".$content_type."} WHERE ".$id['field']."=%d AND ".$attribute['field']."=".$attribute['type'], $id['value'], $attribute['value']);
649 if (!$result) {
650 drupal_set_error("Select query to check for description failed.", 'error');
651 // how do i do this right? http://agaricdesign.com/throwing-errors-in-drupal-agarics-complete-guide
652 return FALSE;
653 }
654 if ($cmt_term_attribute = db_fetch_object($result)) {
655 // should I check to make sure nothing crazy has happened and there's somehow two rows?
656 $content_id = $cmt_term_attribute->content_id;
657 // this is not a new attribute, we won't insert
658 $return = 'UPDATE';
659 }
660 }
661 if ($return == 'INSERT') {
662 $result = db_query("INSERT INTO {".$content_type."} (".$id['field'].", ".$attribute['field'].") VALUES (%d, ".$attribute['type'].")", $id['value'], $attribute['value']);
663 if (!$result) {
664 drupal_set_error("Inserting new term description failed.");
665 return FALSE;
666 }
667 $content_id = cmt_db_last_insert_id($content_type, 'content_id');
668 }
669 // content_id has been set by select or cmt_db_last_insert_id
670 cmt_vote($content_type, $content_id, $vid);
671 // downstream of cmt_vote will have to throw its own errors if anything goes wrong
672 // this function can't take any more feedback!
673 return $return;
674 }
675
676 /**
677 * Inserts or updates a vote count for a facet of community-managed taxonomy, and
678 * checks to see if votes have passed thresholds or surpassed existing results.
679 *
680 * This function accepts a content_type, content_id, and user object (defaults to current)
681 * optionally it can also take a value, the default is one
682 * It calls votingapi
683 */
684 function cmt_vote($content_type, $content_id, $vid, $value = 1, $uid = NULL) {
685 $vote = new stdClass();
686 $vote->tag = 'vote';
687 $vote->value_type = 'points';
688 $vote->value = $value;
689 $votingapi_cache = votingapi_set_vote($content_type, $content_id, $vote, $uid);
690 return cmt_push($content_type, $content_id, $votingapi_cache, $vid);
691 }
692
693 /**
694 * Calls the appropriate function check what effect a vote had vis-a-vis
695 * other votes and thresholds, and adjusts official taxonomy as needed.
696 */
697 function cmt_push($content_type, $content_id, $votingapi_cache, $vid) {
698 // will not be necessary every call so will be moved later
699 $high_content = cmt_get_high($content_id, $vid);
700 $votes = cmt_get_votes($content_type, $content_id);
701 $threshold = cmt_get_threshold($content_type, $vid);
702 switch ($content_type) {
703 case 'cmt_term_description':
704 return cmt_term_description_push($content_id);
705 case 'cmt_term_name':
706 if($votes > $threshold) {
707 return cmt_term_name_push($content_id);
708 }
709 break;
710 case 'cmt_term_node':
711 if($content_id == $high_content && $votes > $threshold) {
712 return cmt_term_node_push($content_id);
713 }
714 break;
715 }
716 }
717
718 function cmt_term_description_push($content_id) {
719 // there can be only one description, makes the SQL simpler
720 agaric_pr($votingapi_cache);
721 }
722
723 function cmt_term_name_push($content_id) {
724 }
725
726 function cmt_term_node_push($content_id) {
727
728 }
729
730 /**
731 * A simple helper function that returns a single cached voting result.
732 *
733 * @param $content_type
734 * A string identifying the type of content (cmt_term_*) whose votes are
735 * being retrieved.
736 * @param $content_id
737 * The key ID of the content whose votes are being retrieved.
738 * @return
739 * A single votes result.
740 */
741 function cmt_get_votes($content_type, $content_id) {
742 $result = db_query("SELECT value FROM {votingapi_cache} WHERE content_type='%s' AND content_id=%d AND value_type='points' AND tag='vote' AND function='sum'", $content_type, $content_id);
743 $voting_results = db_fetch_object($result);
744 return $voting_results->value;
745 }
746
747 /**
748 * Get highest vote for a content type
749 *
750 * Big old logical flaw. We need to know the highest vote *by vocabulary*
751 * We'll need to add an inner join to cmt_term_* on content_id
752 * and to cmt_term_vocab on tid to get the vocab
753 */
754 function cmt_get_high($content_type, $vid, $limit = 1) {
755 $result = db_query("SELECT content_id FROM {votingapi_cache} v WHERE content_type='%s' AND value_type='points' AND tag='vote' AND function='sum' ORDER BY value DESC LIMIT %d", $content_type, $limit);
756 $voting_results = db_fetch_object($result);
757 return $voting_results->content_id;
758 }
759
760 /**
761 * Get thresholds for votes.
762 *
763 * Static function so multiple calls are not a performance hit.
764 */
765 function cmt_get_threshold($content_type, $vid) {
766 static $thresholds = array();
767 if (!isset($thresholds[$content_type][$vid])) {
768 $threshold = substr_replace($content_type, '', 4, 5) . '_th';
769 $c = db_query("SELECT %s FROM {cmt_vocabulary} WHERE vid = %d", $threshold, $vid);
770 $results = db_fetch_object($c);
771 $thresholds[$content_type][$vid] = $results->$threshold;
772 }
773 return $thresholds[$content_type];
774 }
775
776 /******************************************************************************
777 * Utility, helper and wrapper functions
778 */
779
780 /**
781 * Return an array of all vocabulary objects for community-managed
782 * vocabularies.
783 *
784 * @param $cmt_enabled
785 * Return only those CMT vocabularies which are either 'enabled' or
786 * 'disabled'. Default both.
787 */
788 function cmt_get_vocabularies($cmt_enabled = 'both') {
789 $where = "";
790 if ($cmt_enabled == 'enabled') $where = "WHERE cv.cmt_enabled > 0 ";
791 elseif ($cmt_enabled == 'disabled') $where = "WHERE cv.cmt_enabled = 0 ";
792 $c = db_query(db_rewrite_sql("SELECT v.*, n.type FROM {vocabulary} v INNER JOIN {cmt_vocabulary} cv ON v.vid = cv.vid LEFT JOIN {vocabulary_node_types} n ON v.vid = n.vid $where ORDER BY v.weight, v.name", 'v', 'vid'), $where);
793 // below identical to taxonomy_get_vocabularies
794 // couldn't we - and taxonomy core - just use taxonomy_get_vocabulary in a loop?
795 $vocabularies = array();
796 $node_types = array();
797 while ($voc = db_fetch_object($c)) {
798 $node_types[$voc->vid][] = $voc->type;
799 unset($voc->type);
800 $voc->nodes = $node_types[$voc->vid];
801 $vocabularies[$voc->vid] = $voc;
802 }
803 return $vocabularies;
804 }
805
806 /**
807 * Return an array of vocabulary objects for enabled community-managed
808 * vocabularies applicable to a given node type.
809 *
810 * Static by node nid for performance.
811 * Vocabulary objects do not include node types.
812 */
813 function cmt_get_vocabularies_by_node($nid) {
814 static $vocabularies = array();
815 if (!array_key_exists($nid, $vocabularies)) {
816 $node = node_load($nid);
817 $c = db_query(db_rewrite_sql("SELECT v.* FROM {vocabulary} v INNER JOIN {cmt_vocabulary} cv ON v.vid = cv.vid INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' AND cv.cmt_enabled > 0 ORDER BY v.weight, v.name", 'v', 'vid'), $node->type);
818 while ($voc = db_fetch_object($c)) {
819 $vocabularies[$nid][$voc->vid] = $voc;
820 }
821 }
822 return $vocabularies[$nid];
823 }
824
825 /**
826 * Return CMT settings information for the vocabulary object matching a vocabulary ID.
827 *
828 * @param $vid
829 * The vocabulary's ID
830 *
831 * @return Object
832 * The vocabulary object with all of its metadata.
833 * Results are statically cached.
834 */
835 function cmt_get_vocabulary($vid) {
836 static $cmt_vocabularies = array();
837 if (!array_key_exists($vid, $cmt_vocabularies)) {
838 $result = db_query('SELECT c.* FROM {cmt_vocabulary} c WHERE c.vid = %d', $vid);
839 $cmt_vocabularies[$vid] = (array)db_fetch_object($result);
840 }
841 return $cmt_vocabularies[$vid];
842 }
843
844 /**
845 * Get basic term data for a given tid.
846 */
847 function cmt_get_term($tid) {
848 return db_fetch_object(db_query("SELECT t.* FROM {cmt_term_data} t WHERE tid = %d", $tid));
849 }
850 /**
851 * Try to map a string to an existing CMT term.
852 *
853 * Provides a case-insensitive and trimmed mapping, to maximize the
854 * likelihood of a successful match.
855 *
856 * Modeled on taxonomy_get_term_by_name, but with the enhancement of taking an
857 * optional vocabulary ID (vid) to restrict the searching up front
858 * db_rewrite_sql not used, anyone trying to do access control on proposed cmt
859 * terms will have to go through me first
860 *
861 * @param name
862 * Name of the cmt term to search for.
863 *
864 * @return
865 * An array of matching cmt term objects.
866 */
867 function cmt_get_term_by_name($name, $vid = NULL) {
868 if ($vid) {
869 $db_result = db_query("SELECT t.tid, t.* FROM {cmt_term_data} t INNER JOIN {cmt_term_vocab} v ON t.tid = v.tid WHERE v.vid=%d AND LOWER('%s') LIKE LOWER(t.name)", $vid, trim($name));
870 }
871 else {
872 $db_result = db_query("SELECT t.tid, t.* FROM {cmt_term_data} t WHERE LOWER('%s') LIKE LOWER(t.name)", trim($name));
873 }
874 $result = array();
875 while ($term = db_fetch_object($db_result)) {
876 $result[] = $term;
877 }
878 return $result;
879 }
880
881 /**
882 * Find all parents of a given term ID.
883 */
884 function cmt_get_parents($tid, $key = 'tid') {
885 if ($tid) {
886 // need object, so no good: 'SELECT parent FROM {cmt_term_hierarchy} WHERE tid = %d'
887 $result = db_query('SELECT t.tid, t.* FROM {cmt_term_data} t INNER JOIN {cmt_term_hierarchy} h ON h.parent = t.tid WHERE h.tid = %d ORDER BY h.vote DESC, weight, name', $tid);
888 $parents = array();
889 while ($parent = db_fetch_object($result)) {
890 $parents[$parent->$key] = $parent;
891 }
892 return $parents;
893 }
894 else {
895 return array();
896 }
897 }
898
899 /**
900 *
901 *
902 * Extremely loosely based on taxonomy_form_term_submit($form_id, $form_values)
903 * "Accept the form submission for a taxonomy term and save the result."
904 *
905 * The fundamental questions all people must ask themselves at one point in their lives:
906 * What is the core nature of a taxonomy term?
907 * What is the minimum aspects of the term necessary before we push it live?
908 * According to this taxonomy_form_term_submit:
909 * tid
910 * name
911 * description (we know that's optional)
912 * weight (can default to 0)
913 * relation (optional)
914 * hierarchy (0 if nothing else)
915 * synonym
916 */
917 function cmt_expose_term(&$form_values) {
918 if ($form_values['tid'] && $form_values['name']) {
919 db_query("UPDATE {term_data} SET name = '%s', description = '%s', weight = %d WHERE tid = %d", $form_values['name'], $form_values['description'], $form_values['weight'], $form_values['tid']);
920 $hook = 'update';
921 $status = SAVED_UPDATED;
922 }
923 else if ($form_values['tid']) {
924 return taxonomy_del_term($form_values['tid']);
925 }
926 else {
927 $form_values['tid'] = db_next_id('{term_data}_tid');
928 db_query("INSERT INTO {term_data} (tid, name, description, vid, weight) VALUES (%d, '%s', '%s', %d, %d)", $form_values['tid'], $form_values['name'], $form_values['description'], $form_values['vid'], $form_values['weight']);
929 $hook = 'insert';
930 $status = SAVED_NEW;
931 }
932 db_query('DELETE FROM {term_relation} WHERE tid1 = %d OR tid2 = %d', $form_values['tid'], $form_values['tid']);
933 if ($form_values['relations']) {
934 foreach ($form_values['relations'] as $related_id) {
935 if ($related_id != 0) {
936 db_query('INSERT INTO {term_relation} (tid1, tid2) VALUES (%d, %d)', $form_values['tid'], $related_id);
937 }
938 }
939 }
940
941 db_query('DELETE FROM {term_hierarchy} WHERE tid = %d', $form_values['tid']);
942 if (!isset($form_values['parent']) || empty($form_values['parent'])) {
943 $form_values['parent'] = array(0);
944 }
945 if (is_array($form_values['parent'])) {
946 foreach ($form_values['parent'] as $parent) {
947 if (is_array($parent)) {
948 foreach ($parent as $tid) {
949 db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $tid);
950 }
951 }
952 else {
953 db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $parent);
954 }
955 }
956 }
957 else {
958 db_query('INSERT INTO {term_hierarchy} (tid, parent) VALUES (%d, %d)', $form_values['tid'], $form_values['parent']);
959 }
960
961 db_query('DELETE FROM {term_synonym} WHERE tid = %d', $form_values['tid']);
962 if ($form_values['synonyms']) {
963 foreach (explode ("\n", str_replace("\r", '', $form_values['synonyms'])) as $synonym) {
964 if ($synonym) {
965 db_query("INSERT INTO {term_synonym} (tid, name) VALUES (%d, '%s')", $form_values['tid'], chop($synonym));
966 }
967 }
968 }
969
970 if (isset($hook)) {
971 module_invoke_all('taxonomy', $hook, 'term', $form_values);
972 }
973
974 cache_clear_all();
975
976 return $status;
977 }
978
979
980
981 /**
982 * Create a hierarchical representation of a vocabulary.
983 *
984 * @param $vid
985 * Which vocabulary to generate the tree for.
986 *
987 * @param $parent
988 * The term ID under which to generate the tree. If 0, generate the tree
989 * for the entire vocabulary.
990 *
991 * @param $depth
992 * Internal use only.
993 *
994 * @param $max_depth
995 * The number of levels of the tree to return. Leave NULL to return all levels.
996 *
997 * @return
998 * An array of all term objects in the tree. Each term object is extended
999 * to have "depth" and "parents" attributes in addition to its normal ones.
1000 * Results are statically cached.
1001 */
1002 function cmt_get_tree($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
1003 static $children, $parents, $terms;
1004 $depth++;
1005 // We cache trees, so it's not CPU-intensive to call get_tree() on a term
1006 // and its children, too.
1007 if (!isset($children[$vid])) {
1008 $children[$vid] = array();
1009 $result = db_query('SELECT t.tid, t.*, parent FROM {cmt_term_data} t INNER JOIN {cmt_term_vocab} v ON t.tid = v.tid INNER JOIN {cmt_term_hierarchy} h ON t.tid = h.tid WHERE v.vid = %d ORDER BY weight, name', $vid);
1010 while ($term = db_fetch_object($result)) {
1011 $children[$vid][$term->parent][] = $term->tid;
1012 $parents[$vid][$term->tid][] = $term->parent;
1013 $terms[$vid][$term->tid] = $term;
1014 }
1015 }
1016 $max_depth = (is_null($max_depth)) ? count($children[$vid]) : $max_depth;
1017 if ($children[$vid][$parent]) {
1018 foreach ($children[$vid][$parent] as $child) {
1019 if ($max_depth > $depth) {
1020 $term = drupal_clone($terms[$vid][$child]);
1021 $term->depth = $depth;
1022 // The "parent" attribute is not useful, as it would show one parent only.
1023 unset($term->parent);
1024 $term->parents = $parents[$vid][$child];
1025 $tree[] = $term;
1026 if ($children[$vid][$child]) {
1027 $tree = array_merge($tree, cmt_get_tree($vid, $child, $depth, $max_depth));
1028 }
1029 }
1030 }
1031 }
1032 return $tree ? $tree : array();
1033 }
1034
1035 /**
1036 * Create a hierarchical representation of a vocabulary.
1037 *
1038 * @param $vid
1039 * Which vocabulary to generate the tree for.
1040 *
1041 * @param $parent
1042 * The term ID under which to generate the tree. If 0, generate the tree
1043 * for the entire vocabulary.
1044 *
1045 * @param $depth
1046 * Internal use only.
1047 *
1048 * @param $max_depth
1049 * The number of levels of the tree to return. Leave NULL to return all levels.
1050 *
1051 * @return
1052 * An array of all term objects in the tree. Each term object is extended
1053 * to have "depth" and "parents" attributes in addition to its normal ones.
1054 * Results are statically cached.
1055 */
1056 function cmt_get_roots($vid, $tid = 0, $depth = -1, $max_depth = NULL) {
1057 static $ancestors, $parents, $terms;
1058 $depth++;
1059 // We cache trees, so it's not CPU-intensive to call get_tree() on a term
1060 // and its children, too.
1061 if (TRUE || !isset($ancestors[$vid][$tid])) {
1062 $ancestors[$vid][$tid] = array();
1063 $result = db_query('SELECT t.tid, t.*, parent FROM {cmt_term_data} t INNER JOIN {cmt_term_vocab} v ON t.tid = v.tid INNER JOIN {cmt_term_hierarchy} h ON t.tid = h.tid WHERE t.tid = %d AND v.vid = %d ORDER BY weight, name', $tid, $vid);
1064 while ($term = db_fetch_object($result)) {
1065 $ancestors[$vid][$tid][] = $term->parent;
1066 /*
1067 $children[$vid][$tid][$term->parent][] = $term->tid;
1068 $parents[$vid][$term->tid][] = $term->parent;
1069 $terms[$vid][$term->tid] = $term;
1070 */
1071 }
1072 }
1073 $max_depth = (is_null($max_depth)) ? count($ancestors[$vid][$tid]) : $max_depth;
1074 if ($children[$vid][$parent]) {
1075 foreach ($children[$vid][$parent] as $child) {
1076 if ($max_depth > $depth) {
1077 $term = drupal_clone($terms[$vid][$child]);
1078 $term->depth = $depth;
1079 // The "parent" attribute is not useful, as it would show one parent only.
1080 unset($term->parent);
1081 $term->parents = $parents[$vid][$child];
1082 $roots[] = $term;
1083 if ($children[$vid][$child]) {
1084 $roots = array_merge($roots, cmt_get_roots($vid, $parent, $depth, $max_depth));
1085 }
1086 }
1087 }
1088 }
1089 return $roots ? $roots : array();
1090 }
1091
1092 function cmt_get_ancestors($tid, $vid, &$ancestors, $stack=array(), $thread = 0) {
1093 $result = db_query('SELECT t.tid, t.*, parent FROM {cmt_term_data} t INNER JOIN {cmt_term_vocab} v ON t.tid = v.tid INNER JOIN {cmt_term_hierarchy} h ON t.tid = h.tid WHERE t.tid = %d AND v.vid = %d ORDER BY weight, name', $tid, $vid);
1094 $parents = array();
1095 while ($term = db_fetch_object($result)) {
1096 $parents[] = $term->parent;
1097 }
1098 // agaric_a($parents);
1099 if (count($parents) == 0) {
1100 $ancestors[$thread] = $stack;
1101 $stack = array();
1102 $thread++;
1103 }
1104 else {
1105 foreach ($parents as $parent) {
1106 $stack[] = cmt_get_ancestors($parent, $vid, $ancestors, $stack, $thread);
1107 }
1108 }
1109 return $parents;
1110 }
1111
1112 function cmt_brian($tid) {
1113
1114 }
1115
1116 /**
1117 * I am the funkiest function. ... and I don't work. Anyone want to explain why?
1118 */
1119 function cmt_get_hierarchies($tid) {
1120 $term = cmt_get_term($tid);
1121 $hierarchies = array();
1122 $hierarchies[0] = array(0 => $term);
1123 $root = 0;
1124 $h = count($hierarchies);
1125 for ($i = 0; $i < $h; $i++) {
1126 if ($hierarchies[$i][count($hierarchies[$i])-1] == "root") {
1127 $root++;
1128 if($root == $h) {
1129 // for($k=0;$k<$h;$k++) {
1130 // $hierarchies[$k] = array_reverse($hierarchies[$k]);
1131 // }
1132 return $hierarchies;
1133 }
1134 }
1135 else {
1136 $parents = array_values(cmt_get_parents($hierarchies[$i][count($hierarchies[$i])-1]->tid));
1137 // agaric_a($parents);
1138 $c = count($parents);
1139 if ($c > 1) $copy = $hierarchies[$i];
1140 if (!$parents[0]) $parents[0] = "root";
1141 $hierarchies[$i][] = $parents[0];
1142 for ($j = 1; $j < $c; $j++) {
1143 $hierarchies[$i+$j] = $copy;
1144 if(!$parents[$j]) $parents[$j] = "root";
1145 $hierarchies[$i+$j][] = $parents[$j];
1146 }
1147 if ($hierarchies[$i][0] == "root") unset($hierarchies[$i]);
1148 $h = count($hierarchies);
1149 $i = 0;
1150 }
1151 }
1152 }
1153
1154 /**
1155 * Find all ancestors of a given term ID.
1156 */
1157 function cmt_get_parents_all($tid) {
1158 $parents = array();
1159 if ($tid) {
1160 $parents[] = cmt_get_term($tid);
1161 $n = 0;
1162 while ($parent = taxonomy_get_parents($parents[$n]->tid)) {
1163 $parents = array_merge($parents, $parent);
1164 $n++;
1165 }
1166 }
1167 return $parents;
1168 }
1169
1170 /**
1171 * Return true if a vocabulary ID belongs to a community-managed vocabulary.
1172 *
1173 * I used this a few times and it could probably be more efficient as a direct
1174 * query, so let's wrap it up for easy optimization later.
1175 */
1176 function cmt_is_vid_cmt($vid) {
1177 return in_array($vid, array_keys(cmt_get_vocabularies()));
1178 }
1179
1180 /**
1181 * Returns the last insert id.
1182 *
1183 * The need for this will go away in Drupal 6
1184 *
1185 * @param table
1186 * The name of the table you inserted into.
1187 * @param field
1188 * The name of the autoincrement field.
1189 */
1190 function cmt_db_last_insert_id($table, $field) {
1191 switch ($GLOBALS['db_type']) {
1192 case 'mysql':
1193 case 'mysqli':
1194 return db_result(db_query('SELECT LAST_INSERT_ID()'));
1195 break;
1196 case 'pgsql':
1197 // from http://api.drupal.org/api/file/includes/database.pgsql.inc/6/source
1198 return db_result(db_query("SELECT currval('%s_seq')", db_prefix_tables('{'. $table .'}') . '_'. $field));
1199 break;
1200 default:
1201 return drupal_set_message(t('Database type @type not supported', array('@type' => $GLOBALS['db_type'])), 'error');
1202 }
1203 }
1204
1205 /* to be split out into an agaric devel module */
1206 function agaric_pr($array) {
1207 print '<pre>';
1208 print_r($array);
1209 print '</pre>';
1210 }
1211
1212 function agaric_a($array) {
1213 drupal_set_message('<pre>' . print_r($array, true) . '</pre>');
1214 }

  ViewVC Help
Powered by ViewVC 1.1.2