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

Contents of /contributions/modules/glossary/glossary.module

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


Revision 1.122 - (show annotations) (download) (as text)
Fri Jan 4 03:14:22 2008 UTC (22 months, 3 weeks ago) by nancyw
Branch: MAIN
CVS Tags: HEAD
Changes since 1.121: +52 -52 lines
File MIME type: text/x-php
trying to get a 5.x-2.x-dev version
1 <?php
2 // $Id: glossary.module,v 1.121 2007/12/19 23:01:57 nancyw Exp $
3
4 function glossary_help($section) {
5 $output = '';
6
7 switch ($section) {
8 case 'admin/help#glossary':
9 return t('<p>Glossary helps newbies understand the jargon which always crops up when specialists talk about a topic. Doctors discuss CBC and EKG and CCs. Web developers keep talking about CSS, P2P, XSLT, etc. This is all intimidating for newbies.</p>
10 <p>The Glossary module scans posts for glossary terms (and their synonyms) in the body. If found, the glossary indicator is inserted after the term, or the term is turned into an indicator link depending on the settings. By hovering over the indicator, users may learn the definition of that term. Clicking leads the user to that term presented within the whole glossary.</p>
11 <p>Glossary terms are managed as vocabularies within the taxonomy.module. To get started with glossary, create a new vocabulary on the !taxonomy_admin page. The vocabulary need not be associated with any modules, although you can add detailed description to terms by attaching (story or other type of) nodes to them. Add a few terms to the vocabulary. The term title should be the glossary entry and the description should be its definition. You can make use of the hierarchy, synonym, and related terms features. These features impact the display of the glossary when viewed in an overview.</p>
12 <p>Next, you have to setup the input formats you want to use the glossary with. At the !input_formats page select an input format to configure. Select the Glossary filter checkbox and press Save configuration. Now select the configure filters tab and select the vocabulary and apply other settings.</p>
13 <p>You can see how a vocabulary would function as a glossary by going to the !glossaries page and selecting the vocabulary to view.</p>
14 <p>Administration of glossary requires <em>%permissions</em> permissions.</p>',
15 array('%permissions' => join(', ',
16 array(t('administer taxonomy'), t('access administration pages'), t('administer filters'))),
17 '!taxonomy_admin' => l(t('administer') .' &raquo; '. t('content') .' &raquo; '. t('categories'), 'admin/content/taxonomy', array(), null, null, false, true),
18 '!input_formats' => l(t('administer') .' &raquo; '. t('site configuration') .' &raquo; '. t('Glossary'), 'admin/settings/glossary', array(), null, null, false, true),
19 '!glossaries' => l(t('glossaries'), 'glossary')));
20 break;
21
22 case 'admin/settings/glossary':
23 return '<p><big>'. t('This page and its tabs allow you to control how the Glossary module functions') .'</big></p>';
24 break;
25
26 case 'admin/modules#description':
27 return t('Maintain a glossary on your site.');
28 break;
29
30 case 'filter#long-tip':
31 case 'filter#short-tip':
32 return t('Glossary terms will be automatically marked with links to their descriptions');
33 }
34 }
35
36 /**
37 * Implementation of hook_block().
38 */
39 function glossary_block($op = 'list', $delta = 0, $edit = array()) {
40 if ($op == 'list') {
41 $blocks = array();
42 $blocks[0]['info'] = t('Search for glossary terms');
43 return $blocks;
44 }
45 else if ($op == 'view') {
46 if ($delta == 0) {
47 $block['subject'] = t('Search Glossary');
48 $block['content'] = drupal_get_form('glossary_search_form');
49 }
50 return $block;
51 }
52 }
53
54 /**
55 * Implementation of hook_menu().
56 *
57 * This is also the place where we add a link to the CSS sheet.
58 * We need the glossary filter to be a caching filter, because it is
59 * relatively slow. If content is cached, no calls at all are made to
60 * this module for that content. That means we can not detect when the
61 * CSS sheet is actually needed. Therefore, we simply link it in always.
62 * This should really happen always, so we do it when $may_cache == false.
63 */
64 function glossary_menu($may_cache) {
65 if ($may_cache) {
66 $items[] = array(
67 'path' => 'glossary/search',
68 'title' => t('Glossary'),
69 'callback' => 'glossary_search_results',
70 'access' => user_access('access content'),
71 'type' => MENU_CALLBACK,
72 );
73
74 $items[] = array(
75 'path' => 'glossary',
76 'title' => t('Glossary'),
77 'callback' => 'glossary_page',
78 'access' => user_access('access content'),
79 'type' => MENU_SUGGESTED_ITEM,
80 );
81
82 $items[] = array(
83 'path' => 'admin/settings/glossary',
84 'title' => t('Glossary'),
85 'callback' => 'glossary_settings_page',
86 'description' => t('Select how you want the Glossary module to behave.'),
87 'access' => user_access('administer filters'),
88 'type' => MENU_NORMAL_ITEM,
89 );
90
91 $items[] = array(
92 'path' => 'admin/settings/glossary/general',
93 'title' => t('General'),
94 'description' => t('General settings'),
95 'callback' => 'drupal_get_form',
96 'callback arguments' => array('glossary_general_settings_form'),
97 'access' => user_access('administer filters'),
98 'type' => MENU_DEFAULT_LOCAL_TASK,
99 );
100
101 $result = db_query('SELECT format, name FROM {filter_formats}');
102 while ($filter = db_fetch_array($result)) {
103 $items[] = array(
104 'path' => 'admin/settings/glossary/filter'. $filter['format'],
105 'title' => $filter['name'],
106 'access' => user_access('administer filters'),
107 'callback' => 'drupal_get_form',
108 'callback arguments' => array('glossary_filter_form', $filter['format']),
109 'description' => t('Settings for the !name input format.', array('!name' => $filter['name'])),
110 'weight' => 2,
111 'type' => MENU_LOCAL_TASK,
112 );
113 }
114 }
115 else {
116 drupal_add_css(drupal_get_path('module', 'glossary') .'/glossary.css');
117 if (arg(2) ) {
118 $items[] = array(
119 'path' => 'glossary/term/'. arg(2),
120 'title' => t('Glossary'),
121 'callback' => 'glossary_term',
122 'callback arguments' => array(arg(2)),
123 'access' => user_access('access content'),
124 'type' => MENU_CALLBACK,
125 );
126 }
127 }
128 return $items;
129 }
130
131 function glossary_search_form($keys = '') {
132 $form['#action'] = url('glossary/search');
133 $form['#attributes'] = array('class' => 'glossary search-form');
134 $form['keys'] = array(
135 '#type' => 'textfield',
136 '#title' => '',
137 '#default_value' => $keys,
138 '#size' => 20,
139 '#maxlength' => 255,
140 );
141 $form['submit'] = array(
142 '#type' => 'submit',
143 '#value' => t('Search'),
144 );
145 return $form;
146 }
147
148 function glossary_search_form_submit($form_id, $form_values) {
149 $keys = trim($form_values['keys']);
150 return "glossary/search/$keys";
151 }
152
153 function glossary_search_results($keys = null) {
154 $vids = _glossary_get_filter_vids();
155 $output = '';
156
157 $sql = "SELECT tid FROM {term_data} WHERE vid IN (%s) AND (description LIKE '%%%s%%' OR name LIKE '%%%s%%')";
158 $result = db_query($sql, implode(',', $vids), $keys, $keys);
159 while ($row = db_fetch_object($result)) {
160 $term = taxonomy_get_term($row->tid);
161 $output .= theme('glossary_overview_item', $term);
162 }
163 return $output;
164 }
165
166 function theme_glossary_search_form($form) {
167 return '<div class="container-inline">'. drupal_render($form) .'</div>';
168 }
169
170 function glossary_term($tid) {
171 $result = db_query('SELECT * FROM {term_data} WHERE tid = %d', $tid);
172 $output .= '<dl class="glossary-list">'."\n";
173 while ($row = db_fetch_object($result)) {
174 $term = taxonomy_get_term($row->tid);
175 $output .= theme('glossary_overview_item', $term);
176 $vid = $row->vid;
177 }
178 $output .= "</dl>\n";
179
180 $tree = taxonomy_get_tree($vid);
181 $alphabar = _glossary_alphabar($vid, $tree);
182 $output = $alphabar .'<br/>'. $output;
183 return $output;
184 }
185
186 /*
187 * This is the introductory stuff for the settings.
188 */
189 function glossary_settings_page() {
190 $output .= drupal_get_form(glossary_general_settings_form);
191 return $output;
192 }
193
194 function glossary_general_settings_form() {
195 $form = array();
196
197 $form['general'] = array(
198 '#type' => 'fieldset',
199 '#title' => t('General settings'),
200 '#collapsible' => true,
201 '#collapsed' => false,
202 '#description' => $cache_msg,
203 );
204
205 $form['general']['glossary_disable_indicator'] = array(
206 '#type' => 'checkbox',
207 '#title' => t('Allow the user to disable glossary links'),
208 '#default_value' => variable_get('glossary_disable_indicator', false),
209 '#description' => t('Determines whether or not the individual user may disable the Glossary indicators.'),
210 );
211
212 // This next setting cannot vary by format since the glossary overview doesn't care or know about input formats
213 $form['general']["glossary_page_per_letter"] = array(
214 '#type' => 'checkbox',
215 '#title' => t('Show glossary across many smaller pages'),
216 '#default_value' => variable_get("glossary_page_per_letter", false),
217 '#description' => t('Do you want to show all terms on one glossary page or break up the glossary into a page for each first letter (i.e. many pages).'),
218 );
219
220 return system_settings_form($form);
221 }
222
223 /*
224 * This is the form for the settings for an individual input format.
225 */
226 function glossary_filter_form($format = 1) {
227 $form = array();
228
229 $options = array(t('<none>'));
230 foreach (taxonomy_get_vocabularies() as $vocabulary) {
231 $options[$vocabulary->vid] = $vocabulary->name;
232 }
233
234 // Make sure we know if we need to clear the cache.
235 variable_del('glossary_need_to_clear_cache', true);
236
237 // Get information about this filter.
238 $filter = db_fetch_array(db_query('SELECT * FROM {filter_formats} WHERE format=%d LIMIT 1', $format));
239 // See if we are enabled for this input format.
240 $enabled = db_result(db_query("SELECT COUNT(delta) FROM {filters} WHERE format=%d AND module='glossary'", $format));
241 if ($enabled) {
242 $enabled_msg = null;
243 }
244 else {
245 $enabled_msg = '<p><big>'. t('The Glossary module is not enabled for this input format. <a href="!url">Change the settings</a>.', array('!url' => url('admin/settings/filters/'. $format))) .'</big></p>';
246 }
247
248 if ($filter['cache']) {
249 $cache_msg = t('This filter may be cached.');
250
251 // Is there anything in the cache now?
252 // We can't use cache_get because we would need the md5 hash of the text.
253 $cached_content = db_result(db_query("SELECT COUNT(cid) FROM {cache_filter} WHERE cid LIKE ('%d:%%')", $format));
254 if ($cached_content == 0) { $cache_exist = false; }
255 else {
256 $cache_exist = true;
257 $cache_msg .= ' '. t('There is currently cached data for this input format.');
258 $dest = drupal_get_destination();
259 $cache_msg .= ' '. t('<a href="!url">Clear it now.</a>', array('!url' => url('glossary/clearcache', $dest)));
260 }
261 $cache_lifetime = variable_get('cache_lifetime', 0);
262 if ($cache_lifetime) {
263 $cache_msg .= ' '. t('You are using a minimum cache lifetime of !life.', array('!life' => format_interval($cache_lifetime, 1)));
264 }
265 else {
266 $cache_msg .= ' '. t('You do not have a minimum cache lifetime.');
267 }
268 }
269 else {
270 $cache_msg = t('This filter may not be cached.');
271 }
272
273 $form['format'] = array(
274 '#type' => 'value',
275 '#value' => $format,
276 );
277
278 $form['indicator'] = array(
279 '#type' => 'fieldset',
280 '#title' => t('Indicator settings'),
281 '#collapsible' => true,
282 '#collapsed' => false,
283 '#description' => $enabled_msg . $cache_msg,
284 );
285
286 $form['indicator']["glossary_vids_$format"] = array(
287 '#type' => 'select',
288 '#title' => t('Select Vocabulary'),
289 '#default_value' => variable_get("glossary_vids_$format", array()),
290 '#options' => $options,
291 '#description' => t('Select one or more vocabularies which hold all terms for your glossary. When enabled, posts will be scanned for glossary terms. An icon or a superscripted link is inserted beside each term. Hover over the icon or link to learn the meaning of that term.'),
292 '#multiple' => true,
293 '#required' => true,
294 );
295
296 $form['indicator']["glossary_absolute_$format"] = array( /* <<============================== */
297 '#type' => 'checkbox',
298 '#title' => t('Make Glossary links absolute'),
299 '#default_value' => variable_get("glossary_absolute_$format", false),
300 '#description' => t('RSS feeds need absolute links to ensure they point back to this site. If you are not providing RSS feeds, it is better to leave this turned off.'),
301 );
302
303 $form['indicator']["glossary_match_$format"] = array(
304 '#type' => 'select',
305 '#title' => t('Match type'),
306 '#default_value' => variable_get("glossary_match_$format", 'match'),
307 '#options' => array(
308 'b' => t('word'),
309 'lr' => t('right or left substring'),
310 'l' => t('left substring'),
311 'r' => t('right substring'),
312 's' => t('any substring'),
313 ),
314 '#description' => t('Choose the match type of glossary links.'),
315 );
316
317 $form['indicator']["glossary_case_$format"] = array(
318 '#type' => 'select',
319 '#title' => t('Case sensitivity'),
320 '#default_value' => variable_get("glossary_case_$format", '1'),
321 '#options' => array(
322 t('case insensitive'),
323 t('case sensitive')
324 ),
325 '#description' => t('Match either case sensitive or not. Case sensitive matches are not very resource intensive.'),
326 );
327
328 $form['indicator']["glossary_replace_all_$format"] = array(
329 '#type' => 'select',
330 '#title' => t('Replace matches'),
331 '#default_value' => variable_get("glossary_replace_all_$format", 0),
332 '#options' => array(
333 t('only the first match'),
334 t('all matches')
335 ),
336 '#description' => t('Whether only the first match should be replaced or all matches.'),
337 );
338
339 $form['indicator']["glossary_replace_$format"] = array(
340 '#type' => 'select',
341 '#title' => t('Term Indicator'),
342 '#default_value' => variable_get("glossary_replace_$format", 'superscript'),
343 '#options' => array(
344 'superscript' => t('superscript'),
345 'icon' => t('icon'),
346 'acronym link' => t('replace with acronym link'),
347 ),
348 '#description' => t('Display terms using either a superscript formatted link, an icon, or an &lt;acronym&gt; tag.'),
349 '#validate' => array('glossary_indicator_intercept' => array()),
350 );
351
352 $form['indicator']["glossary_superscript_$format"] = array(
353 '#type' => 'textfield',
354 '#title' => t('Superscript'),
355 '#default_value' => variable_get("glossary_superscript_$format", 'i'),
356 '#size' => 15,
357 '#maxlength' => 255,
358 '#description' => t('If you choose !superscript above, enter the superscript text.',
359 array('!superscript' => '<strong>'. t('superscript') .'</strong>')),
360 '#validate' => array('glossary_indicator_intercept' => array()),
361 );
362
363 $mypath = '/'. drupal_get_path('module', 'glossary');
364 $form['indicator']["glossary_icon_$format"] = array(
365 '#type' => 'textfield',
366 '#title' => t('Glossary Icon URL'),
367 '#default_value' => variable_get("glossary_icon_$format", $mypath .'/glossary.gif'),
368 '#size' => 50,
369 '#maxlength' => 255,
370 '#description' => t('If you choose !icon above, enter the URL of the glossary icon relative to the root of your Drupal site.',
371 array('!icon' => '<strong>'. t('icon') .'</strong>')),
372 '#validate' => array('glossary_indicator_intercept' => array()),
373 );
374
375 // Update Button
376 $form['save']['attach'] = array(
377 '#type' => 'submit',
378 '#value' => t('Save configuration'),
379 '#weight' => 5,
380 );
381
382 return $form;
383 }
384
385 // This function checks to see if a value has changed; if so we need to clear the filter cache.
386 function glossary_indicator_intercept($form) {
387 if ($form['#value'] != $form['#default_value']) {
388 variable_set('glossary_need_to_clear_cache', true);
389 };
390 }
391
392 function glossary_filter_form_submit($form_id, $form_values) {
393 $need_to_clear_cache = variable_get('glossary_need_to_clear_cache', false);
394 if ($need_to_clear_cache) {
395 _glossary_clear_cache(null, $object->vid);
396 }
397
398 $format = $form_values['format'];
399 $vids_name = "glossary_vids_$format";
400 $absolute_name = "glossary_absolute_$format";
401 $match_name = "glossary_match_$format";
402 $case_name = "glossary_case_$format";
403 $replace_all_name = "glossary_replace_all_$format";
404 $replace_name = "glossary_replace_$format";
405 $superscript_name = "glossary_superscript_$format";
406 $icon_name = "glossary_icon_$format";
407 variable_set($vids_name, $form_values[$vids_name]);
408 variable_set($absolute_name, $form_values[$absolute_name]);
409 variable_set($match_name, $form_values[$match_name]);
410 variable_set($case_name, $form_values[$case_name]);
411 variable_set($replace_all_name, $form_values[$replace_all_name]);
412 variable_set($replace_name, $form_values[$replace_name]);
413 variable_set($superscript_name, $form_values[$superscript_name]);
414 variable_set($icon_name, $form_values[$icon_name]);
415
416 return;
417 }
418
419 function glossary_filter_tips($delta, $format, $long = false) {
420 return $long ? glossary_help('filter#long-tip') : glossary_help('filter#short-tip');
421 }
422
423 function glossary_filter($op, $delta = 0, $format = -1, $text = "") {
424 switch ($op) {
425 case 'list':
426 return array(0 => t('Glossary filter'));
427 case 'description':
428 return glossary_help('admin/modules#description');
429 case 'process':
430 return _glossary_filter_process($format, $text);
431 // case 'settings':
432 // return _glossary_filter_settings($format);
433 default:
434 return $text;
435 }
436 }
437
438 function glossary_taxonomy($op, $type, $object = null) {
439 if ($object) {
440 _glossary_clear_cache($object->vid);
441 }
442 }
443
444 /**
445 * Implementation of hook_user().
446 */
447 function glossary_user($type, $edit, $user) {
448 switch ($type) {
449 case 'form':
450 // Note: this requires a setting. Do we also need to clear cache if selected?
451 if (variable_get('glossary_disable_indicator', false)) {
452 $form['content_glossary'] = array(
453 '#type' => 'fieldset',
454 '#title' => t('Glossary Indicators'),
455 );
456 $form['content_glossary']['glossary_disable_indicator'] = array(
457 '#type' => 'checkbox',
458 '#title' => t('Disable Glossary indicators'),
459 '#return_value' => 1,
460 '#default_value' => $user->glossary_disable_indicator,
461 '#description' => t('Check this box to disable the display of Glossary indicators.'),
462 );
463 return $form;
464 }
465 break;
466 }
467 }
468
469 function _glossary_filter_process($format, $text) {
470 global $user;
471 if ($user->glossary_disable_indicator) {
472 return $text;
473 }
474
475 if (variable_get("glossary_vids_$format", 0)) {
476
477 $text = ' '. $text .' ';
478 $replace_mode = variable_get("glossary_replace_$format", 'superscript');
479 $absolute_link = variable_get("glossary_absolute_$format", false);
480 $terms = _glossary_get_terms($format);
481 $vids = _glossary_get_filter_vids();
482
483 foreach ($terms as $term) {
484 $term_title = $term->name .': '. strip_tags($term->description);
485 if ($term->nodes > 0) {
486 $linkto = "taxonomy/term/$term->tid";
487 }
488 elseif (!empty($vids)) {
489 $linkto = 'glossary/'. $term->vid;
490 }
491 else {
492 $linkto = 'glossary/term/'. $term->tid;
493 }
494 $ins_before = $ins_after = '';
495
496 switch ($replace_mode) {
497 case 'superscript':
498 $ins_after = l(variable_get("glossary_superscript_$format", 'i'),
499 $linkto, array('title' => $term_title, 'class' => 'glossary-indicator'),
500 null, null, $absolute_link);
501 break;
502 case 'acronym link':
503 $ins_before = '<a class="glossary-term" href="'. url($linkto, null, null, $absolute_link) .'">';
504 $ins_before .= '<acronym title="'. check_plain($term_title) .'">';
505 $ins_after = '</acronym></a>';
506 break;
507 default:
508 $img = "<img src=\"". variable_get("glossary_icon_$format", "glossary.gif") ."\" />";
509 $ins_after = l($img, $linkto, array('title' => $term_title, 'class' => 'glossary-icon'),
510 null, null, $absolute_link, true);
511 break;
512 }
513
514 // replace term and synonyms with the desired new HTML code
515 foreach ($term->synonyms as $candidate) {
516 $text = _glossary_insertlink($format, $text, $candidate, $ins_before, $ins_after);
517 }
518 }
519 }
520 return $text;
521 }
522
523 /**
524 * Insert glossary links to $text after every $match that is not inside a link.
525 * $ins_before is prepended to the matches, $_insafter is appended to them.
526 * Match type and replace mode all depend on user settings.
527 *
528 * TODO: improve performance with not keeping *2.5 copies* of the string in memory:
529 * $text - original
530 * $newtext - transformed
531 * $before . $this_match - for checking stuff
532 */
533 function _glossary_insertlink($format, &$text, $match, $ins_before, $ins_after) {
534
535 $findfunc = (variable_get("glossary_case_$format", "1") ? 'strpos' : 'stripos');
536 $next = $findfunc($text, $match);
537
538 if ($next === false) { // no match at all
539 return $text;
540 }
541 else { // at least one match
542 $prevend = 0;
543 $newtext = '';
544 $matchlen = strlen($match);
545 $textlen = strlen($text);
546 $replaceall = variable_get("glossary_replace_all_$format", 0);
547
548 while ($next && ($next <= $textlen)) {
549
550 // get parts of the match for further investigation
551 $before = substr($text, 0, $next);
552 $this_match = substr($text, $next, $matchlen);
553
554 // see if we have a proper match or not
555 $open = substr_count($before, '<');
556 $close = substr_count($before, '>');
557 $opena = substr_count($before, '<a ');
558 $closea = substr_count($before, '</a>');
559 $openacro = substr_count($before, '<acronym');
560 $closeacro = substr_count($before, '</acronym>');
561 $proper_match = false;
562 if ($opena <= $closea && $open <= $close && $openacro <= $closeacro) { // Not in an open link
563 switch (variable_get("glossary_match_$format", 'b')) {
564 case 'lr': // require word break left or right
565 $proper_match = (_glossary_is_boundary($text {$next - 1}) || _glossary_is_boundary($text {$next + $matchlen}));
566 break;
567 case 'b': // require word break left and right
568 $proper_match = (_glossary_is_boundary($text {$next - 1}) && _glossary_is_boundary($text {$next + $matchlen}));
569 break;
570 case 'l': // require word break left
571 $proper_match = _glossary_is_boundary($text {$next - 1});
572 break;
573 case 'r': // require word break right
574 $proper_match = _glossary_is_boundary($text {$next + $matchlen});
575 break;
576 case 's': // match any substring
577 default:
578 $proper_match = true;
579 break;
580 }
581 }
582
583 if ($proper_match) { // found match
584 $newtext .= substr($text, $prevend, ($next - $prevend)) . $ins_before . $this_match . $ins_after;
585 if ($replaceall == 0) {
586 return $newtext . substr($text, $next + $matchlen);
587 }
588 }
589 else { // not applicable match
590 $newtext .= substr($text, $prevend, ($next - $prevend)) . $this_match;
591 }
592
593 // Step further in finding the next match
594 $prevend = $next + $matchlen;
595 $next = $findfunc($text, $match, $prevend);
596 }
597 // Append remaining part
598 return $newtext . substr($text, $prevend);
599 }
600 }
601
602 function glossary_page($vid = null, $letter = null) {
603 $vids = _glossary_get_filter_vids();
604 $found = false;
605
606 if (!$vid) {
607 if (count($vids) == 1) {
608 $vid = $vids[0];
609 $found = true;
610 }
611 }
612 else {
613 $found = array_search($vid, _glossary_get_filter_vids());
614 }
615 if (!$vid || $found === false) {
616 $breadcrumb = array(l(t('Home'), null));
617 drupal_set_title(t('Glossaries'));
618 drupal_set_breadcrumb($breadcrumb);
619 return _glossary_list();
620 }
621 else {
622 $voc = taxonomy_get_vocabulary($vid);
623 $breadcrumb = array(l(t('Home'), null));
624 if (count($vids) > 1) {
625 $breadcrumb[] = l(t('Glossaries'), 'glossary');
626 }
627 drupal_set_title(ucwords($voc->name));
628 drupal_set_breadcrumb($breadcrumb);
629 return glossary_overview($voc, $letter);
630 }
631 }
632
633 function _glossary_alphabar($vid, &$tree) {
634 $blocks = array(range('0', '9'), range('a', 'z'));
635 $vids = _glossary_get_filter_vids();
636 $page_per_letter = variable_get('glossary_page_per_letter', false);
637
638 $found_letters = array();
639 foreach ($tree as $key => $term) {
640 if ($term->depth == 0) {
641 $firstletter = strtolower($term->name[0]);
642 if (! array_key_exists($firstletter, $found_letters)) {
643 $found_letters[$firstletter] = 1;
644 $tree[$key]->firstletter = $firstletter;
645 }
646 }
647 }
648
649 foreach ($blocks as $block) {
650 $found = false;
651 foreach ($block as $entry) {
652 if (array_key_exists($entry, $found_letters)) {
653 $found = true;
654 break;
655 }
656 }
657 if ($found) {
658 foreach ($block as $entry) {
659 if (! array_key_exists($entry, $found_letters)) {
660 $found_letters[$entry] = 0;
661 }
662 }
663 }
664 }
665
666 $output = '<div class="glossary-links">';
667 $letters = array_keys($found_letters);
668 sort($letters, SORT_STRING);
669 $href = "glossary/$vid";
670 foreach ($letters as $letter) {
671 $letter_link = 'letter'. $letter;
672 if ($page_per_letter) {
673 $href_append .= "/$letter_link";
674 }
675 else {
676 $fragment = $letter_link;
677 }
678
679 if ($found_letters[$letter]) {
680 $links[] = l($letter, $href . $href_append, null, null, $fragment, false, true);
681 }
682 else {
683 $links[] = $letter;
684 }
685 unset($href_append);
686 }
687 if (count($links)) {
688 $output .= join(' ', $links);
689 }
690 $output .= "</div>\n";
691 return $output;
692 }
693
694 function glossary_overview($vocab, $letter = null) {
695 if ($vocab->description) { $output = '<h3>'. $vocab->description .'</h3>'; }
696 else { $output = null; }
697 $vid = $vocab->vid;
698 if ($letter) { $first_let = substr($letter, 6); }
699 else { $first_let = ''; }
700
701 $tree = taxonomy_get_tree($vid);
702 $output .= _glossary_alphabar($vid, $tree);
703 $output .= '<dl class="glossary-list">';
704 if ($tree) {
705 foreach ($tree as $term) {
706 if (!$letter || strtolower($term->name[0]) == $first_let) {
707 $output .= theme('glossary_overview_item', $term);
708 }
709 }
710 }
711 $output .= '</dl>';
712 $output .= glossary_admin_links($vid);
713 return $output;
714 }
715
716 function theme_glossary_overview_item($term) {
717 if (isset($term->firstletter)) {
718 $output .= '<a id="letter'. $term->firstletter .'"></a>';
719 }
720 $output .= '<a id="term'. $term->tid .'"></a>';
721 if (module_exists('taxonomy_image')) {
722 $img = taxonomy_image_display($term->tid);
723 }
724 else {
725 $img = null;
726 }
727 $output .= '<dt class="depth'. $term->depth .'">'. $img . $term->name;
728 if (user_access('administer taxonomy')) {
729 $output .= l(t('edit term'), "admin/content/taxonomy/edit/term/$term->tid",
730 array('title' => t('edit this term and definition.'), 'class' => 'glossary-edit-term'));
731 }
732 $output .= "</dt><dd class=\"depth$term->depth\">";
733 $detailed_exists = db_result(db_query('SELECT COUNT(t.nid) FROM {term_node} t JOIN {node} n USING (nid) WHERE t.tid=%d AND n.status=1', $term->tid));
734 if ($detailed_exists) {
735 $output .= l(t('Detailed definition'), "taxonomy/term/$term->tid");
736 }
737 else {
738 $output .= $term->description;
739 }
740
741 if ($relations = taxonomy_get_related($term->tid, "name")) {
742 $output .= "<span class=\"glossary-related\">". t("See also") .": ";
743 foreach ($relations as $related) {
744 $items[] .= l($related->name, 'glossary/'. $term->vid, null, null, "term". $related->tid);
745 }
746 $output .= implode(', ', $items) ."</span>\n";
747 unset($items);
748 }
749 $output .= "</dd>\n";
750 return $output;
751 }
752
753 function glossary_admin_links($vid) {
754 $output = "";
755 if (user_access('administer taxonomy')) {
756 $links['glossary_add_term'] = array(
757 'title' => t('add term'),
758 'href' => "admin/content/taxonomy/$vid/add/term",
759 );
760 $links['glossary_edit'] = array(
761 'title' => t('edit glossary'),
762 'href' => "admin/content/taxonomy/$vid",
763 );
764 return theme('links', $links);
765 }
766 }
767
768 function _glossary_list() {
769 $output = "";
770 $vids = _glossary_get_filter_vids();
771 $vocs = array();
772 foreach ($vids as $vid) {
773 $voc = taxonomy_get_vocabulary($vid);
774 $vocs[$voc->name] = $voc;
775 }
776 uksort($vocs, _glossary_cmp_strcase);
777
778 $header = array(t("Glossary"), t('Operations'));
779 $rows = array();
780 foreach ($vocs as $voc) {
781 $row = array();
782 $row[0] = $voc->name;
783 $row[1] = l(t('view'), "glossary/". $voc->vid);
784 if (user_access('administer taxonomy')) {
785 $row[1] .= " ". l(t('edit'), "admin/content/taxonomy");
786 }
787 $rows[] = $row;
788 }
789
790 $output = theme('table', $header, $rows);
791 return $output;
792 }
793
794 function _glossary_get_terms($format) {
795 static $terms = false;
796
797 if ($terms === false) {
798 $terms = $synonyms = array();
799 $vids = variable_get("glossary_vids_$format", 0);
800
801 foreach ($vids as $vid) {
802 $synonyms = _glossary_get_synonyms($vid);
803
804 // Get all glossary terms and attach synonyms.
805 // omit terms without a description. those are usually container terms.
806 $result = db_query("SELECT t.name, t.description, t.tid, COUNT(tn.nid) as nodes FROM {term_data} t LEFT JOIN {term_node} tn ON t.tid = tn.tid WHERE t.vid = %d GROUP BY t.tid, t.name, t.description ORDER BY LENGTH(t.name) DESC", $vid);
807 while ($term = db_fetch_object($result)) {
808 if ($term->description) {
809 $term->synonyms = $synonyms[$term->tid];
810 $term->synonyms[] = $term->name;
811 $term->vid = $vid;
812 $terms[] = $term;
813 }
814 }
815 }
816 }
817
818 return $terms;
819 }
820
821 // Get all synonyms for all glossary terms
822 function _glossary_get_synonyms($vid) {
823 $result = db_query("SELECT ts.tid, ts.name FROM {term_synonym} ts, {term_data} t WHERE ts.tid = t.tid AND t.vid = %d", $vid);
824 while ($synonym = db_fetch_object($result)) {
825 $synonyms[$synonym->tid][] = $synonym->name;
826 }
827 return $synonyms;
828 }
829
830 // This seems to be 1.2 times faster in fine-grained testing then
831 // the ereg solution used before. The chars used here are from the
832 // grep info page.
833 function _glossary_is_boundary($char) {
834 return (strpos("!\"#\$%&'()*+,-./:;<=>?@[\]^_`{|}~ \t\n\r", $char) !== false);
835 }
836
837 // Natively only available in PHP 5+
838 // WARNING: Eats a tremendous amount of memory!
839 if (!function_exists("stripos")) {
840 function stripos($haystack, $needle, $offset = 0) {
841 return strpos(strtoupper($haystack), strtoupper($needle), $offset);
842 }
843 }
844
845 function _glossary_clear_cache($vid) {
846 // We could throw less things away if we checked which filter formats
847 // used the glossary filter, and we only threw those away. In practice,
848 // most if not all formats would use the glossary filter, so we just
849 // get rid of them all.
850 cache_clear_all('*', 'cache_filter', true);
851 }
852
853 function _glossary_get_filter_vids() {
854 // We can't use filter_formats() here, because we need all input formats,
855 // not just those we are allowed to post in.
856
857 $vids = array();
858 $result = db_query('SELECT format FROM {filter_formats}');
859 while ($format = db_fetch_object($result)) {
860 $filters = filter_list_format($format->format);
861 foreach ($filters as $filter) {
862 if ($filter->module == "glossary") {
863 $vids = array_merge($vids, variable_get("glossary_vids_". $format->format, array()));
864 }
865 }
866 }
867 return array_unique($vids);
868 }
869
870 function _glossary_cmp_strcase($a, $b) {
871 return strcmp(strtolower($a), strtolower($b));
872 }

  ViewVC Help
Powered by ViewVC 1.1.2