xref: /plugin/commonmark/src/Dokuwiki/Plugin/Commonmark/Commonmark.php (revision c0a62d53d1c413eaa1fe790cf1161a898c5d9eb6)
1<?php
2
3namespace Dokuwiki\Plugin\Commonmark;
4
5use League\CommonMark\Environment\Environment;
6use League\CommonMark\Parser\MarkdownParser;
7use Dokuwiki\Plugin\Commonmark\Extension\CommonmarkToDokuwikiExtension;
8use Dokuwiki\Plugin\Commonmark\Extension\FootnoteToDokuwikiExtension;
9use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
10use Dokuwiki\Plugin\Commonmark\Extension\TableExtension;
11use League\CommonMark\Extension\FrontMatter\FrontMatterExtension;
12use League\CommonMark\Extension\FrontMatter\Output\RenderedContentWithFrontMatter;
13
14class Commonmark {
15    public static function RendtoDW($markdown, $frontmatter_tag = 'off', $render_softbreaks = 0): array {
16        // heading info
17        $headingInfo = [];
18
19        // create environment
20        $environment = self::createDWEnvironment($render_softbreaks);
21
22        // create parser
23        $parser = new MarkdownParser($environment);
24        // create Dokuwiki Renderer
25        $DWRenderer = new DWRenderer($environment);
26
27        # separate frontmatter and main text
28        $FMresult = self::ExtractFrontmatter($markdown);
29        $frontmatter = $FMresult->getFrontMatter();
30        $markdownOnly = $FMresult->getContent();
31        $tagStr = ''; # initialize tag string
32        //print_r($frontmatter);
33
34        # extract tags only
35        if(!empty($frontmatter) && gettype($frontmatter) == "array") { // frontmatter must be array if valid
36            if (array_key_exists('tags', $frontmatter)) {
37                $tags = $frontmatter['tags'];
38                $tagStr = "{{tag>";
39                foreach ($tags as $tag) {
40                    $tagStr = $tagStr. "\"". $tag. "\" ";
41                }
42                $tagStr = $tagStr. "}}";
43            }
44        }
45
46        // pre-processing: convert slash inside wikilink to colon & image wikilinks
47        $markdownOnly = self::ParseDokuwikiWikilinks($markdownOnly);
48        $document = $parser->parse($markdownOnly);
49        $renderResult = $DWRenderer->renderNode($document);
50        foreach ($document->iterator() as $node) {
51            if($node instanceof \League\CommonMark\Extension\CommonMark\Node\Block\Heading) {
52                // collect inline text recursively
53                $collectInlineText = function($node) use (&$collectInlineText) {
54                    $text = '';
55                    for ($child = $node->firstChild(); $child !== null; $child = $child->next()) {
56                        if (method_exists($child, 'getLiteral')) {
57                            $text .= $child->getLiteral();
58                        } else {
59                            // recurse into nested inline nodes (strong, emphasis, etc.)
60                            $text .= $collectInlineText($child);
61                        }
62                    }
63                    return $text;
64                };
65
66                $first = $node->firstChild();
67                if ($first === null) {
68                    $headingName = '';
69                } elseif($first instanceof \League\CommonMark\Extension\CommonMark\Node\Inline\Link) {
70                    // set headingName as [[<Url>|<text>]]
71                    // link URL and its inner text (may contain nested inlines)
72                    $linkText = $collectInlineText($first);
73                    $headingName = '[[' . $first->getUrl() . '|' . $linkText . ']]';
74                } else {
75                    // collect all inline text inside the heading
76                    $headingName = $collectInlineText($node);
77                }
78
79                $headingInfo[$headingName] = array(
80                    'level' => $node->getLevel(),
81                    'startline' => $node->getStartLine(),
82                    'endline' => $node->getEndLine()
83                );
84            }
85        }
86
87        if($frontmatter_tag == 'off') {
88            return array('text'=>$renderResult, 'heading'=>$headingInfo);
89        } elseif($frontmatter_tag == 'upper') {
90            return array('text'=>$tagStr."\n\n".$renderResult, 'heading'=>$headingInfo);
91            //return $tagStr."\n\n".$renderResult;
92        } else {
93            return array('text'=>$renderResult."\n\n".$tagStr, 'heading'=>$headingInfo);
94            //return $renderResult."\n\n".$tagStr;
95        }
96    }
97
98    // Temporary implementation: separate method for frontmatter extraction
99    // Since some parsed frontmatter info must be included in main text, it should be merged
100    public static function ExtractFrontmatter($markdown) {
101        $frontMatterExtension = new FrontMatterExtension();
102        $result = $frontMatterExtension->getFrontMatterParser()->parse($markdown);
103
104        return $result;
105    }
106
107    // replace slash in MD wikilink to colon to match DW syntax
108    public static function ParseDokuwikiWikilinks($text) {
109        $pattern = "/(?:\[\[\b|(?!^)\G)[^\/|\]]*\K\/+/";
110        $result = preg_replace($pattern, ":", $text);
111        $pattern = "/!\[\[(.*)\]\]/";
112        $result = preg_replace($pattern, '{{$1}}', $result);
113        return $result;
114    }
115
116    public static function createDWEnvironment($render_softbreaks): Environment {
117        $config = [
118            'html_input' => 'allow',
119            'commonmark' => ['hard_break'=> "\\\\ "]
120        ];
121        if ($render_softbreaks) {
122            $config['renderer']['soft_break'] = $config['commonmark']['hard_break'];
123        }
124
125        $environment = new Environment($config);
126        $environment->addExtension(new CommonMarkToDokuWikiExtension());
127        $environment->addExtension(new FootnoteToDokuwikiExtension());
128        $environment->addExtension(new StrikethroughExtension());
129        $environment->addExtension(new TableExtension());
130        $environment->addExtension(new FrontMatterExtension());
131
132        return $environment;
133    }
134}
135
136?>
137