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 preformatted text 394 * 395 * @param string $text 396 */ 397 public function preformatted($text) { 398 } 399 400 /** 401 * Start a block quote 402 */ 403 public function quote_open() { 404 } 405 406 /** 407 * Stop a block quote 408 */ 409 public function quote_close() { 410 } 411 412 /** 413 * Display text as file content, optionally syntax highlighted 414 * 415 * @param string $text text to show 416 * @param string $lang programming language to use for syntax highlighting 417 * @param string $file file path label 418 */ 419 public function file($text, $lang = null, $file = null) { 420 } 421 422 /** 423 * Display text as code content, optionally syntax highlighted 424 * 425 * @param string $text text to show 426 * @param string $lang programming language to use for syntax highlighting 427 * @param string $file file path label 428 */ 429 public function code($text, $lang = null, $file = null) { 430 } 431 432 /** 433 * Format an acronym 434 * 435 * Uses $this->acronyms 436 * 437 * @param string $acronym 438 */ 439 public function acronym($acronym) { 440 } 441 442 /** 443 * Format a smiley 444 * 445 * Uses $this->smiley 446 * 447 * @param string $smiley 448 */ 449 public function smiley($smiley) { 450 } 451 452 /** 453 * Format an entity 454 * 455 * Entities are basically small text replacements 456 * 457 * Uses $this->entities 458 * 459 * @param string $entity 460 */ 461 public function entity($entity) { 462 } 463 464 /** 465 * Typographically format a multiply sign 466 * 467 * Example: ($x=640, $y=480) should result in "640×480" 468 * 469 * @param string|int $x first value 470 * @param string|int $y second value 471 */ 472 public function multiplyentity($x, $y) { 473 } 474 475 /** 476 * Render an opening single quote char (language specific) 477 */ 478 public function singlequoteopening() { 479 } 480 481 /** 482 * Render a closing single quote char (language specific) 483 */ 484 public function singlequoteclosing() { 485 } 486 487 /** 488 * Render an apostrophe char (language specific) 489 */ 490 public function apostrophe() { 491 } 492 493 /** 494 * Render an opening double quote char (language specific) 495 */ 496 public function doublequoteopening() { 497 } 498 499 /** 500 * Render an closinging double quote char (language specific) 501 */ 502 public function doublequoteclosing() { 503 } 504 505 /** 506 * Render a CamelCase link 507 * 508 * @param string $link The link name 509 * @see http://en.wikipedia.org/wiki/CamelCase 510 */ 511 public function camelcaselink($link) { 512 } 513 514 /** 515 * Render a page local link 516 * 517 * @param string $hash hash link identifier 518 * @param string $name name for the link 519 */ 520 public function locallink($hash, $name = null) { 521 } 522 523 /** 524 * Render a wiki internal link 525 * 526 * @param string $link page ID to link to. eg. 'wiki:syntax' 527 * @param string|array $title name for the link, array for media file 528 */ 529 public function internallink($link, $title = null) { 530 } 531 532 /** 533 * Render an external link 534 * 535 * @param string $link full URL with scheme 536 * @param string|array $title name for the link, array for media file 537 */ 538 public function externallink($link, $title = null) { 539 } 540 541 /** 542 * Render the output of an RSS feed 543 * 544 * @param string $url URL of the feed 545 * @param array $params Finetuning of the output 546 */ 547 public function rss($url, $params) { 548 } 549 550 /** 551 * Render an interwiki link 552 * 553 * You may want to use $this->_resolveInterWiki() here 554 * 555 * @param string $link original link - probably not much use 556 * @param string|array $title name for the link, array for media file 557 * @param string $wikiName indentifier (shortcut) for the remote wiki 558 * @param string $wikiUri the fragment parsed from the original link 559 */ 560 public function interwikilink($link, $title, $wikiName, $wikiUri) { 561 } 562 563 /** 564 * Link to file on users OS 565 * 566 * @param string $link the link 567 * @param string|array $title name for the link, array for media file 568 */ 569 public function filelink($link, $title = null) { 570 } 571 572 /** 573 * Link to windows share 574 * 575 * @param string $link the link 576 * @param string|array $title name for the link, array for media file 577 */ 578 public function windowssharelink($link, $title = null) { 579 } 580 581 /** 582 * Render a linked E-Mail Address 583 * 584 * Should honor $conf['mailguard'] setting 585 * 586 * @param string $address Email-Address 587 * @param string|array $name name for the link, array for media file 588 */ 589 public function emaillink($address, $name = null) { 590 } 591 592 /** 593 * Render an internal media file 594 * 595 * @param string $src media ID 596 * @param string $title descriptive text 597 * @param string $align left|center|right 598 * @param int $width width of media in pixel 599 * @param int $height height of media in pixel 600 * @param string $cache cache|recache|nocache 601 * @param string $linking linkonly|detail|nolink 602 */ 603 public function internalmedia($src, $title = null, $align = null, $width = null, 604 $height = null, $cache = null, $linking = null) { 605 } 606 607 /** 608 * Render an external media file 609 * 610 * @param string $src full media URL 611 * @param string $title descriptive text 612 * @param string $align left|center|right 613 * @param int $width width of media in pixel 614 * @param int $height height of media in pixel 615 * @param string $cache cache|recache|nocache 616 * @param string $linking linkonly|detail|nolink 617 */ 618 public function externalmedia($src, $title = null, $align = null, $width = null, 619 $height = null, $cache = null, $linking = null) { 620 } 621 622 /** 623 * Render a link to an internal media file 624 * 625 * @param string $src media ID 626 * @param string $title descriptive text 627 * @param string $align left|center|right 628 * @param int $width width of media in pixel 629 * @param int $height height of media in pixel 630 * @param string $cache cache|recache|nocache 631 */ 632 public function internalmedialink($src, $title = null, $align = null, 633 $width = null, $height = null, $cache = null) { 634 } 635 636 /** 637 * Render a link to an external media file 638 * 639 * @param string $src media ID 640 * @param string $title descriptive text 641 * @param string $align left|center|right 642 * @param int $width width of media in pixel 643 * @param int $height height of media in pixel 644 * @param string $cache cache|recache|nocache 645 */ 646 public function externalmedialink($src, $title = null, $align = null, 647 $width = null, $height = null, $cache = null) { 648 } 649 650 /** 651 * Start a table 652 * 653 * @param int $maxcols maximum number of columns 654 * @param int $numrows NOT IMPLEMENTED 655 * @param int $pos byte position in the original source 656 */ 657 public function table_open($maxcols = null, $numrows = null, $pos = null) { 658 } 659 660 /** 661 * Close a table 662 * 663 * @param int $pos byte position in the original source 664 */ 665 public function table_close($pos = null) { 666 } 667 668 /** 669 * Open a table header 670 */ 671 public function tablethead_open() { 672 } 673 674 /** 675 * Close a table header 676 */ 677 public function tablethead_close() { 678 } 679 680 /** 681 * Open a table body 682 */ 683 public function tabletbody_open() { 684 } 685 686 /** 687 * Close a table body 688 */ 689 public function tabletbody_close() { 690 } 691 692 /** 693 * Open a table footer 694 */ 695 public function tabletfoot_open() { 696 } 697 698 /** 699 * Close a table footer 700 */ 701 public function tabletfoot_close() { 702 } 703 704 /** 705 * Open a table row 706 */ 707 public function tablerow_open() { 708 } 709 710 /** 711 * Close a table row 712 */ 713 public function tablerow_close() { 714 } 715 716 /** 717 * Open a table header cell 718 * 719 * @param int $colspan 720 * @param string $align left|center|right 721 * @param int $rowspan 722 */ 723 public function tableheader_open($colspan = 1, $align = null, $rowspan = 1) { 724 } 725 726 /** 727 * Close a table header cell 728 */ 729 public function tableheader_close() { 730 } 731 732 /** 733 * Open a table cell 734 * 735 * @param int $colspan 736 * @param string $align left|center|right 737 * @param int $rowspan 738 */ 739 public function tablecell_open($colspan = 1, $align = null, $rowspan = 1) { 740 } 741 742 /** 743 * Close a table cell 744 */ 745 public function tablecell_close() { 746 } 747 748 #endregion 749 750 #region util functions, you probably won't need to reimplement them 751 752 /** 753 * Creates a linkid from a headline 754 * 755 * @author Andreas Gohr <andi@splitbrain.org> 756 * @param string $title The headline title 757 * @param boolean $create Create a new unique ID? 758 * @return string 759 */ 760 public function _headerToLink($title, $create = false) { 761 if($create) { 762 return sectionID($title, $this->headers); 763 } else { 764 $check = false; 765 return sectionID($title, $check); 766 } 767 } 768 769 /** 770 * Removes any Namespace from the given name but keeps 771 * casing and special chars 772 * 773 * @author Andreas Gohr <andi@splitbrain.org> 774 * 775 * @param string $name 776 * @return string 777 */ 778 public function _simpleTitle($name) { 779 global $conf; 780 781 //if there is a hash we use the ancor name only 782 list($name, $hash) = sexplode('#', $name, 2); 783 if($hash) return $hash; 784 785 if($conf['useslash']) { 786 $name = strtr($name, ';/', ';:'); 787 } else { 788 $name = strtr($name, ';', ':'); 789 } 790 791 return noNSorNS($name); 792 } 793 794 /** 795 * Resolve an interwikilink 796 * 797 * @param string $shortcut identifier for the interwiki link 798 * @param string $reference fragment that refers the content 799 * @param null|bool $exists reference which returns if an internal page exists 800 * @return string interwikilink 801 */ 802 public function _resolveInterWiki(&$shortcut, $reference, &$exists = null) { 803 //get interwiki URL 804 if(isset($this->interwiki[$shortcut])) { 805 $url = $this->interwiki[$shortcut]; 806 }elseif(isset($this->interwiki['default'])) { 807 $shortcut = 'default'; 808 $url = $this->interwiki[$shortcut]; 809 }else{ 810 // not parsable interwiki outputs '' to make sure string manipluation works 811 $shortcut = ''; 812 $url = ''; 813 } 814 815 //split into hash and url part 816 $hash = strrchr($reference, '#'); 817 if($hash) { 818 $reference = substr($reference, 0, -strlen($hash)); 819 $hash = substr($hash, 1); 820 } 821 822 //replace placeholder 823 if(preg_match('#\{(URL|NAME|SCHEME|HOST|PORT|PATH|QUERY)\}#', $url)) { 824 //use placeholders 825 $url = str_replace('{URL}', rawurlencode($reference), $url); 826 //wiki names will be cleaned next, otherwise urlencode unsafe chars 827 $url = str_replace('{NAME}', ($url[0] === ':') ? $reference : 828 preg_replace_callback('/[[\\\\\]^`{|}#%]/', function($match) { 829 return rawurlencode($match[0]); 830 }, $reference), $url); 831 $parsed = parse_url($reference); 832 if (empty($parsed['scheme'])) $parsed['scheme'] = ''; 833 if (empty($parsed['host'])) $parsed['host'] = ''; 834 if (empty($parsed['port'])) $parsed['port'] = 80; 835 if (empty($parsed['path'])) $parsed['path'] = ''; 836 if (empty($parsed['query'])) $parsed['query'] = ''; 837 $url = strtr($url,[ 838 '{SCHEME}' => $parsed['scheme'], 839 '{HOST}' => $parsed['host'], 840 '{PORT}' => $parsed['port'], 841 '{PATH}' => $parsed['path'], 842 '{QUERY}' => $parsed['query'] , 843 ]); 844 } else if($url != '') { 845 // make sure when no url is defined, we keep it null 846 // default 847 $url = $url.rawurlencode($reference); 848 } 849 //handle as wiki links 850 if($url && $url[0] === ':') { 851 $urlparam = null; 852 $id = $url; 853 if (strpos($url, '?') !== false) { 854 list($id, $urlparam) = sexplode('?', $url, 2, ''); 855 } 856 $url = wl(cleanID($id), $urlparam); 857 $exists = page_exists($id); 858 } 859 if($hash) $url .= '#'.rawurlencode($hash); 860 861 return $url; 862 } 863 864 #endregion 865} 866 867 868//Setup VIM: ex: et ts=4 : 869