Fork of bibtexbrowser for static publications lists on the website of the ZRD Saar
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5045 lines
154 KiB

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