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, $returnonly = false) {
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 .= DOKU_LF . '```' . $language . DOKU_LF . trim($text) . DOKU_LF . '```' . 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 the output of an RSS feed
469     *
470     * @param string $url    URL of the feed
471     * @param array  $params Finetuning of the output
472     */
473    function rss($url, $params) {
474        $this->externallink( $url );
475    }
476    /**
477     * Start a table
478     *
479     * @param int $maxcols maximum number of columns
480     * @param int $numrows NOT IMPLEMENTED
481     * @param int $pos     byte position in the original source
482     */
483    function table_open($maxcols = null, $numrows = null, $pos = null, $classes = null) {
484    }
485
486    /**
487     * Close a table
488     *
489     * @param int $pos byte position in the original source
490     */
491    function table_close($pos = null) {
492        $this->doc .= DOKU_LF;
493    }
494
495    private $tableColumns = 0;
496
497    /**
498     * Open a table header
499     */
500    function tablethead_open() {
501        $this->tableColumns = 0;
502        $this->doc .= DOKU_LF; // . '|';
503    }
504
505    /**
506     * Close a table header
507     */
508    function tablethead_close() {
509        $this->doc .= '|' . str_repeat('---|', $this->tableColumns) . DOKU_LF;
510    }
511
512    /**
513     * Open a table body
514     */
515    function tabletbody_open() {
516    }
517
518    /**
519     * Close a table body
520     */
521    function tabletbody_close() {
522    }
523
524    /**
525     * Open a table row
526     */
527    function tablerow_open($classes = null) {
528    }
529
530    /**
531     * Close a table row
532     */
533    function tablerow_close() {
534        $this->doc .= '|' . DOKU_LF;
535    }
536
537    /**
538     * Open a table header cell
539     *
540     * @param int    $colspan
541     * @param string $align left|center|right
542     * @param int    $rowspan
543     */
544    function tableheader_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) {
545        $this->doc .= str_repeat( '|', $colspan );
546        $this->tableColumns += $colspan;
547    }
548
549    /**
550     * Close a table header cell
551     */
552    function tableheader_close() {
553    }
554
555    /**
556     * Open a table cell
557     *
558     * @param int    $colspan
559     * @param string $align left|center|right
560     * @param int    $rowspan
561     */
562    function tablecell_open($colspan = 1, $align = null, $rowspan = 1, $classes = null) {
563        $this->doc .= str_repeat( '|', $colspan );
564    }
565
566    /**
567     * Close a table cell
568     */
569    function tablecell_close() {
570    }
571
572    function getFormat() {
573        return 'markdown';
574    }
575
576    /**
577     * Render a page local link
578     *
579     * @param string $hash       hash link identifier
580     * @param string $name       name for the link
581     * @param bool   $returnonly whether to return html or write to doc attribute
582     * @return void|string writes to doc attribute or returns html depends on $returnonly
583     */
584    public function locallink($hash, $name = null, $returnonly = false) {
585        global $ID;
586        $name  = $this->_getLinkTitle($name, $hash, $isImage);
587        $hash  = $this->_headerToLink($hash);
588
589        $doc = '['.$name.'](#'.$hash.')';
590
591        if($returnonly) {
592          return $doc;
593        } else {
594          $this->doc .= $doc;
595        }
596    }
597
598    #endregion
599    /**
600     * Build a link
601     *
602     * Assembles all parts defined in $link returns HTML for the link
603     *
604     * @param array $link attributes of a link
605     * @return string
606     *
607     * @author Andreas Gohr <andi@splitbrain.org>
608     */
609    public function _formatLink($link) {
610        //make sure the url is XHTML compliant (skip mailto)
611        if(substr($link['url'], 0, 7) != 'mailto:') {
612            $link['url'] = str_replace('&', '&amp;', $link['url']);
613            $link['url'] = str_replace('&amp;amp;', '&amp;', $link['url']);
614        }
615        //remove double encodings in titles
616        $link['title'] = str_replace('&amp;amp;', '&amp;', $link['title']);
617
618        // be sure there are no bad chars in url or title
619        // (we can't do this for name because it can contain an img tag)
620        $link['url']   = strtr($link['url'], array('>' => '%3E', '<' => '%3C', '"' => '%22'));
621        $link['title'] = strtr($link['title'], array('>' => '&gt;', '<' => '&lt;', '"' => '&quot;'));
622
623        $res = $link['pre'] . '[' . $link['name'] . '](' . $link['url'] . ')' . $link['suf'];
624        return $res;
625    }
626}
627