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