From 28c706fee3b094f2a580975592014ce3d5205dce Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 10 Jan 2021 23:01:11 +0000
Subject: [PATCH] Added strikethrough support to back-end md rendering

Needed to tweak the default library strikethrough extension
so that it uses the same element as front-end.
Added testing to cover.
For #2470.
---
 .../Markdown/CustomStrikeThroughExtension.php | 16 +++++++++++++
 .../Markdown/CustomStrikethroughRenderer.php  | 24 +++++++++++++++++++
 app/Entities/Tools/PageContent.php            |  2 ++
 tests/Entity/PageContentTest.php              | 18 ++++++++++++++
 4 files changed, 60 insertions(+)
 create mode 100644 app/Entities/Tools/Markdown/CustomStrikeThroughExtension.php
 create mode 100644 app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php

diff --git a/app/Entities/Tools/Markdown/CustomStrikeThroughExtension.php b/app/Entities/Tools/Markdown/CustomStrikeThroughExtension.php
new file mode 100644
index 000000000..cb8b0ffc2
--- /dev/null
+++ b/app/Entities/Tools/Markdown/CustomStrikeThroughExtension.php
@@ -0,0 +1,16 @@
+<?php namespace BookStack\Entities\Tools\Markdown;
+
+use League\CommonMark\ConfigurableEnvironmentInterface;
+use League\CommonMark\Extension\ExtensionInterface;
+use League\CommonMark\Extension\Strikethrough\Strikethrough;
+use League\CommonMark\Extension\Strikethrough\StrikethroughDelimiterProcessor;
+
+class CustomStrikeThroughExtension implements ExtensionInterface
+{
+
+    public function register(ConfigurableEnvironmentInterface $environment)
+    {
+        $environment->addDelimiterProcessor(new StrikethroughDelimiterProcessor());
+        $environment->addInlineRenderer(Strikethrough::class, new CustomStrikethroughRenderer());
+    }
+}
\ No newline at end of file
diff --git a/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php b/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php
new file mode 100644
index 000000000..4371fb84c
--- /dev/null
+++ b/app/Entities/Tools/Markdown/CustomStrikethroughRenderer.php
@@ -0,0 +1,24 @@
+<?php namespace BookStack\Entities\Tools\Markdown;
+
+use League\CommonMark\ElementRendererInterface;
+use League\CommonMark\Extension\Strikethrough\Strikethrough;
+use League\CommonMark\HtmlElement;
+use League\CommonMark\Inline\Element\AbstractInline;
+use League\CommonMark\Inline\Renderer\InlineRendererInterface;
+
+/**
+ * This is a somewhat clone of the League\CommonMark\Extension\Strikethrough\StrikethroughRender
+ * class but modified slightly to use <s> HTML tags instead of <del> in order to
+ * match front-end markdown-it rendering.
+ */
+class CustomStrikethroughRenderer implements InlineRendererInterface
+{
+    public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer)
+    {
+        if (!($inline instanceof Strikethrough)) {
+            throw new \InvalidArgumentException('Incompatible inline type: ' . get_class($inline));
+        }
+
+        return new HtmlElement('s', $inline->getData('attributes', []), $htmlRenderer->renderInlines($inline->children()));
+    }
+}
\ No newline at end of file
diff --git a/app/Entities/Tools/PageContent.php b/app/Entities/Tools/PageContent.php
index 91de94211..62982f4ad 100644
--- a/app/Entities/Tools/PageContent.php
+++ b/app/Entities/Tools/PageContent.php
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Entities\Tools;
 
 use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\Markdown\CustomStrikeThroughExtension;
 use DOMDocument;
 use DOMNodeList;
 use DOMXPath;
@@ -51,6 +52,7 @@ class PageContent
         $environment = Environment::createCommonMarkEnvironment();
         $environment->addExtension(new TableExtension());
         $environment->addExtension(new TaskListExtension());
+        $environment->addExtension(new CustomStrikeThroughExtension());
         $converter = new CommonMarkConverter([], $environment);
         return $converter->convertToHtml($markdown);
     }
diff --git a/tests/Entity/PageContentTest.php b/tests/Entity/PageContentTest.php
index 857645823..6d5200794 100644
--- a/tests/Entity/PageContentTest.php
+++ b/tests/Entity/PageContentTest.php
@@ -461,4 +461,22 @@ class PageContentTest extends TestCase
         $pageView = $this->get($page->getUrl());
         $pageView->assertElementExists('.page-content input[type=checkbox]');
     }
+
+    public function test_page_markdown_strikethrough_rendering()
+    {
+        $this->asEditor();
+        $page = Page::query()->first();
+
+        $content = '~~some crossed out text~~';
+        $this->put($page->getUrl(), [
+            'name' => $page->name,  'markdown' => $content,
+            'html' => '', 'summary' => ''
+        ]);
+
+        $page->refresh();
+        $this->assertStringMatchesFormat('%A<s%A>some crossed out text</s>%A', $page->html);
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertElementExists('.page-content p > s');
+    }
 }