1<?php
2/**
3 * Renderer for text output
4 *
5 * @author Michael Hamann <michael@content-space.de>
6 * @author Todd Augsburger <todd@rollerorgans.com>
7 */
8
9if(!defined('DOKU_INC')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../../').'/');
10
11if ( !defined('DOKU_LF') ) {
12    define ('DOKU_LF',"\n");
13}
14
15require_once DOKU_INC . 'inc/parser/renderer.php';
16require_once DOKU_INC . 'inc/parser/xhtml.php';
17require_once DOKU_INC . 'inc/html.php';
18
19class renderer_plugin_text extends Doku_Renderer_xhtml {
20
21    // @access public
22    var $nSpan = 0;
23    var $separator = '';
24
25    function getFormat(){
26        return 'text';
27    }
28
29    /* Compatibility functions for the xhtml mode */
30    public function startSectionEdit($start, $type, $title = NULL) {
31    }
32    public function finishSectionEdit($end = NULL) {
33    }
34
35    //handle plugin rendering
36    function plugin($name, $data, $state = '', $match = '') {
37        /** @var DokuWiki_Syntax_Plugin $plugin */
38        $plugin = plugin_load('syntax', $name);
39        if ($plugin != NULL) {
40            if (!$plugin->render($this->getFormat(),$this,$data)) {
41
42                // probably doesn't support text, so use stripped-down xhtml
43                $tmpData = $this->doc;
44                $this->doc = '';
45                if ($plugin->render('xhtml',$this,$data) && ($this->doc != '')) {
46                    $search = array('@<script[^>]*?>.*?</script>@si', // javascript
47                      '@<style[^>]*?>.*?</style>@si',                // style tags
48                      '@<[\/\!]*?[^<>]*?>@si',                        // HTML tags
49                      '@<![\s\S]*?--[ \t\n\r]*>@',                    // multi-line comments
50                      '@\s+@',                                         // extra whitespace
51                    );
52                    $this->doc = $tmpData . DOKU_LF .
53                        trim(html_entity_decode(preg_replace($search,' ',$this->doc),ENT_QUOTES)) .
54                        DOKU_LF;
55                } else  $this->doc = $tmpData;
56            }
57        }
58    }
59
60    function document_start() {
61        global $ID;
62
63        $this->doc = '';
64        $this->toc = array();
65        $this->footnotes = array();
66        $this->store = '';
67        $this->nSpan = 0;
68        $this->separator = '';
69
70        $metaheader = array();
71        $metaheader['Content-Type'] = 'text/plain; charset=utf-8';
72        //$metaheader['Content-Disposition'] = 'attachment; filename="noname.txt"';
73        $meta = array();
74        $meta['format']['text'] = $metaheader;
75        p_set_metadata($ID,$meta);
76    }
77
78    function document_end() {
79        if ( count($this->footnotes) > 0 ) {
80            $this->doc .= DOKU_LF;
81
82            $id = 0;
83            foreach ( $this->footnotes as $footnote ) {
84                $id++;   // the number of the current footnote
85
86                // check its not a placeholder that indicates actual footnote text is elsewhere
87                if (substr($footnote, 0, 5) != "@@FNT") {
88                    $this->doc .= $id.') ';
89                    // get any other footnotes that use the same markup
90                    $alt = array_keys($this->footnotes, "@@FNT$id");
91                    if (count($alt)) {
92                      foreach ($alt as $ref) {
93                        $this->doc .= ($ref+1).') ';
94                      }
95                    }
96                    $this->doc .= $footnote . DOKU_LF;
97                }
98            }
99        }
100
101        // Prepare the TOC
102        global $conf;
103        if($this->info['toc'] && is_array($this->toc) && $conf['tocminheads'] && count($this->toc) >= $conf['tocminheads']) {
104            global $TOC;
105            $TOC = $this->toc;
106        }
107
108        // make sure there are no empty paragraphs
109        $this->doc = preg_replace('#'.DOKU_LF.'\s*'.DOKU_LF.'\s*'.DOKU_LF.'#',DOKU_LF.DOKU_LF,$this->doc);
110    }
111
112    function header($text, $level, $pos) {
113        $this->doc .= DOKU_LF . $text . DOKU_LF;
114    }
115
116    function section_open($level) {
117    }
118
119    function section_close() {
120        $this->doc .= DOKU_LF;
121    }
122
123    function cdata($text) {
124        $this->doc .= $text;
125    }
126
127    function p_open() {
128    }
129
130    function p_close() {
131        $this->doc .= DOKU_LF;
132    }
133
134    function linebreak() {
135        $this->doc .= DOKU_LF;
136    }
137
138    function hr() {
139        $this->doc .= '--------'.DOKU_LF;
140    }
141
142    /**
143     * Start strong (bold) formatting
144     */
145    function strong_open() {
146    }
147
148    /**
149     * Stop strong (bold) formatting
150     */
151    function strong_close() {
152    }
153
154    /**
155     * Start emphasis (italics) formatting
156     */
157    function emphasis_open() {
158    }
159
160    /**
161     * Stop emphasis (italics) formatting
162     */
163    function emphasis_close() {
164    }
165
166    /**
167     * Start underline formatting
168     */
169    function underline_open() {
170    }
171
172    /**
173     * Stop underline formatting
174     */
175    function underline_close() {
176    }
177
178    /**
179     * Start monospace formatting
180     */
181    function monospace_open() {
182    }
183
184    /**
185     * Stop monospace formatting
186     */
187    function monospace_close() {
188    }
189
190    /**
191     * Start a subscript
192     */
193    function subscript_open() {
194    }
195
196    /**
197     * Stop a subscript
198     */
199    function subscript_close() {
200    }
201
202    /**
203     * Start a superscript
204     */
205    function superscript_open() {
206    }
207
208    /**
209     * Stop a superscript
210     */
211    function superscript_close() {
212    }
213
214    /**
215     * Start deleted (strike-through) formatting
216     */
217    function deleted_open() {
218    }
219
220    /**
221     * Stop deleted (strike-through) formatting
222     */
223    function deleted_close() {
224    }
225
226    /**
227     * Callback for footnote start syntax
228     *
229     * All following content will go to the footnote instead of
230     * the document. To achieve this the previous rendered content
231     * is moved to $store and $doc is cleared
232     *
233     * @author Andreas Gohr <andi@splitbrain.org>
234     */
235    function footnote_open() {
236
237        // move current content to store and record footnote
238        $this->store = $this->doc;
239        $this->doc   = '';
240    }
241
242    /**
243     * Callback for footnote end syntax
244     *
245     * All rendered content is moved to the $footnotes array and the old
246     * content is restored from $store again
247     *
248     * @author Andreas Gohr
249     */
250    function footnote_close() {
251
252        // recover footnote into the stack and restore old content
253        $footnote = $this->doc;
254        $this->doc = $this->store;
255        $this->store = '';
256
257        // check to see if this footnote has been seen before
258        $i = array_search($footnote, $this->footnotes);
259
260        if ($i === false) {
261            // its a new footnote, add it to the $footnotes array
262            $id = count($this->footnotes)+1;
263            $this->footnotes[count($this->footnotes)] = $footnote;
264        } else {
265            // seen this one before, translate the index to an id and save a placeholder
266            $i++;
267            $id = count($this->footnotes)+1;
268            $this->footnotes[count($this->footnotes)] = "@@FNT".($i);
269        }
270
271        // output the footnote reference and link
272        $this->doc .= ' '.$id.')';
273    }
274
275    /**
276     * Open an unordered list
277     */
278    function listu_open($classes = NULL) {
279    }
280
281    function listu_close() {
282        $this->doc .= DOKU_LF;
283    }
284
285    /**
286     * Open an ordered list
287     */
288    function listo_open($classes = NULL) {
289    }
290
291    /**
292     * Close an ordered list
293     */
294    function listo_close() {
295        $this->doc .= DOKU_LF;
296    }
297
298    /**
299     * Open a list item
300     *
301     * @param int $level the nesting level
302     * @param bool $node true when a node; false when a leaf
303     */
304    function listitem_open($level, $node=false) {
305    }
306
307    /**
308     * Close a list item
309     */
310    function listitem_close() {
311    }
312
313    /**
314     * Start the content of a list item
315     */
316    function listcontent_open() {
317    }
318
319    function listcontent_close() {
320        $this->doc .= DOKU_LF;
321    }
322
323    function unformatted($text) {
324        $this->doc .= $text;
325    }
326
327    function php($text, $wrapper='code') {
328        global $conf;
329
330        if ($conf['phpok']) {
331            ob_start();
332            eval($text);
333            $this->html(ob_get_contents());
334            ob_end_clean();
335        } else {
336            $this->cdata($text);
337        }
338    }
339
340    function phpblock($text) {
341        $this->doc .= $text;
342    }
343
344    function html($text, $wrapper='code') {
345        $this->doc .= strip_tags($text);
346    }
347
348    function htmlblock($text) {
349        $this->html($text);
350    }
351
352    /**
353     * Start a block quote
354     */
355    function quote_open() {
356    }
357
358    /**
359     * Stop a block quote
360     */
361    function quote_close() {
362        $this->doc .= DOKU_LF;
363    }
364
365    function preformatted($text) {
366        $this->doc .= $text.DOKU_LF;
367    }
368
369    function file($text, $lang = NULL, $file = NULL) {
370        $this->doc .= $text.DOKU_LF;
371    }
372
373    function code($text, $lang = NULL, $file = NULL) {
374        $this->preformatted($text);
375    }
376
377    function acronym($acronym) {
378        if ( array_key_exists($acronym, $this->acronyms) ) {
379            $title = $this->acronyms[$acronym];
380            $this->doc .= $acronym.' ('.$title.')';
381        } else {
382            $this->doc .= $acronym;
383        }
384    }
385
386    function smiley($smiley) {
387        $this->doc .= $smiley;
388    }
389
390    function entity($entity) {
391        if ( array_key_exists($entity, $this->entities) ) {
392            $this->doc .= $this->entities[$entity];
393        } else {
394            $this->doc .= $entity;
395        }
396    }
397
398    function multiplyentity($x, $y) {
399        $this->doc .= $x.'x'.$y;
400    }
401
402    function singlequoteopening() {
403        global $lang;
404        $this->doc .= $lang['singlequoteopening'];
405    }
406
407    function singlequoteclosing() {
408        global $lang;
409        $this->doc .= $lang['singlequoteclosing'];
410    }
411
412    function apostrophe() {
413        global $lang;
414        $this->doc .= $lang['apostrophe'];
415    }
416
417    function doublequoteopening() {
418        global $lang;
419        $this->doc .= $lang['doublequoteopening'];
420    }
421
422    function doublequoteclosing() {
423        global $lang;
424        $this->doc .= $lang['doublequoteclosing'];
425    }
426
427    function camelcaselink($link, $returnonly=false) {
428        $this->internallink($link,$link);
429    }
430
431    function locallink($hash, $name = NULL, $returnonly=false){
432        $name  = $this->_getLinkTitle($name, $hash, $isImage);
433        $this->doc .= $name;;
434    }
435
436    function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') {
437        global $ID;
438        // default name is based on $id as given
439        $default = $this->_simpleTitle($id);
440        resolve_pageid(getNS($ID),$id,$exists);
441        $name = $this->_getLinkTitle($name, $default, $isImage, $id, $linktype);
442        if ($returnonly) {
443            return $name;
444        } else {
445            $this->doc .= $name;
446        }
447    }
448
449    function externallink($url, $name = NULL, $returnonly=false) {
450        $this->doc .= $this->_getLinkTitle($name, $url, $isImage);
451    }
452
453    function interwikilink($match, $name = NULL, $wikiName, $wikiUri, $returnonly=false) {
454        $this->doc .= $this->_getLinkTitle($name, $wikiUri, $isImage);
455    }
456
457    function windowssharelink($url, $name = NULL, $returnonly=false) {
458        $this->doc .= $this->_getLinkTitle($name, $url, $isImage);
459    }
460
461    function emaillink($address, $name = NULL, $returnonly=false) {
462        $name = $this->_getLinkTitle($name, '', $isImage);
463        $address = html_entity_decode(obfuscate($address),ENT_QUOTES,'UTF-8');
464        if (empty($name)) {
465            $name = $address;
466        }
467        $this->doc .= $name;
468    }
469
470    function internalmedia ($src, $title=NULL, $align=NULL, $width=NULL,
471                            $height=NULL, $cache=NULL, $linking=NULL, $return=false) {
472        $this->doc .= $title;
473    }
474
475    function externalmedia ($src, $title=NULL, $align=NULL, $width=NULL,
476                            $height=NULL, $cache=NULL, $linking=NULL, $return=false) {
477        $this->doc .= $title;
478    }
479
480    function rss($url, $params) {
481    }
482
483    /**
484     * Start a table
485     *
486     * @param int $maxcols maximum number of columns
487     * @param int $numrows NOT IMPLEMENTED
488     * @param int $pos     byte position in the original source
489     */
490    function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) {
491    }
492
493    function table_close($pos = NULL) {
494        $this->doc .= DOKU_LF;
495    }
496
497    /**
498     * Open a table header
499     */
500    function tablethead_open() {
501    }
502
503    /**
504     * Close a table header
505     */
506    function tablethead_close() {
507    }
508
509    /**
510     * Open a table body
511     */
512    function tabletbody_open() {
513    }
514
515    /**
516     * Close a table body
517     */
518    function tabletbody_close() {
519    }
520
521    function tablerow_open($classes=NULL) {
522        $this->separator = '';
523    }
524
525    function tablerow_close() {
526        $this->doc .= DOKU_LF;
527    }
528
529    function tableheader_open($colspan = 1, $align = NULL, $rowspan = 1, $classes = null) {
530        $this->tablecell_open();
531    }
532
533    function tableheader_close() {
534        $this->tablecell_close();
535    }
536
537    function tablecell_open($colspan = 1, $align = NULL, $rowspan = 1, $classes = null) {
538        $this->nSpan = $colspan;
539        $this->doc .= $this->separator;
540        $this->separator = ', ';
541    }
542
543    function tablecell_close() {
544        if ($this->nSpan > 0) {
545            $this->doc .= str_repeat(',', $this->nSpan - 1);
546        }
547        $this->nSpan = 0;
548    }
549
550    function _getLinkTitle($title, $default, &$isImage, $id = null, $linktype = 'content') {
551        $isImage = false;
552        if(is_array($title)) {
553            $isImage = true;
554            if (!is_null($default) && ($default != $title['title']))
555                return $default." ".$title['title'];
556            else
557                return $title['title'];
558        } elseif(is_null($title) || trim($title) == '') {
559            if(useHeading($linktype) && $id) {
560                $heading = p_get_first_heading($id);
561                if($heading) {
562                    return $this->_xmlEntities($heading);
563                }
564            }
565            return $this->_xmlEntities($default);
566        } else {
567            return $this->_xmlEntities($title);
568        }
569    }
570
571    function _xmlEntities($string) {
572        return $string; // nothing to do for text
573    }
574
575    function _formatLink($link) {
576        if (!empty($link['name']))
577            return $link['name'];
578        elseif (!empty($link['title']))
579            return $link['title'];
580        return $link['url'];
581    }
582}
583