* @version 0.4.0 * @date 2023-05-28 */ require_once(DOKU_PLUGIN.'bibtex4dw/lib/bibtexparser.php'); class bibtexrender_plugin_bibtex4dw { /** * Array containing all references to objects of this class that already exist. * * Necessary to synchronize calls from the two different syntax plugins for the same page */ public static $resources = array(); /** * Handle to SQLite db */ public static $sqlite = array(); /** * Array containing local configuration of the object * * Options can be set by calling setOptions($options = array()) */ private $_conf = array( 'sqlite' => false, 'file' => array(), 'citetype' => '', 'sort' => false, 'formatstring' => array(), ); private $_langStrings = array( 'pageabbrev', 'pagesabbrev', 'chapterabbrev', 'editorabbrev', 'mastersthesis', 'phdthesis', 'techreport', 'unpublished' ); /** * Array containing all references from the BibTeX files loaded */ private $_bibtex_references = array(); /** * Array containing all keys from the BibTeX records in $_bibtex_references */ private $_bibtex_keys = array(); /** * Array containing all keys already cited together with their number */ private $_bibtex_keysCited = array(); /** * Array containing all keys given with "nocite" together with their number */ private $_bibtex_keysNotCited = array(); /** * Number of the currently highest cite key if style "numeric" is used */ private $_currentKeyNumber = 0; /** * Initializes the class * * As it loads the configuration options set e.g. via the config manager via the admin * interface of dokuwiki, the plugin can be configured such that only the final * pattern is necessary to print the actual bibliography. */ function __construct() { // Use trick to access configuration and language stuff from the actual bibtex plugin // with the builtin methods of DW require_once(DOKU_PLUGIN.'bibtex4dw/syntax/bibtex.php'); $this->plugin = new syntax_plugin_bibtex4dw_bibtex(); // Transfer config settings from plugin config (via config manager) to local config // Note: Only those settings that can be changed by this class need to be transferred. // Therefore it is not necessary to transfer the format strings for the entries. $this->_conf['sqlite'] = $this->plugin->getConf('sqlite'); $this->_conf['file'] = explode(';',$this->plugin->getConf('file')); $this->_conf['pdfdir'] = explode(';',$this->plugin->getConf('pdfdir')); $this->_conf['citetype'] = $this->plugin->getConf('citetype'); // In case we shall use SQLite if ($this->_conf['sqlite']) { $this->sqlite = plugin_load('helper', 'sqlite'); if(!$this->sqlite){ msg('You asked for using the sqlite plugin but it is not installed. Please install it', -1); return; } // initialize the database connection if(!$this->sqlite->init('bibtex4dw', DOKU_PLUGIN.'bibtex4dw/db/')){ return; } } else { // If there are files to load, load and parse them if (array_key_exists('file', $this->_conf)) { $this->_parseBibtexFile(); } } } /** * Gets instance of the class by id * * @param pageid */ public static function getResource($id = NULL) { // exit if given parameters not sufficient. if (is_null($id)) { return null; } if (!array_key_exists($id, self::$resources)) { $x = new bibtexrender_plugin_bibtex4dw($id); self::$resources[$id] = $x; } // return the desired object or null in case of error return self::$resources[$id]; } /** * Set options of object * * @param options */ public function setOptions($options) { // Clear nocite array if it already exists and is not empty. // This is necessary for the "furtherreading" bibliographies, so that you // can have more than one of these bibliographies that are independent. if (isset($this->_bibtex_keysNotCited) && (count($this->_bibtex_keysNotCited))) { $this->_bibtex_keysNotCited = array(); } // Handle options, convert to $_conf if possible foreach($options as $optkey => $optval) { if ('nocite' == $optkey) { // if $optval is an array (i.e. more than one "nocite" was given) if (is_array($optval)) { $optval = implode(',',$optval); } $bibkeys = explode(',',$optval); foreach ($bibkeys as $bibkey) { $this->_bibtex_keysNotCited[$bibkey] = 0; } } if (array_key_exists($optkey,$this->_conf)) { // For file, allow multiple entries if ('file' == $optkey) { if (is_array($optval) && (count($optval) > 1)) { $this->_conf[$optkey] = $optval; } else { $this->_conf[$optkey][] = $optval[0]; } // In all other cases, the last entry of a type wins } else { $this->_conf[$optkey] = $optval[count($optval)-1]; } } } // Set sort option depending on citetype if (!array_key_exists('sort',$options)) { switch ($this->_conf['citetype']) { case 'apa': $this->_conf['sort'] = 'true'; break; case 'alpha': $this->_conf['sort'] = 'true'; break; case 'authordate': $this->_conf['sort'] = 'true'; break; case 'numeric': $this->_conf['sort'] = 'false'; break; default: $this->_conf['sort'] = 'false'; break; } } } /** * Internal function parsing the contents of the BibTeX file * * The result will be stored in $this->_bibtex_references */ private function _parseBibtexFile() { $bibtex = ''; // Load all files and concatenate their contents foreach($this->_conf['file'] as $file) { $bibtex .= $this->_loadBibtexFile($file, 'page'); } $this->_parser = new bibtexparser_plugin_bibtex4dw(); $this->_parser->loadString($bibtex); $stat = $this->_parser->parseBibliography(); if ( !$stat ) { return $stat; } //$this->_bibtex_references = $this->_parser->data; $this->_bibtex_references = $this->_parser->entries; foreach($this->_bibtex_references as $refno => $ref) { if (is_array($ref) && array_key_exists('cite', $ref)) { $this->_bibtex_keys[$ref['cite']] = $refno; } } } /** * Internal function adding the content to the SQLite database */ public function addBibtexToSQLite($bibtex,$ID) { if (!$this->_conf['sqlite']) { return; } if (!in_array(':'.$ID, $this->_conf['file'])) { msg("Current page (:$ID) not configured to be a BibTeX DB, hence ignoring. Change in config if this is not intended."); return; } $this->_parser = new bibtexparser_plugin_bibtex4dw(); $this->_parser->loadString($bibtex); $this->_parser->sqlite = $this->sqlite; $stat = $this->_parser->parseBibliography($sqlite=true); if ( !$stat ) { msg('Some problems with parsing BIBTeX code',-1); } if ( ($this->_parser->warnings['warning']) && (count($this->_parser->warnings['warning']))) { foreach($this->_parser->warnings as $parserWarning) { msg($this->_parser->warnings[$parserWarning]['warning'],'2'); } } } /** * Prints the reference corresponding to the given bibtex key * * The format of the reference can be freely configured using the * dokuwiki configuration interface (see files in conf dir) * * @param string BibTeX key of the reference * @return string (HTML) formatted BibTeX reference */ public function printReference($bibtex_key) { global $INFO; if ($this->_conf['sqlite']) { $this->_parser = new bibtexparser_plugin_bibtex4dw(); $this->_parser->sqlite = $this->sqlite; $rawBibtexEntry = $this->sqlite->res2arr($this->sqlite->query("SELECT entry FROM bibtex WHERE key=?",$bibtex_key)); $this->_parser->loadString($rawBibtexEntry[0]['entry']); $stat = $this->_parser->parse(); if ( !$stat ) { return $stat; } $ref = $this->_parser->data[0]; } else { //$ref = $this->_bibtex_references[$this->_bibtex_keys[$bibtex_key]]; $rawBibtexEntry = $this->_bibtex_references[$bibtex_key]; $this->_parser->loadString($rawBibtexEntry); $stat = $this->_parser->parse(); if ( !$stat ) { return $stat; } $ref = $this->_parser->data[0]; } if (empty($ref)) { return; } // Variant of $ref with normalized (i.e., all uppercase) field names $normalizedRef = []; foreach ($ref as $key => $value) { $normalizedRef[strtoupper($key)] = $value; } // Get format string from plugin config $formatstring = $this->plugin->getConf('fmtstr_'.$normalizedRef['ENTRYTYPE']); // Replace each language string ($this->_langStrings) pattern '@placeholder@' with respective value foreach ($this->_langStrings as $lang) { $formatstring = str_replace('@'.strtolower($lang).'@', $this->plugin->getLang($lang), $formatstring); } // Replace each field pattern '{...@FIELDNAME@...}' with respective value from bib data preg_match_all("#\{([^@]*)@([A-Z]+)@([^@]*)\}#U", $formatstring, $fieldsToBeReplaced, PREG_SET_ORDER); foreach ($fieldsToBeReplaced as $matchPair) { $partOfFormatstring = $matchPair[0]; $priorToName = $matchPair[1]; $fieldName = $matchPair[2]; $afterName = $matchPair[3]; if (empty($normalizedRef[$fieldName])) { $formatstring = str_replace($partOfFormatstring, "", $formatstring); continue; } $formattedPart = $priorToName; $formattedPart .= $normalizedRef[$fieldName]; $formattedPart .= $afterName; $formatstring = str_replace($partOfFormatstring, $formattedPart, $formatstring); } // Handle PDF files // Check whether we have a directory for PDF files if (array_key_exists('pdfdir',$this->_conf)) { // Check whether we are logged in and have permissions to access the PDFs if ((auth_quickaclcheck($this->_conf['pdfdir'][0]) >= AUTH_READ) && array_key_exists('name',$INFO['userinfo'])) { // do sth. $pdffilename = mediaFN($this->_conf['pdfdir'][0]) . "/" . $bibtex_key . ".pdf"; if (file_exists($pdffilename)) { resolve_mediaid($this->_conf['pdfdir'][0], $pdflinkname, $exists); $formatstring .= ' PDF'; } } } return $formatstring; } /** * Prints the whole bibliography * * @param string substate (bibliography, furtherreading) * @return string (HTML) formatted bibliography */ public function printBibliography($substate) { switch ($substate) { case 'bibliography': if (!isset($this->_bibtex_keysCited) || empty($this->_bibtex_keysCited)) { return; } // If there are nocite entries if (isset($this->_bibtex_keysNotCited) && !empty($this->_bibtex_keysNotCited)) { foreach ($this->_bibtex_keysNotCited as $key => $no) { if (!array_key_exists($key,$this->_bibtex_keysCited)) { $this->_bibtex_keysCited[$key] = ++$this->_currentKeyNumber; } } } if ('true' == $this->_conf['sort'] && 'numeric' != $this->_conf['citetype']) { $citedKeys = array(); foreach ($this->_bibtex_keysCited as $key => $no) { if ($this->_conf['sqlite']) { $this->_parser = new bibtexparser_plugin_bibtex4dw(); $this->_parser->sqlite = $this->sqlite; $rawBibtexEntry = $this->sqlite->res2arr($this->sqlite->query("SELECT entry FROM bibtex WHERE key=?",$key)); $this->_parser->loadString($rawBibtexEntry[0]['entry']); $stat = $this->_parser->parse(); if ( !$stat ) { return $stat; } $citedKeys[$key] = $this->_parser->data[0]['authoryear']; } else { $citedKeys[$key] = $this->_bibtex_references[$this->_bibtex_keys[$key]]['authoryear']; } } asort($citedKeys); } else { $citedKeys = $this->_bibtex_keysCited; } if ('authordate' == $this->_conf['citetype']) { $html = $this->_printReferencesAsUnorderedList($citedKeys); } else { $html = $this->_printReferencesAsDefinitionlist($citedKeys); } return $html; case 'furtherreading': if (!isset($this->_bibtex_keysNotCited) || empty($this->_bibtex_keysNotCited)) { return; } $this->_currentKeyNumber = 0; if ('true' == $this->_conf['sort']) { $notcitedKeys = array(); foreach ($this->_bibtex_keysNotCited as $key => $no) { if ($this->_conf['sqlite']) { $this->_parser = new bibtexparser_plugin_bibtex4dw(); $this->_parser->sqlite = $this->sqlite; $rawBibtexEntry = $this->sqlite->res2arr($this->sqlite->query("SELECT entry FROM bibtex WHERE key=?",$key)); $this->_parser->loadString($rawBibtexEntry[0]['entry']); $stat = $this->_parser->parse(); if ( !$stat ) { return $stat; } $notcitedKeys[$key] = $this->_parser->data[0]['authoryear']; } else { $notcitedKeys[$key] = $this->_bibtex_references[$this->_bibtex_keys[$key]]['authoryear']; } } asort($notcitedKeys); } else { $notcitedKeys = $this->_bibtex_keysNotCited; } if ('authordate' == $this->_conf['citetype']) { $html = $this->_printReferencesAsUnorderedList($notcitedKeys); } else { $html = $this->_printReferencesAsDefinitionlist($notcitedKeys); } return $html; } } /** * Print references as unordered list * * Currently used only for "authordate" citation style * * @param array List of keys bibliography should be generated for * @return string rendered HTML of bibliography */ function _printReferencesAsUnorderedList($citedKeys) { $html = ''; return $html; } /** * Print references as definitionlist * * Currently used for all citation styles except "authordate" * * @param array List of keys bibliography should be generated for * @return string rendered HTML of bibliography */ function _printReferencesAsDefinitionlist($citedKeys) { $html = '
' . DOKU_LF; foreach ($citedKeys as $key => $no) { if ($this->keyExists($key)) { $html .= '
['; $html .= $this->printCitekey($key); $html .= ']
' . DOKU_LF; $html .= '
'; $html .= $this->printReference($key); $html .= '
' . DOKU_LF; } else { msg("BibTeX key '$key' could not be found. Possible typo?"); } } $html .= '
'; return $html; } /** * Return the cite key of the given reference for using in the text * The Output depends on the configuration via the variable * $this->_conf['citetype'] * If the citetype is unknown, the bibtex key is returned * * @param string bibtex key of the reference * @return string cite key of the reference (according to the citetype set) */ public function printCitekey($bibtex_key) { if (!array_key_exists($bibtex_key,$this->_bibtex_keysCited)) { $this->_currentKeyNumber++; $this->_bibtex_keysCited[$bibtex_key] = $this->_currentKeyNumber; } if ($this->_conf['sqlite']) { $this->_parser = new bibtexparser_plugin_bibtex4dw(); $rawBibtexEntry = $this->sqlite->res2arr($this->sqlite->query("SELECT entry FROM bibtex WHERE key=?",$bibtex_key)); if (empty($rawBibtexEntry)) { return $bibtex_key; } $this->_parser->loadString($rawBibtexEntry[0]['entry']); $stat = $this->_parser->parse(); if ( !$stat ) { return $stat; } $ref = $this->_parser->data[0]; } else { // Check whether key exists if (empty($this->_bibtex_references[$bibtex_key])) { return $bibtex_key; } $ref = $this->_bibtex_references[$this->_bibtex_keys[$bibtex_key]]; } switch ($this->_conf['citetype']) { case 'apa': $bibtex_key = $ref['authors'][0]['last']; if ($ref['authors'][0]['last'] == '') { $bibtex_key = $ref['editors'][0]['last']; } $bibtex_key .= $ref['year']; break; case 'alpha': $bibtex_key = substr($ref['authors'][0]['last'],0,3); if ($ref['authors'][0]['last'] == '') { $bibtex_key = substr($ref['editors'][0]['last'],0,3); } $bibtex_key .= substr($ref['year'],2,2); break; case 'authordate': $bibtex_key = $ref['authors'][0]['last'] . ", "; if ($ref['authors'][0]['last'] == '') { $bibtex_key = $ref['editors'][0]['last'] . ", "; } $bibtex_key .= $ref['year']; break; case 'numeric': $bibtex_key = $this->_bibtex_keysCited[$bibtex_key]; break; // If no known citation style is given - however, that should not happen default: $bibtex_key = $this->_bibtex_keysCited[$bibtex_key]; break; } return $bibtex_key; } /** * Check if given key exists in currently used BibTeX database * * @param string bibtex key of the reference * @return Boolean value */ function keyExists($bibkey) { if ($this->_conf['sqlite']) { $rawBibtexEntry = $this->sqlite->res2arr($this->sqlite->query("SELECT entry FROM bibtex WHERE key=?",$bibkey)); return (!empty($rawBibtexEntry)); } else { return (!empty($this->_bibtex_references[$bibkey])); } } /** * Debug function to output the raw contents of the BibTeX file * * @return string raw BibTeX code */ function rawOutput() { $bibtex = ''; // Load all files and concatenate their contents foreach($this->_conf['file'] as $file) { $bibtex .= $this->_loadBibtexFile($file, 'page'); } return $bibtex; } /** * Loads BibTeX code and returns the raw BibTeX as string * * @param string uri BibTeX file (path or dokuwiki page) * @param string kind whether uri is a file or a dokuwiki page * @return string raw BibTeX code */ function _loadBibtexFile($uri, $kind) { global $INFO; if ( $kind == 'file' ) { // FIXME: Adjust path - make it configurable return file_get_contents(dirname(__FILE__).'/'.$kind.'/'.$uri); } elseif ($kind == 'page') { $exists = false; resolve_pageid($INFO['namespace'], $uri, $exists); if ( $exists ) { return rawWiki($uri); } } return null; } } ?>