1<?php
2/**
3 * DokuWiki Plugin dw2markdown (Renderer Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  i-net software <tools@inetsoftware.de>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12require_once DOKU_INC.'inc/parser/renderer.php';
13
14
15class Renderer_Plugin_dw2markdown extends Doku_Renderer_xhtml {
16
17    function document_start() {
18        global $ID;
19
20        $this->doc = '';
21        $toc = array();
22        $footnotes = array();
23        $store = '';
24        $nSpan = 0;
25        $separator = '';
26
27        $metaheader = array();
28        $metaheader['Content-Type'] = 'plain/text; charset=iso-8859-1';
29        $metaheader['Content-Disposition'] = 'attachment; filename="' . noNS($ID) . '.md"';
30        $meta = array();
31        $meta['format']['dw2markdown'] = $metaheader;
32        p_set_metadata($ID,$meta);
33    }
34
35    // FIXME implement all methods of Doku_Renderer here
36    public function document_end(){
37        //$converter = new DokuWikiToMarkdown();
38        //$this->doc = $converter->convert( $this->doc );
39
40        $this->doc = preg_replace("/(\r?\n){3,}/", "\n\n", $this->doc);
41        $this->doc = preg_replace("/^\s+/", "", $this->doc); // remove leading space and empty lines
42    }
43
44    /**
45     * Render a heading
46     *
47     * @param string $text  the text to display
48     * @param int    $level header level
49     * @param int    $pos   byte position in the original source
50     */
51    function header($text, $level, $pos) {
52        $this->doc .= str_repeat("#", $level) . ' ' . $text . DOKU_LF;
53    }
54
55    /**
56     * Open a new section
57     *
58     * @param int $level section level (as determined by the previous header)
59     */
60    function section_open($level) {
61        $this->doc .= DOKU_LF;
62    }
63
64    /**
65     * Close the current section
66     */
67    function section_close() {
68        $this->doc .= DOKU_LF;
69    }
70
71    /**
72     * Render plain text data
73     *
74     * @param string $text
75     */
76    function cdata($text) {
77        $this->doc .= $text;
78    }
79
80    /**
81     * Open a paragraph
82     */
83    function p_open() {
84        $this->doc .= DOKU_LF;
85    }
86
87    /**
88     * Close a paragraph
89     */
90    function p_close() {
91        $this->doc .= DOKU_LF;
92    }
93
94    /**
95     * Create a line break
96     */
97    function linebreak() {
98        $this->doc .= DOKU_LF . DOKU_LF;
99    }
100
101    /**
102     * Create a horizontal line
103     */
104    function hr() {
105        $this->doc .= '----';
106    }
107
108    /**
109     * Start strong (bold) formatting
110     */
111    function strong_open() {
112        $this->doc .= '**';
113    }
114
115    /**
116     * Stop strong (bold) formatting
117     */
118    function strong_close() {
119        $this->doc .= '**';
120    }
121
122    /**
123     * Start emphasis (italics) formatting
124     */
125    function emphasis_open() {
126        $this->doc .= '*';
127    }
128
129    /**
130     * Stop emphasis (italics) formatting
131     */
132    function emphasis_close() {
133        $this->doc .= '*';
134    }
135
136    /**
137     * Start underline formatting
138     */
139    function underline_open() {
140        $this->doc .= '__';
141    }
142
143    /**
144     * Stop underline formatting
145     */
146    function underline_close() {
147        $this->doc .= '__';
148    }
149
150    /**
151     * Start monospace formatting
152     */
153    function monospace_open() {
154        $this->doc .= "`";
155    }
156
157    /**
158     * Stop monospace formatting
159     */
160    function monospace_close() {
161        $this->doc .= "`";
162    }
163
164    /**
165     * Start a subscript
166     */
167    function subscript_open() {
168        $this->doc .= '<sub>';
169    }
170
171    /**
172     * Stop a subscript
173     */
174    function subscript_close() {
175        $this->doc .= '</sub>';
176    }
177
178    /**
179     * Start a superscript
180     */
181    function superscript_open() {
182        $this->doc .= '<sup>';
183    }
184
185    /**
186     * Stop a superscript
187     */
188    function superscript_close() {
189        $this->doc .= '</sup>';
190    }
191
192    /**
193     * Start deleted (strike-through) formatting
194     */
195    function deleted_open() {
196        $this->doc .= '~~';
197    }
198
199    /**
200     * Stop deleted (strike-through) formatting
201     */
202    function deleted_close() {
203        $this->doc .= '~~';
204    }
205
206    /**
207     * Start a footnote
208     */
209    function footnote_open() {
210        $this->doc .= '((';
211    }
212
213    /**
214     * Stop a footnote
215     */
216    function footnote_close() {
217        $this->doc .= '))';
218    }
219
220    private $listMode = [];
221
222    /**
223     * Open an unordered list
224     */
225    function listu_open($classes = null) {
226        array_push( $this->listMode, '*' );
227    }
228
229    /**
230     * Close an unordered list
231     */
232    function listu_close() {
233        array_pop( $this->listMode );
234        if ( empty($this->listMode) ) {
235            $this->doc .= DOKU_LF;
236        }
237    }
238
239    /**
240     * Open an ordered list
241     */
242    function listo_open($classes = null) {
243        array_push( $this->listMode, '1.' );
244    }
245
246    /**
247     * Close an ordered list
248     */
249    function listo_close() {
250        array_pop( $this->listMode );
251        if ( empty($this->listMode) ) {
252            $this->doc .= DOKU_LF;
253        }
254    }
255
256    /**
257     * Open a list item
258     *
259     * @param int $level the nesting level
260     * @param bool $node true when a node; false when a leaf
261     */
262    function listitem_open($level,$node=false) {
263        $this->doc .= DOKU_LF;
264        $this->doc .= str_repeat(' ', $level*2) . $this->listMode[count($this->listMode)-1];
265    }
266
267    /**
268     * Close a list item
269     */
270    function listitem_close() {
271//        $this->doc .= DOKU_LF;
272    }
273
274    /**
275     * Start the content of a list item
276     */
277    function listcontent_open() {
278    }
279
280    /**
281     * Stop the content of a list item
282     */
283    function listcontent_close() {
284//        $this->doc .= DOKU_LF;
285    }
286
287    /**
288     * Output unformatted $text
289     *
290     * Defaults to $this->cdata()
291     *
292     * @param string $text
293     */
294    function unformatted($text) {
295        $this->cdata($text);
296    }
297
298    /**
299     * Output inline PHP code
300     *
301     * If $conf['phpok'] is true this should evaluate the given code and append the result
302     * to $doc
303     *
304     * @param string $text The PHP code
305     */
306    function php($text, $wrapper = 'code') {
307        $this->code($text, 'php');
308    }
309
310    /**
311     * Output block level PHP code
312     *
313     * If $conf['phpok'] is true this should evaluate the given code and append the result
314     * to $doc
315     *
316     * @param string $text The PHP code
317     */
318    function phpblock($text) {
319        $this->php($text);
320    }
321
322    /**
323     * Output raw inline HTML
324     *
325     * If $conf['htmlok'] is true this should add the code as is to $doc
326     *
327     * @param string $text The HTML
328     */
329    function html($text, $wrapper = 'code') {
330        $this->code($text, 'html');
331    }
332
333    /**
334     * Output raw block-level HTML
335     *
336     * If $conf['htmlok'] is true this should add the code as is to $doc
337     *
338     * @param string $text The HTML
339     */
340    function htmlblock($text) {
341        $this->html($text);
342    }
343
344    /**
345     * Output preformatted text
346     *
347     * @param string $text
348     */
349    function preformatted($text) {
350        $this->doc .= DOKU_LF . "\t" . implode( "\n\t", explode("\n", $text) );
351    }
352
353    /**
354     * Start a block quote
355     */
356    function quote_open() {
357    }
358
359    /**
360     * Stop a block quote
361     */
362    function quote_close() {
363    }
364
365    /**
366     * Display text as file content, optionally syntax highlighted
367     *
368     * @param string $text text to show
369     * @param string $lang programming language to use for syntax highlighting
370     * @param string $file file path label
371     */
372    function file($text, $language = NULL, $filename = NULL, $options = NULL) {
373    }
374
375    /**
376     * Display text as code content, optionally syntax highlighted
377     *
378     * @param string $text text to show
379     * @param string $lang programming language to use for syntax highlighting
380     * @param string $file file path label
381     */
382    function code($text, $language = NULL, $filename = NULL, $options = NULL) {
383        $this->doc .= '```' . $lang . DOKU_LF . $text . DOKU_LF . '```';
384    }
385
386    /**
387     * Format an acronym
388     *
389     * Uses $this->acronyms
390     *
391     * @param string $acronym
392     */
393    function acronym($acronym) {
394        $this->doc .= $acronym;
395    }
396
397    /**
398     * Format a smiley
399     *
400     * Uses $this->smiley
401     *
402     * @param string $smiley
403     */
404    function smiley($smiley) {
405        $this->doc .= $smiley;
406    }
407
408    /**
409     * Format an entity
410     *
411     * Entities are basically small text replacements
412     *
413     * Uses $this->entities
414     *
415     * @param string $entity
416     */
417    /*function entity($entity) {
418        $this->doc .= $entity;
419    }*/
420
421    /**
422     * Typographically format a multiply sign
423     *
424     * Example: ($x=640, $y=480) should result in "640×480"
425     *
426     * @param string|int $x first value
427     * @param string|int $y second value
428     */
429    /*function multiplyentity($x, $y) {
430        $this->doc .= "$x×$y";
431    }*/
432
433    /**
434     * Render an opening single quote char (language specific)
435     */
436    function singlequoteopening() {
437        $this->doc .= "'";
438    }
439
440    /**
441     * Render a closing single quote char (language specific)
442     */
443    function singlequoteclosing() {
444        $this->doc .= "'";
445    }
446
447    /**
448     * Render an apostrophe char (language specific)
449     */
450    function apostrophe() {
451    }
452
453    /**
454     * Render an opening double quote char (language specific)
455     */
456    function doublequoteopening() {
457        $this->doc .= '"';
458    }
459
460    /**
461     * Render an closinging double quote char (language specific)
462     */
463    function doublequoteclosing() {
464        $this->doc .= '"';
465    }
466
467    /**
468     * Render a CamelCase link
469     *
470     * @param string $link The link name
471     * @see http://en.wikipedia.org/wiki/CamelCase
472     */
473    function camelcaselink($link, $returnonly = false) {
474        $this->externallink($link, $link, $returnonly);
475    }
476
477    /**
478     * Render a page local link
479     *
480     * @param string $hash hash link identifier
481     * @param string $name name for the link
482     */
483    function locallink($hash, $name = null, $returnonly = false) {
484        $this->externallink( $hash, $name, $returnonly );
485    }
486
487    /**
488     * Render a wiki internal link
489     *
490     * @param string       $link  page ID to link to. eg. 'wiki:syntax'
491     * @param string|array $title name for the link, array for media file
492     */
493    function internallink($id, $name = null, $search = null, $returnonly = false, $linktype = 'content') {
494        $this->externallink( wl($id), $name, $returnonly );
495    }
496
497    /**
498     * Render an external link
499     *
500     * @param string       $link  full URL with scheme
501     * @param string|array $title name for the link, array for media file
502     */
503    function externallink($link, $title = null, $returnonly = false) {
504
505        if ( empty($title) ) {
506            $title = $link;
507        }
508
509        $res = '[' . $title . '](' . $link . ')';
510        if ( $returnonly ) {
511            return $res;
512        }
513
514        $this->doc .= $res;
515    }
516
517    /**
518     * Render the output of an RSS feed
519     *
520     * @param string $url    URL of the feed
521     * @param array  $params Finetuning of the output
522     */
523    function rss($url, $params) {
524        $this->externallink( $url );
525    }
526
527    /**
528     * Render an interwiki link
529     *
530     * You may want to use $this->_resolveInterWiki() here
531     *
532     * @param string       $link     original link - probably not much use
533     * @param string|array $title    name for the link, array for media file
534     * @param string       $wikiName indentifier (shortcut) for the remote wiki
535     * @param string       $wikiUri  the fragment parsed from the original link
536     */
537    function interwikilink($match, $name, $wikiName, $wikiUri, $returnonly = false) {
538    }
539
540    /**
541     * Link to file on users OS
542     *
543     * @param string       $link  the link
544     * @param string|array $title name for the link, array for media file
545     */
546    function filelink($link, $title = null) {
547    }
548
549    /**
550     * Link to windows share
551     *
552     * @param string       $link  the link
553     * @param string|array $title name for the link, array for media file
554     */
555    function windowssharelink($link, $title = null, $returnonly = false) {
556    }
557
558    /**
559     * Render a linked E-Mail Address
560     *
561     * Should honor $conf['mailguard'] setting
562     *
563     * @param string $address Email-Address
564     * @param string|array $name name for the link, array for media file
565     */
566    function emaillink($address, $name = null, $returnonly = false) {
567        $this->externallink( 'email:' . $address, $name, $returnonly );
568    }
569
570    /**
571     * Render an internal media file
572     *
573     * @param string $src     media ID
574     * @param string $title   descriptive text
575     * @param string $align   left|center|right
576     * @param int    $width   width of media in pixel
577     * @param int    $height  height of media in pixel
578     * @param string $cache   cache|recache|nocache
579     * @param string $linking linkonly|detail|nolink
580     */
581    function internalmedia($src, $title = null, $align = null, $width = null,
582                           $height = null, $cache = null, $linking = null, $return = false) {
583    }
584
585    /**
586     * Render an external media file
587     *
588     * @param string $src     full media URL
589     * @param string $title   descriptive text
590     * @param string $align   left|center|right
591     * @param int    $width   width of media in pixel
592     * @param int    $height  height of media in pixel
593     * @param string $cache   cache|recache|nocache
594     * @param string $linking linkonly|detail|nolink
595     */
596    function externalmedia($src, $title = null, $align = null, $width = null,
597                           $height = null, $cache = null, $linking = null, $return = false) {
598    }
599
600    /**
601     * Render a link to an internal media file
602     *
603     * @param string $src     media ID
604     * @param string $title   descriptive text
605     * @param string $align   left|center|right
606     * @param int    $width   width of media in pixel
607     * @param int    $height  height of media in pixel
608     * @param string $cache   cache|recache|nocache
609     */
610    function internalmedialink($src, $title = null, $align = null,
611                               $width = null, $height = null, $cache = null) {
612    }
613
614    /**
615     * Render a link to an external media file
616     *
617     * @param string $src     media ID
618     * @param string $title   descriptive text
619     * @param string $align   left|center|right
620     * @param int    $width   width of media in pixel
621     * @param int    $height  height of media in pixel
622     * @param string $cache   cache|recache|nocache
623     */
624    function externalmedialink($src, $title = null, $align = null,
625                               $width = null, $height = null, $cache = null) {
626    }
627
628    /**
629     * Start a table
630     *
631     * @param int $maxcols maximum number of columns
632     * @param int $numrows NOT IMPLEMENTED
633     * @param int $pos     byte position in the original source
634     */
635    function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) {
636    }
637
638    /**
639     * Close a table
640     *
641     * @param int $pos byte position in the original source
642     */
643    function table_close($pos = null) {
644        $this->doc .= DOKU_LF;
645    }
646
647    private $tableColumns = 0;
648
649    /**
650     * Open a table header
651     */
652    function tablethead_open() {
653        $this->tableColumns = 0;
654    }
655
656    /**
657     * Close a table header
658     */
659    function tablethead_close() {
660        $this->doc .= DOKU_LF . str_repeat('--- |', $this->tableColumns);
661    }
662
663    /**
664     * Open a table body
665     */
666    function tabletbody_open() {
667    }
668
669    /**
670     * Close a table body
671     */
672    function tabletbody_close() {
673    }
674
675    /**
676     * Open a table row
677     */
678    function tablerow_open($classes = null) {
679        $this->doc .= DOKU_LF;
680    }
681
682    /**
683     * Close a table row
684     */
685    function tablerow_close() {
686        $this->doc .= DOKU_LF;
687    }
688
689    /**
690     * Open a table header cell
691     *
692     * @param int    $colspan
693     * @param string $align left|center|right
694     * @param int    $rowspan
695     */
696    function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) {
697        $this->doc .= str_repeat( '|', $colspan );
698        $this->tableColumns += $colspan;
699    }
700
701    /**
702     * Close a table header cell
703     */
704    function tableheader_close() {
705        $this->doc .= '|';
706    }
707
708    /**
709     * Open a table cell
710     *
711     * @param int    $colspan
712     * @param string $align left|center|right
713     * @param int    $rowspan
714     */
715    function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) {
716
717        if ( $this->doc[strlen($this->doc)-1] == '|' ) {
718            $colspan--;
719        }
720
721        $this->doc .= str_repeat( '|', $colspan );
722    }
723
724    /**
725     * Close a table cell
726     */
727    function tablecell_close() {
728        $this->doc .= '|';
729    }
730
731    function getFormat() {
732        return 'markdown';
733    }
734
735    #endregion
736}
737