]> BookStack Code Mirror - bookstack/blobdiff - app/Entities/Tools/PageIncludeParser.php
Layout: Converted tri-layout component to ts
[bookstack] / app / Entities / Tools / PageIncludeParser.php
index e55fc22c7fc2734aaacdb431b056b2cfddc0810d..e0b89f158704298702122f9839898be6c9e6ffde 100644 (file)
@@ -19,24 +19,27 @@ class PageIncludeParser
      */
     protected array $toCleanup = [];
 
+    /**
+     * @param Closure(PageIncludeTag $tag): PageContent $pageContentForId
+     */
     public function __construct(
-        protected string $pageHtml,
+        protected HtmlDocument $doc,
         protected Closure $pageContentForId,
     ) {
     }
 
     /**
      * Parse out the include tags.
+     * Returns the count of new content DOM nodes added to the document.
      */
-    public function parse(): string
+    public function parse(): int
     {
-        $doc = new HtmlDocument($this->pageHtml);
-
-        $tags = $this->locateAndIsolateIncludeTags($doc);
+        $nodesAdded = 0;
+        $tags = $this->locateAndIsolateIncludeTags();
 
         foreach ($tags as $tag) {
-            $htmlContent = $this->pageContentForId->call($this, $tag->getPageId());
-            $content = new PageIncludeContent($htmlContent, $tag);
+            /** @var PageIncludeContent $content */
+            $content = $this->pageContentForId->call($this, $tag);
 
             if (!$content->isInline()) {
                 $parentP = $this->getParentParagraph($tag->domNode);
@@ -48,12 +51,14 @@ class PageIncludeParser
                 }
             }
 
-            $this->replaceNodeWithNodes($tag->domNode, $content->toDomNodes());
+            $replacementNodes = $content->toDomNodes();
+            $nodesAdded += count($replacementNodes);
+            $this->replaceNodeWithNodes($tag->domNode, $replacementNodes);
         }
 
         $this->cleanup();
 
-        return $doc->getBodyInnerHtml();
+        return $nodesAdded;
     }
 
     /**
@@ -61,14 +66,14 @@ class PageIncludeParser
      * own nodes in the DOM for future targeted manipulation.
      * @return PageIncludeTag[]
      */
-    protected function locateAndIsolateIncludeTags(HtmlDocument $doc): array
+    protected function locateAndIsolateIncludeTags(): array
     {
-        $includeHosts = $doc->queryXPath("//body//*[text()[contains(., '{{@')]]");
+        $includeHosts = $this->doc->queryXPath("//*[text()[contains(., '{{@')]]");
         $includeTags = [];
 
         /** @var DOMNode $node */
-        /** @var DOMNode $childNode */
         foreach ($includeHosts as $node) {
+            /** @var DOMNode $childNode */
             foreach ($node->childNodes as $childNode) {
                 if ($childNode->nodeName === '#text') {
                     array_push($includeTags, ...$this->splitTextNodesAtTags($childNode));
@@ -99,10 +104,10 @@ class PageIncludeParser
 
             if ($currentOffset < $tagStartOffset) {
                 $previousText = substr($text, $currentOffset, $tagStartOffset - $currentOffset);
-                $textNode->parentNode->insertBefore(new DOMText($previousText), $textNode);
+                $textNode->parentNode->insertBefore($this->doc->createTextNode($previousText), $textNode);
             }
 
-            $node = $textNode->parentNode->insertBefore(new DOMText($tagOuterContent), $textNode);
+            $node = $textNode->parentNode->insertBefore($this->doc->createTextNode($tagOuterContent), $textNode);
             $includeTags[] = new PageIncludeTag($tagInnerContent, $node);
             $currentOffset = $tagStartOffset + strlen($tagOuterContent);
         }
@@ -125,7 +130,7 @@ class PageIncludeParser
 
         foreach ($replacements as $replacement) {
             if ($replacement->ownerDocument !== $targetDoc) {
-                $replacement = $targetDoc->adoptNode($replacement);
+                $replacement = $targetDoc->importNode($replacement, true);
             }
 
             $toReplace->parentNode->insertBefore($replacement, $toReplace);
@@ -143,6 +148,7 @@ class PageIncludeParser
         $parentText = $parent->textContent;
         $tagPos = strpos($parentText, $tag->tagContent);
         $before = $tagPos < (strlen($parentText) / 2);
+        $this->toCleanup[] = $tag->domNode->parentNode;
 
         if ($before) {
             $parent->parentNode->insertBefore($tag->domNode, $parent);
@@ -168,8 +174,8 @@ class PageIncludeParser
         $parentNode->parentNode->insertBefore($parentClone, $parentNode);
         $parentClone->removeAttribute('id');
 
-        /** @var DOMNode $child */
         for ($i = 0; $i < $splitPos; $i++) {
+            /** @var DOMNode $child */
             $child = $children[$i];
             $parentClone->appendChild($child);
         }
@@ -190,7 +196,7 @@ class PageIncludeParser
                 return $parent;
             }
 
-            $parent = $parent->parentElement;
+            $parent = $parent->parentNode;
         } while ($parent !== null);
 
         return null;
@@ -204,8 +210,10 @@ class PageIncludeParser
     {
         foreach ($this->toCleanup as $element) {
             $element->normalize();
-            if ($element->parentNode && !$element->hasChildNodes()) {
-                $element->parentNode->removeChild($element);
+            while ($element->parentNode && !$element->hasChildNodes()) {
+                $parent = $element->parentNode;
+                $parent->removeChild($element);
+                $element = $parent;
             }
         }
     }