X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/4874dc1304ee26737884d787893743ae323e5cf2..refs/pull/5721/head:/app/Entities/Tools/PageIncludeParser.php diff --git a/app/Entities/Tools/PageIncludeParser.php b/app/Entities/Tools/PageIncludeParser.php index e55fc22c7..e0b89f158 100644 --- a/app/Entities/Tools/PageIncludeParser.php +++ b/app/Entities/Tools/PageIncludeParser.php @@ -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; } } }