1<?php /* bibtexbrowser: publication lists with bibtex and PHP 2<!--this is version from commit __GITHUB__ --> 3URL: http://www.monperrus.net/martin/bibtexbrowser/ 4Questions & Bug Reports: https://github.com/monperrus/bibtexbrowser/issues 5 6(C) 2012-2020 Github contributors 7(C) 2006-2020 Martin Monperrus 8(C) 2014 Markus Jochim 9(C) 2013 Matthieu Guillaumin 10(C) 2005-2006 The University of Texas at El Paso / Joel Garcia, Leonardo Ruiz, and Yoonsik Cheon 11This program is free software; you can redistribute it and/or 12modify it under the terms of the GNU General Public License as 13published by the Free Software Foundation; either version 2 of the 14License, or (at your option) any later version. 15 16*/ 17 18// it is be possible to include( 'bibtexbrowser.php' ); several times in the same script 19// added on Wednesday, June 01 2011, bug found by Carlos Bras 20if (!defined('BIBTEXBROWSER')) { 21// this if block ends at the very end of this file, after all class and function declarations. 22define('BIBTEXBROWSER','v__GITHUB__'); 23 24// support for configuration 25// set with bibtexbrowser_configure, get with config_value 26// you may have bibtexbrowser_configure('foo', 'bar') in bibtexbrowser.local.php 27global $CONFIGURATION; 28$CONFIGURATION = array(); 29function bibtexbrowser_configure($key, $value) { 30 global $CONFIGURATION; 31 $CONFIGURATION[$key]=$value; 32 if (!defined($key)) { define($key, $value); } // for backward compatibility 33} 34function bibtexbrowser_configuration($key) { 35 global $CONFIGURATION; 36 if (isset($CONFIGURATION[$key])) {return $CONFIGURATION[$key];} 37 if (defined($key)) {return constant($key);} 38 throw new Exception('no such configuration parameter: '.$key); 39} 40function c($key) { // shortcut 41 return bibtexbrowser_configuration($key); 42} 43 44// *************** CONFIGURATION 45// I recommend to put your changes in bibtexbrowser.local.php 46// it will help you to upgrade the script with a new version 47// the changes that require existing bibtexbrowser symbols should be in bibtexbrowser.after.php (included at the end of this file) 48// per bibtex file configuration 49@include('bibtexbrowser.local.php'); 50 51// the encoding of your bibtex file 52@define('BIBTEX_INPUT_ENCODING','UTF-8');//@define('BIBTEX_INPUT_ENCODING','iso-8859-1');//define('BIBTEX_INPUT_ENCODING','windows-1252'); 53// the encoding of the HTML output 54@define('OUTPUT_ENCODING','UTF-8'); 55 56// print a warning if deprecated variable is used 57if (defined('ENCODING')) { 58 echo 'ENCODING has been replaced by BIBTEX_INPUT_ENCODING and OUTPUT_ENCODING'; 59} 60 61// number of bib items per page 62// we use the same parameter 'num' as Google 63@define('PAGE_SIZE',isset($_GET['num'])?(preg_match('/^\d+$/',$_GET['num'])?$_GET['num']:10000):14); 64 65// bibtexbrowser uses a small piece of Javascript to improve the user experience 66// see http://en.wikipedia.org/wiki/Progressive_enhancement 67// if you don't like it, you can be disable it by adding in bibtexbrowser.local.php 68// @define('BIBTEXBROWSER_USE_PROGRESSIVE_ENHANCEMENT',false); 69@define('BIBTEXBROWSER_USE_PROGRESSIVE_ENHANCEMENT',true); 70@define('BIBLIOGRAPHYSTYLE','DefaultBibliographyStyle');// this is the name of a function 71@define('BIBLIOGRAPHYSECTIONS','DefaultBibliographySections');// this is the name of a function 72@define('BIBLIOGRAPHYTITLE','DefaultBibliographyTitle');// this is the name of a function 73 74// shall we load MathJax to render math in $…$ in HTML? 75@define('BIBTEXBROWSER_RENDER_MATH', true); 76@define('MATHJAX_URI', '//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/config/TeX-AMS_HTML.js?V=2.7.1'); 77 78// the default jquery URI 79@define('JQUERY_URI', '//code.jquery.com/jquery-3.6.4.min.js'); 80 81// can we load bibtex files on external servers? 82@define('BIBTEXBROWSER_LOCAL_BIB_ONLY', true); 83 84// the default view in {SimpleDisplay,AcademicDisplay,RSSDisplay,BibtexDisplay} 85@define('BIBTEXBROWSER_DEFAULT_DISPLAY','SimpleDisplay'); 86 87// the default template 88@define('BIBTEXBROWSER_DEFAULT_TEMPLATE','HTMLTemplate'); 89 90// the target frame of menu links 91@define('BIBTEXBROWSER_MENU_TARGET','main'); // might be define('BIBTEXBROWSER_MENU_TARGET','_self'); in bibtexbrowser.local.php 92 93@define('ABBRV_TYPE','index');// may be year/x-abbrv/key/none/index/keys-index 94 95// are robots allowed to crawl and index bibtexbrowser generated pages? 96@define('BIBTEXBROWSER_ROBOTS_NOINDEX',false); 97 98//the default view in the "main" (right hand side) frame 99@define('BIBTEXBROWSER_DEFAULT_FRAME','year=latest'); // year=latest,all and all valid bibtexbrowser queries 100 101// Wrapper to use when we are included by another script 102@define('BIBTEXBROWSER_EMBEDDED_WRAPPER', 'NoWrapper'); 103 104// Main class to use 105@define('BIBTEXBROWSER_MAIN', 'Dispatcher'); 106 107// default order functions 108// Contract Returns < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal. 109// can be @define('ORDER_FUNCTION','compare_bib_entry_by_title'); 110// can be @define('ORDER_FUNCTION','compare_bib_entry_by_bibtex_order'); 111@define('ORDER_FUNCTION','compare_bib_entry_by_year'); 112@define('ORDER_FUNCTION_FINE','compare_bib_entry_by_month'); 113 114// only displaying the n newest entries 115@define('BIBTEXBROWSER_NEWEST',5); 116 117@define('BIBTEXBROWSER_NO_DEFAULT', false); 118 119// BIBTEXBROWSER_LINK_STYLE defines which function to use to display the links of a bibtex entry 120@define('BIBTEXBROWSER_LINK_STYLE','bib2links_default'); // can be 'nothing' (a function that does nothing) 121 122// do we add [bibtex] links ? 123@define('BIBTEXBROWSER_BIBTEX_LINKS',true); 124// do we add [pdf] links ? 125// if the file extention is not .pdf, the field name (pdf, url, or file) is used instead 126@define('BIBTEXBROWSER_PDF_LINKS',true); 127// do we add [doi] links ? 128@define('BIBTEXBROWSER_DOI_LINKS',true); 129// do we add [gsid] links (Google Scholar)? 130@define('BIBTEXBROWSER_GSID_LINKS',true); 131 132// should pdf, doi, url, gsid links be opened in a new window? 133@define('BIBTEXBROWSER_LINKS_TARGET','_self');// can be _blank (new window), _top (with frames) 134 135// should authors be linked to [none/homepage/resultpage] 136// none: nothing 137// their homepage if defined as @strings 138// their publication lists according to this bibtex 139@define('BIBTEXBROWSER_AUTHOR_LINKS','homepage'); 140 141// BIBTEXBROWSER_LAYOUT defines the HTML rendering layout of the produced HTML 142// may be table/list/ordered_list/definition/none (for <table>, <ol>, <dl>, nothing resp.). 143// for list/ordered_list, the abbrevations are not taken into account (see ABBRV_TYPE) 144// for ordered_list, the index is given by HTML directly (in increasing order) 145@define('BIBTEXBROWSER_LAYOUT','table'); 146 147// should the original bibtex be displayed or a reconstructed one with filtering 148// values: original/reconstructed 149// warning, with reconstructed, the latex markup for accents/diacritics is lost 150@define('BIBTEXBROWSER_BIBTEX_VIEW','original'); 151// a list of fields that will not be shown in the bibtex view if BIBTEXBROWSER_BIBTEX_VIEW=reconstructed 152@define('BIBTEXBROWSER_BIBTEX_VIEW_FILTEREDOUT','comment|note|file'); 153 154// should Latex macros be executed (e.g. \'e -> é 155@define('BIBTEXBROWSER_USE_LATEX2HTML',true); 156 157// Which is the first html <hN> level that should be used in embedded mode? 158@define('BIBTEXBROWSER_HTMLHEADINGLEVEL', 2); 159 160@define('BIBTEXBROWSER_ACADEMIC_TOC', false); 161 162@define('BIBTEXBROWSER_DEBUG',false); 163 164// should we cache the parsed bibtex file? 165// ref: https://github.com/monperrus/bibtexbrowser/issues/128 166@define('BIBTEXBROWSER_USE_CACHE',true); 167 168// how to print authors names? 169// default => as in the bibtex file 170// USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT = true => "Meyer, Herbert" 171// USE_INITIALS_FOR_NAMES = true => "Meyer H" 172// USE_FIRST_THEN_LAST => Herbert Meyer 173@define('USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT',false);// output authors in a comma separated form, e.g. "Meyer, H"? 174@define('USE_INITIALS_FOR_NAMES',false); // use only initials for all first names? 175@define('USE_FIRST_THEN_LAST',false); // put first names before last names? 176@define('FORCE_NAMELIST_SEPARATOR', ''); // if non-empty, use this to separate multiple names regardless of USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT 177@define('LAST_AUTHOR_SEPARATOR',' and '); 178@define('USE_OXFORD_COMMA',false); // adds an additional separator in addition to LAST_AUTHOR_SEPARATOR if there are more than two authors 179 180@define('TYPES_SIZE',10); // number of entry types per table 181@define('YEAR_SIZE',20); // number of years per table 182@define('AUTHORS_SIZE',30); // number of authors per table 183@define('TAGS_SIZE',30); // number of keywords per table 184@define('READLINE_LIMIT',1024); 185@define('Q_YEAR', 'year'); 186@define('Q_YEAR_PAGE', 'year_page'); 187@define('Q_YEAR_INPRESS', 'in press'); 188@define('Q_YEAR_ACCEPTED', 'accepted'); 189@define('Q_YEAR_SUBMITTED', 'submitted'); 190@define('Q_FILE', 'bib'); 191@define('Q_AUTHOR', 'author'); 192@define('Q_AUTHOR_PAGE', 'author_page'); 193@define('Q_TAG', 'keywords'); 194@define('Q_TAG_PAGE', 'keywords_page'); 195@define('Q_TYPE', 'type');// used for queries 196@define('Q_TYPE_PAGE', 'type_page'); 197@define('Q_ALL', 'all'); 198@define('Q_ENTRY', 'entry'); 199@define('Q_KEY', 'key'); 200@define('Q_KEYS', 'keys'); // filter entries using a url-encoded, JSON-encoded array of bibtex keys 201@define('Q_SEARCH', 'search'); 202@define('Q_EXCLUDE', 'exclude'); 203@define('Q_RESULT', 'result'); 204@define('Q_ACADEMIC', 'academic'); 205@define('Q_DB', 'bibdb'); 206@define('Q_LATEST', 'latest'); 207@define('Q_RANGE', 'range'); 208@define('AUTHOR', 'author'); 209@define('EDITOR', 'editor'); 210@define('SCHOOL', 'school'); 211@define('TITLE', 'title'); 212@define('BOOKTITLE', 'booktitle'); 213@define('YEAR', 'year'); 214@define('BUFFERSIZE',100000); 215@define('MULTIPLE_BIB_SEPARATOR',';'); 216@define('METADATA_COINS',true); // see https://en.wikipedia.org/wiki/COinS 217@define('METADATA_GS',false); // metadata google scholar, see http://www.monperrus.net/martin/accurate+bibliographic+metadata+and+google+scholar 218@define('METADATA_DC',true); // see http://dublincore.org/ 219@define('METADATA_OPENGRAPH',true); // see http://ogp.me/ 220@define('METADATA_EPRINTS',false); // see https://wiki.eprints.org/w/Category:EPrints_Metadata_Fields 221 222// define sort order for special values in 'year' field 223// highest number is sorted first 224// don't exceed 0 though, since the values are added to PHP_INT_MAX 225@define('ORDER_YEAR_INPRESS', 0); 226@define('ORDER_YEAR_ACCEPTED', 1); 227@define('ORDER_YEAR_SUBMITTED', 2); 228@define('ORDER_YEAR_OTHERNONINT', 3); 229 230 231// in embedded mode, we still need a URL for displaying bibtex entries alone 232// this is usually resolved to bibtexbrowser.php 233// but can be overridden in bibtexbrowser.local.php 234// for instance with @define('BIBTEXBROWSER_URL',''); // links to the current page with ? 235@define('BIBTEXBROWSER_URL',basename(__FILE__)); 236 237// Specify the location of the cache file for servers that need temporary files written in a specific location 238@define('CACHE_DIR',''); 239 240// Specify the location of the bib file for servers that need do not allow slashes in URLs, 241// where the bib file and bibtexbrowser.php are in different directories. 242@define('DATA_DIR',''); 243 244// *************** END CONFIGURATION 245 246define('Q_INNER_AUTHOR', '_author');// internally used for representing the author 247define('Q_INNER_TYPE', 'x-bibtex-type');// used for representing the type of the bibtex entry internally 248@define('Q_INNER_KEYS_INDEX', '_keys-index');// used for storing indices in $_GET[Q_KEYS] array 249 250define('Q_NAME', 'name');// used to allow for exact last name matches in multisearch 251define('Q_AUTHOR_NAME', 'author_name');// used to allow for exact last name matches in multisearch 252define('Q_EDITOR_NAME', 'editor_name');// used to allow for exact last name matches in multisearch 253 254// for clean search engine links 255// we disable url rewriting 256// ... and hope that your php configuration will accept one of these 257@ini_set("session.use_only_cookies",1); 258@ini_set("session.use_trans_sid",0); 259@ini_set("url_rewriter.tags",""); 260 261function nothing() {} 262 263function config_value($key) { 264 global $CONFIGURATION; 265 if (isset($CONFIGURATION[$key])) { return $CONFIGURATION[$key]; } 266 if (defined($key)) { return constant($key); } 267 die('no such configuration: '.$key); 268} 269 270/** parses $_GET[Q_FILE] and puts the result (an object of type BibDataBase) in $_GET[Q_DB]. 271See also zetDB(). 272 */ 273function setDB() { 274 list($db, $parsed, $updated, $saved) = _zetDB(@$_GET[Q_FILE]); 275 $_GET[Q_DB] = $db; 276 return $updated; 277} 278 279/** parses the $bibtex_filenames (usually semi-column separated) and returns an object of type BibDataBase. 280See also setDB() 281*/ 282function zetDB($bibtex_filenames) { 283 list($db, $parsed, $updated, $saved) = _zetDB($bibtex_filenames); 284 return $db; 285} 286 287/** @nodoc */ 288function default_message() { 289 290 if (config_value('BIBTEXBROWSER_NO_DEFAULT')==true) { return; } 291 292 ?> 293 <div id="bibtexbrowser_message"> 294 Congratulations! bibtexbrowser is correctly installed!<br/> 295 Now you have to pass the name of the bibtex file as parameter (e.g. bibtexbrowser.php?bib=mybib.php)<br/> 296 You may browse:<br/> 297 <?php 298 foreach (glob("*.bib") as $bibfile) { 299 $url="?bib=".urlencode($bibfile); 300 echo '<a href="'.htmlspecialchars($url, ENT_QUOTES, 'UTF-8').'" rel="nofollow">'.htmlspecialchars($bibfile, ENT_QUOTES, 'UTF-8').'</a><br/>'; 301 } 302 echo "</div>"; 303} 304 305/** returns the target of links */ 306function get_target() { 307 if (c('BIBTEXBROWSER_LINKS_TARGET')!='_self') { 308 return " target=\"".c('BIBTEXBROWSER_LINKS_TARGET')."\""; 309 } 310 else return ""; 311} 312 313/** @nodoc */ 314function _zetDB($bibtex_filenames) { 315 316 $db = null; 317 318 // default bib file, if no file is specified in the query string. 319 if (!isset($bibtex_filenames) || $bibtex_filenames == "") { 320 default_message(); 321 exit; 322 } 323 324 // first does the bibfiles exist: 325 // $bibtex_filenames can be urlencoded for instance if they contain slashes 326 // so we decode it 327 $bibtex_filenames = urldecode($bibtex_filenames); 328 329 // ---------------------------- HANDLING unexistent files 330 foreach(explode(MULTIPLE_BIB_SEPARATOR, $bibtex_filenames) as $bib) { 331 332 // get file extension to only allow .bib files 333 $ext = pathinfo($bib, PATHINFO_EXTENSION); 334 // this is a security protection 335 if (BIBTEXBROWSER_LOCAL_BIB_ONLY && (!file_exists(DATA_DIR.$bib) || strcasecmp($ext, 'bib') != 0)) { 336 // to automate dectection of faulty links with tools such as webcheck 337 header('HTTP/1.1 404 Not found'); 338 // escape $bib to prevent XSS 339 $escapedBib = htmlEntities($bib, ENT_QUOTES); 340 die('<b>the bib file '.$escapedBib.' does not exist !</b>'); 341 } 342 } // end for each 343 344 // ---------------------------- HANDLING HTTP If-modified-since 345 // testing with $ curl -v --header "If-Modified-Since: Fri, 23 Oct 2010 19:22:47 GMT" "... bibtexbrowser.php?key=wasylkowski07&bib=..%252Fstrings.bib%253B..%252Fentries.bib" 346 // and $ curl -v --header "If-Modified-Since: Fri, 23 Oct 2000 19:22:47 GMT" "... bibtexbrowser.php?key=wasylkowski07&bib=..%252Fstrings.bib%253B..%252Fentries.bib" 347 348 // save bandwidth and server cpu 349 // (imagine the number of requests from search engine bots...) 350 $bib_is_unmodified = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ; 351 foreach(explode(MULTIPLE_BIB_SEPARATOR, $bibtex_filenames) as $bib) { 352 $bib_is_unmodified = 353 $bib_is_unmodified 354 && (strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>filemtime($bib)); 355 } // end for each 356 if ( $bib_is_unmodified && !headers_sent()) { 357 header("HTTP/1.1 304 Not Modified"); 358 exit; 359 } 360 361 362 $parse=true; 363 $updated = false; 364 365 if (config_value('BIBTEXBROWSER_USE_CACHE')==true) { 366 367 // ---------------------------- HANDLING caching of compiled bibtex files 368 // for sake of performance, once the bibtex file is parsed 369 // we try to save a "compiled" in a txt file 370 $compiledbib = CACHE_DIR.'bibtexbrowser_'.md5($bibtex_filenames).'.dat'; 371 372 $parse=filemtime(__FILE__)>@filemtime($compiledbib); 373 374 // do we have a compiled version ? 375 if (is_file($compiledbib) 376 && is_readable($compiledbib) 377 && filesize($compiledbib)>0 378 ) { 379 $f = fopen($compiledbib,'r+'); // some Unix seem to consider flock as a writing operation 380 //we use a lock to avoid that a call to bibbtexbrowser made while we write the object loads an incorrect object 381 if (flock($f,LOCK_EX)) { 382 $s = filesize($compiledbib); 383 $ser = fread($f,$s); 384 $db = @unserialize($ser); 385 flock($f,LOCK_UN); 386 } else { die('could not get the lock'); } 387 fclose($f); 388 // basic test 389 // do we have an correct version of the file 390 if (!is_a($db,'BibDataBase')) { 391 unlink($compiledbib); 392 if (BIBTEXBROWSER_DEBUG) { die('$db not a BibDataBase. please reload.'); } 393 $parse=true; 394 } 395 } else {$parse=true;} 396 } 397 398 // we don't have a compiled version 399 if ($parse) { 400 //echo '<!-- parsing -->'; 401 // then parsing the file 402 $db = createBibDataBase(); 403 foreach(explode(MULTIPLE_BIB_SEPARATOR, $bibtex_filenames) as $bib) { 404 $db->load($bib); 405 } 406 } 407 408 if (config_value('BIBTEXBROWSER_USE_CACHE')==true) { 409 // now we may update the database 410 if (!file_exists($compiledbib)) { 411 @touch($compiledbib); 412 $updated = true; // limit case 413 } else foreach(explode(MULTIPLE_BIB_SEPARATOR, $bibtex_filenames) as $bib) { 414 // is it up to date ? wrt to the bib file and the script 415 // then upgrading with a new version of bibtexbrowser triggers a new compilation of the bib file 416 if (filemtime($bib)>filemtime($compiledbib) || filemtime(__FILE__)>filemtime($compiledbib)) { 417 // echo "updating ".$bib; 418 $db->update($bib); 419 $updated = true; 420 } 421 } 422 } 423// echo var_export($parse); 424// echo var_export($updated); 425 426 $saved = false; 427 // are we able to save the compiled version ? 428 // note that the compiled version is saved in the current working directory 429 if ( config_value('BIBTEXBROWSER_USE_CACHE')==true && ( $parse || $updated ) && is_writable($compiledbib)) { 430 // we use 'a' because the file is not locked between fopen and flock 431 $f = fopen($compiledbib,'a'); 432 //we use a lock to avoid that a call to bibbtexbrowser made while we write the object loads an incorrect object 433 if (flock($f,LOCK_EX)) { 434// echo '<!-- saving -->'; 435 ftruncate($f,0); 436 fwrite($f,serialize($db)); 437 flock($f,LOCK_UN); 438 $saved = true; 439 } else { die('could not get the lock'); } 440 fclose($f); 441 } // end saving the cached verions 442 //else echo '<!-- please chmod the directory containing the bibtex file to be able to keep a compiled version (much faster requests for large bibtex files) -->'; 443 444 445 return array($db, $parse, $updated, $saved); 446} // end function setDB 447 448// internationalization 449if (!function_exists('__')){ 450 function __($msg) { 451 global $BIBTEXBROWSER_LANG; 452 if (isset($BIBTEXBROWSER_LANG[$msg])) { 453 return $BIBTEXBROWSER_LANG[$msg]; 454 } 455 return $msg; 456 } 457} 458 459// factories 460// may be overridden in bibtexbrowser.local.php 461if (!function_exists('createBibDataBase')) { 462 /** factory method for openness @nodoc */ 463 function createBibDataBase() { $x = new BibDataBase(); return $x;} 464} 465if (!function_exists('createBibEntry')) { 466 /** factory method for openness @nodoc */ 467 function createBibEntry() { $x = new BibEntry(); return $x;} 468} 469if (!function_exists('createBibDBBuilder')) { 470 /** factory method for openness @nodoc */ 471 function createBibDBBuilder() { $x = new BibDBBuilder(); return $x;} 472} 473if (!function_exists('createBasicDisplay')) { 474 /** factory method for openness @nodoc */ 475 function createBasicDisplay() { $x = new SimpleDisplay(); return $x;} 476} 477if (!function_exists('createBibEntryDisplay')) { 478 /** factory method for openness @nodoc */ 479 function createBibEntryDisplay() { $x = new BibEntryDisplay(); return $x;} 480} 481if (!function_exists('createMenuManager')) { 482 /** factory method for openness @nodoc */ 483 function createMenuManager() { $x = new MenuManager(); return $x;} 484} 485 486 487//////////////////////////////////////////////////////// 488 489/** is a generic parser of bibtex files. 490usage: 491<pre> 492 $delegate = new XMLPrettyPrinter();// or another delegate such as BibDBBuilder 493 $parser = new StateBasedBibtexParser($delegate); 494 $parser->parse(fopen('bibacid-utf8.bib','r')); 495</pre> 496notes: 497 - It has no dependencies, it can be used outside of bibtexbrowser 498 - The delegate is expected to have some methods, see classes BibDBBuilder and XMLPrettyPrinter 499 */ 500class StateBasedBibtexParser { 501 502 var $delegate; 503 504 function __construct($delegate) { 505 $this->delegate = $delegate; 506 } 507 508 function parse($handle) { 509 if (gettype($handle) == 'string') { throw new Exception('oops'); } 510 $delegate = $this->delegate; 511 // STATE DEFINITIONS 512 @define('NOTHING',1); 513 @define('GETTYPE',2); 514 @define('GETKEY',3); 515 @define('GETVALUE',4); 516 @define('GETVALUEDELIMITEDBYQUOTES',5); 517 @define('GETVALUEDELIMITEDBYQUOTES_ESCAPED',6); 518 @define('GETVALUEDELIMITEDBYCURLYBRACKETS',7); 519 @define('GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED',8); 520 @define('GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL',9); 521 @define('GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED',10); 522 @define('GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL',11); 523 @define('GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED',12); 524 @define('GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL',13); 525 @define('GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED',14); 526 527 528 $state=NOTHING; 529 $entrytype=''; 530 $entrykey=''; 531 $entryvalue=''; 532 $fieldvaluepart=''; 533 $finalkey=''; 534 $entrysource=''; 535 536 // metastate 537 $isinentry = false; 538 539 $delegate->beginFile(); 540 541 // if you encounter this error "Allowed memory size of xxxxx bytes exhausted" 542 // then decrease the size of the temp buffer below 543 $bufsize=BUFFERSIZE; 544 while (!feof($handle)) { 545 $sread=fread($handle,$bufsize); 546 //foreach(str_split($sread) as $s) { 547 for ( $i=0; $i < strlen( $sread ); $i++) { $s=$sread[$i]; 548 549 if ($isinentry) $entrysource.=$s; 550 551 if ($state==NOTHING) { 552 // this is the beginning of an entry 553 if ($s=='@') { 554 $delegate->beginEntry(); 555 $state = GETTYPE; 556 $isinentry = true; 557 $entrysource='@'; 558 } 559 } 560 561 else if ($state==GETTYPE) { 562 // this is the beginning of a key 563 if ($s=='{') { 564 $state = GETKEY; 565 $delegate->setEntryType($entrytype); 566 $entrytype=''; 567 } 568 else $entrytype=$entrytype.$s; 569 } 570 571 else if ($state==GETKEY) { 572 // now we get the value 573 if ($s=='=') { 574 $state = GETVALUE; 575 $fieldvaluepart=''; 576 $finalkey=$entrykey; 577 $entrykey=''; 578 } 579 // oups we only have the key :-) anyway 580 else if ($s=='}') { 581 $state = NOTHING;$isinentry = false;$delegate->endEntry($entrysource); 582 $entrykey=''; 583 } 584 // OK now we look for values 585 else if ($s==',') { 586 $state=GETKEY; 587 $delegate->setEntryKey($entrykey); 588 $entrykey='';} 589 else { $entrykey=$entrykey.$s; } 590 } 591 // we just got a =, we can now receive the value, but we don't now whether the value 592 // is delimited by curly brackets, double quotes or nothing 593 else if ($state==GETVALUE) { 594 595 // the value is delimited by double quotes 596 if ($s=='"') { 597 $state = GETVALUEDELIMITEDBYQUOTES; 598 } 599 // the value is delimited by curly brackets 600 else if ($s=='{') { 601 $state = GETVALUEDELIMITEDBYCURLYBRACKETS; 602 } 603 // the end of the key and no value found: it is the bibtex key e.g. \cite{Descartes1637} 604 else if ($s==',') { 605 $state = GETKEY; 606 $delegate->setEntryField($finalkey,$entryvalue); 607 $entryvalue=''; // resetting the value buffer 608 } 609 // this is the end of the value AND of the entry 610 else if ($s=='}') { 611 $state = NOTHING; 612 $delegate->setEntryField($finalkey,$entryvalue); 613 $isinentry = false;$delegate->endEntry($entrysource); 614 $entryvalue=''; // resetting the value buffer 615 } 616 else if ($s==' ' || $s=="\t" || $s=="\n" || $s=="\r" ) { 617 // blank characters are not taken into account when values are not in quotes or curly brackets 618 } 619 else { 620 $entryvalue=$entryvalue.$s; 621 } 622 } 623 624 625 /* GETVALUEDELIMITEDBYCURLYBRACKETS* handle entries delimited by curly brackets and the possible nested curly brackets */ 626 else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS) { 627 628 if ($s=='\\') { 629 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED; 630 $entryvalue=$entryvalue.$s;} 631 else if ($s=='{') { 632 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL; 633 $entryvalue=$entryvalue.$s; 634 $delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYTOP'); 635 $fieldvaluepart=''; 636 } 637 else if ($s=='}') { // end entry 638 $state = GETVALUE; 639 $delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYTOP'); 640 } 641 else { 642 $entryvalue=$entryvalue.$s; 643 $fieldvaluepart=$fieldvaluepart.$s; 644 } 645 } 646 // handle anti-slashed brackets 647 else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_ESCAPED) { 648 $state = GETVALUEDELIMITEDBYCURLYBRACKETS; 649 $entryvalue=$entryvalue.$s; 650 } 651 // in first level of curly bracket 652 else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL) { 653 if ($s=='\\') { 654 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED; 655 $entryvalue=$entryvalue.$s;} 656 else if ($s=='{') { 657 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL;$entryvalue=$entryvalue.$s;} 658 else if ($s=='}') { 659 $state = GETVALUEDELIMITEDBYCURLYBRACKETS; 660 $delegate->entryValuePart($finalkey,$fieldvaluepart,'CURLYONE'); 661 $fieldvaluepart=''; 662 $entryvalue=$entryvalue.$s; 663 } 664 else { 665 $entryvalue=$entryvalue.$s; 666 $fieldvaluepart=$fieldvaluepart.$s; 667 } 668 } 669 // handle anti-slashed brackets 670 else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL_ESCAPED) { 671 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL; 672 $entryvalue=$entryvalue.$s; 673 } 674 675 // in second level of curly bracket 676 else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL) { 677 if ($s=='\\') { 678 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED; 679 $entryvalue=$entryvalue.$s;} 680 else if ($s=='{') { 681 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL;$entryvalue=$entryvalue.$s;} 682 else if ($s=='}') { 683 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_1NESTEDLEVEL;$entryvalue=$entryvalue.$s;} 684 else { $entryvalue=$entryvalue.$s;} 685 } 686 // handle anti-slashed brackets 687 else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL_ESCAPED) { 688 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL; 689 $entryvalue=$entryvalue.$s; 690 } 691 692 // in third level of curly bracket 693 else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL) { 694 if ($s=='\\') { 695 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED; 696 $entryvalue=$entryvalue.$s;} 697 else if ($s=='}') { 698 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_2NESTEDLEVEL;$entryvalue=$entryvalue.$s;} 699 else { $entryvalue=$entryvalue.$s;} 700 } 701 // handle anti-slashed brackets 702 else if ($state==GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL_ESCAPED) { 703 $state = GETVALUEDELIMITEDBYCURLYBRACKETS_3NESTEDLEVEL; 704 $entryvalue=$entryvalue.$s; 705 } 706 707 /* handles entries delimited by double quotes */ 708 else if ($state==GETVALUEDELIMITEDBYQUOTES) { 709 710 if ($s=='\\') { 711 $state = GETVALUEDELIMITEDBYQUOTES_ESCAPED; 712 $entryvalue=$entryvalue.$s;} 713 else if ($s=='"') { 714 $state = GETVALUE; 715 } 716 else { $entryvalue=$entryvalue.$s;} 717 } 718 // handle anti-double quotes 719 else if ($state==GETVALUEDELIMITEDBYQUOTES_ESCAPED) { 720 $state = GETVALUEDELIMITEDBYQUOTES; 721 $entryvalue=$entryvalue.$s; 722 } 723 724 } // end for 725 } // end while 726 $delegate->endFile(); 727 //$d = $this->delegate;print_r($d); 728 } // end function 729} // end class 730 731/** a default empty implementation of a delegate for StateBasedBibtexParser 732 * @codeCoverageIgnore 733*/ 734class ParserDelegate { 735 736 function beginFile() {} 737 738 function endFile() {} 739 740 function setEntryField($finalkey,$entryvalue) {} 741 742 function setEntryType($entrytype) {} 743 744 function setEntryKey($entrykey) {} 745 746 function beginEntry() {} 747 748 function endEntry($entrysource) {} 749 750 /** called for each sub parts of type {part} of a field value 751 * for now, only CURLYTOP and CURLYONE events 752 */ 753 function entryValuePart($key, $value, $type) {} 754 755} // end class ParserDelegate 756 757 758/** is a possible delegate for StateBasedBibParser. 759usage: 760see snippet of [[#StateBasedBibParser]] 761*/ 762class XMLPrettyPrinter extends ParserDelegate { 763 var $header = true; 764 function beginFile() { 765 if ($this->header) { 766 header('Content-type: text/xml;'); 767 } 768 print '<?xml version="1.0" encoding="'.OUTPUT_ENCODING.'"?>'; 769 print '<bibfile>'; 770 } 771 772 function endFile() { 773 print '</bibfile>'; 774 } 775 function setEntryField($finalkey,$entryvalue) { 776 print "<data>\n<key>".trim($finalkey)."</key>\n<value>".htmlspecialchars($entryvalue)."</value>\n</data>\n"; 777 } 778 779 function setEntryType($entrytype) { 780 print '<type>'.$entrytype.'</type>'; 781 } 782 783 function setEntryKey($entrykey) { 784 print '<keyonly>'.$entrykey.'</keyonly>'; 785 } 786 787 function beginEntry() { 788 print "<entry>\n"; 789 } 790 791 function endEntry($entrysource) { 792 print "</entry>\n"; 793 } 794} // end class XMLPrettyPrinter 795 796/** represents @string{k=v} */ 797class StringEntry { 798 public $filename; 799 public $name; 800 public $value; 801 802 function __construct($k, $v, $filename) { 803 $this->name=$k; 804 $this->value=$v; 805 $this->filename=$filename; 806 } 807 808 function toString() { 809 return '@string{'.$this->name.'={'.$this->value.'}}'; 810 } 811} // end class StringEntry 812 813 814 815 816/** builds arrays of BibEntry objects from a bibtex file. 817usage: 818<pre> 819 $empty_array = array(); 820 $db = new BibDBBuilder(); // see also factory method createBibDBBuilder 821 $db->build('bibacid-utf8.bib'); // parses bib file 822 print_r($db->builtdb);// an associated array key -> BibEntry objects 823 print_r($db->stringdb);// an associated array key -> strings representing @string 824</pre> 825notes: 826 method build can be used several times, bibtex entries are accumulated in the builder 827*/ 828class BibDBBuilder extends ParserDelegate { 829 830 /** A hashtable from keys to bib entries (BibEntry). */ 831 var $builtdb = array(); 832 833 /** A hashtable of constant strings */ 834 var $stringdb = array(); 835 836 var $filename; 837 838 var $currentEntry; 839 840 function build($bibfilename, $handle = NULL) { 841 842 $this->filename = $bibfilename; 843 if ($handle == NULL) { 844 $handle = fopen(DATA_DIR.$bibfilename, "r"); 845 } 846 847 if (!$handle) die ('cannot open '.$bibfilename); 848 849 $parser = new StateBasedBibtexParser($this); 850 $parser->parse($handle); 851 fclose($handle); 852 //print_r(array_keys($this->builtdb)); 853 //print_r($this->builtdb); 854 } 855 856 857 function getBuiltDb() { 858 //print_r($this->builtdb); 859 return $this->builtdb; 860 } 861 862 function beginFile() { 863 } 864 865 function endFile() { 866 // resolving crossrefs 867 // we are careful with PHP 4 semantics 868 foreach (array_keys($this->builtdb) as $key) { 869 $bib = $this->builtdb[$key]; 870 if ($bib->hasField('crossref')) { 871 if (isset($this->builtdb[$bib->getField('crossref')])) { 872 $crossrefEntry = $this->builtdb[$bib->getField('crossref')]; 873 $bib->crossref = $crossrefEntry; 874 foreach($crossrefEntry->getFields() as $k => $v) { 875 // copying the fields of the cross ref 876 // only if they don't exist yet 877 if (!$bib->hasField($k)) { 878 $bib->setField($k,$v); 879 } 880 } 881 } 882 } 883 } 884 //print_r($this->builtdb); 885 } 886 887 function setEntryField($fieldkey,$entryvalue) { 888 $fieldkey=trim($fieldkey); 889 // support for Bibtex concatenation 890 // see http://newton.ex.ac.uk/tex/pack/bibtex/btxdoc/node3.html 891 // (?<! is a negative look-behind assertion, see http://www.php.net/manual/en/regexp.reference.assertions.php 892 $entryvalue_array=preg_split('/(?<!\\\\)#/', $entryvalue); 893 foreach ($entryvalue_array as $k=>$v) { 894 // spaces are allowed when using # and they are not taken into account 895 // however # is not itself replaced by a space 896 // warning: @strings are not case sensitive 897 // see http://newton.ex.ac.uk/tex/pack/bibtex/btxdoc/node3.html 898 $stringKey=strtolower(trim($v)); 899 if (isset($this->stringdb[$stringKey])) 900 { 901 // this field will be formated later by xtrim and latex2html 902 $entryvalue_array[$k]=$this->stringdb[$stringKey]->value; 903 904 // we keep a trace of this replacement 905 // so as to produce correct bibtex snippets 906 $this->currentEntry->constants[$stringKey]=$this->stringdb[$stringKey]->value; 907 } 908 } 909 $entryvalue=implode('',$entryvalue_array); 910 911 $this->currentEntry->setField($fieldkey,$entryvalue); 912 } 913 914 function setEntryType($entrytype) { 915 $this->currentEntry->setType($entrytype); 916 } 917 918 function setEntryKey($entrykey) { 919 //echo "new entry:".$entrykey."\n"; 920 $this->currentEntry->setKey($entrykey); 921 } 922 923 function beginEntry() { 924 $this->currentEntry = createBibEntry(); 925 $this->currentEntry->setFile($this->filename); 926 } 927 928 function endEntry($entrysource) { 929 930 // we add a timestamp 931 $this->currentEntry->timestamp(); 932 933 // we add a key if there is no key 934 if (!$this->currentEntry->hasField(Q_KEY) && $this->currentEntry->getType()!='string') { 935 $this->currentEntry->setField(Q_KEY,md5($entrysource)); 936 } 937 938 // we set the fulltext 939 $this->currentEntry->text = $entrysource; 940 941 // we format the author names in a special field 942 // to enable search 943 if ($this->currentEntry->hasField('author')) { 944 $this->currentEntry->setField(Q_INNER_AUTHOR,$this->currentEntry->getFormattedAuthorsString()); 945 946 foreach($this->currentEntry->getCanonicalAuthors() as $author) { 947 $homepage_key = $this->currentEntry->getHomePageKey($author); 948 if (isset($this->stringdb[$homepage_key])) { 949 $this->currentEntry->homepages[$homepage_key] = $this->stringdb[$homepage_key]->value; 950 } 951 } 952 } 953 954 // ignoring jabref comments 955 if (($this->currentEntry->getType()=='comment')) { 956 /* do nothing for jabref comments */ 957 } 958 959 // we add it to the string database 960 else if ($this->currentEntry->getType()=='string') { 961 foreach($this->currentEntry->fields as $k => $v) { 962 $k!=Q_INNER_TYPE and $this->stringdb[$k] = new StringEntry($k,$v,$this->filename); 963 } 964 } 965 966 // we add it to the database 967 else { 968 $this->builtdb[$this->currentEntry->getKey()] = $this->currentEntry; 969 } 970 } 971 972} // end class BibDBBuilder 973 974 975 976 977/** is an extended version of the trim function, removes linebreaks, tabs, etc. 978 */ 979function xtrim($line) { 980 $line = trim($line); 981 // we remove the unneeded line breaks 982 // this is *required* to correctly split author lists into names 983 // 2010-06-30 984 // bug found by Thomas 985 // windows new line is **\r\n"** and not the other way around!! 986 // according to php.net: Proncess \r\n's first so they aren't converted twice 987 $line = str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $line); 988 // remove superfluous spaces e.g. John+++Bar 989 $line = preg_replace('/ {2,}/',' ', $line); 990 return $line; 991} 992 993/** encapsulates the conversion of a single latex chars to the corresponding HTML entity. 994It expects a **lower-case** char. 995*/ 996function char2html($line,$latexmodifier,$char,$entitiyfragment) { 997 $line = char2html_case_sensitive($line,$latexmodifier,strtoupper($char),$entitiyfragment); 998 return char2html_case_sensitive($line,$latexmodifier,strtolower($char),$entitiyfragment); 999} 1000 1001function char2html_case_sensitive($line,$latexmodifier,$char,$entitiyfragment) { 1002 $line = preg_replace('/\\{?\\\\'.preg_quote($latexmodifier,'/').' ?\\{?'.$char.'\\}?/','&'.$char.''.$entitiyfragment.';', $line); 1003 return $line; 1004} 1005 1006/** converts latex chars to HTML entities. 1007(I still look for a comprehensive translation table from late chars to html, better than [[http://isdc.unige.ch/Newsletter/help.html]]) 1008 */ 1009function latex2html($line, $do_clean_extra_bracket=true) { 1010 1011 $line = preg_replace('/([^\\\\])~/','\\1 ', $line); 1012 1013 $line = str_replace(array('---', '--'), array('—', '–'), $line); 1014 1015 $line = str_replace(array('``', "''"), array('"', '"'), $line); 1016 1017 // performance increases with this test 1018 // bug found by Serge Barral: what happens if we have curly braces only (typically to ensure case in Latex) 1019 // added && strpos($line,'{')===false 1020 if (strpos($line,'\\')===false && strpos($line,'{')===false) return $line; 1021 1022 // handling uppercase 1023 // echo preg_replace_callback('!\b[a-z]!', 'upper', strtolower($str)); 1024 if (!function_exists("strtolowercallback")) { 1025 function strtolowercallback($array) { 1026 return strtolower($array[1]); 1027 } 1028 } 1029 if (!function_exists("strtouppercallback")) { 1030 function strtouppercallback($array) { 1031 return strtoupper($array[1]); 1032 } 1033 } 1034 $line = preg_replace_callback('/\\\\uppercase\{(.*)\}/U',"strtouppercallback", $line); 1035 $line = preg_replace_callback('/\\\\lowercase\{(.*)\}/U',"strtolowercallback", $line); 1036 1037 $maths = array(); 1038 $index = 0; 1039 // first we escape the math env 1040 preg_match_all('/\$.*?\$/', $line, $matches); 1041 foreach ($matches[0] as $k) { 1042 $maths[] = $k; 1043 $line = str_replace($k, '__MATH'.$index.'__', $line); 1044 $index++; 1045 } 1046 1047 // we should better replace this before the others 1048 // in order not to mix with the HTML entities coming after (just in case) 1049 $line = str_replace('\\&','&', $line); 1050 1051 $line = str_replace('\_','_',$line); 1052 $line = str_replace('\%','%',$line); 1053 1054 // handling \url{....} 1055 // often used in howpublished for @misc 1056 $line = preg_replace('/\\\\url\{(.*)\}/U','<a href="\\1">\\1</a>', $line); 1057 1058 // Friday, April 01 2011 1059 // added support for accented i 1060 // for instance \`\i 1061 // see http://en.wikibooks.org/wiki/LaTeX/Accents 1062 // " the letters "i" and "j" require special treatment when they are given accents because it is often desirable to replace the dot with the accent. For this purpose, the commands \i and \j can be used to produce dotless letters." 1063 $line = preg_replace('/\\\\([ij])/i','\\1', $line); 1064 1065 1066 $line = char2html($line,"'",'a',"acute"); 1067 $line = char2html($line,"'",'c',"acute"); 1068 $line = char2html($line,"'",'e',"acute"); 1069 $line = char2html($line,"'",'i',"acute"); 1070 $line = char2html($line,"'",'o',"acute"); 1071 $line = char2html($line,"'",'u',"acute"); 1072 $line = char2html($line,"'",'y',"acute"); 1073 $line = char2html($line,"'",'n',"acute"); 1074 1075 $line = char2html($line,'`','a',"grave"); 1076 $line = char2html($line,'`','e',"grave"); 1077 $line = char2html($line,'`','i',"grave"); 1078 $line = char2html($line,'`','o',"grave"); 1079 $line = char2html($line,'`','u',"grave"); 1080 1081 $line = char2html($line,'~','a',"tilde"); 1082 $line = char2html($line,'~','n',"tilde"); 1083 $line = char2html($line,'~','o',"tilde"); 1084 1085 $line = char2html($line,'"','a',"uml"); 1086 $line = char2html($line,'"','e',"uml"); 1087 $line = char2html($line,'"','i',"uml"); 1088 $line = char2html($line,'"','o',"uml"); 1089 $line = char2html($line,'"','u',"uml"); 1090 $line = char2html($line,'"','y',"uml"); 1091 $line = char2html($line,'"','s',"zlig"); 1092 1093 $line = char2html($line,'^','a',"circ"); 1094 $line = char2html($line,'^','e',"circ"); 1095 $line = char2html($line,'^','i',"circ"); 1096 $line = char2html($line,'^','o',"circ"); 1097 $line = char2html($line,'^','u',"circ"); 1098 1099 $line = char2html($line,'r','a',"ring"); 1100 1101 $line = char2html($line,'c','c',"cedil"); 1102 $line = char2html($line,'c','s',"cedil"); 1103 $line = char2html($line,'v','s',"caron"); 1104 1105 $line = str_replace('\\ae','æ', $line); 1106 $line = str_replace('\\ss','ß', $line); 1107 1108 $line = str_replace('\\o','ø', $line); 1109 $line = str_replace('\\O','Ø', $line); 1110 $line = str_replace('\\aa','å', $line); 1111 $line = str_replace('\\AA','Å', $line); 1112 1113 $line = str_replace('\\l','ł',$line); 1114 $line = str_replace('\\L','Ł',$line); 1115 $line = str_replace('\\k{a}','ą',$line); 1116 $line = str_replace('\\\'{c}','ć',$line); 1117 1118 $line = str_replace('\\v{c}','č',$line); 1119 $line = str_replace('\\v{C}','Č',$line); 1120 1121 // handling \textsuperscript{....} FAILS if there still are nested {} 1122 $line = preg_replace('/\\\\textsuperscript\{(.*)\}/U','<sup>\\1</sup>', $line); 1123 1124 // handling \textsubscript{....} FAILS if there still are nested {} 1125 $line = preg_replace('/\\\\textsubscript\{(.*)\}/U','<sub>\\1</sub>', $line); 1126 1127 1128 1129 if ($do_clean_extra_bracket) { 1130 // clean extra tex curly brackets, usually used for preserving capitals 1131 // must come before the final math replacement 1132 $line = str_replace('}','',$line); 1133 $line = str_replace('{','',$line); 1134 } 1135 1136 // we restore the math env 1137 for($i = 0; $i < count($maths); $i++) { 1138 $line = str_replace('__MATH'.$i.'__', $maths[$i], $line); 1139 } 1140 1141 return $line; 1142} 1143 1144/** encodes strings for Z3988 URLs. Note that & are encoded as %26 and not as &. */ 1145function s3988($s) { 1146 if ($s == null) return ''; 1147 // first remove the HTML entities (e.g. é) then urlencode them 1148 return urlencode($s); 1149} 1150 1151/** 1152see BibEntry->formatAuthor($author) 1153@deprecated 1154@nodoc 1155@codeCoverageIgnore 1156*/ 1157function formatAuthor() { 1158 die('Sorry, this function does not exist anymore, however, you can simply use $bibentry->formatAuthor($author) instead.'); 1159} 1160 1161// ---------------------------------------------------------------------- 1162// BIB ENTRIES 1163// ---------------------------------------------------------------------- 1164 1165/** represents a bibliographic entry. 1166usage: 1167<pre> 1168 $db = zetDB('bibacid-utf8.bib'); 1169 $entry = $db->getEntryByKey('classical'); 1170 echo bib2html($entry); 1171</pre> 1172notes: 1173- BibEntry are usually obtained with getEntryByKey or multisearch 1174*/ 1175class BibEntry { 1176 1177 /** The fields (fieldName -> value) of this bib entry with Latex macros interpreted and encoded in the desired character set . */ 1178 var $fields = array(); 1179 1180 /** The raw fields (fieldName -> value) of this bib entry. */ 1181 var $raw_fields = array(); 1182 1183 /** The constants @STRINGS referred to by this entry */ 1184 var $constants = array(); 1185 1186 /** The homepages of authors if any */ 1187 var $homepages = array(); 1188 1189 /** The crossref entry if there is one */ 1190 var $crossref; 1191 1192 /** The verbatim copy (i.e., whole text) of this bib entry. */ 1193 var $text = ''; 1194 1195 /** A timestamp to trace when entries have been created */ 1196 var $timestamp; 1197 1198 /** The name of the file containing this entry */ 1199 var $filename = "nofilename-frommemory"; 1200 1201 /** The short name of the entry (parameterized by ABBRV_TYPE) */ 1202 var $abbrv; 1203 1204 /** The index in a list of publications (e.g. [1] Foo */ 1205 var $index = ''; 1206 1207 /** The location in the original bibtex file (set by addEntry) */ 1208 var $order = -1; 1209 1210 1211 /** returns a debug string representation */ 1212 function __toString() { 1213 return $this->getType()." ".$this->getKey(); 1214 } 1215 1216 /** Creates an empty new bib entry. Each bib entry is assigned a unique 1217 * identification number. */ 1218 function __construct() { 1219 } 1220 1221 /** Sets the name of the file containing this entry */ 1222 function setFile($filename) { 1223 $this->filename = $filename; 1224 return $this; 1225 } 1226 1227 /** Adds timestamp to this object */ 1228 function timestamp() { 1229 $this->timestamp = time(); 1230 } 1231 /** Returns the timestamp of this object */ 1232 function getTimestamp() { 1233 return $this->timestamp; 1234 } 1235 1236 /** Returns the type of this bib entry (always lowercase). */ 1237 function getType() { 1238 // strtolower is important to be case-insensitive 1239 $res = $this->getField(Q_INNER_TYPE); 1240 if ($res == NULL) return 'unknown'; 1241 return strtolower($res); 1242 } 1243 1244 /** Sets the key of this bib entry. */ 1245 function setKey($value) { 1246 // Slashes are not allowed in keys because they don't play well with web servers 1247 // if url-rewriting is used 1248 $this->setField(Q_KEY,str_replace('/','-',$value)); 1249 } 1250 1251 function transformValue($value) { 1252 if (c('BIBTEXBROWSER_USE_LATEX2HTML')) 1253 { 1254 // trim space 1255 $value = xtrim($value); 1256 1257 // transform Latex markup to HTML entities (easier than a one to one mapping to each character) 1258 // HTML entity is an intermediate format 1259 $value = latex2html($value); 1260 1261 // transform to the target output encoding 1262 $value = html_entity_decode($value, ENT_QUOTES|ENT_XHTML, OUTPUT_ENCODING); 1263 } 1264 return $value; 1265 } 1266 1267 /** removes a field from this bibtex entry */ 1268 function removeField($name) { 1269 $name = strtolower($name); 1270 unset($this->raw_fields[$name]); 1271 unset($this->fields[$name]); 1272 } 1273 1274 /** Sets a field of this bib entry. */ 1275 function setField($name, $value) { 1276 $name = strtolower($name); 1277 $this->raw_fields[$name] = $value; 1278 1279 // fields that should not be transformed 1280 // we assume that "comment" is never latex code 1281 // but instead could contain HTML code (with links using the character "~" for example) 1282 // so "comment" is not transformed too 1283 if ($name!='url' && $name!='comment' 1284 && !preg_match('/^hp_/',$name) // homepage links should not be transformed with latex2html 1285 ) { 1286 $value = $this->transformValue($value); 1287 1288 // 4. transform existing encoded character in the new format 1289 if (function_exists('mb_convert_encoding') && OUTPUT_ENCODING != BIBTEX_INPUT_ENCODING) { 1290 $value = mb_convert_encoding($value, OUTPUT_ENCODING, BIBTEX_INPUT_ENCODING); 1291 } 1292 1293 } else { 1294 //echo "xx".$value."xx\n"; 1295 } 1296 1297 1298 1299 $this->fields[$name] = $value; 1300 } 1301 1302 function clean_top_curly($value) { 1303 $value = preg_replace('/^\{/','', $value); 1304 $value = preg_replace('/\}$/','', $value); 1305 return $value; 1306 } 1307 1308 /** Sets a type of this bib entry. */ 1309 function setType($value) { 1310 // 2009-10-25 added trim 1311 // to support space e.g. "@article {" 1312 // as generated by ams.org 1313 // thanks to Jacob Kellner 1314 $this->fields[Q_INNER_TYPE] = trim($value); 1315 } 1316 1317 function setIndex($index) { $this->index = $index; } 1318 1319 /** Tries to build a good URL for this entry. The URL should be absolute (better for the generated RSS) */ 1320 function getURL() { 1321 if (defined('BIBTEXBROWSER_URL_BUILDER')) { 1322 $f = BIBTEXBROWSER_URL_BUILDER; 1323 return $f($this); 1324 } 1325// echo $this->filename; 1326// echo $this->getKey(); 1327 return BIBTEXBROWSER_URL.'?'.createQueryString(array(Q_KEY=>$this->getKey(), Q_FILE=>$this->filename)); 1328 } 1329 1330 /** @see bib2links(), kept for backward compatibility */ 1331 function bib2links() { 1332 return bib2links($this); 1333 } 1334 1335 /** Read the bibtex field $bibfield and return a link with icon (if $iconurl is given) or text 1336 * e.g. given the bibtex entry: @article{myarticle, pdf={myarticle.pdf}}, 1337 * $bibtexentry->getLink('pdf') creates a link to myarticle.pdf using the text '[pdf]'. 1338 * $bibtexentry->getLink('pdf','pdficon.png') returns <a href="myarticle.pdf"><img src="pdficon.png"/></a> 1339 * if you want a label that is different from the bibtex field, add a third parameter. 1340 */ 1341 function getLink($bibfield,$iconurl=NULL,$altlabel=NULL) { 1342 $show = true; 1343 if ($altlabel==NULL) { $altlabel=$bibfield; } 1344 $str = $this->getIconOrTxt($altlabel,$iconurl); 1345 if ($this->hasField($bibfield)) { 1346 return '<a'.get_target().' href="'.$this->getField($bibfield).'">'.$str.'</a>'; 1347 } 1348 return ''; 1349 } 1350 1351 /** returns a "[bib]" link */ 1352 function getBibLink($iconurl=NULL) { 1353 $bibstr = $this->getIconOrTxt('bibtex',$iconurl); 1354 $href = 'href="'.$this->getURL().'"'; 1355 // we add biburl and title to be able to retrieve this important information 1356 // using Xpath expressions on the XHTML source 1357 $link = '<a'.get_target()." class=\"biburl\" title=\"".$this->getKey()."\" {$href}>$bibstr</a>"; 1358 return $link; 1359 } 1360 1361 /** kept for backward compatibility */ 1362 function getPdfLink($iconurl = NULL, $label = NULL) { 1363 return $this->getUrlLink($iconurl); 1364 } 1365 1366 /** returns a "[pdf]" link for the entry, if possible. 1367 Tries to get the target URL from the 'pdf' field first, then from 'url' or 'file'. 1368 Performs a sanity check that the file extension is 'pdf' or 'ps' and uses that as link label. 1369 Otherwise (and if no explicit $label is set) the field name is used instead. 1370 */ 1371 function getUrlLink($iconurl = NULL) { 1372 if ($this->hasField('pdf')) { 1373 return $this->getAndRenameLink('pdf', $iconurl); 1374 } 1375 if ($this->hasField('url')) { 1376 return $this->getAndRenameLink('url', $iconurl); 1377 } 1378 // Adding link to PDF file exported by Zotero 1379 // ref: https://github.com/monperrus/bibtexbrowser/pull/14 1380 if ($this->hasField('file')) { 1381 return $this->getAndRenameLink('file', $iconurl); 1382 } 1383 return ""; 1384 } 1385 1386 /** See description of 'getUrlLink' 1387 */ 1388 function getAndRenameLink($bibfield, $iconurl=NULL) { 1389 $extension = strtolower(pathinfo(parse_url($this->getField($bibfield),PHP_URL_PATH),PATHINFO_EXTENSION)); 1390 switch ($extension) { 1391 // overriding the label if it's a known extension 1392 case 'html': return $this->getLink($bibfield, $iconurl, 'html'); break; 1393 case 'pdf': return $this->getLink($bibfield, $iconurl, 'pdf'); break; 1394 case 'ps': return $this->getLink($bibfield, $iconurl, 'ps'); break; 1395 default: 1396 return $this->getLink($bibfield, $iconurl, $bibfield); 1397 } 1398 } 1399 1400 1401 1402 /** DOI are a special kind of links, where the url depends on the doi */ 1403 function getDoiLink($iconurl=NULL) { 1404 $str = $this->getIconOrTxt('doi',$iconurl); 1405 if ($this->hasField('doi')) { 1406 return '<a'.get_target().' href="https://doi.org/'.$this->getField('doi').'">'.$str.'</a>'; 1407 } 1408 return ''; 1409 } 1410 1411 /** GS (Google Scholar) are a special kind of links, where the url depends on the google scholar id */ 1412 function getGSLink($iconurl=NULL) { 1413 $str = $this->getIconOrTxt('citations',$iconurl); 1414 if ($this->hasField('gsid')) { 1415 return ' <a'.get_target().' href="https://scholar.google.com/scholar?cites='.$this->getField("gsid").'">'.$str.'</a>'; 1416 } 1417 return ''; 1418 } 1419 1420 /** replace [$ext] with an icon whose url is defined in a string 1421 * e.g. getIconOrTxt('pdf') will print '[pdf]' 1422 * or getIconOrTxt('pdf','http://link/to/icon.png') will use the icon linked by the url, or print '[pdf'] 1423 * if the url does not point to a valid file (using the "alt" property of the "img" html tag) 1424 */ 1425 function getIconOrTxt($txt,$iconurl=NULL) { 1426 if ( $iconurl==NULL ) { 1427 $str='['.$txt.']'; 1428 } else { 1429 $str='<img class="icon" src="'.$iconurl.'" alt="['.$txt.']" title="'.$txt.'"/>'; 1430 } 1431 return $str; 1432 } 1433 1434 /** Reruns the abstract */ 1435 function getAbstract() { 1436 if ($this->hasField('abstract')) return $this->getField('abstract'); 1437 else return ''; 1438 } 1439 1440 /** 1441 * Returns the last name of an author name. 1442 */ 1443 function getLastName($author){ 1444 list($firstname, $lastname) = splitFullName($author); 1445 return $lastname; 1446 } 1447 1448 /** 1449 * Returns the first name of an author name. 1450 */ 1451 function getFirstName($author){ 1452 list($firstname, $lastname) = splitFullName($author); 1453 return $firstname; 1454 } 1455 1456 /** Has this entry the given field? */ 1457 function hasField($name) { 1458 return isset($this->fields[strtolower($name)]); 1459 } 1460 1461 /** Returns the authors of this entry. If "author" is not given, 1462 * return a string 'Unknown'. */ 1463 function getAuthor() { 1464 if (array_key_exists(AUTHOR, $this->fields)) { 1465 return $this->getFormattedAuthorsString(); 1466 } 1467 // 2010-03-02: commented the following, it results in misleading author lists 1468 // issue found by Alan P. Sexton 1469 //if (array_key_exists(EDITOR, $this->fields)) { 1470 // return $this->fields[EDITOR]; 1471 //} 1472 return 'Unknown'; 1473 } 1474 1475 /** Returns the key of this entry */ 1476 function getKey() { 1477 if ($this->getField(Q_KEY) == null) { 1478 $key = md5($this->getTitle().$this->getFormattedAuthorsString()); 1479 $this->setField(Q_KEY, $key); 1480 } 1481 return $this->getField(Q_KEY); 1482 } 1483 1484 /** Returns the title of this entry? */ 1485 function getTitle() { 1486 return $this->getField('title'); 1487 } 1488 1489 /** Returns the publisher of this entry 1490 * It encodes a specific logic 1491 * */ 1492 function getPublisher() { 1493 // citation_publisher 1494 if ($this->hasField("publisher")) { 1495 return $this->getField("publisher"); 1496 } 1497 if ($this->getType()=="phdthesis") { 1498 return $this->getField(SCHOOL); 1499 } 1500 if ($this->getType()=="mastersthesis") { 1501 return $this->getField(SCHOOL); 1502 } 1503 if ($this->getType()=="bachelorsthesis") { 1504 return $this->getField(SCHOOL); 1505 } 1506 if ($this->getType()=="techreport") { 1507 return $this->getField("institution"); 1508 } 1509 // then we don't know 1510 return ''; 1511 } 1512 1513 /** Returns the authors of this entry as an array (split by " and ") */ 1514 function getRawAuthors() { 1515 return $this->split_names(Q_AUTHOR); 1516 } 1517 1518 // Previously called split_authors. Made generic to allow call on editors as well. 1519 function split_names($key) { 1520 if (!array_key_exists($key, $this->raw_fields)) return array(); 1521 1522 // Sometimes authors/editors are split by line breaks followed by whitespace in bib files. 1523 // In this case we need to replace these with a normal space. 1524 $raw = preg_replace( '/\s+/', ' ', @$this->raw_fields[$key]); 1525 $array = preg_split('/ and( |$)/ims', $raw); 1526 1527 $res = array(); 1528 // we merge the remaining ones 1529 for ($i=0; $i < count($array)-1; $i++) { 1530 if (strpos( latex2html($array[$i],false), '{') !== FALSE && strpos(latex2html($array[$i+1],false),'}') !== FALSE) { 1531 $res[] = $this->clean_top_curly(trim($array[$i])." and ".trim($array[$i+1])); 1532 $i = $i + 1; 1533 } else { 1534 $res[] = trim($array[$i]); 1535 } 1536 } 1537 if (!preg_match('/\}/',latex2html($array[count($array)-1],false))) { 1538 $res[] = trim($array[count($array)-1]); 1539 } 1540 return $res; 1541 } 1542 1543 /** 1544 * Returns the formated author name w.r.t to the user preference 1545 * encoded in USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT and USE_INITIALS_FOR_NAMES 1546 */ 1547 function formatAuthor($author){ 1548 $author = $this->transformValue($author); 1549 if (bibtexbrowser_configuration('USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT')) { 1550 return $this->formatAuthorCommaSeparated($author); 1551 } 1552 1553 if (bibtexbrowser_configuration('USE_INITIALS_FOR_NAMES')) { 1554 return $this->formatAuthorInitials($author); 1555 } 1556 1557 if (bibtexbrowser_configuration('USE_FIRST_THEN_LAST')) { 1558 return $this->formatAuthorCanonical($author); 1559 } 1560 1561 return $author; 1562 } 1563 1564 /** 1565 * Returns the formated author name as "FirstName LastName". 1566 */ 1567 function formatAuthorCanonical($author){ 1568 list($firstname, $lastname) = splitFullName($author); 1569 if ($firstname!='') return $firstname.' '.$lastname; 1570 else return $lastname; 1571 } 1572 1573 /** 1574 * Returns the formated author name as "LastName, FirstName". 1575 */ 1576 function formatAuthorCommaSeparated($author){ 1577 list($firstname, $lastname) = splitFullName($author); 1578 if ($firstname!='') return $lastname.', '.$firstname; 1579 else return $lastname; 1580 } 1581 1582 /** 1583 * Returns the formated author name as "LastName Initials". 1584 * e.g. for Vancouver-style used by PubMed. 1585 */ 1586 function formatAuthorInitials($author){ 1587 list($firstname, $lastname) = splitFullName($author); 1588 if ($firstname!='') return $lastname.' '.preg_replace("/(\p{Lu})\w*[- ]*/Su","$1", $firstname); 1589 else return $lastname; 1590 } 1591 1592 1593 /** @deprecated */ 1594 function formattedAuthors() { return $this->getFormattedAuthorsString(); } 1595 /** @deprecated */ 1596 function getFormattedAuthors() { return $this->getFormattedAuthorsArray(); } 1597 /** @deprecated */ 1598 function getFormattedAuthorsImproved() { return $this->getFormattedAuthorsString(); } 1599 1600 1601 /** Returns the authors as an array of strings (one string per author). 1602 */ 1603 function getFormattedAuthorsArray() { 1604 $array_authors = array(); 1605 1606 1607 // first we use formatAuthor 1608 foreach ($this->getRawAuthors() as $author) { 1609 $array_authors[]=$this->formatAuthor($author); 1610 } 1611 1612 if (BIBTEXBROWSER_AUTHOR_LINKS=='homepage') { 1613 foreach ($array_authors as $k => $author) { 1614 $array_authors[$k]=$this->addHomepageLink($author); 1615 } 1616 } 1617 1618 if (BIBTEXBROWSER_AUTHOR_LINKS=='resultpage') { 1619 foreach ($array_authors as $k => $author) { 1620 $array_authors[$k]=$this->addAuthorPageLink($author); 1621 } 1622 } 1623 1624 return $array_authors; 1625 } 1626 1627 /** Adds to getFormattedAuthors() the home page links and returns a string (not an array). Is configured with BIBTEXBROWSER_AUTHOR_LINKS and USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT. 1628 */ 1629 function getFormattedAuthorsString() { 1630 return $this->implodeAuthors($this->getFormattedAuthorsArray()); 1631 } 1632 1633 function implodeAuthors($authors) { 1634 if (count($authors)==0) return ''; 1635 if (count($authors)==1) return $authors[0]; 1636 1637 $result = ''; 1638 1639 if (bibtexbrowser_configuration('USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT')) {$sep = '; ';} else {$sep = ', ';} 1640 if (FORCE_NAMELIST_SEPARATOR !== '') {$sep = FORCE_NAMELIST_SEPARATOR;} 1641 for ($i=0;$i<count($authors)-2;$i++) { 1642 $result .= $authors[$i].$sep; 1643 } 1644 $lastAuthorSeperator = bibtexbrowser_configuration('LAST_AUTHOR_SEPARATOR'); 1645 // add Oxford comma if there are more than 2 authors 1646 if (bibtexbrowser_configuration('USE_OXFORD_COMMA') && count($authors)>2) { 1647 $lastAuthorSeperator = $sep.$lastAuthorSeperator; 1648 $lastAuthorSeperator = preg_replace("/ {2,}/", " ", $lastAuthorSeperator); // get rid of double spaces 1649 } 1650 $result .= $authors[count($authors)-2].$lastAuthorSeperator.$authors[count($authors)-1]; 1651 return $result; 1652 } 1653 1654 /** adds a link to the author page */ 1655 function addAuthorPageLink($author) { 1656 $link = makeHref(array(Q_AUTHOR => $author)); 1657 return "<a {$link}>$author</a>"; 1658 } 1659 1660 1661 /** Returns the authors of this entry as an array in a canonical form */ 1662 function getCanonicalAuthors() { 1663 $authors = array(); 1664 foreach ($this->getRawAuthors() as $author) { 1665 $authors[]=$this->formatAuthorCanonical($author); 1666 } 1667 return $authors; 1668 } 1669 1670 /** Returns the authors of this entry as an array in a comma-separated form 1671 * Mostly used to create meta tags (eg <meta> 1672 */ 1673 function getArrayOfCommaSeparatedAuthors() { 1674 $authors = array(); 1675 foreach ($this->getRawAuthors() as $author) { 1676 $author = $this->transformValue($author); 1677 $authors[]=$this->formatAuthorCommaSeparated($author); 1678 } 1679 return $authors; 1680 } 1681 1682 /** 1683 * Returns a compacted string form of author names by throwing away 1684 * all author names except for the first one and appending ", et al." 1685 */ 1686 function getCompactedAuthors(){ 1687 $authors = $this->getRawAuthors(); 1688 $etal = count($authors) > 1 ? ', et al.' : ''; 1689 return $this->formatAuthor($authors[0]) . $etal; 1690 } 1691 1692 function getHomePageKey($author) { 1693 1694 return strtolower('hp_'.preg_replace('/ /', '', $this->formatAuthorCanonical(latex2html($author)))); 1695 } 1696 1697 /** add the link to the homepage if it is defined in a string 1698 * e.g. @string{hp_MartinMonperrus="http://www.monperrus.net/martin"} 1699 * The string is a concatenation of firstname, lastname, prefixed by hp_ 1700 * Warning: by convention @string are case sensitive so please be keep the same case as author names 1701 * @thanks Eric Bodden for the idea 1702 */ 1703 function addHomepageLink($author) { 1704 // hp as home page 1705 // accents are normally handled 1706 // e.g. @STRING{hp_Jean-MarcJézéquel="http://www.irisa.fr/prive/jezequel/"} 1707 $homepage = $this->getHomePageKey($author); 1708 if (isset($this->homepages[$homepage])) 1709 $author='<a href="'.$this->homepages[$homepage].'">'.$author.'</a>'; 1710 return $author; 1711 } 1712 1713 1714 /** Returns the editors of this entry as an arry */ 1715 function getEditors() { 1716 $editors = array(); 1717 foreach (preg_split('/ and /i', $this->getField(EDITOR)) as $editor) { 1718 $editors[]=$editor; 1719 } 1720 return $editors; 1721 } 1722 1723 1724 /** Returns the editors of this entry as an array (split by " and ") */ 1725 function getRawEditors() { 1726 return $this->split_names(EDITOR); 1727 } 1728 1729 /** Returns the editors of this entry as an arry */ 1730 function getFormattedEditors() { 1731 $editors = array(); 1732 foreach ($this->getEditors() as $editor) { 1733 $editors[]=$this->formatAuthor($editor); 1734 } 1735 if (bibtexbrowser_configuration('USE_COMMA_AS_NAME_SEPARATOR_IN_OUTPUT')) {$sep = '; ';} else {$sep = ', ';} 1736 if (FORCE_NAMELIST_SEPARATOR !== '') {$sep = FORCE_NAMELIST_SEPARATOR;} 1737 return implode($sep, $editors).', '.(count($editors)>1?'eds.':'ed.'); 1738 } 1739 1740 /** Returns the year of this entry? */ 1741 function getYear() { 1742 return __(strtolower($this->getYearRaw())); 1743 } 1744 function getYearRaw() { 1745 $r = $this->getField('year'); 1746 if ($r == null) return ''; 1747 return $r; 1748 } 1749 1750 /** returns the array of keywords */ 1751 function getKeywords() { 1752 return preg_split('/[,;\\/]/', $this->getField("keywords")); 1753 } 1754 function addKeyword($new_keyword) { 1755 $r = $this->getField('keywords'); 1756 if ($r == null || strlen($r) == 0) return $this->setField('keywords', $new_keyword); 1757 return $this->setField('keywords', $r.";".$new_keyword); 1758 } 1759 1760 /** Returns the value of the given field? */ 1761 function getField($name) { 1762 // 2010-06-07: profiling showed that this is very costly 1763 // hence returning the value directly 1764 //if ($this->hasField($name)) 1765 // {return $this->fields[strtolower($name)];} 1766 //else return 'missing '.$name; 1767 1768 return @$this->fields[strtolower($name)]; 1769 } 1770 1771 1772 1773 /** Returns the fields */ 1774 function getFields() { 1775 return $this->fields; 1776 } 1777 1778 /** Returns the raw, undecorated abbreviation depending on ABBRV_TYPE. */ 1779 function getRawAbbrv() { 1780 if (c('ABBRV_TYPE') == 'index') return $this->index; 1781 if (c('ABBRV_TYPE') == 'none') return ''; 1782 if (c('ABBRV_TYPE') == 'key') return $this->getKey(); 1783 if (c('ABBRV_TYPE') == 'year') return $this->getYear(); 1784 if (c('ABBRV_TYPE') == 'x-abbrv') { 1785 if ($this->hasField('x-abbrv')) {return $this->getField('x-abbrv');} 1786 return $this->abbrv; 1787 } 1788 if (c('ABBRV_TYPE') == 'keys-index') { 1789 if (isset($_GET[Q_INNER_KEYS_INDEX])) {return $_GET[Q_INNER_KEYS_INDEX][$this->getKey()]; } 1790 return ''; 1791 } 1792 1793 // otherwise it is a user-defined function in bibtexbrowser.local.php 1794 $f = c('ABBRV_TYPE'); 1795 return $f($this); 1796 } 1797 1798 /** Returns the abbreviation, etc [1] if ABBRV_TYPE='index'. */ 1799 function getAbbrv() { 1800 $abbrv = $this->getRawAbbrv(); 1801 if ( c('ABBRV_TYPE') != 'none' ) { 1802 $abbrv = '['.$abbrv.']'; 1803 } 1804 return $abbrv; 1805 } 1806 1807 1808 /** Sets the abbreviation (e.g. [OOPSLA] or [1]) */ 1809 function setAbbrv($abbrv) { 1810 //if (!is_string($abbrv)) { throw new Exception('Illegal argument'); } 1811 $this->abbrv = $abbrv; 1812 return $this; 1813 } 1814 1815 1816 /** Returns the verbatim text of this bib entry. */ 1817 function getText() { 1818 if (c('BIBTEXBROWSER_BIBTEX_VIEW') == 'original') { 1819 return $this->text; 1820 } 1821 if (c('BIBTEXBROWSER_BIBTEX_VIEW') == 'reconstructed') { 1822 $result = '@'.$this->getType().'{'.$this->getKey().",\n"; 1823 foreach ($this->raw_fields as $k=>$v) { 1824 if ( !preg_match('/^('.c('BIBTEXBROWSER_BIBTEX_VIEW_FILTEREDOUT').')$/i', $k) 1825 && !preg_match('/^(key|'.Q_INNER_AUTHOR.'|'.Q_INNER_TYPE.')$/i', $k) ) 1826 { 1827 $result .= ' '.$k.' = {'.$v.'},'."\n"; 1828 } 1829 } 1830 $result .= "}\n"; 1831 return $result; 1832 } 1833 throw new Exception('incorrect value of BIBTEXBROWSER_BIBTEX_VIEW: '+BIBTEXBROWSER_BIBTEX_VIEW); 1834 } 1835 1836 /** Returns true if this bib entry contains the given phrase (PREG regexp) 1837 * in the given field. if $field is null, all fields are considered. 1838 * Note that this method is NOT case sensitive */ 1839 function hasPhrase($phrase, $field = null) { 1840 1841 // if empty 1842 if ($phrase == '') return false; 1843 1844 $phrase = str_replace('/', '.', $phrase); 1845 1846 // we have to search in the formatted fields and not in the raw entry 1847 // i.e. all latex markups are not considered for searches 1848 if (!$field) { 1849 return preg_match('/'.$phrase.'/i',$this->getConstants().' '.join(" ",$this->getFields())) == 1; 1850 //return stripos($this->getText(), $phrase) !== false; 1851 } 1852 if ($this->hasField($field) && (preg_match('/'.$phrase.'/i',$this->getField($field)) ) ) { 1853 //if ($this->hasField($field) && (stripos($this->getField($field), $phrase) !== false) ) { 1854 return true; 1855 } 1856 1857 return false; 1858 } 1859 1860 1861 /** Outputs HTML line according to layout */ 1862 function toHTML($wrapped=false) { 1863 $result = ''; 1864 if ($wrapped) { 1865 switch(BIBTEXBROWSER_LAYOUT) { // open row 1866 case 'list': 1867 $result .= '<li class="bibline">'; 1868 break; 1869 case 'ordered_list': 1870 $result .= '<li class="bibline">'; 1871 break; 1872 case 'table': 1873 $result .= '<tr class="bibline"><td class="bibref">'; 1874 break; 1875 case 'definition': 1876 $result .= '<dl class="bibline"><dt class="bibref">'; 1877 if (c('ABBRV_TYPE')=='none') { die ('Cannot define an empty term!'); } 1878 break; 1879 case 'none': 1880 break; 1881 } 1882 $result .= $this->anchor(); 1883 switch(BIBTEXBROWSER_LAYOUT) { // close bibref and open bibitem 1884 case 'table': 1885 $result .= $this->getAbbrv().'</td><td class="bibitem">'; 1886 break; 1887 case 'definition': 1888 $result .= $this->getAbbrv().'</dt><dd class="bibitem">'; 1889 break; 1890 } 1891 } 1892 1893 // may be overridden using configuration value of BIBLIOGRAPHYSTYLE 1894 $result .= bib2html($this); 1895 1896 // may be overridden using configuration value of BIBTEXBROWSER_LINK_STYLE 1897 $result .= ' '.bib2links($this); 1898 1899 if ($wrapped) { 1900 switch(BIBTEXBROWSER_LAYOUT) { // close row 1901 case 'list': 1902 $result .= '</li>'."\n"; 1903 break; 1904 case 'ordered_list': 1905 $result .= '</li>'."\n"; 1906 break; 1907 case 'table': 1908 $result .= '</td></tr>'."\n"; 1909 break; 1910 case 'definition': 1911 $result .= '</dd></dl>'."\n"; 1912 break; 1913 case 'none': 1914 break; 1915 } 1916 } 1917 return $result; 1918 } 1919 1920 1921 /** Outputs an coins URL: see http://ocoins.info/cobg.html 1922 * Used by Zotero, mendeley, etc. 1923 */ 1924 function toCoins() { 1925 if (c('METADATA_COINS') == false) { 1926 return; 1927 } 1928 $url_parts=array(); 1929 $url_parts[]='ctx_ver=Z39.88-2004'; 1930 1931 $type = $this->getType(); 1932 if ($type=="book") { 1933 $url_parts[]='rft_val_fmt='.s3988('info:ofi/fmt:kev:mtx:book'); 1934 $url_parts[]='rft.btitle='.s3988($this->getTitle()); 1935 $url_parts[]='rft.genre=book'; 1936 } else if ($type=="inproceedings") { 1937 $url_parts[]='rft_val_fmt='.s3988('info:ofi/fmt:kev:mtx:book'); 1938 $url_parts[]='rft.atitle='.s3988($this->getTitle()); 1939 $url_parts[]='rft.btitle='.s3988($this->getField(BOOKTITLE)); 1940 1941 // zotero does not support with this proceeding and conference 1942 // they give the wrong title 1943 //$url_parts[]='rft.genre=proceeding'; 1944 //$url_parts[]='rft.genre=conference'; 1945 $url_parts[]='rft.genre=bookitem'; 1946 } else if ($type=="incollection" ) { 1947 $url_parts[]='rft_val_fmt='.s3988('info:ofi/fmt:kev:mtx:book'); 1948 $url_parts[]='rft.btitle='.s3988($this->getField(BOOKTITLE)); 1949 $url_parts[]='rft.atitle='.s3988($this->getTitle()); 1950 $url_parts[]='rft.genre=bookitem'; 1951 } else if ($type=="article") { 1952 $url_parts[]='rft_val_fmt='.s3988('info:ofi/fmt:kev:mtx:journal'); 1953 $url_parts[]='rft.atitle='.s3988($this->getTitle()); 1954 $url_parts[]='rft.jtitle='.s3988($this->getField("journal")); 1955 $url_parts[]='rft.volume='.s3988($this->getField("volume")); 1956 $url_parts[]='rft.issue='.s3988($this->getField("issue")); 1957 } else { // techreport, phdthesis 1958 $url_parts[]='rft_val_fmt='.s3988('info:ofi/fmt:kev:mtx:book'); 1959 $url_parts[]='rft.btitle='.s3988($this->getTitle()); 1960 $url_parts[]='rft.genre=report'; 1961 } 1962 1963 $url_parts[]='rft.pub='.s3988($this->getPublisher()); 1964 1965 // referent 1966 if ($this->hasField('url')) { 1967 $url_parts[]='rft_id='.s3988($this->getField('url')); 1968 } else if ($this->hasField('doi')) { 1969 $url_parts[]='rft_id='.s3988('info:doi/'.$this->getField("doi")); 1970 } 1971 1972 // referrer, the id of a collection of objects 1973 // see also http://www.openurl.info/registry/docs/pdf/info-sid.pdf 1974 if (@$_GET[Q_FILE] != null ) { 1975 $url_parts[]='rfr_id='.s3988('info:sid/'.@$_SERVER['HTTP_HOST'].':'.basename(@$_GET[Q_FILE])); 1976 } 1977 1978 $url_parts[]='rft.date='.s3988($this->getYear()); 1979 1980 foreach ($this->getFormattedAuthorsArray() as $au) $url_parts[]='rft.au='.s3988($au); 1981 1982 1983 return '<span class="Z3988" title="'.implode('&',$url_parts).'"></span>'; 1984 1985 } 1986 1987 /** Returns an anchor for this entry. */ 1988 function anchor() { 1989 return '<a class="bibanchor" name="'.$this->getRawAbbrv().'"></a>'; 1990 } 1991 1992 /** 1993 * rebuild the set of constants used if any as a string 1994 */ 1995 function getConstants() { 1996 $result=''; 1997 foreach ($this->constants as $k=>$v) { 1998 $result.='@string{'.$k.'="'.$v."\"}\n"; 1999 } 2000 return $result; 2001 } 2002 2003 /** 2004 * Displays a <pre> text of the given bib entry. 2005 * URLs are replaced by HTML links. 2006 */ 2007 function toEntryUnformatted() { 2008 $result = ""; 2009 $result .= '<pre class="purebibtex">'; // pre is nice when it is embedded with no CSS available 2010 $entry = htmlspecialchars($this->getFullText(),ENT_NOQUOTES|ENT_XHTML, OUTPUT_ENCODING); 2011 2012 // Fields that should be hyperlinks 2013 // the order matters 2014 $hyperlinks = array('url' => '%O', 'file' => '%O', 'pdf' => '%O', 'doi' => 'https://doi.org/%O', 'gsid' => 'https://scholar.google.com/scholar?cites=%O'); 2015 2016 $vals = array(); 2017 foreach ($hyperlinks as $field => $url) { 2018 if ($this->hasField($field)) { 2019 $href = str_replace('%O', $this->getField($field), $url); 2020 // this is not a parsing but a simple replacement 2021 $entry = str_replace($this->getField($field), '___'.$field.'___', $entry); 2022 $vals[$field] = $href; 2023 } 2024 } 2025 foreach ($vals as $field => $href) { 2026 if ($this->hasField($field)) { 2027 // this is not a parsing but a simple replacement 2028 $entry = str_replace('___'.$field.'___', '<a'.get_target().' href="'.$href.'">'.$this->getField($field).'</a>', $entry); 2029 } 2030 } 2031 2032 $result .= $entry; 2033 $result .= '</pre>'; 2034 return $result; 2035 } 2036 2037 /** 2038 * Gets the raw text of the entry (crossref + strings + entry) 2039 */ 2040 function getFullText() { 2041 $s = ''; 2042 // adding the crossref if necessary 2043 if ($this->crossref!=null) { $s .= $this->crossref->getFullText()."\n";} 2044 $s.=$this->getConstants(); 2045 $s.=$this->getText(); 2046 return $s; 2047 } 2048 2049 /** returns the first and last page of the entry as an array ([0]->first, [2]->last) */ 2050 function getPages() { 2051 preg_match('/([0-9]+).*?([0-9]+)/',$this->getField('pages'),$matches); 2052 array_shift($matches); 2053 return $matches; 2054 } 2055 2056 /** returns in the citation file format, tailored for github */ 2057 function toCFF() { 2058 $result = ""; 2059 $result .= "cff-version: 1.2.0"."\n"; 2060 $result .= "# CITATION.cff created with https://github.com/monperrus/bibtexbrowser/"."\n"; 2061 $result .= "preferred-citation:"."\n"; 2062 $result .= " title: \"".$this->getTitle()."\""."\n"; 2063 if ($this->hasField("doi")) { 2064 $result .= " doi: \"".$this->getField("doi")."\""."\n"; 2065 } 2066 if ($this->hasField("year")) { 2067 $result .= " year: \"".$this->getField("year")."\""."\n"; 2068 } 2069 if ($this->hasField("journal")) { 2070 $result .= " type: article\n"; 2071 $result .= " journal: \"".$this->getField("journal")."\""."\n"; 2072 } 2073 if ($this->hasField("booktitle")) { 2074 $result .= " type: conference-paper\n"; 2075 $result .= " conference: \"".$this->getField("booktitle")."\""."\n"; 2076 } 2077 $result .= " authors:"."\n"; 2078 foreach ($this->getFormattedAuthorsArray() as $author) { 2079 $split = splitFullName($author); 2080 $result .= " - family-names: ".$split[1]."\n"; 2081 $result .= " given-names: ".$split[0]."\n"; 2082 } 2083 return $result; 2084 } 2085 2086 2087} // end class BibEntry 2088 2089class RawBibEntry extends BibEntry { 2090 2091 function setField($name, $value) { 2092 $this->fields[$name]=$value; 2093 $this->raw_fields[$name]=$value; 2094 } 2095} 2096 2097/** returns an HTML tag depending on BIBTEXBROWSER_LAYOUT e.g. <TABLE> */ 2098function get_HTML_tag_for_layout() { 2099 switch(BIBTEXBROWSER_LAYOUT) { /* switch for different layouts */ 2100 case 'list': 2101 $tag='ul'; 2102 break; 2103 case 'ordered_list': 2104 $tag='ol'; 2105 break; 2106 case 'table': 2107 $tag = 'table'; 2108 break; 2109 case 'definition': 2110 $tag = 'div'; 2111 break; 2112 default: 2113 die('Unknown BIBTEXBROWSER_LAYOUT'); 2114 } 2115 return $tag; 2116} 2117 2118/** returns a collection of links for the given bibtex entry 2119 * e.g. [bibtex] [doi][pdf] 2120 */ 2121function bib2links_default($bibentry) { 2122 $links = array(); 2123 2124 if (c('BIBTEXBROWSER_BIBTEX_LINKS')) { 2125 $link = $bibentry->getBibLink(); 2126 if ($link != '') { $links[] = $link; }; 2127 } 2128 2129 if (c('BIBTEXBROWSER_PDF_LINKS')) { 2130 $link = $bibentry->getUrlLink(); 2131 if ($link != '') { $links[] = $link; }; 2132 } 2133 2134 if (c('BIBTEXBROWSER_DOI_LINKS')) { 2135 $link = $bibentry->getDoiLink(); 2136 if ($link != '') { $links[] = $link; }; 2137 } 2138 2139 if (c('BIBTEXBROWSER_GSID_LINKS')) { 2140 $link = $bibentry->getGSLink(); 2141 if ($link != '') { $links[] = $link; }; 2142 } 2143 2144 return '<span class="bibmenu">'.implode(" ",$links).'</span>'; 2145} 2146 2147 2148/** prints the header of a layouted HTML, depending on BIBTEXBROWSER_LAYOUT e.g. <TABLE> */ 2149function print_header_layout() { 2150 if (BIBTEXBROWSER_LAYOUT == 'list') return; 2151 echo '<' . get_HTML_tag_for_layout() . ' class="result">'."\n"; 2152} 2153 2154/** prints the footer of a layouted HTML, depending on BIBTEXBROWSER_LAYOUT e.g. </TABLE> */ 2155function print_footer_layout() { 2156 echo '</' . get_HTML_tag_for_layout() . '>'; 2157} 2158 2159/** this function encapsulates the user-defined name for bib to HTML*/ 2160function bib2html($bibentry) { 2161 $function = bibtexbrowser_configuration('BIBLIOGRAPHYSTYLE'); 2162 return $function($bibentry); 2163} 2164 2165/** this function encapsulates the user-defined name for bib2links */ 2166function bib2links($bibentry) { 2167 $function = c('BIBTEXBROWSER_LINK_STYLE'); 2168 return $function($bibentry); 2169} 2170 2171/** encapsulates the user-defined sections. @nodoc */ 2172function _DefaultBibliographySections() { 2173 $function = BIBLIOGRAPHYSECTIONS; 2174 return $function(); 2175} 2176 2177/** encapsulates the user-defined sections. @nodoc */ 2178function _DefaultBibliographyTitle($query) { 2179 $function = BIBLIOGRAPHYTITLE; 2180 return $function($query); 2181} 2182 2183function DefaultBibliographyTitle($query) { 2184 $result = 'Publications in '.$_GET[Q_FILE]; 2185 if (isset($query['all'])) { 2186 unset($query['all']); 2187 } 2188 if (count($query)>0) { 2189 $result .= ' - '.query2title($query); 2190 } 2191 return $result; 2192} 2193 2194/** compares two instances of BibEntry by modification time 2195 */ 2196function compare_bib_entry_by_mtime($a, $b) 2197{ 2198 return -($a->getTimestamp()-$b->getTimestamp()); 2199} 2200 2201/** compares two instances of BibEntry by order in Bibtex file 2202 */ 2203function compare_bib_entry_by_bibtex_order($a, $b) 2204{ 2205 return $a->order-$b->order; 2206} 2207 2208/** compares two instances of BibEntry by year 2209 */ 2210function compare_bib_entry_by_year($a, $b) 2211{ 2212 $yearA = (int) $a->getYear(); // 0 if no year 2213 $yearB = (int) $b->getYear(); 2214 2215 if ($yearA === 0) { 2216 switch (strtolower($a->getYearRaw())) { 2217 case Q_YEAR_INPRESS: 2218 $yearA = PHP_INT_MIN + ORDER_YEAR_INPRESS; 2219 break; 2220 case Q_YEAR_ACCEPTED: 2221 $yearA = PHP_INT_MIN + ORDER_YEAR_ACCEPTED; 2222 break; 2223 case Q_YEAR_SUBMITTED: 2224 $yearA = PHP_INT_MIN + ORDER_YEAR_SUBMITTED; 2225 break; 2226 default: 2227 $yearA = PHP_INT_MIN + ORDER_YEAR_OTHERNONINT; 2228 } 2229 } 2230 2231 if ($yearB === 0) { 2232 switch (strtolower($b->getYearRaw())) { 2233 case Q_YEAR_INPRESS: 2234 $yearB = PHP_INT_MIN + ORDER_YEAR_INPRESS; 2235 break; 2236 case Q_YEAR_ACCEPTED: 2237 $yearB = PHP_INT_MIN + ORDER_YEAR_ACCEPTED; 2238 break; 2239 case Q_YEAR_SUBMITTED: 2240 $yearB = PHP_INT_MIN + ORDER_YEAR_SUBMITTED; 2241 break; 2242 default: 2243 $yearB = PHP_INT_MIN + ORDER_YEAR_OTHERNONINT; 2244 } 2245 } 2246 2247 if ($yearA === $yearB) 2248 return 0; 2249 else if ($yearA < $yearB) 2250 return -1; 2251 else 2252 return 1; 2253} 2254 2255/** compares two instances of BibEntry by title 2256 */ 2257function compare_bib_entry_by_title($a, $b) 2258{ 2259 return strcmp($a->getTitle(),$b->getTitle()); 2260} 2261 2262/** compares two instances of BibEntry by undecorated Abbrv 2263 */ 2264function compare_bib_entry_by_raw_abbrv($a, $b) 2265{ 2266 return strcmp($a->getRawAbbrv(),$b->getRawAbbrv()); 2267} 2268 2269/** compares two instances of BibEntry by author or editor 2270 */ 2271function compare_bib_entry_by_name($a, $b) 2272{ 2273 if ($a->hasField(AUTHOR)) 2274 $namesA = $a->getAuthor(); 2275 else if ($a->hasField(EDITOR)) 2276 $namesA = $a->getField(EDITOR); 2277 else 2278 $namesA = __('No author'); 2279 2280 if ($b->hasField(AUTHOR)) 2281 $namesB = $b->getAuthor(); 2282 else if ($b->hasField(EDITOR)) 2283 $namesB = $b->getField(EDITOR); 2284 else 2285 $namesB = __('No author'); 2286 2287 return strcmp($namesA, $namesB); 2288} 2289 2290/** compares two instances of BibEntry by month 2291 * @author Jan Geldmacher 2292 */ 2293function compare_bib_entry_by_month($a, $b) 2294{ 2295 // this was the old behavior 2296 // return strcmp($a->getKey(),$b->getKey()); 2297 2298 //bibkey which is used for sorting 2299 $sort_key = 'month'; 2300 //desired order of values 2301 $sort_order_values = array('jan','january','feb','february','mar','march','apr','april','may','jun','june','jul','july','aug','august','sep','september','oct','october','nov','november','dec','december'); 2302 //order: 1=as specified in $sort_order_values or -1=reversed 2303 2304 2305 //first check if the search key exists 2306 if (!array_key_exists($sort_key,$a->fields) && !array_key_exists($sort_key,$b->fields)) { 2307 //neither a nor b have the key -> we compare the keys 2308 $retval=strcmp($a->getKey(),$b->getKey()); 2309 } 2310 elseif (!array_key_exists($sort_key,$a->fields)) { 2311 //only b has the field -> b is greater 2312 $retval=-1; 2313 } 2314 elseif (!array_key_exists($sort_key,$b->fields)) { 2315 //only a has the key -> a is greater 2316 $retval=1; 2317 } 2318 else { 2319 //both have the key, sort using the order given in $sort_order_values 2320 2321 $val_a = array_search(strtolower($a->fields[$sort_key]), $sort_order_values); 2322 $val_b = array_search(strtolower($b->fields[$sort_key]), $sort_order_values); 2323 2324 if (($val_a === FALSE && $val_b === FALSE) || ($val_a === $val_b)) { 2325 //neither a nor b are in the search array or a=b -> both are equal 2326 $retval=0; 2327 } 2328 elseif (($val_a === FALSE) || ($val_a < $val_b)) { 2329 //a is not in the search array or a<b -> b is greater 2330 $retval=-1; 2331 } 2332 elseif (($val_b === FALSE) || (($val_a > $val_b))){ 2333 //b is not in the search array or a>b -> a is greater 2334 $retval=1; 2335 } 2336 } 2337 2338 return $retval; 2339} 2340 2341/** is the default sectioning for AcademicDisplay (books, articles, proceedings, etc. ) */ 2342function DefaultBibliographySections() { 2343return 2344 array( 2345 // Books 2346 array( 2347 'query' => array(Q_TYPE=>'book|proceedings'), 2348 'title' => __('Books') 2349 ), 2350 // Book chapters 2351 array( 2352 'query' => array(Q_TYPE=>'incollection|inbook'), 2353 'title' => __('Book Chapters') 2354 ), 2355 // Journal / Bookchapters 2356 array( 2357 'query' => array(Q_TYPE=>'article'), 2358 'title' => __('Refereed Articles') 2359 ), 2360 // conference papers 2361 array( 2362 'query' => array(Q_TYPE=>'inproceedings|conference',Q_EXCLUDE=>'workshop'), 2363 'title' => __('Refereed Conference Papers') 2364 ), 2365 // workshop papers 2366 array( 2367 'query' => array(Q_TYPE=>'inproceedings',Q_SEARCH=>'workshop'), 2368 'title' => __('Refereed Workshop Papers') 2369 ), 2370 // misc and thesis 2371 array( 2372 'query' => array(Q_TYPE=>'misc|phdthesis|mastersthesis|bachelorsthesis|techreport'), 2373 'title' => __('Other Publications') 2374 ) 2375 ); 2376} 2377 2378 2379/** transforms a $bibentry into an HTML string. 2380 It is called by function bib2html if the user did not choose a specific style 2381 the default usable CSS styles are 2382 .bibtitle { font-weight:bold; } 2383 .bibbooktitle { font-style:italic; } 2384 .bibauthor { } 2385 .bibpublisher { } 2386 2387 See http://schema.org/ScholarlyArticle for the metadata 2388*/ 2389function DefaultBibliographyStyle($bibentry) { 2390 $title = $bibentry->getTitle(); 2391 $type = $bibentry->getType(); 2392 2393 // later on, all values of $entry will be joined by a comma 2394 $entry=array(); 2395 2396 // title 2397 // usually in bold: .bibtitle { font-weight:bold; } 2398 $title = '<span class="bibtitle" itemprop="name">'.$title.'</span>'; 2399 if ($bibentry->hasField('url')) $title = ' <a'.get_target().' href="'.$bibentry->getField('url').'">'.$title.'</a>'; 2400 2401 2402 $coreInfo = $title; 2403 2404 // adding author info 2405 if ($bibentry->hasField('author')) { 2406 $coreInfo .= ' (<span class="bibauthor">'; 2407 2408 $authors = array(); 2409 foreach ($bibentry->getFormattedAuthorsArray() as $a) { 2410 $authors[]='<span itemprop="author" itemtype="http://schema.org/Person">'.$a.'</span>'; 2411 } 2412 $coreInfo .= $bibentry->implodeAuthors($authors); 2413 2414 $coreInfo .= '</span>)'; 2415 } 2416 2417 // core info usually contains title + author 2418 $entry[] = $coreInfo; 2419 2420 // now the book title 2421 $booktitle = ''; 2422 if ($type=="inproceedings") { 2423 $booktitle = __('In').' '.'<span itemprop="isPartOf">'.$bibentry->getField(BOOKTITLE).'</span>'; } 2424 if ($type=="incollection") { 2425 $booktitle = __('Chapter in').' '.'<span itemprop="isPartOf">'.$bibentry->getField(BOOKTITLE).'</span>';} 2426 if ($type=="inbook") { 2427 $booktitle = __('Chapter in').' '.$bibentry->getField('chapter');} 2428 if ($type=="article") { 2429 $booktitle = __('In').' '.'<span itemprop="isPartOf">'.$bibentry->getField("journal").'</span>';} 2430 2431 //// we may add the editor names to the booktitle 2432 $editor=''; 2433 if ($bibentry->hasField(EDITOR)) { 2434 $editor = $bibentry->getFormattedEditors(); 2435 } 2436 if ($editor!='') $booktitle .=' ('.$editor.')'; 2437 // end editor section 2438 2439 // is the booktitle available 2440 if ($booktitle!='') { 2441 $entry[] = '<span class="bibbooktitle">'.$booktitle.'</span>'; 2442 } 2443 2444 2445 $publisher=''; 2446 if ($type=="phdthesis") { 2447 $publisher = __('PhD thesis').', '.$bibentry->getField(SCHOOL); 2448 } 2449 if ($type=="mastersthesis") { 2450 $publisher = __('Master\'s thesis').', '.$bibentry->getField(SCHOOL); 2451 } 2452 if ($type=="bachelorsthesis") { 2453 $publisher = __('Bachelor\'s thesis').', '.$bibentry->getField(SCHOOL); 2454 } 2455 if ($type=="techreport") { 2456 $publisher = __('Technical report'); 2457 if ($bibentry->hasField("number")) { 2458 $publisher .= ' '.$bibentry->getField("number"); 2459 } 2460 $publisher .= ', '.$bibentry->getField("institution"); 2461 } 2462 2463 if ($bibentry->hasField("publisher")) { 2464 $publisher = $bibentry->getField("publisher"); 2465 } 2466 2467 if ($type=="misc") { 2468 $publisher = $bibentry->getField('howpublished'); 2469 } 2470 2471 2472 if ($publisher!='') $entry[] = '<span class="bibpublisher">'.$publisher.'</span>'; 2473 2474 2475 if ($bibentry->hasField('volume')) $entry[] = __('volume').' '.$bibentry->getField("volume"); 2476 2477 2478 if ($bibentry->hasField(YEAR)) $entry[] = '<span itemprop="datePublished">'.$bibentry->getYear().'</span>'; 2479 2480 if ($bibentry->hasField("note")) { 2481 $entry[] = $bibentry->getField("note"); 2482 } 2483 2484 $result = implode(", ",$entry).'.'; 2485 2486 // add the Coin URL 2487 $result .= $bibentry->toCoins(); 2488 2489 return '<span itemscope="" itemtype="http://schema.org/ScholarlyArticle">'.$result.'</span>'; 2490} 2491 2492 2493 2494/** is the Bibtexbrowser style contributed by Janos Tapolcai. It looks like the IEEE transaction style. 2495usage: 2496Add the following line in "bibtexbrowser.local.php" 2497<pre> 2498@define('BIBLIOGRAPHYSTYLE','JanosBibliographyStyle'); 2499</pre> 2500*/ 2501function JanosBibliographyStyle($bibentry) { 2502 $title = $bibentry->getTitle(); 2503 $type = $bibentry->getType(); 2504 2505 $entry=array(); 2506 2507 // author 2508 if ($bibentry->hasField('author')) { 2509 $entry[] = '<span class="bibauthor">'.$bibentry->getFormattedAuthorsString().'</span>'; 2510 } 2511 2512 // title 2513 $title = '"'.'<span class="bibtitle">'.$title.'</span>'.'"'; 2514 if ($bibentry->hasField('url')) $title = ' <a'.get_target().' href="'.$bibentry->getField('url').'">'.$title.'</a>'; 2515 $entry[] = $title; 2516 2517 2518 // now the origin of the publication is in italic 2519 $booktitle = ''; 2520 2521 if (($type=="misc") && $bibentry->hasField("note")) { 2522 $booktitle = $bibentry->getField("note"); 2523 } 2524 2525 if ($type=="inproceedings" && $bibentry->hasField(BOOKTITLE)) { 2526 $booktitle = '<span class="bibbooktitle">'.'In '.$bibentry->getField(BOOKTITLE).'</span>'; 2527 } 2528 2529 if ($type=="incollection" && $bibentry->hasField(BOOKTITLE)) { 2530 $booktitle = '<span class="bibbooktitle">'.'Chapter in '.$bibentry->getField(BOOKTITLE).'</span>'; 2531 } 2532 2533 if ($type=="article" && $bibentry->hasField("journal")) { 2534 $booktitle = '<span class="bibbooktitle">'.''.$bibentry->getField("journal").'</span>'; 2535 } 2536 2537 2538 2539 //// ******* EDITOR 2540 $editor=''; 2541 if ($bibentry->hasField(EDITOR)) { 2542 $editor = $bibentry->getFormattedEditors(); 2543 } 2544 2545 if ($booktitle!='') { 2546 if ($editor!='') $booktitle .=' ('.$editor.')'; 2547 $entry[] = '<i>'.$booktitle.'</i>'; 2548 } 2549 2550 2551 $publisher=''; 2552 if ($type=="phdthesis") { 2553 $publisher = 'PhD thesis, '.$bibentry->getField(SCHOOL); 2554 } 2555 2556 if ($type=="mastersthesis") { 2557 $publisher = 'Master\'s thesis, '.$bibentry->getField(SCHOOL); 2558 } 2559 if ($type=="techreport") { 2560 $publisher = 'Technical report, '; 2561 $publisher .=$bibentry->getField("institution"); 2562 if ($bibentry->hasField("number")) { 2563 $publisher .= ' '.$bibentry->getField("number"); 2564 } 2565 } 2566 if ($bibentry->hasField("publisher")) { 2567 $publisher = $bibentry->getField("publisher"); 2568 } 2569 2570 if ($publisher!='') $entry[] = $publisher; 2571 2572 if ($type=="article") { 2573 if ($bibentry->hasField('volume')) $entry[] = "vol. ".$bibentry->getField("volume"); 2574 if ($bibentry->hasField('number')) $entry[] = 'no. '.$bibentry->getField("number"); 2575 } 2576 2577 if ($bibentry->hasField('address')) $entry[] = $bibentry->getField("address"); 2578 2579 if ($bibentry->hasField('pages')) $entry[] = str_replace("--", "-", "pp. ".$bibentry->getField("pages")); 2580 2581 2582 if ($bibentry->hasField(YEAR)) $entry[] = $bibentry->getYear(); 2583 2584 $result = implode(", ",$entry).'.'; 2585 2586 // add the Coin URL 2587 $result .= "\n".$bibentry->toCoins(); 2588 2589 return '<span itemscope="" itemtype="http://schema.org/ScholarlyArticle">'.$result.'</span>'; 2590} 2591 2592 2593/** Bibtexbrowser style producing vancouver style often used in medicine. 2594 * 2595 * See: Patrias K. Citing medicine: the NLM style guide for authors, editors, 2596 * and publishers [Internet]. 2nd ed. Wendling DL, technical editor. 2597 * Bethesda (MD): National Library of Medicine (US); 2007 - 2598 * [updated 2011 Sep 15; cited 2015 April 18]. 2599 * Available from: http://www.nlm.nih.gov/citingmedicine 2600 * 2601 * usage: Add the following lines to "bibtexbrowser.local.php" 2602<pre> 2603@define('BIBLIOGRAPHYSTYLE','VancouverBibliographyStyle'); 2604@define('USE_INITIALS_FOR_NAMES',true); 2605</pre> 2606*/ 2607 2608function VancouverBibliographyStyle($bibentry) { 2609 $title = $bibentry->getTitle(); 2610 $type = $bibentry->getType(); 2611 2612 $entry=array(); 2613 2614 // author 2615 if ($bibentry->hasField('author')) { 2616 $entry[] = $bibentry->getFormattedAuthorsString().'. '; 2617 } 2618 2619 // Ensure punctuation mark at title's end 2620 if (strlen(rtrim($title))>0 && strpos(":.;,?!", substr(rtrim($title), -1)) > 0) { 2621 $title = $title . ' '; 2622 } else { 2623 $title = $title . '. '; 2624 } 2625 if ($bibentry->hasField('url')) { 2626 $title = ' <a'.get_target().' href="'.$bibentry->getField('url').'">'.$title.'</a>'; 2627 } 2628 2629 $entry[] = $title; 2630 2631 $booktitle = ''; 2632 2633 //// ******* EDITOR 2634 $editor=''; 2635 if ($bibentry->hasField(EDITOR)) { 2636 $editor = $bibentry->getFormattedEditors() . ' '; 2637 } 2638 2639 if (($type=="misc") && $bibentry->hasField("note")) { 2640 $booktitle = $editor; 2641 $booktitle = $bibentry->getField("note"); 2642 } else if ($type=="inproceedings") { 2643 $booktitle = 'In: ' . $editor . $bibentry->getField(BOOKTITLE); 2644 } else if ($type=="incollection") { 2645 $booktitle = 'Chapter in '; 2646 if ($editor!='') $booktitle .= $editor; 2647 $booktitle .= $bibentry->getField(BOOKTITLE); 2648 } else if ($type=="article") { 2649 $booktitle = $bibentry->getField("journal"); 2650 } 2651 if ($booktitle!='') { 2652 $entry[] = $booktitle . '. '; 2653 } 2654 2655 2656 $publisher=''; 2657 if ($type=="phdthesis") { 2658 $publisher = 'PhD thesis, '.$bibentry->getField(SCHOOL); 2659 } else if ($type=="mastersthesis") { 2660 $publisher = 'Master\'s thesis, '.$bibentry->getField(SCHOOL); 2661 } else if ($type=="techreport") { 2662 $publisher = 'Technical report, '.$bibentry->getField("institution"); 2663 } 2664 if ($bibentry->hasField("publisher")) { 2665 $publisher = $bibentry->getField("publisher"); 2666 } 2667 if ($publisher!='') { 2668 if ($bibentry->hasField('address')) { 2669 $entry[] = $bibentry->getField("address").': '; 2670 } 2671 $entry[] = $publisher . "; "; 2672 } 2673 2674 2675 if ($bibentry->hasField(YEAR)) $entry[] = $bibentry->getYear(); 2676 2677 if ($bibentry->hasField('volume')) $entry[] = ";".$bibentry->getField("volume"); 2678 if ($bibentry->hasField('number')) $entry[] = '('.$bibentry->getField("number").')'; 2679 2680 if ($bibentry->hasField('pages')) $entry[] = str_replace("--", "-", ":".$bibentry->getField("pages")); 2681 2682 $result = implode($entry).'.'; 2683 2684 // some comments (e.g. acceptance rate)? 2685 if ($bibentry->hasField('comment')) { 2686 $result .= " (".$bibentry->getField("comment").")"; 2687 } 2688 2689 // add the Coin URL 2690 $result .= "\n".$bibentry->toCoins(); 2691 2692 return $result; 2693} 2694 2695 2696 2697 2698// ---------------------------------------------------------------------- 2699// DISPLAY MANAGEMENT 2700// ---------------------------------------------------------------------- 2701/** orders two BibEntry as defined by ORDER_FUNCTION 2702 * (by default compares two instances of BibEntry by year and then month) 2703 */ 2704function compare_bib_entries($bib1, $bib2) { 2705 $f1 = ORDER_FUNCTION; 2706 $cmp = $f1($bib1, $bib2); 2707 if ($cmp ==0) { 2708 $f2 = ORDER_FUNCTION_FINE; 2709 $cmp = $f2($bib1, $bib2); 2710 } 2711 return $cmp; 2712} 2713 2714/** creates a query string given an array of parameter, with all specifities of bibtexbrowser_ (such as adding the bibtex file name &bib=foo.bib 2715 */ 2716function createQueryString($array_param) { 2717 // then a simple transformation and implode 2718 foreach ($array_param as $key => $val) { 2719 // the inverse transformation should also be implemented into query2title 2720 if($key == Q_INNER_AUTHOR) { $key = Q_AUTHOR; } 2721 if($key == Q_INNER_TYPE) { $key = Q_TYPE; } 2722 if($key == Q_KEYS) { $val = urlencode(json_encode($val)); } 2723 if($key == Q_INNER_KEYS_INDEX) {continue;} 2724 $array_param[$key]=$key .'='. urlencode($val); 2725 } 2726 2727 // adding the bibtex file name is not already there 2728 if (isset($_GET[Q_FILE]) && !isset($array_param[Q_FILE])) { 2729 // first we add the name of the bib file 2730 $array_param[Q_FILE] = Q_FILE .'='. urlencode($_GET[Q_FILE]); 2731 } 2732 2733 return implode("&",$array_param); 2734} 2735 2736/** returns a href string of the form: href="?bib=testing.bib&search=JML. 2737Based on createQueryString. 2738@nodoc 2739 */ 2740function makeHref($query = NULL) { 2741 return 'href="'.bibtexbrowser_configuration('BIBTEXBROWSER_URL').'?'. createQueryString($query) .'"'; 2742} 2743 2744 2745/** returns the splitted name of an author name as an array. The argument is assumed to be 2746 "FirstName LastName" or "LastName, FirstName". 2747 */ 2748function splitFullName($author){ 2749 $author = trim($author); 2750 // the author format is "Joe Dupont" 2751 if (strpos($author,',')===false) { 2752 $parts=explode(' ', $author); 2753 // get the last name 2754 $lastname = array_pop($parts); 2755 $firstname = implode(" ", $parts); 2756 } 2757 // the author format is "Dupont, J." 2758 else { 2759 $parts=explode(',', $author); 2760 // get the last name 2761 $lastname = str_replace(',','',array_shift($parts)); 2762 $firstname = implode(" ", $parts); 2763 } 2764 return array(trim($firstname), trim($lastname)); 2765} 2766 2767 2768/** outputs an horizontal year-based menu 2769usage: 2770<pre> 2771 $_GET['library']=1; 2772 $_GET['bib']='bibacid-utf8.bib'; 2773 $_GET['all']=1; 2774 include( 'bibtexbrowser.php' ); 2775 setDB(); 2776 new IndependentYearMenu($_GET[Q_DB]); 2777</pre> 2778 */ 2779class IndependentYearMenu { 2780 function __construct($db) { 2781 $yearIndex = $db->yearIndex(); 2782 echo '<div id="yearmenu">Year: '; 2783 $formatedYearIndex = array(); 2784 $formatedYearIndex[] = '<a '.makeHref(array(Q_YEAR=>'.*')).'>All</a>'; 2785 foreach($yearIndex as $year) { 2786 $formatedYearIndex[] = '<a '.makeHref(array(Q_YEAR=>$year)).'>'.$year.'</a>'; 2787 } 2788 2789 // by default the separator is a | 2790 echo implode('|',$formatedYearIndex); 2791 echo '</div>'; 2792 } 2793} 2794 2795if (!function_exists('poweredby')) { 2796 /** Returns the powered by part. @nodoc */ 2797 function poweredby() { 2798 $poweredby = "\n".'<div style="text-align:right;font-size: xx-small;opacity: 0.6;" class="poweredby">'; 2799 $poweredby .= '<!-- If you like bibtexbrowser, thanks to keep the link :-) -->'; 2800 $poweredby .= 'Powered by <a href="http://www.monperrus.net/martin/bibtexbrowser/">bibtexbrowser</a><!--v__GITHUB__-->'; 2801 $poweredby .= '</div>'."\n"; 2802 return $poweredby; 2803 } 2804} 2805 2806if (!function_exists('bibtexbrowser_top_banner')) { 2807 function bibtexbrowser_top_banner() { 2808 return ''; 2809 } 2810} 2811 2812/** ^^adds a touch of AJAX in bibtexbrowser to display bibtex entries inline. 2813 It uses the HIJAX design pattern: the Javascript code fetches the normal bibtex HTML page 2814 and extracts the bibtex. 2815 In other terms, URLs and content are left perfectly optimized for crawlers 2816 Note how beautiful is this piece of code thanks to JQuery.^^ 2817 */ 2818function javascript() { 2819 // we use jquery with the official content delivery URLs 2820 // Microsoft and Google also provide jquery with their content delivery networks 2821?><script type="text/javascript" src="<?php echo JQUERY_URI ?>"></script> 2822<script type="text/javascript" ><!-- 2823// Javascript progressive enhancement for bibtexbrowser 2824$('a.biburl').each(function() { // for each url "[bibtex]" 2825 var biburl = $(this); 2826 if (biburl.attr('bibtexbrowser') === undefined) 2827 { 2828 biburl.click(function(ev) { // we change the click semantics 2829 ev.preventDefault(); // no open url 2830 if (biburl.nextAll('pre').length == 0) { // we don't have yet the bibtex data 2831 var bibtexEntryUrl = $(this).attr('href'); 2832 $.ajax({url: bibtexEntryUrl, dataType: 'html', success: function (data) { // we download it 2833 // elem is the element containing bibtex entry, creating a new element is required for Chrome and IE 2834 var elem = $('<pre class="purebibtex"/>'); 2835 elem.text($('.purebibtex', data).text()); // both text() are required for IE 2836 // we add a link so that users clearly see that even with AJAX 2837 // there is still one URL per paper. 2838 elem.append( 2839 $('<div class="bibtex_entry_url">%% Bibtex entry URL: <a href="'+bibtexEntryUrl+'">'+bibtexEntryUrl+'</a></div>') 2840 ).appendTo(biburl.parent()); 2841 }, error: function() {window.location.href = biburl.attr('href');}}); 2842 } else {biburl.nextAll('pre').toggle();} // we toggle the view 2843 }); 2844 biburl.attr('bibtexbrowser','done'); 2845 } // end if biburl.bibtexbrowser; 2846}); 2847 2848 2849--></script><?php 2850} // end function javascript 2851 2852 2853if (!function_exists('javascript_math')) { 2854 function javascript_math() { 2855 ?> 2856<script> 2857MathJax = { 2858 tex: { 2859 inlineMath: [['$', '$'], ['\\(', '\\)']] 2860 } 2861}; 2862</script> 2863<script type="text/javascript" id="MathJax-script" async 2864 src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"> 2865</script> 2866 <?php 2867 } 2868} 2869 2870 2871/** is used for creating menus (by type, by year, by author, etc.). 2872usage: 2873<pre> 2874 $db = zetDB('bibacid-utf8.bib'); 2875 $menu = new MenuManager(); 2876 $menu->setDB($db); 2877 $menu->year_size=100;// should display all years :) 2878 $menu->display(); 2879</pre> 2880 */ 2881class MenuManager { 2882 2883 /** The bibliographic database, an instance of class BibDataBase. */ 2884 var $db; 2885 2886 var $type_size = TYPES_SIZE; 2887 var $year_size = YEAR_SIZE; 2888 var $author_size = AUTHORS_SIZE; 2889 var $tag_size = TAGS_SIZE; 2890 2891 function __construct() { 2892 } 2893 2894 /** sets the database that is used to create the menu */ 2895 function setDB($db) { 2896 $this->db =$db; 2897 return $this; 2898 } 2899 2900 function getTitle() { 2901 return ''; 2902 } 2903 2904 function metadata() { 2905 return array(array('robots','noindex')); 2906 } 2907 2908 /** function called back by HTMLTemplate */ 2909 function display() { 2910 echo $this->searchView().'<br/>'; 2911 echo $this->venueVC().'<br/>'; 2912 echo $this->yearVC().'<br/>'; 2913 echo $this->authorVC().'<br/>'; 2914 echo $this->tagVC().'<br/>'; 2915 echo $this->typeVC().'<br/>'; 2916 } 2917 2918 /** Displays the title in a table. */ 2919 function titleView() { 2920 ?> 2921 <table> 2922 <tr> 2923 <td class="rheader">Generated from <?php echo $_GET[Q_FILE]; ?></td> 2924 </tr> 2925 </table> 2926 <?php 2927 } 2928 2929 /** Displays the search view in a form. */ 2930 function searchView() { 2931 ?> 2932 <form action="?" method="get" target="<?php echo BIBTEXBROWSER_MENU_TARGET;?>"> 2933 <input type="text" name="<?php echo Q_SEARCH; ?>" class="input_box" size="18"/> 2934 <input type="hidden" name="<?php echo Q_FILE; ?>" value="<?php echo @$_GET[Q_FILE]; ?>"/> 2935 <br/> 2936 <!-- submit button --> 2937 <input type="submit" value="<?php echo __("search"); ?>" class="input_box"/> 2938 </form> 2939 <?php 2940 } 2941 2942 /** Displays and controls the types menu in a table. */ 2943 function typeVC() { 2944 $types = array(); 2945 foreach ($this->db->getTypes() as $type) { 2946 $types[$type] = __($type); 2947 } 2948 $types['.*'] = __('all types');; 2949 // retreive or calculate page number to display 2950 if (isset($_GET[Q_TYPE_PAGE])) { 2951 $page = $_GET[Q_TYPE_PAGE]; 2952 } 2953 else $page = 1; 2954 2955 $this->displayMenu('Types', $types, $page, $this->type_size, Q_TYPE_PAGE, Q_INNER_TYPE); 2956 } 2957 2958 2959 /** Displays and controls the venues. */ 2960 function venueVC() { 2961 // retrieve authors list to display 2962 $data = $this->db->venueIndex(); 2963 2964 $this->displayMenu('Venues', $data, 1, 100000, Q_SEARCH, 2965 Q_SEARCH); 2966 } 2967 2968 /** Displays and controls the authors menu in a table. */ 2969 function authorVC() { 2970 // retrieve authors list to display 2971 $authors = $this->db->authorIndex(); 2972 2973 // determine the authors page to display 2974 if (isset($_GET[Q_AUTHOR_PAGE])) { 2975 $page = $_GET[Q_AUTHOR_PAGE]; 2976 } 2977 else $page = 1; 2978 2979 2980 $this->displayMenu('Authors', $authors, $page, $this->author_size, Q_AUTHOR_PAGE, 2981 Q_AUTHOR); 2982 } 2983 2984 /** Displays and controls the tag menu in a table. */ 2985 function tagVC() { 2986 // retrieve authors list to display 2987 $tags = $this->db->tagIndex(); 2988 2989 // determine the authors page to display 2990 if (isset($_GET[Q_TAG_PAGE])) { 2991 $page = $_GET[Q_TAG_PAGE]; 2992 } else $page = 1; 2993 2994 2995 if (count($tags)>0) $this->displayMenu('Keywords', $tags, $page, $this->tag_size, Q_TAG_PAGE, 2996 Q_TAG); 2997 } 2998 2999 /** Displays and controls the tag menu in a table. */ 3000 function yearVC() { 3001 // retrieve authors list to display 3002 $years = $this->db->yearIndex(); 3003 3004 // determine the authors page to display 3005 if (isset($_GET[Q_YEAR_PAGE])) { 3006 $page = $_GET[Q_YEAR_PAGE]; 3007 } 3008else $page = 1; 3009 3010 3011 $this->displayMenu('Years', $years, $page, $this->year_size, Q_YEAR_PAGE, 3012 Q_YEAR); 3013 } 3014 3015 /** Displays the main contents . */ 3016 function mainVC() { 3017 $this->display->display(); 3018 } 3019 3020 /** Displays a list menu in a table. 3021 * 3022 * $title: title of the menu (string) 3023 * $list: list of menu items (string) 3024 * $page: page number to display (number) 3025 * $pageSize: size of each page 3026 * $pageKey: URL query name to send the page number to the server 3027 * $targetKey: URL query name to send the target of the menu item 3028 */ 3029 function displayMenu($title, $list, $page, $pageSize, $pageKey, 3030 $targetKey) { 3031 $numEntries = count($list); 3032 $startIndex = ($page - 1) * $pageSize; 3033 $endIndex = $startIndex + $pageSize; 3034 ?> 3035 <table style="width:100%" class="menu"> 3036 <tr> 3037 <td> 3038 <!-- this table is used to have the label on the left 3039 and the navigation links on the right --> 3040 <table style="width:100%" border="0" cellspacing="0" cellpadding="0"> 3041 <tr class="btb-nav-title"> 3042 <td><b><?php echo __($title); ?></b></td> 3043 <td class="btb-nav"><b> 3044 <?php echo $this->menuPageBar($pageKey, $numEntries, $page, 3045 $pageSize, $startIndex, $endIndex);?></b></td> 3046 </tr> 3047 </table> 3048 </td> 3049 </tr> 3050 <tr> 3051 <td class="btb-menu-items"> 3052 <?php $this->displayMenuItems($list, $startIndex, $endIndex, 3053 $targetKey); ?> 3054 </td> 3055 </tr> 3056 </table> 3057 <?php 3058 } 3059 3060 /** Returns a string to displays forward and reverse page controls. 3061 * 3062 * $queryKey: key to send the page number as a URL query string 3063 * $page: current page number to display 3064 * $numEntries: number of menu items 3065 * $start: start index of the current page 3066 * $end: end index of the current page 3067 */ 3068 function menuPageBar($queryKey, $numEntries, $page, $pageSize, 3069 $start, $end) { 3070 3071 $result = ''; 3072 3073 // (1 page) reverse (<) 3074 if ($start > 0) { 3075 $href = makeHref(array($queryKey => $page - 1,'menu'=>''));//menuPageBar 3076 $result .= '<a '. $href ."><b>[prev]</b></a>\n"; 3077 } 3078 3079 // (1 page) forward (>) 3080 if ($end < $numEntries) { 3081 $href = makeHref(array($queryKey => $page + 1,'menu'=>''));//menuPageBar 3082 $result .= '<a '. $href ."><b>[next]</b></a>\n"; 3083 } 3084 3085 return $result; 3086 } 3087 3088 /** 3089 * Displays menu items (anchors) from the start index (inclusive) to 3090 * the end index (exclusive). For each menu, the following form of 3091 * string is printed: 3092 * 3093 * <a href="...?bib=cheon.bib&author=Yoonsik+Cheon"> 3094 * Cheon, Yoonsik</a> 3095 * <div class="mini_se"></div> 3096 */ 3097 function displayMenuItems($items, $startIndex, $endIndex, $queryKey) { 3098 $index = 0; 3099 foreach ($items as $key => $item) { 3100 if ($index >= $startIndex && $index < $endIndex) { 3101 if ($queryKey === 'year') { 3102 $href = makeHref(array($queryKey => __($item))); 3103 } else { 3104 $href = makeHref(array($queryKey => $key)); 3105 } 3106 echo '<a '. $href .' target="'.BIBTEXBROWSER_MENU_TARGET.'">'. $item ."</a>\n"; 3107 echo "<div class=\"mini_se\"></div>\n"; 3108 } 3109 $index++; 3110 } 3111 } 3112} 3113 3114if (!function_exists('query2title')) { 3115/** transforms an array representing a query into a formatted string */ 3116function query2title($query) { 3117 $headers = array(); 3118 foreach($query as $k=>$v) { 3119 if($k == Q_INNER_AUTHOR) { $k = 'author'; } 3120 if($k == Q_INNER_TYPE) { 3121 // we changed from x-bibtex-type to type 3122 $k = 'type'; 3123 // and we remove the regexp modifiers ^ $ 3124 $v = preg_replace('/[$^]/','',$v); 3125 } 3126 if($k == Q_KEYS) { $v=json_encode(array_values($v)); } 3127 if($k == Q_RANGE) { 3128 foreach ($v as $range) { 3129 $range = $range[0].'-'.$range[1]; 3130 } 3131 $v = join($v, ','); 3132 } 3133 $headers[$k] = __(ucwords($k)).': '.ucwords(htmlspecialchars($v,ENT_NOQUOTES|ENT_XHTML, OUTPUT_ENCODING)); 3134 } 3135 return join(' & ',$headers); 3136} 3137} // if (!function_exists('query2title')) 3138 3139/** displays the latest modified bibtex entries. 3140usage: 3141<pre> 3142 $db = zetDB('bibacid-utf8.bib'); 3143 $d = new NewEntriesDisplay(); 3144 $d->setDB($db); 3145 $d->setN(7);// optional 3146 $d->display(); 3147</pre> 3148 */ 3149class NewEntriesDisplay { 3150 var $n=5; 3151 var $db; 3152 3153 function setDB($bibdatabase) { 3154 $this->db = $bibdatabase; 3155 } 3156 3157 function setN($n) {$this->n = $n;return $this;} 3158 3159 /** sets the entries to be shown */ 3160 function setEntries($entries) { 3161 $this->db = createBibDataBase(); 3162 $this->db->bibdb = $entries; 3163 } 3164 3165 /** Displays a set of bibtex entries in an HTML table */ 3166 function display() { 3167 $array = $this->db->getLatestEntries($this->n); 3168 $delegate = createBasicDisplay(); 3169 $delegate->setEntries($array); 3170 $delegate->display(); 3171 } 3172} 3173 3174 3175/** displays the entries by year in reverse chronological order. 3176usage: 3177<pre> 3178 $db = zetDB('bibacid-utf8.bib'); 3179 $d = new YearDisplay(); 3180 $d->setDB($db); 3181 $d->display(); 3182</pre> 3183*/ 3184class YearDisplay { 3185 3186 /** is an array of strings representing years */ 3187 var $yearIndex; 3188 var $entries; 3189 3190 function setDB($bibdatabase) { 3191 $this->setEntries($bibdatabase->bibdb); 3192 } 3193 3194 /** creates a YearDisplay */ 3195 function setOptions($options) {} 3196 3197 function getTitle() {return '';} 3198 3199 /** sets the entries to be shown */ 3200 function setEntries($entries) { 3201 $this->entries = $entries; 3202 $db= createBibDataBase(); 3203 $db->bibdb = $entries; 3204 $this->yearIndex = $db->yearIndex(); 3205 } 3206 3207 /** Displays a set of bibtex entries in an HTML table */ 3208 function display() { 3209 $delegate = createBasicDisplay(); 3210 $delegate->setEntries($this->entries); 3211 $index = count($this->entries); 3212 foreach($this->yearIndex as $year) { 3213 $x = array(); 3214 uasort($x,'compare_bib_entry_by_month'); 3215 foreach($this->entries as $e) { 3216 if ($e->getYear() == $year) { 3217 $x[] = $e; 3218 } 3219 } 3220 3221 if (count($x)>0) { 3222 echo '<div class="theader">'.$year.'</div>'; 3223 $delegate->setEntries($x); 3224 $delegate->display(); 3225 } 3226 3227 $index = $index - count($x); 3228 } 3229 } 3230} 3231 3232 3233/** displays the summary information of all bib entries. 3234usage: 3235<pre> 3236 $db = zetDB('bibacid-utf8.bib'); 3237 $d = new SimpleDisplay(); 3238 $d->setDB($db); 3239 $d->display(); 3240</pre> 3241 */ 3242class SimpleDisplay { 3243 3244 var $headerCSS = 'sheader'; 3245 3246 var $options = array(); 3247 3248 var $entries = array(); 3249 3250 var $headingLevel = BIBTEXBROWSER_HTMLHEADINGLEVEL; 3251 3252 function __construct($db = NULL, $query = array()) { 3253 if ($db == NULL) return; 3254 $this->setEntries($db->multisearch($query)); 3255 } 3256 3257 function incHeadingLevel ($by=1) { 3258 $this->headingLevel += $by; 3259 } 3260 function decHeadingLevel ($by=1) { 3261 $this->headingLevel -= $by; 3262 } 3263 3264 function setDB($bibdatabase) { 3265 $this->setEntries($bibdatabase->bibdb); 3266 } 3267 3268 function metadata() { 3269 if (BIBTEXBROWSER_ROBOTS_NOINDEX) { 3270 return array(array('robots','noindex')); 3271 } else { 3272 return array(); 3273 } 3274 } 3275 3276 /** sets the entries to be shown */ 3277 function setEntries($entries) { 3278 $this->entries = array_values($entries); 3279 } 3280 3281 function indexUp() { 3282 $index=1; 3283 foreach ($this->entries as $bib) { 3284 $bib->setAbbrv((string)$index++); 3285 } // end foreach 3286 return $this->entries; 3287 } 3288 3289 function newest($entries) { 3290 return array_slice($entries,0,BIBTEXBROWSER_NEWEST); 3291 } 3292 3293 function indexDown() { 3294 $index=count($this->entries); 3295 foreach ($this->entries as $bib) { 3296 $bib->setAbbrv((string)$index--); 3297 } // end foreach 3298 return $this->entries; 3299 } 3300 3301 function setQuery($query) { 3302 $this->query = $query; 3303 } 3304 function getTitle() { 3305 return _DefaultBibliographyTitle($this->query); 3306 } 3307 3308 function setIndices() { 3309 $this->setIndicesInDecreasingOrder(); 3310 } 3311 3312 function setIndicesInIncreasingOrderChangingEveryYear() { 3313 $i=1; 3314 $pred = NULL; 3315 foreach ($this->entries as $bib) { 3316 if ($this->changeSection($pred, $bib)) { 3317 $i=1; 3318 } 3319 $bib->setIndex($i++); 3320 $pred = $bib; 3321 } // end foreach 3322 } 3323 3324 function setIndicesInDecreasingOrder() { 3325 $count = count($this->entries); 3326 $i=0; 3327 foreach ($this->entries as $bib) { 3328 // by default, index are in decreasing order 3329 // so that when you add a publicaton recent , the indices of preceding publications don't change 3330 $bib->setIndex($count-($i++)); 3331 } // end foreach 3332 } 3333 3334 /** Displays a set of bibtex entries in an HTML table */ 3335 function display() { 3336 usort($this->entries, 'compare_bib_entries'); 3337 3338 // now that the entries are sorted, setting the index of entries 3339 // this function can be overloaded 3340 $this->setIndices(); 3341 3342 if ($this->options) { 3343 foreach($this->options as $fname=>$opt) { 3344 $this->$fname($opt,$entries); 3345 } 3346 } 3347 3348 if (BIBTEXBROWSER_DEBUG) { 3349 echo 'Style: '.bibtexbrowser_configuration('BIBLIOGRAPHYSTYLE').'<br/>'; 3350 echo 'Order: '.ORDER_FUNCTION.'<br/>'; 3351 echo 'Abbrv: '.c('ABBRV_TYPE').'<br/>'; 3352 echo 'Options: '.@implode(',',$this->options).'<br/>'; 3353 } 3354 3355// if ($this->headingLevel == BIBTEXBROWSER_HTMLHEADINGLEVEL) { 3356// echo "\n".'<span class="count">'; 3357// if (count($this->entries) == 1) { 3358// echo count ($this->entries).' '.__('result'); 3359// } else if (count($this->entries) != 0) { 3360// echo count ($this->entries).' '.__('results'); 3361// } 3362// echo "</span>\n"; 3363// } 3364 print_header_layout(); 3365 3366 $pred = NULL; 3367 foreach ($this->entries as $bib) { 3368 if ($this->changeSection($pred, $bib)) { 3369 echo $this->sectionHeader($bib, $pred); 3370 } 3371 3372 echo $bib->toHTML(true); 3373 3374 $pred = $bib; 3375 } // end foreach 3376 3377 print_footer_layout(); 3378 3379 } // end function 3380 3381 function changeSection($pred, $bib) { 3382 3383 // for the first one we output the header 3384 if ($pred == NULL) { return true; } 3385 3386 $f = ORDER_FUNCTION; 3387 return $f($pred, $bib) != 0; 3388 } 3389 3390 function sectionHeader($bib, $pred) { 3391 switch(BIBTEXBROWSER_LAYOUT) { 3392 case 'table': 3393 return '<tr><td colspan="2" class="'.$this->headerCSS.'">'.$bib->getYear().'</td></tr>'."\n"; 3394 break; 3395 case 'definition': 3396 return '<div class="'.$this->headerCSS.'">'.$bib->getYear().'</div>'."\n"; 3397 break; 3398 case 'list': 3399 $string = ''; 3400 if ($pred) $string .= "</ul>\n"; 3401 if ($bib->hasField(YEAR)) 3402 $year = $bib->getYear(); 3403 else 3404 $year = __('No date'); 3405 return $string.'<h'.$this->headingLevel.'>'.$year."</h".$this->headingLevel.">\n<ul class=\"result\">\n"; 3406 break; 3407 default: 3408 return ''; 3409 } 3410 } 3411 3412} // end class 3413 3414 3415/** returns an HTTP 404 and displays en error message. */ 3416function nonExistentBibEntryError() { 3417 header('HTTP/1.1 404 Not found'); 3418 ?> 3419 <b>Sorry, this bib entry does not exist.</b> 3420 <a href="?">Back to bibtexbrowser</a> 3421 <?php 3422 exit; 3423} 3424 3425/** handles queries with no result */ 3426class NotFoundDisplay { 3427 function display() { 3428 header('HTTP/1.1 404 Not found'); 3429 echo '<span class="count">'.__('Sorry, no results for this query').'</span>'; 3430 } 3431} 3432/** displays the publication records sorted by publication types (as configured by constant BIBLIOGRAPHYSECTIONS). 3433usage: 3434<pre> 3435 $db = zetDB('bibacid-utf8.bib'); 3436 $d = new AcademicDisplay(); 3437 $d->setDB($db); 3438 $d->display(); 3439</pre> 3440 */ 3441class AcademicDisplay { 3442 public $db; 3443 public $entries; 3444 public $title; 3445 3446 function getTitle() { return $this->title; } 3447 function setTitle($title) { $this->title = $title; return $this; } 3448 3449 function setDB($bibdatabase) { 3450 $this->setEntries($bibdatabase->bibdb); 3451 } 3452 3453 /** sets the entries to be shown */ 3454 function setEntries($entries) { 3455 $this->entries = $entries; 3456 } 3457 3458 /** transforms a query to HTML 3459 * $ query is an array (e.g. array(Q_YEAR=>'2005')) 3460 * $title is a string, the title of the section 3461 */ 3462 function search2html($query, $title) { 3463 $entries = $this->db->multisearch($query); 3464 if (count($entries)>0) { 3465 echo "\n".'<div class="sheader">'.$title.'</div>'."\n"; 3466 } 3467 $display = createBasicDisplay(); 3468 $display->setEntries($entries); 3469 $display->headerCSS = 'theader'; 3470 $display->display(); 3471 3472 } 3473 3474 function display() { 3475 $this->db = createBibDataBase(); 3476 $this->db->bibdb = $this->entries; 3477 3478 if (BIBTEXBROWSER_ACADEMIC_TOC != true) { 3479 foreach (_DefaultBibliographySections() as $section) { 3480 $this->search2html($section['query'],$section['title']); 3481 } 3482 } else { 3483 $sections = array(); 3484 echo "<ul>"; 3485 3486 foreach (_DefaultBibliographySections() as $section) { 3487 $entries = $this->db->multisearch($section['query']); 3488 3489 if (count($entries)>0) { 3490 $anchor = preg_replace("/[^a-zA-Z]/", "", $section['title']); 3491 echo "<li><a href=\"#".$anchor."\">".$section['title']." (".count($entries).")</a></li>"; 3492 3493 $display = createBasicDisplay(); 3494 $display->incHeadingLevel(); 3495 $display->setEntries($entries); 3496 $display->headerCSS = 'theader'; 3497 3498 $sections[] = array ( 3499 'display' => $display, 3500 'anchor' => $anchor, 3501 'title' => $section['title'], 3502 'count' => count($entries) 3503 ); 3504 } 3505 } 3506 echo "</ul>"; 3507 3508 foreach ($sections as $section) { 3509 echo "\n<a name=\"".$section['anchor']."\"></a>"; 3510 echo "<h".BIBTEXBROWSER_HTMLHEADINGLEVEL.">"; 3511 echo $section['title']." (".$section['count'].")"; 3512 echo "</h".BIBTEXBROWSER_HTMLHEADINGLEVEL.">\n", 3513 $section['display']->display(); 3514 } 3515 } 3516 } 3517} 3518 3519 3520 3521 3522/** displays a single bib entry. 3523usage: 3524<pre> 3525 $db = zetDB('bibacid-utf8.bib'); 3526 $dis = new BibEntryDisplay($db->getEntryByKey('classical')); 3527 $dis->display(); 3528</pre> 3529notes: 3530- the top-level header (usually <H1>) must be done by the caller. 3531- this view is optimized for Google Scholar 3532 */ 3533class BibEntryDisplay { 3534 3535 /** the bib entry to display */ 3536 var $bib; 3537 3538 function __construct($bib=null) { 3539 $this->bib = $bib; 3540 } 3541 3542 function setEntries($entries) { 3543 $this->bib = $entries[0]; 3544 //$this->title = $this->bib->getTitle().' (bibtex)'.$this->bib->getUrlLink(); 3545 } 3546 3547 /** returns the title */ 3548 function getTitle() { 3549 return $this->bib->getTitle().' (bibtex)'; 3550 } 3551 3552 /** 2011/10/02: new display, inspired from Tom Zimmermann's home page */ 3553 function displayOnSteroids() { 3554 $subtitle = '<div class="bibentry-by">by '.$this->bib->getFormattedAuthorsString().'</div>'; 3555 3556 $abstract = ''; 3557 if ($this->bib->hasField('abstract')) { 3558 $abstract = '<div class="bibentry-label">Abstract:</div><div class="bibentry-abstract">'.$this->bib->getAbstract().'</div>'; 3559 } 3560 3561 $download = ''; 3562 if ($this->bib->hasField('url')) { 3563 $download = '<div class="bibentry-document-link"><a href="'.$this->bib->getField('url').'">View PDF</a></div>'; 3564 } 3565 $reference= '<div class="bibentry-label">Reference:</div><div class="bibentry-reference">'.strip_tags(bib2html($this->bib)).'</div>'; 3566 3567 $bibtex = '<div class="bibentry-label">Bibtex Entry:</div>'.$this->bib->toEntryUnformatted().''; 3568 return $subtitle.$abstract.$download.$reference.$bibtex.$this->bib->toCoins(); 3569 } 3570 3571 function display() { 3572 // we encapsulate everything so that the output of display() is still valid XHTML 3573 echo '<div>'; 3574 //echo $this->display_old(); 3575 echo $this->displayOnSteroids(); 3576 echo '</div>'; 3577 } 3578 3579 // old display 3580 function display_old() { 3581 return $this->bib->toCoins().$this->bib->toEntryUnformatted(); 3582 } 3583 3584 /** Returns a dictionary of metadata. If the same metadata appears multiple times, it is concatenated with ";" 3585 */ 3586 function metadata_dict() { 3587 $result = array(); 3588 foreach($this->metadata() as $v) { 3589 if (!in_array($v[0], $result)) { 3590 $result[$v[0]] = $v[1]; 3591 } else { 3592 $result[$v[0]] .= ';'.$v[1]; 3593 } 3594 } 3595 return $result; 3596 } 3597 3598 /** Returns an array containing the metadata for Google Scholar 3599 * array (array('citation_title', 'foo'), ....) 3600 * @see http://scholar.google.com/intl/en/scholar/inclusion.html 3601 * @see http://www.monperrus.net/martin/accurate+bibliographic+metadata+and+google+scholar 3602 * */ 3603 function metadata() { 3604 $result=array(); 3605 3606 if (c('BIBTEXBROWSER_ROBOTS_NOINDEX')) { 3607 $result[] = array('robots','noindex'); 3608 } 3609 3610 if (c('METADATA_GS')) { 3611 $result = $this->metadata_google_scholar($result); 3612 } // end Google Scholar 3613 3614 // a fallback to essential dublin core 3615 if (c('METADATA_DC')) { 3616 $result = $this->metadata_dublin_core($result); 3617 } 3618 3619 if (c('METADATA_OPENGRAPH')) { 3620 $result = $this->metadata_opengraph($result); 3621 } 3622 3623 if (c('METADATA_EPRINTS')) { 3624 $result = $this->metadata_eprints($result); 3625 } 3626 3627 return $result; 3628 } // end function metadata 3629 3630 function metadata_opengraph($result) { 3631 // Facebook metadata 3632 // see http://ogp.me 3633 // https://developers.facebook.com/tools/debug/og/object/ 3634 $result[] = array('og:type','article'); 3635 $result[] = array('og:title',$this->bib->getTitle()); 3636 foreach($this->bib->getRawAuthors() as $author) { 3637 // opengraph requires a URL as author value 3638 $result[] = array('og:author',"http://".@$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'].'?bib='.urlencode($this->bib->filename).'&author='.urlencode($author)); 3639 } 3640 $result[] = array('og:published_time',$this->bib->getYear()); 3641 return $result; 3642 } // end function metadata_opengraph 3643 3644 function metadata_dublin_core($result) { 3645 // Dublin Core should not be used for bibliographic metadata 3646 // according to several sources 3647 // * Google Scholar: "Use Dublin Core tags (e.g., DC.title) as a last resort - they work poorly for journal papers" 3648 // * http://reprog.wordpress.com/2010/09/03/bibliographic-data-part-2-dublin-cores-dirty-little-secret/ 3649 // however it seems that Google Scholar needs at least DC.Title to trigger referencing 3650 // reference documentation: http://dublincore.org/documents/dc-citation-guidelines/ 3651 $result[] = array('DC.Title',$this->bib->getTitle()); 3652 foreach($this->bib->getArrayOfCommaSeparatedAuthors() as $author) { 3653 $result[] = array('DC.Creator',$author); 3654 } 3655 $result[] = array('DC.Issued',$this->bib->getYear()); 3656 return $result; 3657 } 3658 3659 function metadata_google_scholar($result) { 3660 // the description may mix with the Google Scholar tags 3661 // we remove it 3662 // $result[] = array('description',trim(strip_tags(str_replace('"','',bib2html($this->bib))))); 3663 $result[] = array('citation_title',$this->bib->getTitle()); 3664 $authors = $this->bib->getArrayOfCommaSeparatedAuthors(); 3665 $result[] = array('citation_authors',implode("; ",$authors)); 3666 foreach($authors as $author) { 3667 $result[] = array('citation_author',$author); 3668 } 3669 3670 // the date 3671 $result[] = array('citation_publication_date',$this->bib->getYear()); 3672 $result[] = array('citation_date',$this->bib->getYear()); 3673 $result[] = array('citation_year',$this->bib->getYear()); 3674 3675 if ($this->bib->hasField("publisher")) { 3676 $result[] = array('citation_publisher',$this->bib->getPublisher()); 3677 } 3678 3679 // BOOKTITLE: JOURNAL NAME OR PROCEEDINGS 3680 if ($this->bib->getType()=="article") { // journal article 3681 $result[] = array('citation_journal_title',$this->bib->getField("journal")); 3682 $result[] = array('citation_volume',$this->bib->getField("volume")); 3683 if ($this->bib->hasField("number")) { 3684 // in bibtex, the issue number is usually in a field "number" 3685 $result[] = array('citation_issue',$this->bib->getField("number")); 3686 } 3687 if ($this->bib->hasField("issue")) { 3688 $result[] = array('citation_issue',$this->bib->getField("issue")); 3689 } 3690 if ($this->bib->hasField("issn")) { 3691 $result[] = array('citation_issue',$this->bib->getField("issn")); 3692 } 3693 } 3694 3695 if ($this->bib->getType()=="inproceedings" || $this->bib->getType()=="conference") { 3696 $result[] = array('citation_conference_title',$this->bib->getField(BOOKTITLE)); 3697 $result[] = array('citation_conference',$this->bib->getField(BOOKTITLE)); 3698 } 3699 3700 if ($this->bib->getType()=="phdthesis" 3701 || $this->bib->getType()=="mastersthesis" 3702 || $this->bib->getType()=="bachelorsthesis" 3703 ) 3704 { 3705 $result[] = array('citation_dissertation_institution',$this->bib->getField('school')); 3706 } 3707 3708 if ($this->bib->getType()=="techreport" 3709 && $this->bib->hasField("number") 3710 ) 3711 { 3712 $result[] = array('citation_technical_report_number',$this->bib->getField('number')); 3713 } 3714 3715 if ($this->bib->getType()=="techreport" 3716 && $this->bib->hasField("institution") 3717 ) 3718 { 3719 $result[] = array('citation_technical_report_institution',$this->bib->getField('institution')); 3720 } 3721 3722 // generic 3723 if ($this->bib->hasField("doi")) { 3724 $result[] = array('citation_doi',$this->bib->getField("doi")); 3725 } 3726 3727 if ($this->bib->hasField('url')) { 3728 $result[] = array('citation_pdf_url',$this->bib->getField('url')); 3729 } 3730 3731 if ($this->bib->hasField("pages")) { 3732 $pages = $this->bib->getPages(); 3733 if (count($pages)==2) { 3734 $result[] = array('citation_firstpage',$pages[0]); 3735 $result[] = array('citation_lastpage',$pages[1]); 3736 } 3737 } 3738 3739 return $result; 3740 } 3741 3742 function metadata_eprints($result) { 3743 // --------------------------------- BEGIN METADATA EPRINTS 3744 // and now adding eprints metadata 3745 // why adding eprints metadata? 3746 // because eprints is a well known bibliographic software and several crawlers/desktop software 3747 // use their metadata 3748 // unfortunately, the metadata is even less documented than Google Scholar citation_ 3749 // reference documentation: the eprints source code (./perl_lib/EPrints/Plugin/Export/Simple.pm) 3750 // examples: conference paper: http://tubiblio.ulb.tu-darmstadt.de/44344/ 3751 // journal paper: http://tubiblio.ulb.tu-darmstadt.de/44344/ 3752 $result[] = array('eprints.title',$this->bib->getTitle()); 3753 $authors = $this->bib->getArrayOfCommaSeparatedAuthors(); 3754 foreach($authors as $author) { 3755 $result[] = array('eprints.creators_name',$author); 3756 } 3757 $result[] = array('eprints.date',$this->bib->getYear()); 3758 3759 if ($this->bib->hasField("publisher")) { 3760 $result[] = array('eprints.publisher',$this->bib->getPublisher()); 3761 } 3762 3763 if ($this->bib->getType()=="article") { // journal article 3764 $result[] = array('eprints.type','article'); 3765 $result[] = array('eprints.publication',$this->bib->getField("journal")); 3766 $result[] = array('eprints.volume',$this->bib->getField("volume")); 3767 if ($this->bib->hasField("issue")) { 3768 $result[] = array('eprints.number',$this->bib->getField("issue"));} 3769 } 3770 3771 if ($this->bib->getType()=="inproceedings" || $this->bib->getType()=="conference") { 3772 $result[] = array('eprints.type','proceeding'); 3773 $result[] = array('eprints.book_title',$this->bib->getField(BOOKTITLE)); 3774 } 3775 3776 if ($this->bib->getType()=="phdthesis" 3777 || $this->bib->getType()=="mastersthesis" 3778 || $this->bib->getType()=="bachelorsthesis" 3779 ) 3780 { 3781 $result[] = array('eprints.type','thesis'); 3782 $result[] = array('eprints.institution',$this->bib->getField('school')); 3783 } 3784 3785 if ($this->bib->getType()=="techreport") 3786 { 3787 $result[] = array('eprints.type','monograph'); 3788 if ($this->bib->hasField("number")) { 3789 $result[] = array('eprints.number',$this->bib->getField('number')); 3790 } 3791 if ($this->bib->hasField("institution")) { 3792 $result[] = array('eprints.institution',$this->bib->getField('institution')); 3793 } 3794 } 3795 3796 // generic 3797 if ($this->bib->hasField("doi")) { 3798 $result[] = array('eprints.id_number',$this->bib->getField("doi")); 3799 } 3800 3801 if ($this->bib->hasField('url')) { 3802 $result[] = array('eprints.official_url',$this->bib->getField('url')); 3803 } 3804 // --------------------------------- END METADATA EPRINTS 3805 return $result; 3806 } // end method metatada_eprints; 3807} // end class BibEntryDisplay 3808 3809 3810// ---------------------------------------------------------------------- 3811// DATABASE MANAGEMENT 3812// ---------------------------------------------------------------------- 3813 3814/** represents a bibliographic database that contains a set of bibliographic entries. 3815usage: 3816<pre> 3817$db = new BibDataBase(); 3818$db->load('bibacid-utf8.bib'); 3819$query = array('author'=>'martin', 'year'=>2008); 3820foreach ($db->multisearch($query) as $bibentry) { echo $bibentry->getTitle(); } 3821</pre> 3822*/ 3823class BibDataBase { 3824 /** A hash table from keys (e.g. Goody1994) to bib entries (BibEntry instances). */ 3825 var $bibdb; 3826 3827 /** A hashtable of constant strings */ 3828 var $stringdb; 3829 3830 /** A list of file names */ 3831 var $from_files; 3832 3833 /** Creates a new database by parsing bib entries from the given 3834 * file. (backward compatibility) */ 3835 function load($filename) { 3836 $this->from_files[] = $filename; 3837 $this->update($filename); 3838 } 3839 3840 /** Updates a database (replaces the new bibtex entries by the most recent ones) */ 3841 function update($filename) { 3842 $this->from_files[] = $filename; 3843 $this->update_internal($filename, NULL); 3844 } 3845 3846 /** returns true if this file is already loaded in this BibDataBase object */ 3847 function is_already_loaded($filename) { 3848 return in_array($filename, $this->from_files); 3849 } 3850 3851 /** See update */ 3852 function update_internal($resource_name, $resource) { 3853 $empty_array = array(); 3854 $db = createBibDBBuilder(); 3855 $db->build($resource_name, $resource); 3856 3857 $this->stringdb = array_merge($this->stringdb, $db->stringdb); 3858 3859 $result = $db->builtdb; 3860 3861 3862 foreach ($result as $b) { 3863 // new entries: 3864 if (!isset($this->bibdb[$b->getKey()])) { 3865 //echo 'adding...<br/>'; 3866 $this->addEntry($b); 3867 } 3868 // update entry 3869 else if (isset($this->bibdb[$b->getKey()]) && ($b->toHTML() !== $this->bibdb[$b->getKey()]->toHTML())) { 3870 //echo 'replacing...<br/>'; 3871 $this->bibdb[$b->getKey()] = $b; 3872 } 3873 } 3874 3875 // some entries have been removed 3876 foreach ($this->bibdb as $e) { 3877 if (!isset($result[$e->getKey()]) 3878 && $e->filename==$resource_name // bug reported by Thomas on Dec 4 2012 3879 ) { 3880 //echo 'deleting...<br/>'; 3881 unset($this->bibdb[$e->getKey()]); 3882 } 3883 } 3884 3885 // some @string have been removed 3886 foreach ($this->stringdb as $k=>$e) { 3887 if (!isset($db->stringdb[$k]) 3888 && $e->filename==$resource_name ) { 3889 //echo 'deleting...<br/>'; 3890 unset($this->stringdb[$e->name]); 3891 } 3892 } 3893 } 3894 3895 /** Creates a new empty database */ 3896 function __construct() { 3897 $this->bibdb = array(); 3898 $this->stringdb = array(); 3899 } 3900 3901 /** Returns the $n latest modified bibtex entries/ */ 3902 function getLatestEntries($n) { 3903 $order='compare_bib_entry_by_mtime'; 3904 $array = $this->bibdb; // array passed by value 3905 uasort($array, $order); 3906 $result = array_slice($array,0,$n); 3907 return $result; 3908 } 3909 3910 /** Returns all entries as an array. Each entry is an instance of 3911 * class BibEntry. */ 3912 function getEntries() { 3913 return $this->bibdb; 3914 } 3915 /** tests wheter the database contains a bib entry with $key */ 3916 function contains($key) { 3917 return isset($this->bibdb[$key]); 3918 } 3919 /** Returns all entries categorized by types. The returned value is 3920 * a hashtable from types to arrays of bib entries. 3921 */ 3922 function getEntriesByTypes() { 3923 $result = array(); 3924 foreach ($this->bibdb as $b) { 3925 $result[$b->getType()][] = $b; 3926 } 3927 return $result; 3928 } 3929 3930 /** Returns an array containing all the bib types (strings). */ 3931 function getTypes() { 3932 $result = array(); 3933 foreach ($this->bibdb as $b) { 3934 $result[$b->getType()] = 1; 3935 } 3936 $result = array_keys($result); 3937 return $result; 3938 } 3939 3940 /** Generates and returns an array consisting of all authors. 3941 * The returned array is a hash table with keys <FirstName LastName> 3942 * and values <LastName, FirstName>. 3943 */ 3944 function authorIndex(){ 3945 $tmp = array(); 3946 foreach ($this->bibdb as $bib) { 3947 foreach($bib->getFormattedAuthorsArray() as $a){ 3948 $a = strip_tags($a); 3949 //we use an array because several authors can have the same lastname 3950 @$tmp[$bib->getLastName($a)]=$a; 3951 } 3952 } 3953 ksort($tmp); 3954 $result=array(); 3955 foreach ($tmp as $k=>$v) { 3956 $result[$v]=$v; 3957 } 3958 3959 return $result; 3960 } 3961 3962 function venueIndex(){ 3963 $tmp = array(); 3964 foreach ($this->bibdb as $bib) { 3965 if ($bib->getType()=="article") { 3966 @$tmp[$bib->getField("journal")]++; 3967 } 3968 if ($bib->getType()=="inproceedings") { 3969 @$tmp[$bib->getField("booktitle")]++; 3970 } 3971 } 3972 arsort($tmp); 3973 $result=array(); 3974 foreach ($tmp as $k=>$v) { 3975 $result[$k]=$k; 3976 } 3977 return $result; 3978 } 3979 3980 /** Generates and returns an array consisting of all tags. 3981 */ 3982 function tagIndex(){ 3983 $result = array(); 3984 foreach ($this->bibdb as $bib) { 3985 if (!$bib->hasField("keywords")) continue; 3986 $tags = $bib->getKeywords(); 3987 foreach($tags as $a){ 3988 $ta = trim($a); 3989 $result[$ta] = $ta; 3990 } 3991 } 3992 asort($result); 3993 return $result; 3994 } 3995 3996 /** Generates and returns an array consisting of all years. 3997 */ 3998 function yearIndex(){ 3999 $result = array(); 4000 foreach ($this->bibdb as $bib) { 4001 if (!$bib->hasField("year")) continue; 4002 $year = strtolower($bib->getYearRaw()); 4003 $yearInt = (int) $year; 4004 4005 // Allow for ordering of non-string values ('in press' etc.) 4006 switch ($year) { 4007 case (string) $yearInt: // Sorry for this hacky type-casting 4008 $key = $year; 4009 break; 4010 case Q_YEAR_INPRESS: 4011 $key = PHP_INT_MAX + ORDER_YEAR_INPRESS; 4012 break; 4013 case Q_YEAR_ACCEPTED: 4014 $key = PHP_INT_MAX + ORDER_YEAR_ACCEPTED; 4015 break; 4016 case Q_YEAR_SUBMITTED: 4017 $key = PHP_INT_MAX + ORDER_YEAR_SUBMITTED; 4018 break; 4019 default: 4020 $key = PHP_INT_MAX + ORDER_YEAR_OTHERNONINT; 4021 } 4022 4023 $result[$key] = $year; 4024 } 4025 4026 krsort($result); 4027 return $result; 4028 } 4029 4030 /** Given its key, return the bib entry. */ 4031 function getEntryByKey($key) { 4032 return $this->bibdb[$key]; 4033 } 4034 4035 /** Adds a new bib entry to the database. */ 4036 function addEntry($entry) { 4037 if (!$entry->hasField('key')) { 4038 throw new Exception('error: a bibliographic entry must have a key '.$entry->getText()); 4039 } 4040 // we keep its insertion order 4041 $entry->order = count($this->bibdb); 4042 $this->bibdb[$entry->getKey()] = $entry; 4043 } 4044 4045 4046 /** 4047 * Returns an array containing all bib entries matching the given 4048 * type. 4049 */ 4050 function searchType($type){ 4051 $result = array(); 4052 foreach($this->bibdb as $bib) { 4053 if($bib->getType() == $type) 4054 $result[] = $bib; 4055 } 4056 return $result; 4057 } 4058 4059 /** Returns an array of bib entries (BibEntry) that satisfy the query 4060 * $query is an hash with entry type as key and searched fragment as value 4061 */ 4062 function multisearch($query) { 4063 if (count($query)<1) {return array();} 4064 if (isset($query[Q_ALL])) return array_values($this->bibdb); 4065 4066 $result = array(); 4067 4068 foreach ($this->bibdb as $bib) { 4069 $entryisselected = true; 4070 foreach ($query as $field => $fragment) { 4071 $field = strtolower($field); 4072 if ($field==Q_SEARCH) { 4073 // we search in the whole bib entry 4074 if (!$bib->hasPhrase($fragment)) { 4075 $entryisselected = false; 4076 break; 4077 } 4078 } 4079 else if ($field==Q_EXCLUDE) { 4080 if ($bib->hasPhrase($fragment)) { 4081 $entryisselected = false; 4082 break; 4083 } 4084 } 4085 else if ($field==Q_TYPE || $field==Q_INNER_TYPE) { 4086 // types are always exact search 4087 // remarks Ken 4088 // type:"book" should only select book (and not inbook, book, bookchapter) 4089 // this was before in Dispatch:type() 4090 // moved here so that it is also used by AcademicDisplay:search2html() 4091 if (!$bib->hasPhrase('^('.$fragment.')$', Q_INNER_TYPE)) { 4092 $entryisselected = false; 4093 break; 4094 } 4095 } 4096 else if ($field==Q_NAME || $field==Q_AUTHOR_NAME || $field==Q_EDITOR_NAME) { 4097 // Names require exact matching per name. Although a preg_match over the entire author field is possible, 4098 // it's inconvenient and often results in unwanted matches if not done careful. Instead, use 4099 // 'name'=>'(M. Monperrus|Monperrus, M.)' to exact match the name of an author or editor, use 4100 // 'author_name' to match the name of an author, and use 'editor_name' to match the name of an editor. 4101 $names = []; 4102 if ($field==Q_NAME || $field==Q_AUTHOR_NAME) 4103 $names = array_merge($bib->getRawAuthors(), $names); 4104 if ($field==Q_NAME || $field==Q_EDITOR_NAME) 4105 $names = array_merge($bib->getRawEditors(), $names); 4106 4107 if (empty($names)) { 4108 $entryisselected = false; 4109 } else { 4110 foreach ($names as $name) { 4111 $entryisselected = preg_match('/^' . $fragment . '$/', trim($name)); 4112 if ($entryisselected) { 4113 break; 4114 } 4115 } 4116 } 4117 if (!$entryisselected) { 4118 break; 4119 } 4120 } 4121 else if ($field==Q_KEYS) { 4122 if ( ! in_array( $bib->getKey(), $query[Q_KEYS] ) ) { 4123 $entryisselected = false; 4124 break; 4125 } 4126 } 4127 else if ($field==Q_RANGE) { 4128 $year = $bib->getYear(); 4129 $withinRange = false; 4130 4131 foreach ($query[Q_RANGE] as $elements) { 4132 if ($elements[0] === "" && $elements[1] === "") 4133 $withinRange = true; 4134 else if ($elements[0] === "" && $year <= $elements[1]) 4135 $withinRange = true; 4136 else if ($elements[1] === "" && $year >= $elements[0]) 4137 $withinRange = true; 4138 else if ($year <= $elements[1] && $year >= $elements[0]) { 4139 $withinRange = true; 4140 } 4141 } 4142 4143 if (!$withinRange) 4144 $entryisselected = false; 4145 } 4146 else { 4147 if (!$bib->hasPhrase($fragment, $field)) { 4148 $entryisselected = false; 4149 break; 4150 } 4151 } 4152 4153 } 4154 if ($entryisselected) { 4155 $result[] = $bib; 4156 } 4157 } 4158 return $result; 4159 } 4160 4161 /** returns the text of all @String entries of this dabatase */ 4162 function stringEntriesText() { 4163 $s = ""; 4164 foreach($this->stringdb as $entry) { $s.=$entry->toString()."\n"; } 4165 return $s; 4166 } 4167 4168 /** returns a classical textual Bibtex representation of this database */ 4169 function toBibtex() { 4170 $s = ""; 4171 $s .= $this->stringEntriesText(); 4172 foreach($this->bibdb as $bibentry) { $s.=$bibentry->getText()."\n"; } 4173 return $s; 4174 } 4175 4176} // end class 4177 4178/** returns the default CSS of bibtexbrowser */ 4179function bibtexbrowserDefaultCSS() { 4180?> 4181 4182/* title */ 4183.bibtitle { font-weight:bold; } 4184/* author */ 4185.bibauthor { /* nothing by default */ } 4186/* booktitle (e.g. proceedings title, journal name, etc )*/ 4187.bibbooktitle { font-style:italic; } 4188/* publisher */ 4189.bibpublisher { /* nothing by default */ } 4190 4191 4192/* 1st level headers, equivalent H1 */ 4193.rheader { 4194 color: #003366; 4195 font-size: large; 4196 font-weight: bold; 4197} 4198 4199/* 2nd level headers, equivalent H2 */ 4200.sheader { 4201 font-weight: bold; 4202 background-color: #003366; 4203 color: #ffffff; 4204 padding: 2px; 4205 margin-bottom: 0px; 4206 margin-top: 7px; 4207 border-bottom: #ff6633 2px solid; 4208 4209} 4210 4211/* 3rd level headers, equivalent H3 */ 4212.theader { 4213 background-color: #995124; 4214 color: #FFFFFF; 4215 padding: 1px 2px 1px 2px; 4216} 4217 4218.btb-nav-title { 4219 background-color: #995124; 4220 color: #FFFFFF; 4221 padding: 1px 2px 1px 2px; 4222} 4223 4224.menu { 4225 font-size: x-small; 4226 background-color: #EFDDB4; 4227 padding: 0px; 4228 border: 1px solid #000000; 4229 margin: 0px; 4230} 4231.menu a { 4232 text-decoration: none; 4233 color: #003366; 4234} 4235.menu a:hover { 4236 color: #ff6633; 4237} 4238 4239dd { 4240 display: inline; /* for <dt> if BIBTEXBROWSER_LAYOUT='definition' */ 4241} 4242 4243.bibitem { 4244 margin-left:5px; 4245} 4246 4247.bibref { 4248 padding:7px; 4249 padding-left:15px; 4250 vertical-align:text-top; 4251 display: inline; /* for <dt> if BIBTEXBROWSER_LAYOUT='definition' */ 4252} 4253 4254.result { 4255 border: 1px solid #000000; 4256 margin:0px; 4257 background-color: #ffffff; 4258 width:100%; 4259} 4260.result a { 4261 text-decoration: none; 4262 color: #469AF8; 4263} 4264 4265.result a:hover { 4266 color: #ff6633; 4267} 4268 4269.input_box{ 4270 margin-bottom : 2px; 4271} 4272.mini_se { 4273 border: none 0; 4274 border-top: 1px dashed #717171; 4275 height: 1px; 4276} 4277.a_name a { 4278 color:#469AF8; 4279 width:130px; 4280} 4281 4282.rsslink { 4283 text-decoration: none; 4284 color:#F88017; 4285/* could be fancy, see : http://www.feedicons.com/ for icons*/ 4286 /*background-image: url("rss.png"); text-indent: -9999px;*/ 4287} 4288 4289.purebibtex { 4290 font-family: monospace; 4291 font-size: small; 4292 border: 1px solid #DDDDDD; 4293 background: none repeat scroll 0 0 #F5F5F5; 4294 padding:10px; 4295 4296 overflow:auto; 4297 width:600px; 4298 4299 clear:both; 4300} 4301.bibentry-by { font-style: italic; } 4302.bibentry-abstract { margin:15px; } 4303.bibentry-label { margin-top:15px; } 4304.bibentry-reference { margin-bottom:15px; padding:10px; background: none repeat scroll 0 0 #F5F5F5; border: 1px solid #DDDDDD; } 4305 4306.btb-nav { text-align: right; } 4307 4308<?php 4309} // end function bibtexbrowserDefaultCSS 4310 4311/** encapsulates the content of a delegate into full-fledged HTML (<HTML><BODY> and TITLE) 4312usage: 4313<pre> 4314 $db = zetDB('bibacid-utf8.bib'); 4315 $dis = new BibEntryDisplay($db->getEntryByKey('classical')); 4316 HTMLTemplate($dis); 4317</pre> 4318 * $content: an object with methods 4319 display() 4320 getRSS() 4321 getTitle() 4322 * $title: title of the page 4323 */ 4324function HTMLTemplate($content, $header = true) { 4325 4326// when we load a page with AJAX 4327// the HTTP header is taken into account, not the <meta http-equiv> 4328if ($header) { 4329 header('Content-type: text/html; charset='.OUTPUT_ENCODING); 4330} 4331echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n"; 4332 4333?> 4334<html xmlns="http://www.w3.org/1999/xhtml"> 4335<head> 4336<meta http-equiv="Content-Type" content="text/html; charset=<?php echo OUTPUT_ENCODING ?>"/> 4337<meta name="generator" content="bibtexbrowser v__GITHUB__" /> 4338<?php 4339// if ($content->getRSS()!='') echo '<link rel="alternate" type="application/rss+xml" title="RSS" href="'.$content->getRSS().'&rss" />'; 4340?> 4341<?php 4342 4343// we may add new metadata tags 4344$metatags = array(); 4345if (method_exists($content, 'metadata')) { 4346 $metatags = $content->metadata(); 4347} 4348foreach($metatags as $item) { 4349 list($name,$value) = $item; 4350 echo '<meta name="'.$name.'" property="'.$name.'" content="'.$value.'"/>'."\n"; 4351} // end foreach 4352 4353 4354 4355// now the title 4356if (method_exists($content, 'getTitle')) { 4357 echo '<title>'.strip_tags($content->getTitle()).'</title>'; 4358} 4359 4360// now the CSS 4361echo '<style type="text/css"><!-- '."\n"; 4362 4363if (method_exists($content, 'getCSS')) { 4364 echo $content->getCSS(); 4365} else if (is_readable(dirname(__FILE__).'/bibtexbrowser.css')) { 4366 readfile(dirname(__FILE__).'/bibtexbrowser.css'); 4367} 4368else { bibtexbrowserDefaultCSS(); } 4369 4370echo "\n".' --></style>'; 4371 4372?> 4373</head> 4374<body> 4375<?php 4376// configuration point to add a banner 4377echo bibtexbrowser_top_banner(); 4378?> 4379<?php 4380if (method_exists($content, 'getTitle')) { 4381 echo "<div class=\"rheader\">" . $content->getTitle() . "</div>"; 4382} 4383?> 4384<?php 4385 $content->display(); 4386 echo poweredby(); 4387 4388 if (c('BIBTEXBROWSER_USE_PROGRESSIVE_ENHANCEMENT')) { 4389 javascript(); 4390 } 4391 4392 if (BIBTEXBROWSER_RENDER_MATH) { 4393 javascript_math(); 4394 } 4395?> 4396</body> 4397</html> 4398<?php 4399//exit; 4400} // end function HTMLTemplate 4401 4402 4403/** does nothing but calls method display() on the content. 4404usage: 4405<pre> 4406 $db = zetDB('bibacid-utf8.bib'); 4407 $dis = new SimpleDisplay($db); 4408 NoWrapper($dis); 4409</pre> 4410*/ 4411function NoWrapper($content) { 4412 echo $content->display(); 4413 if (c('BIBTEXBROWSER_USE_PROGRESSIVE_ENHANCEMENT')) { 4414 javascript(); 4415 } 4416} 4417 4418/** is used to create an subset of a bibtex file. 4419usage: 4420<pre> 4421 $db = zetDB('bibacid-utf8.bib'); 4422 $query = array('year'=>2005); 4423 $dis = new BibtexDisplay(); 4424 $dis->setEntries($db->multisearch($query)); 4425 $dis->display(); 4426</pre> 4427*/ 4428class BibtexDisplay { 4429 var $header = true; 4430 var $entries; 4431 var $title; 4432 function __construct() {} 4433 4434 function setTitle($title) { $this->title = $title; return $this; } 4435 4436 /** sets the entries to be shown */ 4437 function setEntries($entries) { 4438 $this->entries = $entries; 4439 } 4440 4441 function setWrapper($x) { $x->wrapper = 'NoWrapper'; } 4442 4443 function display() { 4444 if ($this->header) { 4445 header('Content-type: text/plain; charset='.OUTPUT_ENCODING); 4446 } 4447 echo '% generated by bibtexbrowser <http://www.monperrus.net/martin/bibtexbrowser/>'."\n"; 4448 echo '% '.@$this->title."\n"; 4449 echo '% Encoding: '.OUTPUT_ENCODING."\n"; 4450 foreach($this->entries as $bibentry) { echo $bibentry->getText()."\n"; } 4451 } 4452 4453} 4454 4455/** creates paged output, e.g: [[http://localhost/bibtexbrowser/testPagedDisplay.php?page=1]] 4456usage: 4457<pre> 4458 $_GET['library']=1; 4459 include( 'bibtexbrowser.php' ); 4460 $db = zetDB('bibacid-utf8.bib'); 4461 $pd = new PagedDisplay(); 4462 $pd->setEntries($db->bibdb); 4463 $pd->display(); 4464</pre> 4465*/ 4466class PagedDisplay { 4467 4468 var $query = array(); 4469 var $page = 1; 4470 var $entries = array(); 4471 4472 function __construct() { 4473 $this->setPage(); 4474 } 4475 4476 /** sets the entries to be shown */ 4477 function setEntries($entries) { 4478 uasort($entries, 'compare_bib_entries'); 4479 $this->entries = array_values($entries); 4480 } 4481 4482 /** sets $this->page from $_GET, defaults to 1 */ 4483 function setPage() { 4484 $this->page = 1; 4485 if (isset($_GET['page'])) { 4486 $this->page = $_GET['page']; 4487 } 4488 } 4489 4490 function setQuery($query) { 4491 $this->query = $query; 4492 } 4493 4494 function getTitle() { 4495 return query2title($this->query). ' - page '.$this->page; 4496 } 4497 4498 function display() { 4499 $less = false; 4500 4501 if ($this->page>1) {$less = true;} 4502 4503 $more = true; 4504 4505 // computing $more 4506 $index = ($this->page)*bibtexbrowser_configuration('PAGE_SIZE'); 4507 if (!isset($this->entries[$index])) { 4508 $more = false; 4509 } 4510 4511 $this->menu($less, $more); 4512 print_header_layout(); 4513 for ($i = 0; $i < bibtexbrowser_configuration('PAGE_SIZE'); $i++) { 4514 $index = ($this->page-1)*bibtexbrowser_configuration('PAGE_SIZE') + $i; 4515 if (isset($this->entries[$index])) { 4516 $bib = $this->entries[$index]; 4517 echo $bib->toHTML(true); 4518 4519 } else { 4520 //break; 4521 } 4522 } // end foreach 4523 4524 print_footer_layout(); 4525 4526 $this->menu($less, $more); 4527 } 4528 4529 function menu($less, $more) { 4530 4531 echo '<span class="nav-menu">'; 4532 4533 $prev = $this->query; 4534 $prev['page'] = $this->page-1; 4535 if ($less == true) { echo '<a '.makeHref($prev).'>Prev Page</a>'; } 4536 4537 if ($less && $more) { echo ' | '; } 4538 4539 $next = $this->query; 4540 $next['page'] = $this->page+1; 4541 if ($more == true) { echo '<a '.makeHref($next).'>Next Page</a>'; } 4542 echo '</span>'; 4543 4544 } 4545} 4546 4547/** is used to create an RSS feed. 4548usage: 4549<pre> 4550 $db = zetDB('bibacid-utf8.bib'); 4551 $query = array('year'=>2005); 4552 $rss = new RSSDisplay(); 4553 $entries = $db->getLatestEntries(10); 4554 $rss->setEntries($entries); 4555 $rss->display(); 4556</pre> 4557*/ 4558class RSSDisplay { 4559 4560 var $title = 'RSS produced by bibtexbrowser'; 4561 var $header = true; 4562 var $entries; 4563 function __construct() { 4564 // nothing by default 4565 } 4566 4567 function setTitle($title) { $this->title = $title; return $this; } 4568 4569 /** tries to always output a valid XML/RSS string 4570 * based on OUTPUT_ENCODING, HTML tags, and the transformations 4571 * that happened in latex2html */ 4572 function text2rss($desc) { 4573 // first strip HTML tags 4574 $desc = strip_tags($desc); 4575 4576 // some entities may still be here, we remove them 4577 // we replace html entities e.g. é by nothing 4578 // however XML entities are kept (e.g. 5) 4579 $desc = preg_replace('/&\w+;/','',$desc); 4580 4581 // bullet proofing ampersand 4582 $desc = preg_replace('/&([^#])/','&$1',$desc); 4583 4584 // be careful of < 4585 $desc = str_replace('<','<',$desc); 4586 $desc = str_replace('>','>',$desc); 4587 4588 // final test with encoding: 4589 if (function_exists('mb_check_encoding')) { // (PHP 4 >= 4.4.3, PHP 5 >= 5.1.3) 4590 if (!mb_check_encoding($desc,OUTPUT_ENCODING)) { 4591 return 'encoding error: please check the content of OUTPUT_ENCODING'; 4592 } 4593 } 4594 4595 return $desc; 4596 } 4597 4598 /** sets the entries to be shown */ 4599 function setEntries($entries) { 4600 $this->entries = $entries; 4601 } 4602 4603 function setWrapper($x) { $x->wrapper = 'NoWrapper'; } 4604 4605 function display() { 4606 if ($this->header) { 4607 header('Content-type: application/rss+xml'); 4608 } 4609 echo '<?xml version="1.0" encoding="'.OUTPUT_ENCODING.'"?>'; 4610// 4611 4612?> 4613<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> 4614 <channel> 4615 <title><?php echo $this->title;?></title> 4616 <link>http://<?php echo getServerData('HTTP_HOST').htmlentities(getServerData('REQUEST_URI'));?></link> 4617 <atom:link href="http://<?php echo getServerData('HTTP_HOST').htmlentities(getServerData('REQUEST_URI'));?>" rel="self" type="application/rss+xml" /> 4618 <description></description> 4619 <generator>bibtexbrowser v__GITHUB__</generator> 4620 4621<?php 4622 foreach($this->entries as $bibentry) { 4623 ?> 4624 <item> 4625 <title><?php echo $this->text2rss($bibentry->getTitle());?></title> 4626 <link><?php echo $bibentry->getURL();?></link> 4627 <description> 4628 <?php 4629 // we are in XML, so we cannot have HTML entitites 4630 echo $this->text2rss(bib2html($bibentry)."\n".$bibentry->getAbstract()); 4631 ?> 4632 </description> 4633 <guid isPermaLink="false"><?php echo urlencode(@$_GET[Q_FILE].'::'.$bibentry->getKey());?></guid> 4634 </item> 4635 <?php } /* end foreach */?> 4636 </channel> 4637</rss> 4638 4639<?php 4640 //exit; 4641 } 4642} 4643 4644/** 4645 * workaround deprecation 4646 */ 4647function getServerData($key) { 4648 // if exists 4649 if (isset($_SERVER[$key])) { 4650 return $_SERVER[$key]; 4651 } 4652 return ""; 4653} 4654 4655 4656/** is responsible for transforming a query string of $_GET[..] into a publication list. 4657usage: 4658<pre> 4659 $_GET['library']=1; 4660 @require('bibtexbrowser.php'); 4661 $_GET['bib']='bibacid-utf8.bib'; 4662 $_GET['year']='2006'; 4663 $x = new Dispatcher(); 4664 $x->main(); 4665</pre> 4666*/ 4667class Dispatcher { 4668 4669 /** this is the query */ 4670 var $query = array(); 4671 4672 /** the displayer of selected entries. The default is set in BIBTEXBROWSER_DEFAULT_DISPLAY. 4673 * It could also be an RSSDisplay if the rss keyword is present 4674 */ 4675 var $displayer = ''; 4676 4677 /** the wrapper of selected entries. The default is an HTML wrapper 4678 * It could also be a NoWrapper when you include your pub list in your home page 4679 */ 4680 var $wrapper = BIBTEXBROWSER_DEFAULT_TEMPLATE; 4681 4682 /** The BibDataBase object */ 4683 var $db = null; 4684 4685 function __construct() {} 4686 4687 /** returns the underlying BibDataBase object */ 4688 function getDB() { 4689 // by default set it from $_GET[Q_FILE] 4690 // first we set the database (load from disk or parse the bibtex file) 4691 if ($this->db == null) { 4692 list($db, $parsed, $updated, $saved) = _zetDB($_GET[Q_FILE]); 4693 $this->db = $db; 4694 } 4695 return $this->db; 4696 } 4697 4698 function main() { 4699 // are we in test mode, or libray mode 4700 // then this file is just a library 4701 if (isset($_GET['test']) || isset($_GET['library'])) { 4702 // we unset in order to use the dispatcher afterwards 4703 unset($_GET['test']); 4704 unset($_GET['library']); 4705 return; 4706 } 4707 4708 if (!isset($_GET[Q_FILE])) { die('$_GET[\''.Q_FILE.'\'] is not set!'); } 4709 4710 // is the publication list included in another page? 4711 // strtr is used for Windows where __FILE__ contains C:\toto and SCRIPT_FILENAME contains C:/toto (bug reported by Marco) 4712 // realpath is required if the path contains sym-linked directories (bug found by Mark Hereld) 4713 if (strtr(realpath(__FILE__),"\\","/")!=strtr(realpath($_SERVER['SCRIPT_FILENAME']),"\\","/")) $this->wrapper=BIBTEXBROWSER_EMBEDDED_WRAPPER; 4714 4715 // first pass, we will exit if we encounter key or menu or academic 4716 // other wise we just create the $this->query 4717 foreach($_GET as $keyword=>$value) { 4718 if (method_exists($this,$keyword)) { 4719 // if the return value is END_DISPATCH, we finish bibtexbrowser (but not the whole PHP process in case we are embedded) 4720 if ($this->$keyword()=='END_DISPATCH') return; 4721 } 4722 } 4723 4724 // at this point, we may have a query 4725 4726 if (count($this->query)>0) { 4727 4728 // first test for inconsistent queries 4729 if (isset($this->query[Q_ALL]) && count($this->query)>1) { 4730 // we discard the Q_ALL, it helps in embedded mode 4731 unset($this->query[Q_ALL]); 4732 } 4733 4734 $selectedEntries = $this->getDB()->multisearch($this->query); 4735 4736 if (count($selectedEntries)==0) { 4737 $this->displayer = 'NotFoundDisplay'; 4738 } 4739 4740 // default order 4741 uasort($selectedEntries, 'compare_bib_entries'); 4742 $selectedEntries = array_values($selectedEntries); 4743 4744 //echo '<pre>';print_r($selectedEntries);echo '</pre>'; 4745 4746 if ($this->displayer=='') { 4747 $this->displayer = bibtexbrowser_configuration('BIBTEXBROWSER_DEFAULT_DISPLAY'); 4748 } 4749 } // otherwise the query is left empty 4750 4751 // do we have a displayer? 4752 if ($this->displayer!='') { 4753 4754 $options = array(); 4755 if (isset($_GET['dopt'])) { 4756 $options = json_decode($_GET['dopt'],true); 4757 } 4758 4759 // required for PHP4 to have this intermediate variable 4760 $x = new $this->displayer(); 4761 4762 if (method_exists($x,'setEntries')) { 4763 $x->setEntries($selectedEntries); 4764 } 4765 4766 if (method_exists($x,'setTitle')) { 4767 $x->setTitle(query2title($this->query)); 4768 } 4769 4770 if (method_exists($x,'setQuery')) { 4771 $x->setQuery($this->query); 4772 } 4773 4774 // should call method display() on $x 4775 $fun = $this->wrapper; 4776 $fun($x); 4777 4778 $this->clearQuery(); 4779 } 4780 else { 4781 // we send a redirection for having the frameset 4782 // if some contents have already been sent, for instance if we are included 4783 // this means doing nothing 4784 if ( headers_sent() == false ) { /* to avoid sending an unnecessary frameset */ 4785 header("Location: ".$_SERVER['SCRIPT_NAME']."?frameset&bib=".$_GET[Q_FILE]); 4786 } 4787 } 4788 } 4789 4790 /** clears the query string in $_GET so that bibtexbrowser can be called multiple times */ 4791 function clearQuery() { 4792 $params= array(Q_ALL,'rss', 'astext', Q_SEARCH, Q_EXCLUDE, Q_YEAR, EDITOR, Q_TAG, Q_AUTHOR, Q_TYPE, Q_ACADEMIC, Q_KEY); 4793 foreach($params as $p) { unset($_GET[$p]); } 4794 } 4795 4796 function all() { 4797 $this->query[Q_ALL]=1; 4798 } 4799 4800 function display() { 4801 $this->displayer=$_GET['display']; 4802 } 4803 4804 function rss() { 4805 $this->displayer='RSSDisplay'; 4806 $this->wrapper='NoWrapper'; 4807 } 4808 4809 function astext() { 4810 $this->displayer='BibtexDisplay'; 4811 $this->wrapper='NoWrapper'; 4812 } 4813 4814 function search() { 4815 if (preg_match('/utf-?8/i',OUTPUT_ENCODING)) { 4816 $_GET[Q_SEARCH] = urldecode($_GET[Q_SEARCH]); 4817 } 4818 $this->query[Q_SEARCH]=$_GET[Q_SEARCH]; 4819 } 4820 4821 function exclude() { $this->query[Q_EXCLUDE]=$_GET[Q_EXCLUDE]; } 4822 4823 function year() { 4824 // we may want the latest 4825 if ($_GET[Q_YEAR]=='latest') { 4826 $years = $this->getDB()->yearIndex(); 4827 $_GET[Q_YEAR]=array_shift($years); 4828 } 4829 $this->query[Q_YEAR]=$_GET[Q_YEAR]; 4830 } 4831 4832 function editor() { $this->query[EDITOR]=$_GET[EDITOR]; } 4833 4834 function keywords() { $this->query[Q_TAG]=$_GET[Q_TAG]; } 4835 4836 function author() { 4837 // Friday, October 29 2010 4838 // changed from 'author' to '_author' 4839 // in order to search at the same time "Joe Dupont" an "Dupont, Joe" 4840 $this->query[Q_INNER_AUTHOR]=$_GET[Q_AUTHOR]; 4841 } 4842 4843 function type() { 4844 $this->query[Q_TYPE]= $_GET[Q_TYPE]; 4845 } 4846 /** 4847 * Allow the user to search for a range of dates 4848 * 4849 * The query string can comprise several elements separated by commas and 4850 * optionally white-space. 4851 * Each element can either be one number (a year) or two numbers 4852 * (a range of years) separated by anything non-numerical. 4853 * 4854 */ 4855 function range() { 4856 $ranges = explode(',', $_GET[Q_RANGE]); 4857 $result = array(); 4858 4859 $nextYear = 1 + (int) date('Y'); 4860 $nextYear2D = $nextYear % 100; 4861 $thisCentury = $nextYear - $nextYear2D; 4862 4863 foreach ($ranges as $range) { 4864 $range = trim($range); 4865 preg_match('/([0-9]*)([^0-9]*)([0-9]*)/', $range, $matches); 4866 array_shift($matches); 4867 4868 // If the number is empty, leave it empty - dont put it to 0 4869 // If the number is two-digit, assume it to be within the last century or next year 4870 if ($matches[0] === "") { 4871 $lower = ""; 4872 } else if ($matches[0] < 100) { 4873 if ($matches[0] > $nextYear2D) { 4874 $lower = $thisCentury + $matches[0] - 100; 4875 } else { 4876 $lower = $thisCentury + $matches[0]; 4877 } 4878 } else { 4879 $lower = $matches[0]; 4880 } 4881 4882 // If no separator to indicate a range of years was supplied, 4883 // the upper and lower boundaries are the same. 4884 // 4885 // Otherwise, again: 4886 // If the number is empty, leave it empty - dont put it to 0 4887 // If the number is two-digit, assume it to be within the last century or next year 4888 if ($matches[1] === "") 4889 $upper = $lower; 4890 else { 4891 if ($matches[2] === "") { 4892 $upper = ""; 4893 } else if ($matches[2] < 100) { 4894 if ($matches[2] > $nextYear2D) { 4895 $upper = $thisCentury + $matches[2] - 100; 4896 } else { 4897 $upper = $thisCentury + $matches[2]; 4898 } 4899 } else { 4900 $upper = $matches[2]; 4901 } 4902 } 4903 4904 $result[] = array($lower, $upper); 4905 } 4906 $this->query[Q_RANGE] = $result; 4907 } 4908 4909 function menu() { 4910 $menu = createMenuManager(); 4911 $menu->setDB($this->getDB()); 4912 $fun = $this->wrapper; 4913 $fun($menu); 4914 return 'END_DISPATCH'; 4915 } 4916 4917 /** the academic keyword in URLs switch from a year based viey to a publication type based view */ 4918 function academic() { 4919 $this->displayer='AcademicDisplay'; 4920 4921 4922 // backward compatibility with old GET API 4923 // this is deprecated 4924 // instead of academic=Martin+Monperrus 4925 // you should use author=Martin+Monperrus&academic 4926 // be careful of the semantics of === and !== 4927 // 'foo bar' == true is true 4928 // 123 == true is true (and whatever number different from 0 4929 // 0 == true is true 4930 // '1'!=1 is **false** 4931 if(!isset($_GET[Q_AUTHOR]) && $_GET[Q_ACADEMIC]!==true && $_GET[Q_ACADEMIC]!=='true' && $_GET[Q_ACADEMIC]!=1 && $_GET[Q_ACADEMIC]!='') { 4932 $_GET[Q_AUTHOR]=$_GET[Q_ACADEMIC]; 4933 $this->query[Q_AUTHOR]=$_GET[Q_ACADEMIC]; 4934 } 4935 4936 } 4937 4938 function key() { 4939 $entries = array(); 4940 // case 1: this is a single key 4941 if ($this->getDB()->contains($_GET[Q_KEY])) { 4942 $entries[] = $this->getDB()->getEntryByKey($_GET[Q_KEY]); 4943 if (isset($_GET['astext'])) { 4944 $bibdisplay = new BibtexDisplay(); 4945 $bibdisplay->setEntries($entries); 4946 $bibdisplay->display(); 4947 } else { 4948 $bibdisplay = createBibEntryDisplay(); 4949 $bibdisplay->setEntries($entries); 4950 $fun = $this->wrapper; 4951 $fun($bibdisplay); 4952 } 4953 return 'END_DISPATCH'; 4954 } 4955 4956 // case two: multiple keys 4957 if (preg_match('/[|,]/',$_GET[Q_KEY])) { 4958 $this->query[Q_SEARCH]=str_replace(',','|',$_GET[Q_KEY]); 4959 } else { nonExistentBibEntryError(); } 4960 } 4961 4962 function keys() { 4963 // Create array from list of bibtex entries 4964 $_GET[Q_KEYS] = (array) json_decode(urldecode($_GET[Q_KEYS])); // decode and cast the object into an (associative) array 4965 // Make the array 1-based (keeps the string keys unchanged) 4966 array_unshift($_GET[Q_KEYS],"__DUMMY__"); 4967 unset($_GET[Q_KEYS][0]); 4968 // Keep a flipped version for efficient search in getRawAbbrv() 4969 $_GET[Q_INNER_KEYS_INDEX] = array_flip($_GET[Q_KEYS]); 4970 $this->query[Q_KEYS]=$_GET[Q_KEYS]; 4971 } 4972 4973 /** is used to remotely analyzed a situation */ 4974 function diagnosis() { 4975 header('Content-type: text/plain'); 4976 echo "php version: ".phpversion()."\n"; 4977 echo "bibtexbrowser version: __GITHUB__\n"; 4978 echo "dir: ".decoct(fileperms(dirname(__FILE__)))."\n"; 4979 echo "bibtex file: ".decoct(fileperms($_GET[Q_FILE]))."\n"; 4980 exit; 4981 } 4982 4983 function frameset() { 4984 $this->getDB(); // will throw 404 if bib file does not exist 4985 4986 ?> 4987 4988 4989 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> 4990 <html xmlns="http://www.w3.org/1999/xhtml"> 4991 <head> 4992 <meta name="generator" content="bibtexbrowser v__GITHUB__" /> 4993 <meta http-equiv="Content-Type" content="text/html; charset=<?php echo OUTPUT_ENCODING ?>"/> 4994 <title>You are browsing <?php echo htmlentities($_GET[Q_FILE], ENT_QUOTES); ?> with bibtexbrowser</title> 4995 </head> 4996 <frameset cols="15%,*"> 4997 <frame name="menu" src="<?php echo '?'.Q_FILE.'='. urlencode($_GET[Q_FILE]).'&menu'; ?>" /> 4998 <frame name="main" src="<?php echo '?'.Q_FILE.'='. urlencode($_GET[Q_FILE]).'&'.BIBTEXBROWSER_DEFAULT_FRAME?>" /> 4999 </frameset> 5000 </html> 5001 5002 <?php 5003 return 'END_DISPATCH'; 5004} 5005 5006} // end class Dispatcher 5007 5008function bibtexbrowser_cli($arguments) { 5009 $db = new BibDataBase(); 5010 $db->load($arguments[1]); 5011 $current_entry=NULL; 5012 $current_field=NULL; 5013 for ($i=2;$i<count($arguments); $i++) { 5014 $arg=$arguments[$i]; 5015 if ($arg=='--id') { 5016 $current_entry = $db->getEntryByKey($arguments[$i+1]); 5017 $i=$i+1; 5018 } 5019 if (preg_match('/^--set-(.*)/',$arg,$matches)) { 5020 $current_entry->setField($matches[1],$arguments[$i+1]); 5021 $i=$i+1; 5022 } 5023 } 5024 file_put_contents($arguments[1],$db->toBibtex()); 5025} 5026 5027} // end if (!defined('BIBTEXBROWSER')) 5028 5029@include(preg_replace('/\.php$/','.after.php',__FILE__)); 5030$class = BIBTEXBROWSER_MAIN;// extension point 5031$main = new $class(); 5032$main->main(); 5033?> 5034