1<?php 2/** 3 * Renderer output base class 4 * 5 * @author Harry Fuecks <hfuecks@gmail.com> 6 * @author Andreas Gohr <andi@splitbrain.org> 7 */ 8if(!defined('DOKU_INC')) die('meh.'); 9 10/** 11 * Allowed chars in $language for code highlighting 12 * @see GeSHi::set_language() 13 */ 14define('PREG_PATTERN_VALID_LANGUAGE', '#[^a-zA-Z0-9\-_]#'); 15 16/** 17 * An empty renderer, produces no output 18 * 19 * Inherits from DokuWiki_Plugin for giving additional functions to render plugins 20 * 21 * The renderer transforms the syntax instructions created by the parser and handler into the 22 * desired output format. For each instruction a corresponding method defined in this class will 23 * be called. That method needs to produce the desired output for the instruction and add it to the 24 * $doc field. When all instructions are processed, the $doc field contents will be cached by 25 * DokuWiki and sent to the user. 26 */ 27class Doku_Renderer extends DokuWiki_Plugin { 28 /** @var array Settings, control the behavior of the renderer */ 29 public $info = array( 30 'cache' => true, // may the rendered result cached? 31 'toc' => true, // render the TOC? 32 ); 33 34 /** @var array contains the smiley configuration, set in p_render() */ 35 public $smileys = array(); 36 /** @var array contains the entity configuration, set in p_render() */ 37 public $entities = array(); 38 /** @var array contains the acronym configuration, set in p_render() */ 39 public $acronyms = array(); 40 /** @var array contains the interwiki configuration, set in p_render() */ 41 public $interwiki = array(); 42 43 /** 44 * @var string the rendered document, this will be cached after the renderer ran through 45 */ 46 public $doc = ''; 47 48 /** 49 * clean out any per-use values 50 * 51 * This is called before each use of the renderer object and should be used to 52 * completely reset the state of the renderer to be reused for a new document 53 */ 54 function reset() { 55 $this->doc = ''; 56 $this->info['cache'] = true; 57 $this->info['toc'] = true; 58 } 59 60 /** 61 * Allow the plugin to prevent DokuWiki from reusing an instance 62 * 63 * Since most renderer plugins fail to implement Doku_Renderer::reset() we default 64 * to reinstantiating the renderer here 65 * 66 * @return bool false if the plugin has to be instantiated 67 */ 68 function isSingleton() { 69 return false; 70 } 71 72 /** 73 * Returns the format produced by this renderer. 74 * 75 * Has to be overidden by sub classes 76 * 77 * @return string 78 */ 79 function getFormat() { 80 trigger_error('getFormat() not implemented in '.get_class($this), E_USER_WARNING); 81 return ''; 82 } 83 84 /** 85 * Disable caching of this renderer's output 86 */ 87 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 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 function plugin($name, $data, $state = '', $match = '') { 111 /** @var DokuWiki_Syntax_Plugin $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 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 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 function document_start() { 148 } 149 150 /** 151 * Finalize the document 152 */ 153 function document_end() { 154 } 155 156 /** 157 * Render the Table of Contents 158 * 159 * @return string 160 */ 161 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 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 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 function section_open($level) { 191 } 192 193 /** 194 * Close the current section 195 */ 196 function section_close() { 197 } 198 199 /** 200 * Render plain text data 201 * 202 * @param string $text 203 */ 204 function cdata($text) { 205 } 206 207 /** 208 * Open a paragraph 209 */ 210 function p_open() { 211 } 212 213 /** 214 * Close a paragraph 215 */ 216 function p_close() { 217 } 218 219 /** 220 * Create a line break 221 */ 222 function linebreak() { 223 } 224 225 /** 226 * Create a horizontal line 227 */ 228 function hr() { 229 } 230 231 /** 232 * Start strong (bold) formatting 233 */ 234 function strong_open() { 235 } 236 237 /** 238 * Stop strong (bold) formatting 239 */ 240 function strong_close() { 241 } 242 243 /** 244 * Start emphasis (italics) formatting 245 */ 246 function emphasis_open() { 247 } 248 249 /** 250 * Stop emphasis (italics) formatting 251 */ 252 function emphasis_close() { 253 } 254 255 /** 256 * Start underline formatting 257 */ 258 function underline_open() { 259 } 260 261 /** 262 * Stop underline formatting 263 */ 264 function underline_close() { 265 } 266 267 /** 268 * Start monospace formatting 269 */ 270 function monospace_open() { 271 } 272 273 /** 274 * Stop monospace formatting 275 */ 276 function monospace_close() { 277 } 278 279 /** 280 * Start a subscript 281 */ 282 function subscript_open() { 283 } 284 285 /** 286 * Stop a subscript 287 */ 288 function subscript_close() { 289 } 290 291 /** 292 * Start a superscript 293 */ 294 function superscript_open() { 295 } 296 297 /** 298 * Stop a superscript 299 */ 300 function superscript_close() { 301 } 302 303 /** 304 * Start deleted (strike-through) formatting 305 */ 306 function deleted_open() { 307 } 308 309 /** 310 * Stop deleted (strike-through) formatting 311 */ 312 function deleted_close() { 313 } 314 315 /** 316 * Start a footnote 317 */ 318 function footnote_open() { 319 } 320 321 /** 322 * Stop a footnote 323 */ 324 function footnote_close() { 325 } 326 327 /** 328 * Open an unordered list 329 */ 330 function listu_open() { 331 } 332 333 /** 334 * Close an unordered list 335 */ 336 function listu_close() { 337 } 338 339 /** 340 * Open an ordered list 341 */ 342 function listo_open() { 343 } 344 345 /** 346 * Close an ordered list 347 */ 348 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 function listitem_open($level,$node=false) { 358 } 359 360 /** 361 * Close a list item 362 */ 363 function listitem_close() { 364 } 365 366 /** 367 * Start the content of a list item 368 */ 369 function listcontent_open() { 370 } 371 372 /** 373 * Stop the content of a list item 374 */ 375 function listcontent_close() { 376 } 377 378 /** 379 * Output unformatted $text 380 * 381 * Defaults to $this->cdata() 382 * 383 * @param string $text 384 */ 385 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 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 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 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 function htmlblock($text) { 429 } 430 431 /** 432 * Output preformatted text 433 * 434 * @param string $text 435 */ 436 function preformatted($text) { 437 } 438 439 /** 440 * Start a block quote 441 */ 442 function quote_open() { 443 } 444 445 /** 446 * Stop a block quote 447 */ 448 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 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 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 function acronym($acronym) { 479 } 480 481 /** 482 * Format a smiley 483 * 484 * Uses $this->smiley 485 * 486 * @param string $smiley 487 */ 488 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 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 function multiplyentity($x, $y) { 512 } 513 514 /** 515 * Render an opening single quote char (language specific) 516 */ 517 function singlequoteopening() { 518 } 519 520 /** 521 * Render a closing single quote char (language specific) 522 */ 523 function singlequoteclosing() { 524 } 525 526 /** 527 * Render an apostrophe char (language specific) 528 */ 529 function apostrophe() { 530 } 531 532 /** 533 * Render an opening double quote char (language specific) 534 */ 535 function doublequoteopening() { 536 } 537 538 /** 539 * Render an closinging double quote char (language specific) 540 */ 541 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 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 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 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 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 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 function interwikilink($link, $title = null, $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 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 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 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 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 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 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 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 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 function table_close($pos = null) { 705 } 706 707 /** 708 * Open a table header 709 */ 710 function tablethead_open() { 711 } 712 713 /** 714 * Close a table header 715 */ 716 function tablethead_close() { 717 } 718 719 /** 720 * Open a table body 721 */ 722 function tabletbody_open() { 723 } 724 725 /** 726 * Close a table body 727 */ 728 function tabletbody_close() { 729 } 730 731 /** 732 * Open a table footer 733 */ 734 function tabletfoot_open() { 735 } 736 737 /** 738 * Close a table footer 739 */ 740 function tabletfoot_close() { 741 } 742 743 /** 744 * Open a table row 745 */ 746 function tablerow_open() { 747 } 748 749 /** 750 * Close a table row 751 */ 752 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 function tableheader_open($colspan = 1, $align = null, $rowspan = 1) { 763 } 764 765 /** 766 * Close a table header cell 767 */ 768 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 function tablecell_open($colspan = 1, $align = null, $rowspan = 1) { 779 } 780 781 /** 782 * Close a table cell 783 */ 784 function tablecell_close() { 785 } 786 787 #endregion 788 789 #region util functions, you probably won't need to reimplement them 790 791 /** 792 * Removes any Namespace from the given name but keeps 793 * casing and special chars 794 * 795 * @author Andreas Gohr <andi@splitbrain.org> 796 * 797 * @param string $name 798 * @return string 799 */ 800 function _simpleTitle($name) { 801 global $conf; 802 803 //if there is a hash we use the ancor name only 804 @list($name, $hash) = explode('#', $name, 2); 805 if($hash) return $hash; 806 807 if($conf['useslash']) { 808 $name = strtr($name, ';/', ';:'); 809 } else { 810 $name = strtr($name, ';', ':'); 811 } 812 813 return noNSorNS($name); 814 } 815 816 /** 817 * Resolve an interwikilink 818 * 819 * @param string $shortcut identifier for the interwiki link 820 * @param string $reference fragment that refers the content 821 * @param null|bool $exists reference which returns if an internal page exists 822 * @return string interwikilink 823 */ 824 function _resolveInterWiki(&$shortcut, $reference, &$exists = null) { 825 //get interwiki URL 826 if(isset($this->interwiki[$shortcut])) { 827 $url = $this->interwiki[$shortcut]; 828 } else { 829 // Default to Google I'm feeling lucky 830 $url = 'https://www.google.com/search?q={URL}&btnI=lucky'; 831 $shortcut = 'go'; 832 } 833 834 //split into hash and url part 835 $hash = strrchr($reference, '#'); 836 if($hash) { 837 $reference = substr($reference, 0, -strlen($hash)); 838 $hash = substr($hash, 1); 839 } 840 841 //replace placeholder 842 if(preg_match('#\{(URL|NAME|SCHEME|HOST|PORT|PATH|QUERY)\}#', $url)) { 843 //use placeholders 844 $url = str_replace('{URL}', rawurlencode($reference), $url); 845 //wiki names will be cleaned next, otherwise urlencode unsafe chars 846 $url = str_replace('{NAME}', ($url{0} === ':') ? $reference : 847 preg_replace_callback('/[[\\\\\]^`{|}#%]/', function($match) { 848 return rawurlencode($match[0]); 849 }, $reference), $url); 850 $parsed = parse_url($reference); 851 if (empty($parsed['scheme'])) $parsed['scheme'] = ''; 852 if (empty($parsed['host'])) $parsed['host'] = ''; 853 if (empty($parsed['port'])) $parsed['port'] = 80; 854 if (empty($parsed['path'])) $parsed['path'] = ''; 855 if (empty($parsed['query'])) $parsed['query'] = ''; 856 $url = strtr($url,[ 857 '{SCHEME}' => $parsed['scheme'], 858 '{HOST}' => $parsed['host'], 859 '{PORT}' => $parsed['port'], 860 '{PATH}' => $parsed['path'], 861 '{QUERY}' => $parsed['query'] , 862 ]); 863 } else { 864 //default 865 $url = $url.rawurlencode($reference); 866 } 867 //handle as wiki links 868 if($url{0} === ':') { 869 $urlparam = null; 870 $id = $url; 871 if (strpos($url, '?') !== false) { 872 list($id, $urlparam) = explode('?', $url, 2); 873 } 874 $url = wl(cleanID($id), $urlparam); 875 $exists = page_exists($id); 876 } 877 if($hash) $url .= '#'.rawurlencode($hash); 878 879 return $url; 880 } 881 882 #endregion 883} 884 885 886//Setup VIM: ex: et ts=4 : 887