From 0dbf08453fcb79efc9aae21f5fa55f4547083456 Mon Sep 17 00:00:00 2001 From: Dan Brown 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 = '' . $html . ''; + 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.',