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