1<?php 2 3use dokuwiki\Extension\SyntaxPlugin; 4use dokuwiki\File\PageResolver; 5 6/** 7 * DokuWiki Plugin bb4dw (Syntax Component) 8 * 9 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 10 * @author Hans-Nikolai Viessmann <hans@viess.mn> 11 * 12 * This Syntax plugin is inspired by the deprecated publistf Dokuwiki plugin, and 13 * tries to recreate the same output using a BibBrowser 14 * (https://github.com/monperrus/bibtexbrowser) Bibtex processing script. 15 * 16 * Templating is handled through the BB4DWTemplating class. 17 */ 18 19/** 20 * BibBrowser Configurations 21 */ 22$_GET['library'] = 1; // cause BibBrowser to run in 'library' mode 23define('BIBTEXBROWSER_BIBTEX_LINKS', false); // disable links back to bibtex 24define('USE_FIRST_THEN_LAST', true); // ensure that author names are consistently ordered 25 26class syntax_plugin_bb4dw extends SyntaxPlugin 27{ 28 /** @inheritDoc */ 29 public function getType() 30 { 31 return 'substition'; 32 } 33 34 /** @inheritDoc */ 35 public function getPType() 36 { 37 return 'block'; 38 } 39 40 /** @inheritDoc */ 41 public function getSort() 42 { 43 return 105; 44 } 45 46 /** @inheritDoc */ 47 public function connectTo($mode) 48 { 49 $this->Lexer->addSpecialPattern('\[bb4dw\|.+?\]', $mode, 'plugin_bb4dw'); 50 } 51 52 /** @inheritDoc */ 53 public function handle($match, $state, $pos, Doku_Handler $handler) 54 { 55 $data = [ 56 'error' => false, 57 'groups' => [], 58 'bibtex' => [], 59 'template' => [], 60 'raw' => [], 61 'config' => ['target' => 'dokuwiki', # XXX likely we won't be able to support anything else! 62 'usegroup' => true, 63 'groupby' => 'year', # call also be 'none', 'author', or 'title' 64 'order' => 'newest', 65 'globalkey' => 'all', 66 'filter' => []], # or 'descending' 67 ]; 68 69 // parse the bb4dw plugin pattern 70 $matchs = []; 71 $pattern = '/\[bb4dw(?:\|bibtex=(dokuwiki|url):(.+?))(?:\|template=(dokuwiki|url):(.+?))(?:\|(.+?(?:\|.+?)*))?\]/'; 72 if (preg_match($pattern, $match, $matches) === 0) { 73 msg('Not valid bb4dw syntax: '.$match, -1); 74 $data['error'] = true; 75 } else { 76 // capture matches in config 77 $data['bibtex'] = ['type' => $matches[1], 'ref' => $matches[2]]; 78 $data['template'] = ['type' => $matches[3], 'ref' => $matches[4]]; 79 80 if (!empty($matches[5])) { 81 $matches = explode('|', $matches[5]); 82 foreach ( $matches as $opt ) { 83 $optparts = array(); 84 if (preg_match('/(.+?)=(.+)/', $opt, $optparts) ) { 85 $optparts[2] = explode(';', $optparts[2]); 86 $option = array(); 87 foreach ($optparts[2] as $single) { 88 $single = explode(':', $single); 89 if (count($single) == 1 && count($optparts[2]) == 1) { 90 $option = $single[0]; 91 } 92 else { 93 $option[$single[0]] = str_replace(',', '|', $single[1]); 94 } 95 } 96 $data['config'][$optparts[1]] = $option; 97 } 98 } 99 } 100 101 // init BibBrowser library 102 require_once(dirname(__FILE__).'/bibtexbrowser.php'); 103 global $db; 104 $db = new BibDataBase(); 105 106 // Load Bibtex into db structure 107 $db->load($this->retrieve_resource($data['bibtex']['type'], $data['bibtex']['ref'], true)); 108 109 // get all entries and sort (internally the default is by year) 110 $data['raw'] = $db->getEntries(); 111 //uasort($data['raw'], 'compare_bib_entries'); 112 113 foreach ($data['raw'] as $entry) { 114 // we decouple the read in fields from the bibbrowser library 115 // we format authors into consistent state 116 $_tmp_entry = $entry->getFields(); 117 $_tmp_entry['niceauthor'] = $entry->getFormattedAuthorsString(); 118 $_tmp_entry['bibtex'] = $entry->getText(); 119 120 $keep = true; 121 foreach ($data['config']['filter'] as $field => $regexp) { 122 if (!empty($_tmp_entry[$field])) { 123 $val = $field === 'author' 124 ? $_tmp_entry['niceauthor'] 125 : $_tmp_entry[$field]; 126 127 $keep = $keep && preg_match('/'.$regexp.'/i', $val); 128 } 129 else 130 $keep = false; 131 132 } 133 if (!$keep) { 134 unset($_tmp_entry); 135 continue; 136 } 137 138 switch($data['config']['groupby']) { 139 case 'none': 140 $groupby = 'none'; 141 break; 142 case 'author': 143 $_authors = $entry->getRawAuthors(); 144 $groupby = mb_substr($entry->getLastName($_authors[0]), 0, 1); 145 break; 146 case 'title': 147 $groupby = mb_substr($entry->getTitle(), 0, 1); 148 break; 149 case 'year': 150 $groupby = $entry->getYear(); 151 break; 152 default: 153 msg('Unknown groupby `'.$data['config']['groupby'].'` passed!', -1); 154 $data['error'] = true; 155 break 2; 156 } 157 158 // ensure that we don't append to null array 159 if (empty($data['groups'][$groupby])) 160 $data['groups'][$groupby] = [$_tmp_entry]; 161 else 162 $data['groups'][$groupby][] = $_tmp_entry; 163 } 164 165 // ensure that the groups are sorted and in the specified order 166 ksort($data['groups']); 167 if ($data['config']['order'] == 'newest' || $data['config']['order'] == 'descending') 168 $data['groups'] = array_reverse($data['groups'], true); 169 } 170 171 return $data; 172 } 173 174 /** @inheritDoc */ 175 public function render($mode, Doku_Renderer $renderer, $data) 176 { 177 if ($data['error']) return false; 178 if ($mode !== 'xhtml') return false; 179 180 // activate caching of results 181 $renderer->info['cache'] = $this->getConf('cache'); 182 183 $tpl = $this->retrieve_resource($data['template']['type'], $data['template']['ref']); 184 185 require_once(dirname(__FILE__).'/templating.php'); 186 $bb4dw_tpl_class = new BB4DWTemplating(); 187 $proc_tpl = $bb4dw_tpl_class->process_template($tpl, $data); 188 189 if ($data['config']['target'] == 'dokuwiki') { 190 $proc_tpl = p_render($mode, p_get_instructions($proc_tpl), $info); 191 } 192 193 $renderer->doc .= $proc_tpl; 194 195 return true; 196 } 197 198 /** 199 * Retrieve resource from one of file, URL, or dokuwiki page. The result of this 200 * function with either be the verbatim content of the resource, or an absolute path. 201 * 202 * @param string $type 203 * @param string $ref 204 * @param bool $path 205 * @return string Content of or path to resource 206 */ 207 private function retrieve_resource(string $type, string $ref, bool $path = false): string 208 { 209 $res = ''; 210 211 switch ($type) 212 { 213 case 'url': 214 if ($path) 215 $res = $ref; 216 else 217 $res = file_get_contents($ref); 218 break; 219 case 'dokuwiki': 220 $resolver = new PageResolver($ID); 221 $mid = $resolver->resolveId($ref); 222 if(page_exists($mid)) { 223 if ($path) 224 $res = wikiFN($mid); 225 else 226 $res = rawWiki($mid); 227 } 228 break; 229 default: 230 msg('Unknown type '.$type.', unable to process!', -1); 231 break; 232 } 233 234 return $res; 235 } 236} 237