xref: /plugin/commonmark/src/Dokuwiki/Plugin/Commonmark/Commonmark.php (revision c0a62d53d1c413eaa1fe790cf1161a898c5d9eb6)
18ec9a8f2SSungbin Jeon<?php
28ec9a8f2SSungbin Jeon
38ec9a8f2SSungbin Jeonnamespace Dokuwiki\Plugin\Commonmark;
48ec9a8f2SSungbin Jeon
594a075eeSSungbin Jeonuse League\CommonMark\Environment\Environment;
694a075eeSSungbin Jeonuse League\CommonMark\Parser\MarkdownParser;
71e536badSSungbin Jeonuse Dokuwiki\Plugin\Commonmark\Extension\CommonmarkToDokuwikiExtension;
8f46768a8SSungbin Jeonuse Dokuwiki\Plugin\Commonmark\Extension\FootnoteToDokuwikiExtension;
978c882e7SSungbin Jeonuse League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
10656793f4SSungbin Jeonuse Dokuwiki\Plugin\Commonmark\Extension\TableExtension;
1181da5a38SSungbin Jeonuse League\CommonMark\Extension\FrontMatter\FrontMatterExtension;
1281da5a38SSungbin Jeonuse League\CommonMark\Extension\FrontMatter\Output\RenderedContentWithFrontMatter;
138ec9a8f2SSungbin Jeon
148ec9a8f2SSungbin Jeonclass Commonmark {
15836a4a7aSChalix    public static function RendtoDW($markdown, $frontmatter_tag = 'off', $render_softbreaks = 0): array {
1680734199SSungbin Jeon        // heading info
1780734199SSungbin Jeon        $headingInfo = [];
1880734199SSungbin Jeon
1980734199SSungbin Jeon        // create environment
20836a4a7aSChalix        $environment = self::createDWEnvironment($render_softbreaks);
218ec9a8f2SSungbin Jeon
2280734199SSungbin Jeon        // create parser
2394a075eeSSungbin Jeon        $parser = new MarkdownParser($environment);
2480734199SSungbin Jeon        // create Dokuwiki Renderer
258ec9a8f2SSungbin Jeon        $DWRenderer = new DWRenderer($environment);
268ec9a8f2SSungbin Jeon
2781da5a38SSungbin Jeon        # separate frontmatter and main text
2881da5a38SSungbin Jeon        $FMresult = self::ExtractFrontmatter($markdown);
2981da5a38SSungbin Jeon        $frontmatter = $FMresult->getFrontMatter();
3081da5a38SSungbin Jeon        $markdownOnly = $FMresult->getContent();
318bcf583bSSungbin Jeon        $tagStr = ''; # initialize tag string
3281da5a38SSungbin Jeon        //print_r($frontmatter);
3381da5a38SSungbin Jeon
3481da5a38SSungbin Jeon        # extract tags only
35022ce692SSungbin Jeon        if(!empty($frontmatter) && gettype($frontmatter) == "array") { // frontmatter must be array if valid
36022ce692SSungbin Jeon            if (array_key_exists('tags', $frontmatter)) {
3781da5a38SSungbin Jeon                $tags = $frontmatter['tags'];
387569cca4SSungbin Jeon                $tagStr = "{{tag>";
3981da5a38SSungbin Jeon                foreach ($tags as $tag) {
4081da5a38SSungbin Jeon                    $tagStr = $tagStr. "\"". $tag. "\" ";
4181da5a38SSungbin Jeon                }
424384789bSSungbin Jeon                $tagStr = $tagStr. "}}";
438bcf583bSSungbin Jeon            }
44022ce692SSungbin Jeon        }
4581da5a38SSungbin Jeon
46ac4825fcSSungbin Jeon        // pre-processing: convert slash inside wikilink to colon & image wikilinks
475ba4c344SSungbin Jeon        $markdownOnly = self::ParseDokuwikiWikilinks($markdownOnly);
4881da5a38SSungbin Jeon        $document = $parser->parse($markdownOnly);
4981da5a38SSungbin Jeon        $renderResult = $DWRenderer->renderNode($document);
5080734199SSungbin Jeon        foreach ($document->iterator() as $node) {
51*5458b938SSungbin Jeon            if($node instanceof \League\CommonMark\Extension\CommonMark\Node\Block\Heading) {
52*5458b938SSungbin Jeon                // collect inline text recursively
53*5458b938SSungbin Jeon                $collectInlineText = function($node) use (&$collectInlineText) {
54*5458b938SSungbin Jeon                    $text = '';
55*5458b938SSungbin Jeon                    for ($child = $node->firstChild(); $child !== null; $child = $child->next()) {
56*5458b938SSungbin Jeon                        if (method_exists($child, 'getLiteral')) {
57*5458b938SSungbin Jeon                            $text .= $child->getLiteral();
589b5593feSSungbin Jeon                        } else {
59*5458b938SSungbin Jeon                            // recurse into nested inline nodes (strong, emphasis, etc.)
60*5458b938SSungbin Jeon                            $text .= $collectInlineText($child);
619b5593feSSungbin Jeon                        }
62*5458b938SSungbin Jeon                    }
63*5458b938SSungbin Jeon                    return $text;
64*5458b938SSungbin Jeon                };
65*5458b938SSungbin Jeon
66*5458b938SSungbin Jeon                $first = $node->firstChild();
67*5458b938SSungbin Jeon                if ($first === null) {
68*5458b938SSungbin Jeon                    $headingName = '';
69*5458b938SSungbin Jeon                } elseif($first instanceof \League\CommonMark\Extension\CommonMark\Node\Inline\Link) {
70*5458b938SSungbin Jeon                    // set headingName as [[<Url>|<text>]]
71*5458b938SSungbin Jeon                    // link URL and its inner text (may contain nested inlines)
72*5458b938SSungbin Jeon                    $linkText = $collectInlineText($first);
73*5458b938SSungbin Jeon                    $headingName = '[[' . $first->getUrl() . '|' . $linkText . ']]';
74*5458b938SSungbin Jeon                } else {
75*5458b938SSungbin Jeon                    // collect all inline text inside the heading
76*5458b938SSungbin Jeon                    $headingName = $collectInlineText($node);
77*5458b938SSungbin Jeon                }
78*5458b938SSungbin Jeon
799b5593feSSungbin Jeon                $headingInfo[$headingName] = array(
8080734199SSungbin Jeon                    'level' => $node->getLevel(),
8180734199SSungbin Jeon                    'startline' => $node->getStartLine(),
8280734199SSungbin Jeon                    'endline' => $node->getEndLine()
8380734199SSungbin Jeon                );
8480734199SSungbin Jeon            }
8580734199SSungbin Jeon        }
8681da5a38SSungbin Jeon
877569cca4SSungbin Jeon        if($frontmatter_tag == 'off') {
8880734199SSungbin Jeon            return array('text'=>$renderResult, 'heading'=>$headingInfo);
897569cca4SSungbin Jeon        } elseif($frontmatter_tag == 'upper') {
9080734199SSungbin Jeon            return array('text'=>$tagStr."\n\n".$renderResult, 'heading'=>$headingInfo);
9180734199SSungbin Jeon            //return $tagStr."\n\n".$renderResult;
927569cca4SSungbin Jeon        } else {
9380734199SSungbin Jeon            return array('text'=>$renderResult."\n\n".$tagStr, 'heading'=>$headingInfo);
9480734199SSungbin Jeon            //return $renderResult."\n\n".$tagStr;
957569cca4SSungbin Jeon        }
9681da5a38SSungbin Jeon    }
9781da5a38SSungbin Jeon
9881da5a38SSungbin Jeon    // Temporary implementation: separate method for frontmatter extraction
9980734199SSungbin Jeon    // Since some parsed frontmatter info must be included in main text, it should be merged
10081da5a38SSungbin Jeon    public static function ExtractFrontmatter($markdown) {
10181da5a38SSungbin Jeon        $frontMatterExtension = new FrontMatterExtension();
10281da5a38SSungbin Jeon        $result = $frontMatterExtension->getFrontMatterParser()->parse($markdown);
10381da5a38SSungbin Jeon
10481da5a38SSungbin Jeon        return $result;
1058ec9a8f2SSungbin Jeon    }
1068ec9a8f2SSungbin Jeon
107ac4825fcSSungbin Jeon    // replace slash in MD wikilink to colon to match DW syntax
1085ba4c344SSungbin Jeon    public static function ParseDokuwikiWikilinks($text) {
109ac4825fcSSungbin Jeon        $pattern = "/(?:\[\[\b|(?!^)\G)[^\/|\]]*\K\/+/";
110ac4825fcSSungbin Jeon        $result = preg_replace($pattern, ":", $text);
1115ba4c344SSungbin Jeon        $pattern = "/!\[\[(.*)\]\]/";
112ac4825fcSSungbin Jeon        $result = preg_replace($pattern, '{{$1}}', $result);
113ac4825fcSSungbin Jeon        return $result;
114ac4825fcSSungbin Jeon    }
115ac4825fcSSungbin Jeon
116836a4a7aSChalix    public static function createDWEnvironment($render_softbreaks): Environment {
11751966319SChalix        $config = [
11851966319SChalix            'html_input' => 'allow',
1190f46309aSChalix            'commonmark' => ['hard_break'=> "\\\\ "]
12051966319SChalix        ];
12158199fd4SChalix        if ($render_softbreaks) {
1220f46309aSChalix            $config['renderer']['soft_break'] = $config['commonmark']['hard_break'];
12358199fd4SChalix        }
12458199fd4SChalix
125b0a36678SSungbin Jeon        $environment = new Environment($config);
1268ec9a8f2SSungbin Jeon        $environment->addExtension(new CommonMarkToDokuWikiExtension());
127f46768a8SSungbin Jeon        $environment->addExtension(new FootnoteToDokuwikiExtension());
12878c882e7SSungbin Jeon        $environment->addExtension(new StrikethroughExtension());
129656793f4SSungbin Jeon        $environment->addExtension(new TableExtension());
13081da5a38SSungbin Jeon        $environment->addExtension(new FrontMatterExtension());
131f46768a8SSungbin Jeon
1328ec9a8f2SSungbin Jeon        return $environment;
1338ec9a8f2SSungbin Jeon    }
1348ec9a8f2SSungbin Jeon}
1358ec9a8f2SSungbin Jeon
1368ec9a8f2SSungbin Jeon?>
137