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