1<?php 2/** 3* Wikindx Citation Module 4* Derived from: Refworks Plugin by Daniel Terry, Amazon Plugin by Andreas Gohr 5* 6* @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7* @author Andreas Wagner <Andreas.Wagner@em.uni-frankfurt.de> 8* @author Stéphane Aulery <lkppo@users.sourceforge.net> 9* 10* Online Resources: 11* https://www.dokuwiki.org/plugin:wikindx 12* https://wikindx.sourceforge.io/web/trunk/interfacing/cms/ 13* http://www.commontology.de/polphil/literatur/einbindung_der_literaturdatenbank (broken link) 14* 15* CHANGELOG: 16* 17* 2024-01-27: Finish v2 of the plugin for WIKINDX 6.6.0 and higher 18* Compatible with: 19* - Jack Jackrum+ 20* - Igor 21* - Hogfather 22* 23* 2021-10-12: Finish v1 of the plugin for WIKINDX 6.6.0 and higher 24* Compatible with: 25* - Hogfather 26* 27* 2013-01-16: Update for dokuwiki Release 2012-10-13 "Adora Belle" and 28* wikindx v4.1. 29* (wikindx4's index.php provides the following actions out 30* of the box: getResource, getCategory, getSubcategory, 31* getKeyword, getCreator, getPublisher, getCollection, 32* getRecent. 33* Optional parameters are: 34* limit, days, order, sqlMethod(=and), bibstyle) 35* New functionality: I have added these actions: 36* getAbstract, getNotes, getQuote, getParaphr, getMusing. 37* 38* 2013-01-21: Update to take advantage of wikindx4 svn changes rather than 39* patch it myself (patch hence no longer needed). 40* 41* TODO: 42* + Provide switch to not insert a citation into the tracking array 43* + Allow to put bibliography before all the citations? 44* 45* 46*/ 47 48use dokuwiki\HTTP\DokuHTTPClient; 49 50/* have an array ready to keep track of all cited works on the page */ 51global $WKX_USED_IDS; 52 53/** 54* All DokuWiki plugins to extend the parser/rendering mechanism 55* need to inherit from this class 56*/ 57class syntax_plugin_wikindx extends \dokuwiki\Extension\SyntaxPlugin 58{ 59 /** 60 * Syntax Type 61 * 62 * Needs to return one of the mode types defined in $PARSER_MODES in Parser.php 63 * 64 * @return string 65 */ 66 public function getType() 67 { 68 return "container"; 69 } 70 71 /** 72 * Where to sort in? 73 */ 74 public function getSort() 75 { 76 // Just after internal link mode 77 return 301; 78 } 79 80 /** 81 * Connect pattern to lexer 82 */ 83 function connectTo($mode) 84 { 85 // Grab everything between {{wxcite> and }} (resp. grab {{wxbib}}) 86 $this->Lexer->addSpecialPattern( 87 '\{\{(?:' . 88 'wxbib|' . 89 'wxabstract>[^}]*?|' . 90 'wxcite>[^}]*?|' . 91 'wxmusing>[^}]*?|' . 92 'wxnotes>[^}]*?|' . 93 'wxparaphrase>[^}]*?|' . 94 'wxquote>[^}]*?|' . 95 ')\}\}', 96 $mode, 97 'plugin_wikindx' 98 ); 99 } 100 101 /** 102 * Allowed Mode Types 103 * 104 * Defines the mode types for other dokuwiki markup that maybe nested within the 105 * plugin's own markup. Needs to return an array of one or more of the mode types 106 * defined in $PARSER_MODES in Parser.php 107 * 108 * @return array 109 */ 110 function getAllowedTypes() 111 { 112 return [ 113 "container", 114 "formatting", 115 "substition", 116 ]; 117 } 118 119 /** 120 * Handler to prepare matched data for the rendering process 121 * 122 * This function can only pass data to render() via its return value - render() 123 * may be not be run during the object's current life. 124 * 125 * Usually you should only need the $match param. 126 * 127 * @param string $match The text matched by the patterns 128 * @param int $state The lexer state for the match 129 * @param int $pos The character position of the matched text 130 * @param Doku_Handler $handler The Doku_Handler object 131 * @return bool|array Return an array with all data you want to use in render, false don't add an instruction 132 */ 133 public function handle($match, $state, $pos, Doku_Handler $handler) 134 { 135 global $WKX_USED_IDS; 136 if ($match == '{{wxbib}}') 137 { 138 $callingmodus = "wxbib"; 139 $ids = array_unique($WKX_USED_IDS); 140 } 141 else 142 { 143 // Remove {{ from start 144 // Remove }} from end 145 // E.g. {{wxcite>128}} => wxcite>128 146 $match_without_bracket = substr($match, strlen("{{"), -1 * strlen("}}")); 147 148 // Cut on first ">" character into callingmodus and data parts 149 $match_parts = explode(">", $match_without_bracket); 150 $callingmodus = $match_parts[0]; // E.g. wxcite, wxmusing ... 151 $data = trim($match_parts[1]); 152 $data = trim($data, ";"); //remove trailing ; 153 154 // Extract ids from data 155 $ids = explode(";", $data); 156 } 157 158 159 //Lookup data 160 $http = new DokuHTTPClient(); 161 foreach ($ids as $id) 162 { 163 $iex = explode(':', $id); 164 $resId = $iex[0] ?? NULL; 165 $page = $iex[1] ?? NULL; 166 167 if ($callingmodus != "wxbib") 168 { 169 $WKX_USED_IDS[] = $resId; 170 } 171 172 $callmode[] = $callingmodus; 173 $resourceId[] = $resId; 174 $resourceHtml = []; 175 176 switch ($callingmodus) 177 { 178 case 'wxbib': 179 global $WKX_USED_IDS; 180 181 $citetext = ""; 182 foreach ($WKX_USED_IDS as $rid) 183 { 184 $citetext .= "[cite]" . $resId . "[/cite]\n"; 185 } 186 187 $aResponse = $this->WikindxparseText($resId, $this->getConf('url'), $citetext); 188 189 // Search for the bibliograpy which is the last block 190 $cite_blocks = $this->mb_explode("<br><br>", $aResponse["text"] ?? ""); 191 if (count($cite_blocks) == 3) 192 $bibliography = $cite_blocks[2]; 193 elseif (count($cite_blocks) == 2) 194 $bibliography = $cite_blocks[1]; 195 else 196 $bibliography = ""; 197 198 $resourceHtml[] = $bibliography; 199 break; 200 case 'wxabstract': 201 $queryUrl = rtrim($this->getConf('url'), "/") . "/cmsprint.php?action=getAbstract&bibStyle=" . $this->getConf('bibStyle') . "&id=" . $resId; 202 $aResponse = $this->_CMSPHPResponse2Array($http->get($queryUrl)); 203 foreach ($aResponse as $rid => $item) 204 { 205 $resourceHtml[] = $item; 206 } 207 break; 208 case 'wxcite': 209 default: 210 $aResponse = $this->WikindxparseText($resId, $this->getConf('url'), "[cite]" . rtrim($resId . "|" . $page, "|") . "[/cite]"); 211 212 // Search for the citation which is the first block in regulat style, 213 // and the second block for footnote style 214 $cite_blocks = $this->mb_explode("<br><br>", $aResponse["text"] ?? ""); 215 if (count($cite_blocks) == 3) 216 { 217 $citation = $cite_blocks[1]; 218 } 219 elseif (count($cite_blocks) == 2) 220 { 221 $citation = '<a href=' . rtrim($this->getConf('url'), "/") . "/index.php?action=cms_CMS_CORE&method=resource_RESOURCEVIEW_CORE&id=" . $resId . '">'; 222 $citation .= trim($cite_blocks[0]); 223 $citation .= '</a>'; 224 } 225 else 226 { 227 $citation = ""; 228 } 229 230 $resourceHtml[] = $citation; 231 break; 232 case 'wxmusing': 233 $queryUrl = rtrim($this->getConf('url'), "/") . "/cmsprint.php?action=getMusing&bibStyle=" . $this->getConf('bibStyle') . "&id=" . $resId; 234 $aResponse = $this->_CMSPHPResponse2Array($http->get($queryUrl)); 235 236 if (!$page) 237 $page = 1; 238 else 239 $page = $page; 240 241 if (array_key_exists(intval($resId), $aResponse)) 242 { 243 $musings = $aResponse[intval($resId)]; 244 $n = 0; 245 foreach ($musings as $key => $musing) 246 { 247 $n++; 248 if ($page == $n) 249 { 250 $resourceHtml[] = $musing; 251 break; 252 } 253 } 254 } 255 break; 256 case 'wxnotes': 257 $queryUrl = rtrim($this->getConf('url'), "/") . "/cmsprint.php?action=getNotes&bibStyle=" . $this->getConf('bibStyle') . "&id=" . $resId; 258 $aResponse = $this->_CMSPHPResponse2Array($http->get($queryUrl)); 259 foreach ($aResponse as $rid => $item) 260 { 261 $resourceHtml[] = $item; 262 } 263 break; 264 case 'wxparaphrase': 265 $queryUrl = rtrim($this->getConf('url'), "/") . "/cmsprint.php?action=getParaphrase&bibStyle=" . $this->getConf('bibStyle') . "&id=" . $resId; 266 $aResponse = $this->_CMSPHPResponse2Array($http->get($queryUrl)); 267 268 if (!$page) 269 $page = 1; 270 else 271 $page = $page; 272 273 if (array_key_exists(intval($resId), $aResponse)) 274 { 275 $paraphrases = $aResponse[intval($resId)]; 276 $n = 0; 277 foreach ($paraphrases as $key => $paraphrase) 278 { 279 $n++; 280 if ($page == $n) 281 { 282 $resourceHtml[] = $paraphrase; 283 break; 284 } 285 } 286 } 287 break; 288 case 'wxquote': 289 $queryUrl = rtrim($this->getConf('url'), "/") . "/cmsprint.php?action=getQuote&bibStyle=" . $this->getConf('bibStyle') . "&id=" . $resId; 290 $aResponse = $this->_CMSPHPResponse2Array($http->get($queryUrl)); 291 292 if (!$page) 293 $page = 1; 294 else 295 $page = $page; 296 297 if (array_key_exists(intval($resId), $aResponse)) 298 { 299 $quotes = $aResponse[intval($resId)]; 300 $n = 0; 301 foreach ($quotes as $key => $quote) 302 { 303 $n++; 304 if ($page == $n) 305 { 306 $resourceHtml[] = $quote; 307 break; 308 } 309 } 310 } 311 break; 312 } 313 } 314 315 return [$resourceId, $resourceHtml, $callmode]; 316 } 317 318 /** 319 * Handles the actual output creation. 320 * 321 * The function must not assume any other of the classes methods have been run 322 * during the object's current life. The only reliable data it receives are its 323 * parameters. 324 * 325 * The function should always check for the given output format and return false 326 * when a format isn't supported. 327 * 328 * $renderer contains a reference to the renderer object which is 329 * currently handling the rendering. You need to use it for writing 330 * the output. How this is done depends on the renderer used (specified 331 * by $format 332 * 333 * The contents of the $data array depends on what the handler() function above 334 * created 335 * 336 * @param string $format output format being rendered 337 * @param Doku_Renderer $renderer the current renderer object 338 * @param array $data data created by handler() 339 * @return boolean rendered correctly? (however, returned value is not used at the moment) 340 */ 341 public function render($format, Doku_Renderer $renderer, $data) 342 { 343 if ($format != 'xhtml') 344 { 345 return false; 346 } 347 348 if (is_array($data[1])) 349 { 350 $output = ""; 351 352 for ($i = 0; $i < count($data[1]); $i++) 353 { 354 $output .= $data[1][$i]; 355 } 356 357 $renderer->doc .= $output; 358 } 359 return true; 360 } 361 362 function WikindxparseText($resId, $baseurl, $text) 363 { 364 static $mem_text = []; 365 366 if (!array_key_exists($text, $mem_text)) 367 { 368 $queryUrl = rtrim($baseurl, "/") . "/cmsprint.php?action=parseText&bibStyle=" . $this->getConf('bibStyle'); 369 370 $http = new DokuHTTPClient(); 371 $resp = $http->post($queryUrl, ["text" => $text]); 372 $memresId[$resId] = $this->_CMSPHPResponse2Array($resp, true); 373 } 374 375 return $memresId[$resId]; 376 } 377 378 /** 379 * Decode an object or an array serialized with PHPH serialize(), and other data type 380 * 381 * Return a human-readable string representing $encodedData. If the decoding fails $encodedData is returned. 382 * 383 * @param mixed $EncodedResponse 384 * 385 * @return array 386 */ 387 private function _CMSPHPResponse2Array($EncodedResponse) 388 { 389 if ($EncodedResponse !== FALSE) 390 { 391 $array_serialized_pattern = '/^a:\d+:{.+/u'; 392 $object_serialized_pattern = '/^O:\d+:".+/u'; 393 394 if (preg_match($array_serialized_pattern, $EncodedResponse) > 0 || preg_match($object_serialized_pattern, $EncodedResponse) > 0) 395 { 396 $tmp = @unserialize($EncodedResponse); 397 if ($tmp !== FALSE) 398 { 399 return $tmp; 400 } 401 } 402 } 403 404 // Fallback on error 405 return []; 406 } 407 408 /** 409 * Simulate explode() for multibytes strings (as documented for PHP 7.0) 410 * 411 * @param string $delimiter 412 * @param string $string 413 * @param int $limit Default is PHP_INT_MAX. 414 * 415 * @return string 416 */ 417 function mb_explode($delimiter, $string, $limit = PHP_INT_MAX) 418 { 419 if ($delimiter == '') 420 { 421 return FALSE; 422 } 423 424 if ($limit === NULL) 425 { 426 PHP_INT_MAX; 427 } 428 if ($limit == 0) 429 { 430 $limit = 1; 431 } 432 433 $pattern = '/' . preg_quote($delimiter, '/') . '/u'; 434 435 $aString = preg_split($pattern, $string, $limit); 436 437 if ($limit < 0 && count($aString) == 1) 438 { 439 return []; 440 } 441 elseif ($limit < 0 && count($aString) > 1) 442 { 443 $length = count($aString) - abs($limit); 444 if ($length <= 0) 445 { 446 return []; 447 } 448 else 449 { 450 return array_slice($aString, 0, $length, TRUE); 451 } 452 } 453 else 454 { 455 return $aString; 456 } 457 } 458} 459 460//Setup VIM: ex: et ts=4 enc=utf-8 :