diff options
| author | Greg Roach <greg@subaqua.co.uk> | 2021-06-28 11:27:44 +0100 |
|---|---|---|
| committer | Greg Roach <greg@subaqua.co.uk> | 2021-06-28 11:33:16 +0100 |
| commit | 4d35caa736b1f4119b8a949d1cbca5644dbf4e23 (patch) | |
| tree | 2c47f968ccb5710a842688aa489624bcd7e5d2ca /app | |
| parent | 1197bb9cb49467b99c3a10f99876cf6c5b3b37ff (diff) | |
| download | webtrees-4d35caa736b1f4119b8a949d1cbca5644dbf4e23.tar.gz webtrees-4d35caa736b1f4119b8a949d1cbca5644dbf4e23.tar.bz2 webtrees-4d35caa736b1f4119b8a949d1cbca5644dbf4e23.zip | |
Fix: #2600 - refactor markdown formatting to allow XREF links to be disabled
Diffstat (limited to 'app')
| -rw-r--r-- | app/CommonMark/ResponsiveTableExtension.php | 2 | ||||
| -rw-r--r-- | app/CommonMark/ResponsiveTableRenderer.php | 2 | ||||
| -rw-r--r-- | app/Contracts/MarkdownFactoryInterface.php | 43 | ||||
| -rw-r--r-- | app/Elements/TextFromSource.php | 4 | ||||
| -rw-r--r-- | app/Factories/MarkdownFactory.php | 94 | ||||
| -rw-r--r-- | app/Filter.php | 119 | ||||
| -rw-r--r-- | app/Functions/FunctionsPrint.php | 8 | ||||
| -rw-r--r-- | app/Functions/FunctionsPrintFacts.php | 30 | ||||
| -rw-r--r-- | app/Http/RequestHandlers/NotePage.php | 1 | ||||
| -rw-r--r-- | app/Note.php | 10 | ||||
| -rw-r--r-- | app/Registry.php | 19 | ||||
| -rw-r--r-- | app/Report/ReportParserGenerate.php | 8 | ||||
| -rw-r--r-- | app/Webtrees.php | 2 |
13 files changed, 200 insertions, 142 deletions
diff --git a/app/CommonMark/ResponsiveTableExtension.php b/app/CommonMark/ResponsiveTableExtension.php index 8eb933353d..88ce3301ec 100644 --- a/app/CommonMark/ResponsiveTableExtension.php +++ b/app/CommonMark/ResponsiveTableExtension.php @@ -32,8 +32,6 @@ use League\CommonMark\Extension\Table\TableSectionRenderer; /** * Class ResponsiveTableExtension - wrap markdown tables in a responsive DIV element. - * - * @package Fisharebest\Webtrees\CommonMark */ class ResponsiveTableExtension implements ExtensionInterface { diff --git a/app/CommonMark/ResponsiveTableRenderer.php b/app/CommonMark/ResponsiveTableRenderer.php index 8137f2c7bb..d69c6326d2 100644 --- a/app/CommonMark/ResponsiveTableRenderer.php +++ b/app/CommonMark/ResponsiveTableRenderer.php @@ -27,8 +27,6 @@ use League\CommonMark\HtmlElement; /** * Class ResponsiveTableRenderer - wrap markdown tables in a responsive DIV element. - * - * @package Fisharebest\Webtrees\CommonMark */ class ResponsiveTableRenderer implements BlockRendererInterface { diff --git a/app/Contracts/MarkdownFactoryInterface.php b/app/Contracts/MarkdownFactoryInterface.php new file mode 100644 index 0000000000..9b8c3152fd --- /dev/null +++ b/app/Contracts/MarkdownFactoryInterface.php @@ -0,0 +1,43 @@ +<?php + +/** + * webtrees: online genealogy + * Copyright (C) 2021 webtrees development team + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Contracts; + +use Fisharebest\Webtrees\Tree; +use League\CommonMark\CommonMarkConverter; + +/** + * Create a markdown converter. + */ +interface MarkdownFactoryInterface +{ + /** + * @param Tree|null $tree + * + * @return CommonMarkConverter + */ + public function autolink(Tree $tree = null): CommonMarkConverter; + + /** + * @param Tree|null $tree + * + * @return CommonMarkConverter + */ + public function markdown(Tree $tree = null): CommonMarkConverter; +} diff --git a/app/Elements/TextFromSource.php b/app/Elements/TextFromSource.php index 7acac56eab..449eb21a30 100644 --- a/app/Elements/TextFromSource.php +++ b/app/Elements/TextFromSource.php @@ -19,7 +19,7 @@ declare(strict_types=1); namespace Fisharebest\Webtrees\Elements; -use Fisharebest\Webtrees\Filter; +use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Tree; /** @@ -71,6 +71,6 @@ class TextFromSource extends AbstractElement */ public function value(string $value, Tree $tree): string { - return Filter::formatText($value, $tree); + return Registry::markdownFactory()->autolink()->convertToHtml($value); } } diff --git a/app/Factories/MarkdownFactory.php b/app/Factories/MarkdownFactory.php new file mode 100644 index 0000000000..3f3054d6c1 --- /dev/null +++ b/app/Factories/MarkdownFactory.php @@ -0,0 +1,94 @@ +<?php + +/** + * webtrees: online genealogy + * Copyright (C) 2021 webtrees development team + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Factories; + +use Fisharebest\Webtrees\CommonMark\CensusTableExtension; +use Fisharebest\Webtrees\CommonMark\ResponsiveTableExtension; +use Fisharebest\Webtrees\CommonMark\XrefExtension; +use Fisharebest\Webtrees\Contracts\MarkdownFactoryInterface; +use Fisharebest\Webtrees\Tree; +use League\CommonMark\Block\Element\Document; +use League\CommonMark\Block\Element\Paragraph; +use League\CommonMark\Block\Renderer\DocumentRenderer; +use League\CommonMark\Block\Renderer\ParagraphRenderer; +use League\CommonMark\CommonMarkConverter; +use League\CommonMark\Environment; +use League\CommonMark\Extension\Autolink\AutolinkExtension; +use League\CommonMark\Inline\Element\Link; +use League\CommonMark\Inline\Element\Text; +use League\CommonMark\Inline\Renderer\LinkRenderer; +use League\CommonMark\Inline\Renderer\TextRenderer; + +/** + * Create a markdown converter. + */ +class MarkdownFactory implements MarkdownFactoryInterface +{ + protected const CONFIG = [ + 'allow_unsafe_links' => false, + 'html_input' => Environment::HTML_INPUT_ESCAPE, + ]; + + /** + * @param Tree|null $tree + * + * @return CommonMarkConverter + */ + public function autolink(Tree $tree = null): CommonMarkConverter + { + // Create a minimal commonmark processor - just add support for auto-links. + $environment = new Environment(); + $environment->addBlockRenderer(Document::class, new DocumentRenderer()); + $environment->addBlockRenderer(Paragraph::class, new ParagraphRenderer()); + $environment->addInlineRenderer(Text::class, new TextRenderer()); + $environment->addInlineRenderer(Link::class, new LinkRenderer()); + $environment->addExtension(new AutolinkExtension()); + + // Optionally create links to other records. + if ($tree instanceof Tree) { + $environment->addExtension(new XrefExtension($tree)); + } + + return new CommonMarkConverter(static::CONFIG, $environment); + } + + /** + * @param Tree|null $tree + * + * @return CommonMarkConverter + */ + public function markdown(Tree $tree = null): CommonMarkConverter + { + $environment = Environment::createCommonMarkEnvironment(); + + // Wrap tables so support horizontal scrolling with bootstrap. + $environment->addExtension(new ResponsiveTableExtension()); + + // Convert webtrees 1.x style census tables to commonmark format. + $environment->addExtension(new CensusTableExtension()); + + // Optionally create links to other records. + if ($tree instanceof Tree) { + $environment->addExtension(new XrefExtension($tree)); + } + + return new CommonMarkConverter(static::CONFIG, $environment); + } +} diff --git a/app/Filter.php b/app/Filter.php deleted file mode 100644 index e9cf7484d7..0000000000 --- a/app/Filter.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php - -/** - * webtrees: online genealogy - * Copyright (C) 2021 webtrees development team - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -declare(strict_types=1); - -namespace Fisharebest\Webtrees; - -use Fisharebest\Webtrees\CommonMark\CensusTableExtension; -use Fisharebest\Webtrees\CommonMark\ResponsiveTableExtension; -use Fisharebest\Webtrees\CommonMark\XrefExtension; -use League\CommonMark\Block\Element\Document; -use League\CommonMark\Block\Element\Paragraph; -use League\CommonMark\Block\Renderer\DocumentRenderer; -use League\CommonMark\Block\Renderer\ParagraphRenderer; -use League\CommonMark\CommonMarkConverter; -use League\CommonMark\Environment; -use League\CommonMark\Inline\Element\Link; -use League\CommonMark\Inline\Element\Text; -use League\CommonMark\Inline\Parser\AutolinkParser; -use League\CommonMark\Inline\Renderer\LinkRenderer; -use League\CommonMark\Inline\Renderer\TextRenderer; - -/** - * Filter input and escape output. - */ -class Filter -{ - // REGEX to match a URL - // Some versions of RFC3987 have an appendix B which gives the following regex - // (([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? - // This matches far too much while a “precise” regex is several pages long. - // This is a compromise. - private const URL_REGEX = '((https?|ftp]):)(//([^\s/?#<>]*))?([^\s?#<>]*)(\?([^\s#<>]*))?(#[^\s?#<>]+)?'; - - /** - * Format block-level text such as notes or transcripts, etc. - * - * @param string $text - * @param Tree $tree - * - * @return string - */ - public static function formatText(string $text, Tree $tree): string - { - switch ($tree->getPreference('FORMAT_TEXT')) { - case 'markdown': - return '<div class="markdown" dir="auto">' . self::markdown($text, $tree) . '</div>'; - default: - return '<div class="markdown" style="white-space: pre-wrap;" dir="auto">' . self::expandUrls($text, $tree) . '</div>'; - } - } - - /** - * Format a block of text, expanding URLs and XREFs. - * - * @param string $text - * @param Tree $tree - * - * @return string - */ - public static function expandUrls(string $text, Tree $tree): string - { - // If it looks like a URL, turn it into a markdown autolink. - $text = preg_replace('/' . addcslashes(self::URL_REGEX, '/') . '/', '<$0>', $text); - - // Create a minimal commonmark processor - just add support for autolinks. - $environment = new Environment(); - $environment - ->addBlockRenderer(Document::class, new DocumentRenderer()) - ->addBlockRenderer(Paragraph::class, new ParagraphRenderer()) - ->addInlineRenderer(Text::class, new TextRenderer()) - ->addInlineRenderer(Link::class, new LinkRenderer()) - ->addInlineParser(new AutolinkParser()) - ->addExtension(new XrefExtension($tree)); - - $converter = new CommonMarkConverter(['html_input' => Environment::HTML_INPUT_ESCAPE], $environment); - - return $converter->convertToHtml($text); - } - - /** - * Format a block of text, using "Markdown". - * - * @param string $text - * @param Tree $tree - * - * @return string - */ - public static function markdown(string $text, Tree $tree): string - { - $environment = Environment::createCommonMarkEnvironment(); - $environment->addExtension(new ResponsiveTableExtension()); - $environment->addExtension(new CensusTableExtension()); - $environment->addExtension(new XrefExtension($tree)); - - $config = [ - 'allow_unsafe_links' => false, - 'html_input' => Environment::HTML_INPUT_ESCAPE, - ]; - - $converter = new CommonMarkConverter($config, $environment); - - return $converter->convertToHtml($text); - } -} diff --git a/app/Functions/FunctionsPrint.php b/app/Functions/FunctionsPrint.php index 35eb61b542..4ba4907a5a 100644 --- a/app/Functions/FunctionsPrint.php +++ b/app/Functions/FunctionsPrint.php @@ -24,7 +24,6 @@ use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Date; use Fisharebest\Webtrees\Fact; use Fisharebest\Webtrees\Family; -use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\Gedcom; use Fisharebest\Webtrees\GedcomRecord; use Fisharebest\Webtrees\I18N; @@ -37,7 +36,6 @@ use Fisharebest\Webtrees\Services\ModuleService; use Fisharebest\Webtrees\Tree; use Illuminate\Support\Collection; use Illuminate\Support\Str; -use LogicException; use Ramsey\Uuid\Uuid; use function app; @@ -50,13 +48,11 @@ use function explode; use function in_array; use function preg_match; use function preg_match_all; -use function preg_replace_callback; use function preg_split; use function str_contains; use function strip_tags; use function strlen; use function strpos; -use function strtoupper; use function substr; use function uasort; use function view; @@ -92,14 +88,14 @@ class FunctionsPrint assert($note instanceof Note); $label = I18N::translate('Shared note'); - $html = Filter::formatText($note->getNote(), $tree); + $html = Registry::markdownFactory()->markdown($tree)->convertToHtml($note->getNote()); $first_line = '<a href="' . e($note->url()) . '">' . $note->fullName() . '</a>'; $one_line_only = strip_tags($note->fullName()) === strip_tags($note->getNote()); } else { // Inline note. $label = I18N::translate('Note'); - $html = Filter::formatText($text, $tree); + $html = Registry::markdownFactory()->markdown($tree)->convertToHtml($text); [$first_line] = explode("\n", strip_tags($text)); // Use same logic as note objects diff --git a/app/Functions/FunctionsPrintFacts.php b/app/Functions/FunctionsPrintFacts.php index c4b371a7be..dacce3899d 100644 --- a/app/Functions/FunctionsPrintFacts.php +++ b/app/Functions/FunctionsPrintFacts.php @@ -23,7 +23,6 @@ use Fisharebest\Webtrees\Age; use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Fact; use Fisharebest\Webtrees\Family; -use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\Gedcom; use Fisharebest\Webtrees\Elements\UnknownElement; use Fisharebest\Webtrees\GedcomRecord; @@ -433,7 +432,19 @@ class FunctionsPrintFacts for ($j = 0; $j < $ct; $j++) { if (!str_contains($match[$j][1], '@')) { $source = e($match[$j][1] . preg_replace('/\n\d CONT ?/', "\n", $match[$j][2])); - $data .= '<div class="fact_SOUR"><span class="label">' . I18N::translate('Source') . ':</span> <span class="field" dir="auto">' . Filter::formatText($source, $tree) . '</span></div>'; + $data .= '<div class="fact_SOUR"><span class="label">' . I18N::translate('Source') . ':</span> <span class="field" dir="auto">'; + + if ($tree->getPreference('FORMAT_TEXT') === 'markdown') { + $data .= '<div class="markdown" dir="auto">' ; + $data .= Registry::markdownFactory()->markdown($tree)->convertToHtml($source); + $data .= '</div>'; + } else { + $data .= '<div class="markdown" dir="auto" style="white-space: pre-wrap;">'; + $data .= Registry::markdownFactory()->autolink($tree)->convertToHtml($source); + $data .= '</div>'; + } + + $data .= '</span></div>'; } } // Find source for each fact @@ -833,16 +844,25 @@ class FunctionsPrintFacts echo '</th>'; if ($note) { // Note objects - $text = Filter::formatText($note->getNote(), $tree); + $text = $note->getNote(); } else { // Inline notes $nrec = Functions::getSubRecord($level, "$level NOTE", $factrec, $j + 1); $text = $match[$j][1] . Functions::getCont($level + 1, $nrec); - $text = Filter::formatText($text, $tree); + } + + if ($tree->getPreference('FORMAT_TEXT') === 'markdown') { + $formatted_text = '<div class="markdown" dir="auto">' ; + $formatted_text .= Registry::markdownFactory()->markdown($tree)->convertToHtml($text); + $formatted_text .= '</div>'; + } else { + $formatted_text = '<div class="markdown" dir="auto" style="white-space: pre-wrap;">'; + $formatted_text .= Registry::markdownFactory()->autolink($tree)->convertToHtml($text); + $formatted_text .= '</div>'; } echo '<td class="', $styleadd, ' wrap">'; - echo $text; + echo $formatted_text; echo '</td></tr>'; } } diff --git a/app/Http/RequestHandlers/NotePage.php b/app/Http/RequestHandlers/NotePage.php index bd7499f460..62f3c4b8a3 100644 --- a/app/Http/RequestHandlers/NotePage.php +++ b/app/Http/RequestHandlers/NotePage.php @@ -21,7 +21,6 @@ namespace Fisharebest\Webtrees\Http\RequestHandlers; use Fig\Http\Message\StatusCodeInterface; use Fisharebest\Webtrees\Auth; -use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\Http\ViewResponseTrait; use Fisharebest\Webtrees\Registry; use Fisharebest\Webtrees\Services\ClipboardService; diff --git a/app/Note.php b/app/Note.php index 34cd69f57e..5f003c9cc3 100644 --- a/app/Note.php +++ b/app/Note.php @@ -80,9 +80,15 @@ class Note extends GedcomRecord */ public function extractNames(): void { - $text = trim($this->getNote()); + if ($this->tree->getPreference('FORMAT_TEXT') === 'markdown') { + $text = Registry::markdownFactory()->markdown()->convertToHtml($this->getNote()); + } else { + $text = Registry::markdownFactory()->autolink()->convertToHtml($this->getNote()); + } + - [$text] = explode("\n", $text); + // Take the first line + [$text] = explode("\n", strip_tags(trim($text))); if ($text !== '') { $this->addName('NOTE', Str::limit($text, 100, I18N::translate('…')), $this->gedcom()); diff --git a/app/Registry.php b/app/Registry.php index 32d699c31e..e584cfc3bd 100644 --- a/app/Registry.php +++ b/app/Registry.php @@ -28,6 +28,7 @@ use Fisharebest\Webtrees\Contracts\HeaderFactoryInterface; use Fisharebest\Webtrees\Contracts\ImageFactoryInterface; use Fisharebest\Webtrees\Contracts\IndividualFactoryInterface; use Fisharebest\Webtrees\Contracts\LocationFactoryInterface; +use Fisharebest\Webtrees\Contracts\MarkdownFactoryInterface; use Fisharebest\Webtrees\Contracts\MediaFactoryInterface; use Fisharebest\Webtrees\Contracts\NoteFactoryInterface; use Fisharebest\Webtrees\Contracts\RepositoryFactoryInterface; @@ -60,6 +61,8 @@ class Registry private static LocationFactoryInterface $location_factory; + private static MarkdownFactoryInterface $markdown_factory; + private static MediaFactoryInterface $media_factory; private static NoteFactoryInterface $note_factory; @@ -223,6 +226,22 @@ class Registry /** * Store or retrieve a factory object. * + * @param MarkdownFactoryInterface|null $factory + * + * @return MarkdownFactoryInterface + */ + public static function markdownFactory(MarkdownFactoryInterface $factory = null): MarkdownFactoryInterface + { + if ($factory instanceof MarkdownFactoryInterface) { + self::$markdown_factory = $factory; + } + + return self::$markdown_factory; + } + + /** + * Store or retrieve a factory object. + * * @param MediaFactoryInterface|null $factory * * @return MediaFactoryInterface diff --git a/app/Report/ReportParserGenerate.php b/app/Report/ReportParserGenerate.php index 86544dfc77..74268f5ad6 100644 --- a/app/Report/ReportParserGenerate.php +++ b/app/Report/ReportParserGenerate.php @@ -24,7 +24,6 @@ use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Carbon; use Fisharebest\Webtrees\Date; use Fisharebest\Webtrees\Family; -use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\Functions\Functions; use Fisharebest\Webtrees\Gedcom; use Fisharebest\Webtrees\GedcomRecord; @@ -1046,11 +1045,14 @@ class ReportParserGenerate extends ReportParserBase } $tmp = explode(':', $tag); if (in_array(end($tmp), ['NOTE', 'TEXT'], true)) { - $value = Filter::formatText($value, $this->tree); // We'll strip HTML in addText() + if ($this->tree->getPreference('FORMAT_TEXT') === 'markdown') { + $value = strip_tags(Registry::markdownFactory()->markdown($this->tree)->convertToHtml($value)); + } else { + $value = strip_tags(Registry::markdownFactory()->autolink($this->tree)->convertToHtml($value)); + } } if (!empty($attrs['truncate'])) { - $value = strip_tags($value); $value = Str::limit($value, (int) $attrs['truncate'], I18N::translate('…')); } $this->current_element->addText($value); diff --git a/app/Webtrees.php b/app/Webtrees.php index cbb519d2f5..937ad9b61f 100644 --- a/app/Webtrees.php +++ b/app/Webtrees.php @@ -30,6 +30,7 @@ use Fisharebest\Webtrees\Factories\HeaderFactory; use Fisharebest\Webtrees\Factories\ImageFactory; use Fisharebest\Webtrees\Factories\IndividualFactory; use Fisharebest\Webtrees\Factories\LocationFactory; +use Fisharebest\Webtrees\Factories\MarkdownFactory; use Fisharebest\Webtrees\Factories\MediaFactory; use Fisharebest\Webtrees\Factories\NoteFactory; use Fisharebest\Webtrees\Factories\RepositoryFactory; @@ -185,6 +186,7 @@ class Webtrees Registry::imageFactory(new ImageFactory()); Registry::individualFactory(new IndividualFactory()); Registry::locationFactory(new LocationFactory()); + Registry::markdownFactory(new MarkdownFactory()); Registry::mediaFactory(new MediaFactory()); Registry::noteFactory(new NoteFactory()); Registry::repositoryFactory(new RepositoryFactory()); |
