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