Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 13 additions & 25 deletions app/Entities/Tools/ExportFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
use BookStack\Entities\Tools\Markdown\HtmlToMarkdown;
use BookStack\Uploads\ImageService;
use BookStack\Util\CspService;
use DOMDocument;
use BookStack\Util\HtmlDocument;
use DOMElement;
use DOMXPath;
use Exception;
use Throwable;

Expand Down Expand Up @@ -151,45 +150,36 @@ public function bookToPdf(Book $book): string
protected function htmlToPdf(string $html): string
{
$html = $this->containHtml($html);
$html = $this->replaceIframesWithLinks($html);
$html = $this->openDetailElements($html);
$doc = new HtmlDocument();
$doc->loadCompleteHtml($html);

return $this->pdfGenerator->fromHtml($html);
$this->replaceIframesWithLinks($doc);
$this->openDetailElements($doc);
$cleanedHtml = $doc->getHtml();

return $this->pdfGenerator->fromHtml($cleanedHtml);
}

/**
* Within the given HTML content, Open any detail blocks.
*/
protected function openDetailElements(string $html): string
protected function openDetailElements(HtmlDocument $doc): void
{
libxml_use_internal_errors(true);

$doc = new DOMDocument();
$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$xPath = new DOMXPath($doc);

$details = $xPath->query('//details');
$details = $doc->queryXPath('//details');
/** @var DOMElement $detail */
foreach ($details as $detail) {
$detail->setAttribute('open', 'open');
}

return $doc->saveHTML();
}

/**
* Within the given HTML content, replace any iframe elements
* Within the given HTML document, replace any iframe elements
* with anchor links within paragraph blocks.
*/
protected function replaceIframesWithLinks(string $html): string
protected function replaceIframesWithLinks(HtmlDocument $doc): void
{
libxml_use_internal_errors(true);

$doc = new DOMDocument();
$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
$xPath = new DOMXPath($doc);
$iframes = $doc->queryXPath('//iframe');

$iframes = $xPath->query('//iframe');
/** @var DOMElement $iframe */
foreach ($iframes as $iframe) {
$link = $iframe->getAttribute('src');
Expand All @@ -203,8 +193,6 @@ protected function replaceIframesWithLinks(string $html): string
$paragraph->appendChild($anchor);
$iframe->parentNode->replaceChild($paragraph, $iframe);
}

return $doc->saveHTML();
}

/**
Expand Down
74 changes: 17 additions & 57 deletions app/Entities/Tools/PageContent.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
use BookStack\Uploads\ImageRepo;
use BookStack\Uploads\ImageService;
use BookStack\Util\HtmlContentFilter;
use DOMDocument;
use BookStack\Util\HtmlDocument;
use DOMElement;
use DOMNode;
use DOMNodeList;
use DOMXPath;
use Illuminate\Support\Str;

class PageContent
Expand Down Expand Up @@ -56,27 +55,17 @@ protected function extractBase64ImagesFromHtml(string $htmlText): string
return $htmlText;
}

$doc = $this->loadDocumentFromHtml($htmlText);
$container = $doc->documentElement;
$body = $container->childNodes->item(0);
$childNodes = $body->childNodes;
$xPath = new DOMXPath($doc);
$doc = new HtmlDocument($htmlText);

// Get all img elements with image data blobs
$imageNodes = $xPath->query('//img[contains(@src, \'data:image\')]');
$imageNodes = $doc->queryXPath('//img[contains(@src, \'data:image\')]');
foreach ($imageNodes as $imageNode) {
$imageSrc = $imageNode->getAttribute('src');
$newUrl = $this->base64ImageUriToUploadedImageUrl($imageSrc);
$imageNode->setAttribute('src', $newUrl);
}

// Generate inner html as a string
$html = '';
foreach ($childNodes as $childNode) {
$html .= $doc->saveHTML($childNode);
}

return $html;
return $doc->getBodyInnerHtml();
}

/**
Expand Down Expand Up @@ -172,27 +161,18 @@ protected function formatHtml(string $htmlText): string
return $htmlText;
}

$doc = $this->loadDocumentFromHtml($htmlText);
$container = $doc->documentElement;
$body = $container->childNodes->item(0);
$childNodes = $body->childNodes;
$xPath = new DOMXPath($doc);
$doc = new HtmlDocument($htmlText);

// Map to hold used ID references
$idMap = [];
// Map to hold changing ID references
$changeMap = [];

$this->updateIdsRecursively($body, 0, $idMap, $changeMap);
$this->updateLinks($xPath, $changeMap);
$this->updateIdsRecursively($doc->getBody(), 0, $idMap, $changeMap);
$this->updateLinks($doc, $changeMap);

// Generate inner html as a string
$html = '';
foreach ($childNodes as $childNode) {
$html .= $doc->saveHTML($childNode);
}

// Perform required string-level tweaks
// Generate inner html as a string & perform required string-level tweaks
$html = $doc->getBodyInnerHtml();
$html = str_replace(' ', ' ', $html);

return $html;
Expand Down Expand Up @@ -225,13 +205,13 @@ protected function updateIdsRecursively(DOMNode $element, int $depth, array &$id
* Update the all links in the given xpath to apply requires changes within the
* given $changeMap array.
*/
protected function updateLinks(DOMXPath $xpath, array $changeMap): void
protected function updateLinks(HtmlDocument $doc, array $changeMap): void
{
if (empty($changeMap)) {
return;
}

$links = $xpath->query('//body//*//*[@href]');
$links = $doc->queryXPath('//body//*//*[@href]');
/** @var DOMElement $domElem */
foreach ($links as $domElem) {
$href = ltrim($domElem->getAttribute('href'), '#');
Expand Down Expand Up @@ -321,11 +301,10 @@ public function getNavigation(string $htmlContent): array
return [];
}

$doc = $this->loadDocumentFromHtml($htmlContent);
$xPath = new DOMXPath($doc);
$headers = $xPath->query('//h1|//h2|//h3|//h4|//h5|//h6');
$doc = new HtmlDocument($htmlContent);
$headers = $doc->queryXPath('//h1|//h2|//h3|//h4|//h5|//h6');

return $headers ? $this->headerNodesToLevelList($headers) : [];
return $headers->count() === 0 ? [] : $this->headerNodesToLevelList($headers);
}

