1<?php 2/** 3 * Renderer output base class 4 * 5 * @author Harry Fuecks <hfuecks@gmail.com> 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8 9use dokuwiki\Extension\Plugin; 10use dokuwiki\Extension\SyntaxPlugin; 11 12/** 13 * Allowed chars in $language for code highlighting 14 * @see GeSHi::set_language() 15 */ 16define('PREG_PATTERN_VALID_LANGUAGE', '#[^a-zA-Z0-9\-_]#'); 17 18/** 19 * An empty renderer, produces no output 20 * 21 * Inherits from dokuwiki\Plugin\DokuWiki_Plugin for giving additional functions to render plugins 22 * 23 * The renderer transforms the syntax instructions created by the parser and handler into the 24 * desired output format. For each instruction a corresponding method defined in this class will 25 * be called. That method needs to produce the desired output for the instruction and add it to the 26 * $doc field. When all instructions are processed, the $doc field contents will be cached by 27 * DokuWiki and sent to the user. 28 */ 29abstract class Doku_Renderer extends Plugin { 30 /** @var array Settings, control the behavior of the renderer */ 31 public $info = array( 32 'cache' => true, // may the rendered result cached? 33 'toc' => true, // render the TOC? 34 ); 35 36 /** @var array contains the smiley configuration, set in p_render() */ 37 public $smileys = array(); 38 /** @var array contains the entity configuration, set in p_render() */ 39 public $entities = array(); 40 /** @var array contains the acronym configuration, set in p_render() */ 41 public $acronyms = array(); 42 /** @var array contains the interwiki configuration, set in p_render() */ 43 public $interwiki = array(); 44 45 /** @var array the list of headers used to create unique link ids */ 46 protected $headers = array(); 47 48 /** 49 * @var string the rendered document, this will be cached after the renderer ran through 50 */ 51 public $doc = ''; 52 53 /** 54 * clean out any per-use values 55 * 56 * This is called before each use of the renderer object and should be used to 57 * completely reset the state of the renderer to be reused for a new document 58 */ 59 public function reset(){ 60 $this->headers = array(); 61 } 62 63 /** 64 * Allow the plugin to prevent DokuWiki from reusing an instance 65 * 66 * Since most renderer plugins fail to implement Doku_Renderer::reset() we default 67 * to reinstantiating the renderer here 68 * 69 * @return bool false if the plugin has to be instantiated 70 */ 71 public function isSingleton() { 72 return false; 73 } 74 75 /** 76 * Returns the format produced by this renderer. 77 * 78 * Has to be overidden by sub classes 79 * 80 * @return string 81 */ 82 abstract public function getFormat(); 83 84 /** 85 * Disable caching of this renderer's output 86 */ 87 public function nocache() { 88 $this->info['cache'] = false; 89 } 90 91 /** 92 * Disable TOC generation for this renderer's output 93 * 94 * This might not be used for certain sub renderer 95 */ 96 public function notoc() { 97 $this->info['toc'] = false; 98 } 99 100 /** 101 * Handle plugin rendering 102 * 103 * Most likely this needs NOT to be overwritten by sub classes 104 * 105 * @param string $name Plugin name 106 * @param mixed $data custom data set by handler 107 * @param string $state matched state if any 108 * @param string $match raw matched syntax 109 */ 110 public function plugin($name, $data, $state = '', $match = '') { 111 /** @var SyntaxPlugin $plugin */ 112 $plugin = plugin_load('syntax', $name); 113 if($plugin != null) { 114 $plugin->render($this->getFormat(), $this, $data); 115 } 116 } 117 118 /** 119 * handle nested render instructions 120 * this method (and nest_close method) should not be overloaded in actual renderer output classes 121 * 122 * @param array $instructions 123 */ 124 public function nest($instructions) { 125 foreach($instructions as $instruction) { 126 // execute the callback against ourself 127 if(method_exists($this, $instruction[0])) { 128 call_user_func_array(array($this, $instruction[0]), $instruction[1] ? $instruction[1] : array()); 129 } 130 } 131 } 132 133 /** 134 * dummy closing instruction issued by Doku_Handler_Nest 135 * 136 * normally the syntax mode should override this instruction when instantiating Doku_Handler_Nest - 137 * however plugins will not be able to - as their instructions require data. 138 */ 139 public function nest_close() { 140 } 141 142 #region Syntax modes - sub classes will need to implement them to fill $doc 143 144 /** 145 * Initialize the document 146 */ 147 public function document_start() { 148 } 149 150 /** 151 * Finalize the document 152 */ 153 public function document_end() { 154 } 155 156 /** 157 * Render the Table of Contents 158 * 159 * @return string 160 */ 161 public function render_TOC() { 162 return ''; 163 } 164 165 /** 166 * Add an item to the TOC 167 * 168 * @param string $id the hash link 169 * @param string $text the text to display 170 * @param int $level the nesting level 171 */ 172 public function toc_additem($id, $text, $level) { 173 } 174 175 /** 176 * Render a heading 177 * 178 * @param string $text the text to display 179 * @param int $level header level 180 * @param int $pos byte position in the original source 181 */ 182 public function header($text, $level, $pos) { 183 } 184 185 /** 186 * Open a new section 187 * 188 * @param int $level section level (as determined by the previous header) 189 */ 190 public function section_open($level) { 191 } 192 193 /** 194 * Close the current section 195 */ 196 public function section_close() { 197 } 198 199 /** 200 * Render plain text data 201 * 202 * @param string $text 203 */ 204 public function cdata($text) { 205 } 206 207 /** 208 * Open a paragraph 209 */ 210 public function p_open() { 211 } 212 213 /** 214 * Close a paragraph 215 */ 216 public function p_close() { 217 } 218 219 /** 220 * Create a line break 221 */ 222 public function linebreak() { 223 } 224 225 /** 226 * Create a horizontal line 227 */ 228 public function hr() { 229 } 230 231 /** 232 * Start strong (bold) formatting 233 */ 234 public function strong_open() { 235 } 236 237 /** 238 * Stop strong (bold) formatting 239 */ 240 public function strong_close() { 241 } 242 243 /** 244 * Start emphasis (italics) formatting 245 */ 246 public function emphasis_open() { 247 } 248 249 /** 250 * Stop emphasis (italics) formatting 251 */ 252 public function emphasis_close() { 253 } 254 255 /** 256 * Start underline formatting 257 */ 258 public function underline_open() { 259 } 260 261 /** 262 * Stop underline formatting 263 */ 264 public function underline_close() { 265 } 266 267 /** 268 * Start monospace formatting 269 */ 270 public function monospace_open() { 271 } 272 273 /** 274 * Stop monospace formatting 275 */ 276 public function monospace_close() { 277 } 278 279 /** 280 * Start a subscript 281 */ 282 public function subscript_open() { 283 } 284 285 /** 286 * Stop a subscript 287 */ 288 public function subscript_close() { 289 } 290 291 /** 292 * Start a superscript 293 */ 294 public function superscript_open() { 295 } 296 297 /** 298 * Stop a superscript 299 */ 300 public function superscript_close() { 301 } 302 303 /** 304 * Start deleted (strike-through) formatting 305 */ 306 public function deleted_open() { 307 } 308 309 /** 310 * Stop deleted (strike-through) formatting 311 */ 312 public function deleted_close() { 313 } 314 315 /** 316 * Start a footnote 317 */ 318 public function footnote_open() { 319 } 320 321 /** 322 * Stop a footnote 323 */ 324 public function footnote_close() { 325 } 326 327 /** 328 * Open an unordered list 329 */ 330 public function listu_open() { 331 } 332 333 /** 334 * Close an unordered list 335 */ 336 public function listu_close() { 337 } 338 339 /** 340 * Open an ordered list 341 */ 342 public function listo_open() { 343 } 344 345 /** 346 * Close an ordered list 347 */ 348 public function listo_close() { 349 } 350 351 /** 352 * Open a list item 353 * 354 * @param int $level the nesting level 355 * @param bool $node true when a node; false when a leaf 356 */ 357 public function listitem_open($level,$node=false) { 358 } 359 360 /** 361 * Close a list item 362 */ 363 public function listitem_close() { 364 } 365 366 /** 367 * Start the content of a list item 368 */ 369 public function listcontent_open() { 370 } 371 372 /** 373 * Stop the content of a list item 374 */ 375 public function listcontent_close() { 376 } 377 378 /** 379 * Output unformatted $text 380 * 381 * Defaults to $this->cdata() 382 * 383 * @param string $text 384 */ 385 public function unformatted($text) { 386 $this->cdata($text); 387 } 388 389 /** 390 * Output inline PHP code 391 * 392 * If $conf['phpok'] is true this should evaluate the given code and append the result 393 * to $doc 394 * 395 * @param string $text The PHP code 396 */ 397 public function php($text) { 398 } 399 400 /** 401 * Output block level PHP code 402 * 403 * If $conf['phpok'] is true this should evaluate the given code and append the result 404 * to $doc 405 * 406 * @param string $text The PHP code 407 */ 408 public function phpblock($text) { 409 } 410 411 /** 412 * Output raw inline HTML 413 * 414 * If $conf['htmlok'] is true this should add the code as is to $doc 415 * 416 * @param string $text The HTML 417 */ 418 public function html($text) { 419 } 420 421 /** 422 * Output raw block-level HTML 423 * 424 * If $conf['htmlok'] is true this should add the code as is to $doc 425 * 426 * @param string $text The HTML 427 */ 428 public function htmlblock($text) { 429 } 430 431 /** 432 * Output preformatted text 433 * 434 * @param string $text 435 */ 436 public function preformatted($text) { 437 } 438 439 /** 440 * Start a block quote 441 */ 442 public function quote_open() { 443 } 444 445 /** 446 * Stop a block quote 447 */ 448 public function quote_close() { 449 } 450 451 /** 452 * Display text as file content, optionally syntax highlighted 453 * 454 * @param string $text text to show 455 * @param string $lang programming language to use for syntax highlighting 456 * @param string $file file path label 457 */ 458 public function file($text, $lang = null, $file = null) { 459 } 460 461 /** 462 * Display text as code content, optionally syntax highlighted 463 * 464 * @param string $text text to show 465 * @param string $lang programming language to use for syntax highlighting 466 * @param string $file file path label 467 */ 468 public function code($text, $lang = null, $file = null) { 469 } 470 471 /** 472 * Format an acronym 473 * 474 * Uses $this->acronyms 475 * 476 * @param string $acronym 477 */ 478 public function acronym($acronym) { 479 } 480 481 /** 482 * Format a smiley 483 * 484 * Uses $this->smiley 485 * 486 * @param string $smiley 487 */ 488 public function smiley($smiley) { 489 } 490 491 /** 492 * Format an entity 493 * 494 * Entities are basically small text replacements 495 * 496 * Uses $this->entities 497 * 498 * @param string $entity 499 */ 500 public function entity($entity) { 501 } 502 503 /** 504 * Typographically format a multiply sign 505 * 506 * Example: ($x=640, $y=480) should result in "640×480" 507 * 508 * @param string|int $x first value 509 * @param string|int $y second value 510 */ 511 public function multiplyentity($x, $y) { 512 } 513 514 /** 515 * Render an opening single quote char (language specific) 516 */ 517 public function singlequoteopening() { 518 } 519 520 /** 521 * Render a closing single quote char (language specific) 522 */ 523 public function singlequoteclosing() { 524 } 525 526 /** 527 * Render an apostrophe char (language specific) 528 */ 529 public function apostrophe() { 530 } 531 532 /** 533 * Render an opening double quote char (language specific) 534 */ 535 public function doublequoteopening() { 536 } 537 538 /** 539 * Render an closinging double quote char (language specific) 540 */ 541 public function doublequoteclosing() { 542 } 543 544 /** 545 * Render a CamelCase link 546 * 547 * @param string $link The link name 548 * @see http://en.wikipedia.org/wiki/CamelCase 549 */ 550 public function camelcaselink($link) { 551 } 552 553 /** 554 * Render a page local link 555 * 556 * @param string $hash hash link identifier 557 * @param string $name name for the link 558 */ 559 public function locallink($hash, $name = null) { 560 } 561 562 /** 563 * Render a wiki internal link 564 * 565 * @param string $link page ID to link to. eg. 'wiki:syntax' 566 * @param string|array $title name for the link, array for media file 567 */ 568 public function internallink($link, $title = null) { 569 } 570 571 /** 572 * Render an external link 573 * 574 * @param string $link full URL with scheme 575 * @param string|array $title name for the link, array for media file 576 */ 577 public function externallink($link, $title = null) { 578 } 579 580 /** 581 * Render the output of an RSS feed 582 * 583 * @param string $url URL of the feed 584 * @param array $params Finetuning of the output 585 */ 586 public function rss($url, $params) { 587 } 588 589 /** 590 * Render an interwiki link 591 * 592 * You may want to use $this->_resolveInterWiki() here 593 * 594 * @param string $link original link - probably not much use 595 * @param string|array $title name for the link, array for media file 596 * @param string $wikiName indentifier (shortcut) for the remote wiki 597 * @param string $wikiUri the fragment parsed from the original link 598 */ 599 public function interwikilink($link, $title, $wikiName, $wikiUri) { 600 } 601 602 /** 603 * Link to file on users OS 604 * 605 * @param string $link the link 606 * @param string|array $title name for the link, array for media file 607 */ 608 public function filelink($link, $title = null) { 609 } 610 611 /** 612 * Link to windows share 613 * 614 * @param string $link the link 615 * @param string|array $title name for the link, array for media file 616 */ 617 public function windowssharelink($link, $title = null) { 618 } 619 620 /** 621 * Render a linked E-Mail Address 622 * 623 * Should honor $conf['mailguard'] setting 624 * 625 * @param string $address Email-Address 626 * @param string|array $name name for the link, array for media file 627 */ 628 public function emaillink($address, $name = null) { 629 } 630 631 /** 632 * Render an internal media file 633 * 634 * @param string $src media ID 635 * @param string $title descriptive text 636 * @param string $align left|center|right 637 * @param int $width width of media in pixel 638 * @param int $height height of media in pixel 639 * @param string $cache cache|recache|nocache 640 * @param string $linking linkonly|detail|nolink 641 */ 642 public function internalmedia($src, $title = null, $align = null, $width = null, 643 $height = null, $cache = null, $linking = null) { 644 } 645 646 /** 647 * Render an external media file 648 * 649 * @param string $src full media URL 650 * @param string $title descriptive text 651 * @param string $align left|center|right 652 * @param int $width width of media in pixel 653 * @param int $height height of media in pixel 654 * @param string $cache cache|recache|nocache 655 * @param string $linking linkonly|detail|nolink 656 */ 657 public function externalmedia($src, $title = null, $align = null, $width = null, 658 $height = null, $cache = null, $linking = null) { 659 } 660 661 /** 662 * Render a link to an internal media file 663 * 664 * @param string $src media ID 665 * @param string $title descriptive text 666 * @param string $align left|center|right 667 * @param int $width width of media in pixel 668 * @param int $height height of media in pixel 669 * @param string $cache cache|recache|nocache 670 */ 671 public function internalmedialink($src, $title = null, $align = null, 672 $width = null, $height = null, $cache = null) { 673 } 674 675 /** 676 * Render a link to an external media file 677 * 678 * @param string $src media ID 679 * @param string $title descriptive text 680 * @param string $align left|center|right 681 * @param int $width width of media in pixel 682 * @param int $height height of media in pixel 683 * @param string $cache cache|recache|nocache 684 */ 685 public function externalmedialink($src, $title = null, $align = null, 686 $width = null, $height = null, $cache = null) { 687 } 688 689 /** 690 * Start a table 691 * 692 * @param int $maxcols maximum number of columns 693 * @param int $numrows NOT IMPLEMENTED 694 * @param int $pos byte position in the original source 695 */ 696 public function table_open($maxcols = null, $numrows = null, $pos = null) { 697 } 698 699 /** 700 * Close a table 701 * 702 * @param int $pos byte position in the original source 703 */ 704 public function table_close($pos = null) { 705 } 706 707 /** 708 * Open a table header 709 */ 710 public function tablethead_open() { 711 } 712 713 /** 714 * Close a table header 715 */ 716 public function tablethead_close() { 717 } 718 719 /** 720 * Open a table body 721 */ 722 public function tabletbody_open() { 723 } 724 725 /** 726 * Close a table body 727 */ 728 public function tabletbody_close() { 729 } 730 731 /** 732 * Open a table footer 733 */ 734 public function tabletfoot_open() { 735 } 736 737 /** 738 * Close a table footer 739 */ 740 public function tabletfoot_close() { 741 } 742 743 /** 744 * Open a table row 745 */ 746 public function tablerow_open() { 747 } 748 749 /** 750 * Close a table row 751 */ 752 public function tablerow_close() { 753 } 754 755 /** 756 * Open a table header cell 757 * 758 * @param int $colspan 759 * @param string $align left|center|right 760 * @param int $rowspan 761 */ 762 public function tableheader_open($colspan = 1, $align = null, $rowspan = 1) { 763 } 764 765 /** 766 * Close a table header cell 767 */ 768 public function tableheader_close() { 769 } 770 771 /** 772 * Open a table cell 773 * 774 * @param int $colspan 775 * @param string $align left|center|right 776 * @param int $rowspan 777 */ 778 public function tablecell_open($colspan = 1, $align = null, $rowspan = 1) { 779 } 780 781 /** 782 * Close a table cell 783 */ 784 public function tablecell_close() { 785 } 786 787 #endregion 788 789 #region util functions, you probably won't need to reimplement them 790 791 /** 792 * Creates a linkid from a headline 793 * 794 * @author Andreas Gohr <andi@splitbrain.org> 795 * @param string $title The headline title 796 * @param boolean $create Create a new unique ID? 797 * @return string 798 */ 799 public function _headerToLink($title, $create = false) { 800 if($create) { 801 return sectionID($title, $this->headers); 802 } else { 803 $check = false; 804 return sectionID($title, $check); 805 } 806 } 807 808 /** 809 * Removes any Namespace from the given name but keeps 810 * casing and special chars 811 * 812 * @author Andreas Gohr <andi@splitbrain.org> 813 * 814 * @param string $name 815 * @return string 816 */ 817 protected function _simpleTitle($name) { 818 global $conf; 819 820 //if there is a hash we use the ancor name only 821 @list($name, $hash) = explode('#', $name, 2); 822 if($hash) return $hash; 823 824 if($conf['useslash']) { 825 $name = strtr($name, ';/', ';:'); 826 } else { 827 $name = strtr($name, ';', ':'); 828 } 829 830 return noNSorNS($name); 831 } 832 833 /** 834 * Resolve an interwikilink 835 * 836 * @param string $shortcut identifier for the interwiki link 837 * @param string $reference fragment that refers the content 838 * @param null|bool $exists reference which returns if an internal page exists 839 * @return string interwikilink 840 */ 841 public function _resolveInterWiki(&$shortcut, $reference, &$exists = null) { 842 //get interwiki URL 843 if(isset($this->interwiki[$shortcut])) { 844 $url = $this->interwiki[$shortcut]; 845 } else { 846 // Default to Google I'm feeling lucky 847 $url = 'https://www.google.com/search?q={URL}&btnI=lucky'; 848 $shortcut = 'go'; 849 } 850 851 //split into hash and url part 852 $hash = strrchr($reference, '#'); 853 if($hash) { 854 $reference = substr($reference, 0, -strlen($hash)); 855 $hash = substr($hash, 1); 856 } 857 858 //replace placeholder 859 if(preg_match('#\{(URL|NAME|SCHEME|HOST|PORT|PATH|QUERY)\}#', $url)) { 860 //use placeholders 861 $url = str_replace('{URL}', rawurlencode($reference), $url); 862 //wiki names will be cleaned next, otherwise urlencode unsafe chars 863 $url = str_replace('{NAME}', ($url{0} === ':') ? $reference : 864 preg_replace_callback('/[[\\\\\]^`{|}#%]/', function($match) { 865 return rawurlencode($match[0]); 866 }, $reference), $url); 867 $parsed = parse_url($reference); 868 if (empty($parsed['scheme'])) $parsed['scheme'] = ''; 869 if (empty($parsed['host'])) $parsed['host'] = ''; 870 if (empty($parsed['port'])) $parsed['port'] = 80; 871 if (empty($parsed['path'])) $parsed['path'] = ''; 872 if (empty($parsed['query'])) $parsed['query'] = ''; 873 $url = strtr($url,[ 874 '{SCHEME}' => $parsed['scheme'], 875 '{HOST}' => $parsed['host'], 876 '{PORT}' => $parsed['port'], 877 '{PATH}' => $parsed['path'], 878 '{QUERY}' => $parsed['query'] , 879 ]); 880 } else { 881 //default 882 $url = $url.rawurlencode($reference); 883 } 884 //handle as wiki links 885 if($url{0} === ':') { 886 $urlparam = null; 887 $id = $url; 888 if (strpos($url, '?') !== false) { 889 list($id, $urlparam) = explode('?', $url, 2); 890 } 891 $url = wl(cleanID($id), $urlparam); 892 $exists = page_exists($id); 893 } 894 if($hash) $url .= '#'.rawurlencode($hash); 895 896 return $url; 897 } 898 899 #endregion 900} 901 902 903//Setup VIM: ex: et ts=4 : 904