From 0dbf08453fcb79efc9aae21f5fa55f4547083456 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 21 Aug 2022 11:29:34 +0100
Subject: [PATCH] Built out cross link replacer, not yet tested

---
 app/References/CrossLinkReplacer.php | 53 ++++++++++++++++++++++++++--
 resources/lang/en/entities.php       |  1 +
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/app/References/CrossLinkReplacer.php b/app/References/CrossLinkReplacer.php
index 5581fe33f..2df87fc83 100644
--- a/app/References/CrossLinkReplacer.php
+++ b/app/References/CrossLinkReplacer.php
@@ -5,6 +5,8 @@ namespace BookStack\References;
 use BookStack\Entities\Models\Entity;
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\RevisionRepo;
+use DOMDocument;
+use DOMXPath;
 
 class CrossLinkReplacer
 {
@@ -33,15 +35,60 @@ class CrossLinkReplacer
     protected function updateReferencesWithinPage(Page $page, string $oldLink, string $newLink)
     {
         $page = (clone $page)->refresh();
-        $html = '';// TODO - Update HTML content
-        $markdown = '';// TODO - Update markdown content
+        $html = $this->updateLinksInHtml($page->html, $oldLink, $newLink);
+        $markdown = $this->updateLinksInMarkdown($page->markdown, $oldLink, $newLink);
 
         $page->html = $html;
         $page->markdown = $markdown;
         $page->revision_count++;
         $page->save();
 
-        $summary = ''; // TODO - Get default summary from translations
+        $summary = trans('entities.pages_references_update_revision');
         $this->revisionRepo->storeNewForPage($page, $summary);
     }
+
+    protected function updateLinksInMarkdown(string $markdown, string $oldLink, string $newLink): string
+    {
+        if (empty($markdown)) {
+            return $markdown;
+        }
+
+        $commonLinkRegex = '/(\[.*?\]\()' . preg_quote($oldLink) . '(.*?\))/i';
+        $markdown = preg_replace($commonLinkRegex, '$1' . $newLink . '$2', $markdown);
+
+        $referenceLinkRegex = '/(\[.*?\]:\s?)' . preg_quote($oldLink) . '(.*?)($|\s)/i';
+        $markdown = preg_replace($referenceLinkRegex, '$1' . $newLink . '$2$3', $markdown);
+
+        return $markdown;
+    }
+
+    protected function updateLinksInHtml(string $html, string $oldLink, string $newLink): string
+    {
+        if (empty($html)) {
+            return $html;
+        }
+
+        $html = '<body>' . $html . '</body>';
+        libxml_use_internal_errors(true);
+        $doc = new DOMDocument();
+        $doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
+
+        $xPath = new DOMXPath($doc);
+        $anchors = $xPath->query('//a[@href]');
+
+        /** @var \DOMElement $anchor */
+        foreach ($anchors as $anchor) {
+            $link = $anchor->getAttribute('href');
+            $updated = str_ireplace($oldLink, $newLink, $link);
+            $anchor->setAttribute('href', $updated);
+        }
+
+        $html = '';
+        $topElems = $doc->documentElement->childNodes->item(0)->childNodes;
+        foreach ($topElems as $child) {
+            $html .= $doc->saveHTML($child);
+        }
+
+        return $html;
+    }
 }
\ No newline at end of file
diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php
index 527665f88..07d4b625d 100644
--- a/resources/lang/en/entities.php
+++ b/resources/lang/en/entities.php
@@ -249,6 +249,7 @@ return [
     'pages_edit_content_link' => 'Edit Content',
     'pages_permissions_active' => 'Page Permissions Active',
     'pages_initial_revision' => 'Initial publish',
+    'pages_references_update_revision' => 'System auto-update of internal links',
     'pages_initial_name' => 'New Page',
     'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.',
     'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.',