xref: /webtrees/app/Factories/MarkdownFactory.php (revision 202c018b592d5a516e4a465dc6dc515f3be37399)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2023 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Factories;
21
22use Fisharebest\Webtrees\CommonMark\CensusTableExtension;
23use Fisharebest\Webtrees\CommonMark\XrefExtension;
24use Fisharebest\Webtrees\Contracts\MarkdownFactoryInterface;
25use Fisharebest\Webtrees\Tree;
26use League\CommonMark\Environment\Environment;
27use League\CommonMark\Extension\Autolink\AutolinkExtension;
28use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
29use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
30use League\CommonMark\Extension\CommonMark\Renderer\Inline\LinkRenderer;
31use League\CommonMark\Extension\Table\TableExtension;
32use League\CommonMark\MarkdownConverter;
33use League\CommonMark\Node\Block\Document;
34use League\CommonMark\Node\Block\Paragraph;
35use League\CommonMark\Node\Inline\Newline;
36use League\CommonMark\Node\Inline\Text;
37use League\CommonMark\Parser\Inline\NewlineParser;
38use League\CommonMark\Renderer\Block\DocumentRenderer;
39use League\CommonMark\Renderer\Block\ParagraphRenderer;
40use League\CommonMark\Renderer\Inline\NewlineRenderer;
41use League\CommonMark\Renderer\Inline\TextRenderer;
42use League\CommonMark\Util\HtmlFilter;
43
44use function strip_tags;
45use function strtr;
46
47/**
48 * Create a markdown converter.
49 */
50class MarkdownFactory implements MarkdownFactoryInterface
51{
52    // Commonmark uses the self-closing form of this tag, so we do the same for consistency.
53    public const BREAK = '<br />';
54
55    protected const CONFIG_AUTOLINK = [
56        'allow_unsafe_links' => false,
57        'html_input'         => HtmlFilter::ESCAPE,
58        'renderer'           => [
59            'soft_break' => self::BREAK,
60        ],
61    ];
62
63    protected const CONFIG_MARKDOWN = [
64        'allow_unsafe_links' => false,
65        'html_input'         => HtmlFilter::ESCAPE,
66        'renderer'           => [
67            'soft_break' => self::BREAK,
68        ],
69        'table'              => [
70            'wrap' => [
71                'enabled'    => true,
72                'tag'        => 'div',
73                'attributes' => [
74                    'class' => 'table-responsive',
75                ],
76            ],
77        ],
78    ];
79
80    /**
81     * @param string    $markdown
82     * @param Tree|null $tree
83     *
84     * @return string
85     */
86    public function autolink(string $markdown, Tree|null $tree = null): string
87    {
88        // Create a minimal commonmark processor - just add support for auto-links.
89        $environment = new Environment(static::CONFIG_AUTOLINK);
90        $environment->addInlineParser(new NewlineParser());
91        $environment->addRenderer(Document::class, new DocumentRenderer());
92        $environment->addRenderer(Paragraph::class, new ParagraphRenderer());
93        $environment->addRenderer(Text::class, new TextRenderer());
94        $environment->addRenderer(Link::class, new LinkRenderer());
95        $environment->addRenderer(Newline::class, new NewlineRenderer());
96        $environment->addExtension(new AutolinkExtension());
97
98        // Optionally create links to other records.
99        if ($tree instanceof Tree) {
100            $environment->addExtension(new XrefExtension($tree));
101        }
102
103        $converter = new MarkDownConverter($environment);
104
105        $html = $converter->convert($markdown)->getContent();
106
107        // We should only have certain tags.  Make sure of this.
108        $html = strip_tags($html, ['a', 'br', 'p']);
109
110        // The markdown convert adds newlines, but not in a documented way.  Safest to ignore them.
111        return strtr($html, ["\n"   => '']);
112    }
113
114    /**
115     * @param string    $markdown
116     * @param Tree|null $tree
117     *
118     * @return string
119     */
120    public function markdown(string $markdown, Tree|null $tree = null): string
121    {
122        $environment = new Environment(static::CONFIG_MARKDOWN);
123        $environment->addExtension(new CommonMarkCoreExtension());
124        $environment->addExtension(new TableExtension());
125
126        // Convert webtrees 1.x style census tables to commonmark format.
127        $environment->addExtension(new CensusTableExtension());
128
129        // Optionally create links to other records.
130        if ($tree instanceof Tree) {
131            $environment->addExtension(new XrefExtension($tree));
132        }
133
134        $converter = new MarkDownConverter($environment);
135
136        $html = $converter->convert($markdown)->getContent();
137
138        // The markdown convert adds newlines, but not in a documented way.  Safest to ignore them.
139        return strtr($html, ["\n"   => '']);
140    }
141}
142