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