1<?php
2  /**
3   * bibtex-Plugin: Parses bibtex-blocks
4   *
5   * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6   * @author     Christophe Ambroise <ambroise@utc.fr>
7   * @date       2005-08-10
8   *
9   * Modifications: 2006-07-03 Jean-Francois Lalande.
10   * Adding:
11   * - Chronological ordering support
12   * - Type of references groups
13   * - PDF and POSTSCRIPT links
14   *
15   * Modifications: 2006-08-22 Jean-Francois Lalande.
16   * - Fix for hyphaned name in OSBib (reported to developers)
17   */
18
19  /**
20   * Parameters for the plugin:
21   *
22   * - $conf['bibtex_format']
23   *   default: "APA"
24   *   This control the formatting output of the bibtex plugin.
25   *   Options are: APA, ieee, britishmedicaljournal, chicago, harvard, mla, turabian
26   *
27   * - $conf['bibtex_sort_year']
28   *   default: no value (no sorting by date)
29   *   Controls the sorting of the references by year.
30   *   Options are: 1 (most recent) or -1 (oldest first)
31   *
32   * - $conf['bibtex_sort_type']
33   *   default: no value (no sorting by type)
34   *   Controls the sorting of the references by type.
35   *   Options are: 1 (ascending order) or -1 (descending order)
36   *   The values for each type of reference is controlled by
37   *   the array of $conf["bibtex_types_order"]. You can overide this array
38   *   in your local configuration. You can find an example of this array in
39   *   the syntax.php file of this plugin.
40   *
41   * - $conf["bibtex_types_order"]
42   *   default: see below
43   *   If the 'bibtex_sort_type' has been activated, this array controls
44   *   the order of appearing of the different type of references. For each kind
45   *   of reference a value is associated. For example a book is 10, an article 7
46   *   and a techreport 1. If the same value is given for two kinds of references
47   *   they are grouped (in the example inproceedings and conference are grouped)
48   *
49   *   Example of array:
50   *   $conf["bibtex_types_order"] = array (
51   *	"book"=> 10,
52   *    "inbook"=> 9,
53   *    "phdthesis"=> 8,
54   *    "article"=> 7,
55   *    "inproceedings"=> 3,
56   *    "conference"=> 3,
57   *    "techreport"=> 1,
58   *    );
59   *
60   * - $conf["bibtex_types_names"]
61   *   default see below
62   *   If the 'bibtex_sort_type' has beec activated, this array controls
63   *   the name of the section for each group of references. The idea
64   *   is to personalize the titles for exemple for the entries 'inbook' by
65   *   putting a label "Chapter in books". For each level defined in the
66   *   array $conf["bibtex_types_order"], we associate a label.
67   *
68   *   Example of array:
69   *   $conf["bibtex_types_names"] = array (
70   *    10 => "Books",
71   *    9 => "Chapters in books",
72   *    8 => "Phd Thesis",
73   *    7 => "Articles",
74   *    3 => "Proceedings",
75   *    1 => "Technical reports",
76   *    );
77   *
78   * - $conf['bibtex_types_title_level']
79   *   default: 2
80   *   This parameter sets the level of the label of each section of reference.
81   *   By default the level is 2 corresponding to h2 tags.
82   *
83   *
84   * Example of parameters for activating all features:
85   *
86   * $conf['bibtex_format'] = "ieee";
87   * $conf['bibtex_sort_year'] = 1;
88   * $conf['bibtex_sort_type'] = 1;
89   * $conf['bibtex_sort_type_title'] = 1;
90   *
91   * Special parameters in the bibtex entry:
92   *
93   * - The tag 'file' is considered as an internal document of dokuwiki
94   * - The tag 'pdf' is considerd as an external url
95   * - The tag 'postscript' is considered as an external url
96   */
97
98// Default values
99global $conf;
100if(!isset($conf["bibtex_types_order"]))
101{
102	$conf["bibtex_types_order"] = array (
103		"book"=> 10,
104		"inbook"=> 9,
105		"phdthesis"=> 8,
106		"article"=> 7,
107		"inproceedings"=> 3,
108		"conference"=> 3,
109		"techreport"=> 1,
110	);
111}
112if(!isset($conf["bibtex_types_names"]))
113{
114	$conf["bibtex_types_names"] = array (
115		10 => "Books",
116		9 => "Chapters in books",
117		8 => "Phd Thesis",
118		7 => "Articles",
119		3 => "Proceedings",
120		1 => "Technical reports",
121	);
122}
123if(!isset($conf["bibtex_types_title_level"]))
124{
125	$conf['bibtex_types_title_level'] = 2;
126}
127
128if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
129if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
130require_once(DOKU_PLUGIN.'syntax.php');
131
132/**
133* usort callback for years
134*/
135function compare_year($a, $b) {
136	global $conf;
137
138	// No sorting
139	if ($conf['bibtex_sort_year'] == "")
140	  return 0;
141
142	$adist = $a['year'];
143	$bdist = $b['year'];
144
145	if ($adist == $bdist) {
146		return 0;
147	}
148	return ($adist < $bdist) ? $conf['bibtex_sort_year'] : -$conf['bibtex_sort_year'];
149}
150
151/**
152* usort callback for types
153*/
154function compare_type($a, $b) {
155	global $conf;
156
157	// No sorting
158	if ($conf['bibtex_sort_type'] == "")
159	  return 0;
160
161	$adist = $conf["bibtex_types_order"][$a['bibtexEntryType']];
162	$bdist = $conf["bibtex_types_order"][$b['bibtexEntryType']];
163
164	if ($adist == $bdist) {
165		// If the year sorting is activated...
166		if ($conf['bibtex_sort_year'] != "")
167		{
168			return compare_year($a,$b);
169		}
170		else
171		{
172			return 0;
173		}
174	}
175	return ($adist < $bdist) ? $conf['bibtex_sort_type'] : -$conf['bibtex_sort_type'];
176}
177
178/**
179 * All DokuWiki plugins to extend the parser/rendering mechanism
180 * need to inherit from this class
181 */
182class syntax_plugin_bibtex extends DokuWiki_Syntax_Plugin {
183
184  function getInfo(){
185    return array(
186		 'author' => 'Christophe Ambroise',
187		 'email'  => 'ambroise@utc.fr',
188		 'date'   => '2006-06-29',
189		 'name'   => 'Bitext Plugin',
190		 'desc'   => 'parses bibtex blocks',
191		 'url'    => 'http://www.hds.utc.fr/~ambroise/doku.php?id=softwares:dokuwikibibtexplugin'
192		 );
193  }
194
195  /**
196   * What kind of syntax are we?
197   */
198  function getType(){
199    return 'protected';
200  }
201
202  /**
203   * Where to sort in?
204   */
205  function getSort(){
206    return 102;
207  }
208
209  /**
210   * Connect pattern to lexer
211   */
212  function connectTo($mode) {
213    $this->Lexer->addEntryPattern('<bibtex(?=.*\x3C/bibtex\x3E)',$mode,'plugin_bibtex');
214  }
215
216  function postConnect() {
217    $this->Lexer->addExitPattern('</bibtex>','plugin_bibtex');
218  }
219
220  /**
221   * Handle the match
222   */
223  function handle($match, $state, $pos) {
224    if ( $state == DOKU_LEXER_UNMATCHED ) {
225      $matches = preg_split('/>/u',$match,2);
226      $matches[0] = trim($matches[0]);
227      if ( trim($matches[0]) == '' ) {
228	$matches[0] = NULL;
229      }
230      return array($matches[1],$matches[0]);
231    }
232    return TRUE;
233  }
234  /**
235   * Create output
236   */
237  function render($mode, &$renderer, $data) {
238    global $conf;
239    if($mode == 'xhtml' && strlen($data[0]) > 1) {
240      $renderer->doc .= $this->createCitations($data[0]);
241      return true;
242    }
243  }
244
245
246
247  function createCitations(&$data) {
248    global $conf;
249    $pathToOsbib = DOKU_PLUGIN.'bibtex/OSBib/';
250    include_once($pathToOsbib.'format/bibtexParse/PARSEENTRIES.php');
251    include_once( $pathToOsbib.'format/BIBFORMAT.php');
252
253    // Getting conf (JFL)
254    if ($conf['bibtex_format'] != "")
255    {
256	    $bibtex_format = $conf['bibtex_format'];
257    }
258    else
259    {
260	    $bibtex_format = "APA";
261    }
262
263    /* Get the bibtex entries into an associative array */
264    $parse = NEW PARSEENTRIES();
265    $parse->expandMacro = TRUE;
266    $parse->fieldExtract = TRUE;
267    $parse->removeDelimit = TRUE;
268    $parse->loadBibtexString($data);
269    $parse->extractEntries();
270    list($preamble, $strings, $entries) = $parse->returnArrays();
271
272    /* Format the entries array  for html output */
273    $bibformat = NEW BIBFORMAT($pathToOsbib, TRUE);
274    $bibformat->cleanEntry=TRUE; // The entries will be transformed into nice utf8
275    list($info, $citation, $styleCommon, $styleTypes) = $bibformat->loadStyle(DOKU_PLUGIN.'bibtex/OSBib/styles/bibliography/', $bibtex_format);
276    $bibformat->getStyle($styleCommon, $styleTypes);
277
278    $citations='<dl>';
279
280    // Sorting ?
281    if ($conf['bibtex_sort_year'] != "")
282    {
283      usort($entries, "compare_year");
284    }
285
286    if ($conf['bibtex_sort_type'] != "")
287    {
288      usort($entries, "compare_type");
289    }
290
291    foreach ($entries as $entry){
292      // Get the resource type ('book', 'article', 'inbook' etc.)
293      $resourceType = $entry['bibtexEntryType'];
294
295      if ($conf['bibtex_sort_type_title'] == 1)
296      {
297	      if ($conf['bibtex_types_order'][$resourceType] != $conf['bibtex_types_order'][$resourceTypeLast])
298	      {
299		      $resourceTypeLast = $resourceType;
300		      $value = $conf['bibtex_types_order'][$resourceType];
301		      $citations.="<h" . $conf['bibtex_types_title_level'] . ">";
302		      $citations.=$conf['bibtex_types_names'][$value];
303		      $citations.="</h" . $conf['bibtex_types_title_level'] . ">";
304	      }
305      }
306
307      // In this case, BIBFORMAT::preProcess() adds all the resource elements automatically to the BIBFORMAT::item array...
308      $bibformat->preProcess($resourceType, $entry);
309      // Finally, get the formatted resource string ready for printing to the web browser or exporting to RTF, OpenOffice or plain text
310      $citations.= '<dt><div id="bibtexdt">[' . $entry['year'] . ", " . $entry['bibtexEntryType'] . $this->toDownload($entry) . ']</div></dt><dd><div id="bibtexdd">'.   $bibformat->map()    . "</div></dd> \n" ;
311    }
312    $citations.= "</dl>";
313    return $citations;
314
315    //$entry['bibtexCitation']
316
317  }
318
319
320
321  function toDownload($entry) {
322    $string="";
323    if(array_key_exists('file',$entry)){
324      $string.= " | ".$this->internalmedia($entry['file']);
325    }
326    if(array_key_exists('url',$entry)){
327      $string.= " | ".$this->externallink($entry['url'],"www");
328    }
329    if(array_key_exists('pdf',$entry)){
330      $string.= " | ".$this->externallink($entry['pdf'],"pdf");
331    }
332    if(array_key_exists('postscript',$entry)){
333      $string.= " | ".$this->externallink($entry['postscript'],"ps");
334    }
335    return $string;
336  }
337
338
339  function externallink($url, $name = NULL) {
340    global $conf;
341
342    $name = $this->_getLinkTitle($name, $url, $isImage);
343
344    // add protocol on simple short URLs
345    if(substr($url,0,3) == 'ftp' && (substr($url,0,6) != 'ftp://')) $url = 'ftp://'.$url;
346    if(substr($url,0,3) == 'www') $url = 'http://'.$url;
347
348    if ( !$isImage ) {
349      $class='urlextern';
350    } else {
351      $class='media';
352    }
353
354    //prepare for formating
355    $link['target'] = $conf['target']['extern'];
356    $link['style']  = '';
357    $link['pre']    = '';
358    $link['suf']    = '';
359    $link['more']   = 'onclick="return svchk()" onkeypress="return svchk()"';
360    $link['class']  = $class;
361    $link['url']    = $url;
362    $link['name']   = $name;
363    $link['title']  = $this->_xmlEntities($url);
364    if($conf['relnofollow']) $link['more'] .= ' rel="nofollow"';
365
366    list($ext,$mime) = mimetype($url);
367    if(substr($mime,0,15) == 'application/pdf' || substr($mime,0,24) == 'application/octet-stream'){
368	    // add file icons
369      $link['class'] = 'urlextern';
370      if(@file_exists(DOKU_INC.'lib/images/fileicons/'.$ext.'.png')){
371	$link['style']='background-image: url('.DOKU_BASE.'lib/images/fileicons/'.$ext.'.png)';
372      }elseif(@file_exists(DOKU_INC.'lib/images/fileicons/'.$ext.'.gif')){
373	$link['style']='background-image: url('.DOKU_BASE.'lib/images/fileicons/'.$ext.'.gif)';
374      }else{
375	$link['style']='background-image: url('.DOKU_BASE.'lib/images/fileicons/file.gif)';
376      }
377      //$link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),true);
378    }
379
380    //output formatted
381    return $this->_formatLink($link);
382  }
383
384
385  function internalmedia ($src, $title=NULL, $align=NULL, $width=NULL,
386			  $height=NULL, $cache=NULL, $linking=NULL) {
387    global $conf;
388    global $ID;
389    resolve_mediaid(getNS($ID),$src, $exists);
390
391    $link = array();
392    $link['class']  = 'media';
393    $link['style']  = '';
394    $link['pre']    = '';
395    $link['suf']    = '';
396    $link['more']   = 'onclick="return svchk()" onkeypress="return svchk()"';
397    $link['target'] = $conf['target']['media'];
398    $link['title']  = $this->_xmlEntities($src);
399    list($ext,$mime) = mimetype($src);
400    if(substr($mime,0,5) == 'image'){
401      // link only jpeg images
402      // if ($ext != 'jpg' && $ext != 'jpeg') $noLink = TRUE;
403      $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),($linking=='direct'));
404    }elseif($mime == 'application/x-shockwave-flash'){
405      // don't link flash movies
406      $noLink = TRUE;
407    }else{
408      // add file icons
409      $link['class'] = 'urlextern';
410      if(@file_exists(DOKU_INC.'lib/images/fileicons/'.$ext.'.png')){
411	$link['style']='background-image: url('.DOKU_BASE.'lib/images/fileicons/'.$ext.'.png)';
412      }elseif(@file_exists(DOKU_INC.'lib/images/fileicons/'.$ext.'.gif')){
413	$link['style']='background-image: url('.DOKU_BASE.'lib/images/fileicons/'.$ext.'.gif)';
414      }else{
415	$link['style']='background-image: url('.DOKU_BASE.'lib/images/fileicons/file.gif)';
416      }
417      $link['url'] = ml($src,array('id'=>$ID,'cache'=>$cache),true);
418    }
419    $link['name']   = $this->_media ($src, $title, $align, $width, $height, $cache);
420
421    //output formatted
422    if ($linking == 'nolink' || $noLink){
423      return  $link['name'];
424    } else {
425      return $this->_formatLink($link);
426    }
427  }
428
429
430  function _formatLink($link){
431    //make sure the url is XHTML compliant (skip mailto)
432    if(substr($link['url'],0,7) != 'mailto:'){
433      $link['url'] = str_replace('&','&amp;',$link['url']);
434      $link['url'] = str_replace('&amp;amp;','&amp;',$link['url']);
435    }
436    //remove double encodings in titles
437    $link['title'] = str_replace('&amp;amp;','&amp;',$link['title']);
438
439    $ret  = '';
440    $ret .= $link['pre'];
441    $ret .= '<a href="'.$link['url'].'"';
442    if($link['class'])  $ret .= ' class="'.$link['class'].'"';
443    if($link['target']) $ret .= ' target="'.$link['target'].'"';
444    if($link['title'])  $ret .= ' title="'.$link['title'].'"';
445    if($link['style'])  $ret .= ' style="'.$link['style'].'"';
446    if($link['more'])   $ret .= ' '.$link['more'];
447    $ret .= '>';
448    $ret .= $link['name'];
449    $ret .= '</a>';
450    $ret .= $link['suf'];
451    return $ret;
452  }
453
454  /**
455   * Construct a title and handle images in titles
456   *
457   * @author Harry Fuecks <hfuecks@gmail.com>
458   */
459  function _getLinkTitle($title, $default, & $isImage, $id=NULL) {
460    global $conf;
461
462    $isImage = FALSE;
463    if ( is_null($title) ) {
464      if ($conf['useheading'] && $id) {
465	$heading = p_get_first_heading($id);
466	if ($heading) {
467	  return $this->_xmlEntities($heading);
468	}
469      }
470      return $this->_xmlEntities($default);
471    } else if ( is_string($title) ) {
472      return $this->_xmlEntities($title);
473    } else if ( is_array($title) ) {
474      $isImage = TRUE;
475      return $this->_imageTitle($title);
476    }
477  }
478
479  function _xmlEntities($string) {
480    return htmlspecialchars($string);
481  }
482
483  function _simpleTitle($name){
484    global $conf;
485
486    if($conf['useslash']){
487      $nssep = '[:;/]';
488    }else{
489      $nssep = '[:;]';
490    }
491    $name = preg_replace('!.*'.$nssep.'!','',$name);
492    //if there is a hash we use the ancor name only
493    $name = preg_replace('!.*#!','',$name);
494    return $name;
495  }
496
497  /**
498   * Renders internal and external media
499   *
500   * @author Andreas Gohr <andi@splitbrain.org>
501   */
502  function _media ($src, $title=NULL, $align=NULL, $width=NULL,
503		   $height=NULL, $cache=NULL) {
504
505    $ret = '';
506
507    list($ext,$mime) = mimetype($src);
508    if(substr($mime,0,5) == 'image'){
509      //add image tag
510      $ret .= '<img src="'.ml($src,array('w'=>$width,'h'=>$height,'cache'=>$cache)).'"';
511      $ret .= ' class="media'.$align.'"';
512
513      if (!is_null($title)) {
514	$ret .= ' title="'.$this->_xmlEntities($title).'"';
515	$ret .= ' alt="'.$this->_xmlEntities($title).'"';
516      }elseif($ext == 'jpg' || $ext == 'jpeg'){
517	//try to use the caption from IPTC/EXIF
518	require_once(DOKU_INC.'inc/JpegMeta.php');
519	$jpeg =& new JpegMeta(mediaFN($src));
520	if($jpeg !== false) $cap = $jpeg->getTitle();
521	if($cap){
522	  $ret .= ' title="'.$this->_xmlEntities($cap).'"';
523	  $ret .= ' alt="'.$this->_xmlEntities($cap).'"';
524	}
525      }else{
526	$ret .= ' alt=""';
527      }
528
529      if ( !is_null($width) )
530	$ret .= ' width="'.$this->_xmlEntities($width).'"';
531
532      if ( !is_null($height) )
533	$ret .= ' height="'.$this->_xmlEntities($height).'"';
534
535      $ret .= ' />';
536
537    }elseif($mime == 'application/x-shockwave-flash'){
538      $ret .= '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'.
539	' codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"';
540      if ( !is_null($width) ) $ret .= ' width="'.$this->_xmlEntities($width).'"';
541      if ( !is_null($height) ) $ret .= ' height="'.$this->_xmlEntities($height).'"';
542      $ret .= '>'.DOKU_LF;
543      $ret .= '<param name="movie" value="'.ml($src).'" />'.DOKU_LF;
544      $ret .= '<param name="quality" value="high" />'.DOKU_LF;
545      $ret .= '<embed src="'.ml($src).'"'.
546	' quality="high"';
547      if ( !is_null($width) ) $ret .= ' width="'.$this->_xmlEntities($width).'"';
548      if ( !is_null($height) ) $ret .= ' height="'.$this->_xmlEntities($height).'"';
549      $ret .= ' type="application/x-shockwave-flash"'.
550	' pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>'.DOKU_LF;
551      $ret .= '</object>'.DOKU_LF;
552
553    }elseif(!is_null($title)){
554      // well at least we have a title to display
555      $ret .= $this->_xmlEntities($title);
556    }else{
557      // just show the sourcename
558      $ret .= $this->_xmlEntities(noNS($src));
559    }
560
561    return $ret;
562  }
563
564
565}
566?>
567