/**
Expand Down Expand Up @@ -420,7 +399,7 @@ protected function parsePageIncludes(string $html): string
protected function fetchSectionOfPage(Page $page, string $sectionId): string
{
$topLevelTags = ['table', 'ul', 'ol', 'pre'];
$doc = $this->loadDocumentFromHtml($page->html);
$doc = new HtmlDocument($page->html);

// Search included content for the id given and blank out if not exists.
$matchingElem = $doc->getElementById($sectionId);
Expand All @@ -430,30 +409,11 @@ protected function fetchSectionOfPage(Page $page, string $sectionId): string

// Otherwise replace the content with the found content
// Checks if the top-level wrapper should be included by matching on tag types
$innerContent = '';
$isTopLevel = in_array(strtolower($matchingElem->nodeName), $topLevelTags);
if ($isTopLevel) {
$innerContent .= $doc->saveHTML($matchingElem);
} else {
foreach ($matchingElem->childNodes as $childNode) {
$innerContent .= $doc->saveHTML($childNode);
}
return $doc->getNodeOuterHtml($matchingElem);
}
libxml_clear_errors();

return $innerContent;
}

/**
* Create and load a DOMDocument from the given html content.
*/
protected function loadDocumentFromHtml(string $html): DOMDocument
{
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$html = '<?xml encoding="utf-8" ?><body>' . $html . '</body>';
$doc->loadHTML($html);

return $doc;
return $doc->getNodeInnerHtml($matchingElem);
}
}
12 changes: 3 additions & 9 deletions app/References/CrossLinkParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
use BookStack\References\ModelResolvers\CrossLinkModelResolver;
use BookStack\References\ModelResolvers\PageLinkModelResolver;
use BookStack\References\ModelResolvers\PagePermalinkModelResolver;
use DOMDocument;
use DOMXPath;
use BookStack\Util\HtmlDocument;

class CrossLinkParser
{
Expand Down Expand Up @@ -54,13 +53,8 @@ protected function getLinksFromContent(string $html): array
{
$links = [];

$html = '<?xml encoding="utf-8" ?><body>' . $html . '</body>';
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML($html);

$xPath = new DOMXPath($doc);
$anchors = $xPath->query('//a[@href]');
$doc = new HtmlDocument($html);
$anchors = $doc->queryXPath('//a[@href]');

/** @var \DOMElement $anchor */
foreach ($anchors as $anchor) {
Expand Down
31 changes: 8 additions & 23 deletions app/References/ReferenceUpdater.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,14 @@
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\RevisionRepo;
use DOMDocument;
use DOMXPath;
use BookStack\Util\HtmlDocument;

class ReferenceUpdater
{
protected ReferenceFetcher $referenceFetcher;
protected RevisionRepo $revisionRepo;

public function __construct(ReferenceFetcher $referenceFetcher, RevisionRepo $revisionRepo)
{
$this->referenceFetcher = $referenceFetcher;
$this->revisionRepo = $revisionRepo;
public function __construct(
protected ReferenceFetcher $referenceFetcher,
protected RevisionRepo $revisionRepo
) {
}

public function updateEntityPageReferences(Entity $entity, string $oldLink)
Expand Down Expand Up @@ -96,13 +92,8 @@ protected function updateLinksInHtml(string $html, string $oldLink, string $newL
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]');
$doc = new HtmlDocument($html);
$anchors = $doc->queryXPath('//a[@href]');

/** @var \DOMElement $anchor */
foreach ($anchors as $anchor) {
Expand All @@ -111,12 +102,6 @@ protected function updateLinksInHtml(string $html, string $oldLink, string $newL
$anchor->setAttribute('href', $updated);
}

$html = '';
$topElems = $doc->documentElement->childNodes->item(0)->childNodes;
foreach ($topElems as $child) {
$html .= $doc->saveHTML($child);
}

return $html;
return $doc->getBodyInnerHtml();
}
}
12 changes: 3 additions & 9 deletions app/Search/SearchIndex.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use DOMDocument;
use BookStack\Util\HtmlDocument;
use DOMNode;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
Expand Down Expand Up @@ -138,16 +138,11 @@ protected function generateTermScoreMapFromHtml(string $html): array
'h6' => 1.5,
];

$html = '<?xml encoding="utf-8" ?><body>' . $html . '</body>';
$html = str_ireplace(['<br>', '<br />', '<br/>'], "\n", $html);
$doc = new HtmlDocument($html);

libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML($html);

$topElems = $doc->documentElement->childNodes->item(0)->childNodes;
/** @var DOMNode $child */
foreach ($topElems as $child) {
foreach ($doc->getBodyChildren() as $child) {
$nodeName = $child->nodeName;
$termCounts = $this->textToTermCountMap(trim($child->textContent));
foreach ($termCounts as $term => $count) {
Expand All @@ -168,7 +163,6 @@ protected function generateTermScoreMapFromHtml(string $html): array
*/
protected function generateTermScoreMapFromTags(array $tags): array
{
$scoreMap = [];
$names = [];
$values = [];

Expand Down
Loading