Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- use BookStack\Entities\Models\Page;
- use BookStack\Facades\Theme;
- use BookStack\Theming\ThemeEvents;
- use BookStack\Entities\Tools\PageContent;
- Theme::listen(ThemeEvents::PAGE_INCLUDE_PARSE, function(string $tagReference, string $replacementHTML, Page $currentPage, ?Page $referencedPage) {
- $splitInclude = explode('#', $tagReference, 2);
- //don't do anything if we only have the page ID.
- if (count($splitInclude) >= 2) {
- $sectionID = $splitInclude[1];
- $sectionsBelowHeaders = (new ExtendedPageContent($currentPage))->fetchSectionsBelowHeader($referencedPage, $sectionID);
- if ( $sectionsBelowHeaders != '' ){
- $replacementHTML = $sectionsBelowHeaders;
- }
- }
- return $replacementHTML;
- });
- class ExtendedPageContent extends PageContent {
- /**
- * Fetch the sub-contents from below a header, and add a "Quelle des Auszugs" blockquote link at the start.
- */
- public function fetchSectionsBelowHeader(Page $page, string $sectionId): string
- {
- // Lade den HTML-Inhalt der Seite.
- $html = $page->html;
- $doc = new DOMDocument();
- libxml_use_internal_errors(true); // Fehler unterdrücken, um leere HTML-Tags zu verarbeiten
- $doc->loadHTML($html);
- // Suche das Element mit der angegebenen ID
- $matchingElem = $doc->getElementById($sectionId);
- if ($matchingElem === null) {
- return '';
- }
- // Wenn das Element kein Heading ist, gib nichts zurück.
- if (!preg_match("/^h\\d$/i", $matchingElem->nodeName)) {
- return '';
- }
- // Hole die URL der Seite
- $url = $page->getUrl();
- // Füge das <hr> vor dem Blockquote hinzu
- $hrTagBefore = '<hr>';
- // Füge den Link zur Originalseite als Blockquote hinzu
- $linkHTML = '<blockquote><a href="' . htmlspecialchars($url) . '" target="_blank">Quelle des Auszugs</a></blockquote>';
- // Hole den Inhalt unterhalb des Headers
- $innerContent = '';
- $originalHeadingLevel = (int) filter_var($matchingElem->nodeName, FILTER_SANITIZE_NUMBER_INT);
- $nextSib = $matchingElem->nextSibling;
- while ($nextSib) {
- // Breche ab, wenn der nächste Bruder ein Heading ist, das gleich oder wichtiger als das Original ist
- if (preg_match("/^h\\d$/i", $nextSib->nodeName)) {
- $nextSibHeadingLevel = (int) filter_var($nextSib->nodeName, FILTER_SANITIZE_NUMBER_INT);
- $preHeadingLevel = $this->getPreHeadingLevel($sectionId);
- // Berechne das neue Level der Überschrift
- $newLevel = $nextSibHeadingLevel - ($originalHeadingLevel - $preHeadingLevel);
- $newLevel = max(5, $newLevel); // Verhindere, dass die Überschrift unter H5 sinkt
- // Setze das neue Tag, je nach Level
- $name = ($newLevel <= 5) ? "h" . $newLevel : "strong";
- $nextSib = $this->changeTagName($nextSib, $name);
- // Breche bei einem Heading auf gleichem oder höherem Niveau ab
- if ($nextSibHeadingLevel <= $originalHeadingLevel) {
- break;
- }
- }
- // Füge den HTML-Inhalt des aktuellen Siblings hinzu
- $innerContent .= $doc->saveHTML($nextSib);
- // Gehe zum nächsten Sibling
- $nextSib = $nextSib->nextSibling;
- }
- libxml_clear_errors(); // Fehlerbereinigung
- // Füge das <hr> am Ende des Inhalts hinzu
- $hrTagAfter = '<hr>';
- // Füge das <hr> vor dem Blockquote, den Blockquote-Link und das <hr> nach dem Inhalt zusammen
- return $hrTagBefore . $linkHTML . $innerContent . $hrTagAfter;
- }
- /**
- * Gets the level of the heading on the currentPage immediately before the insertion point.
- * Fallback value is 1 if no headings are found.
- *
- * @param string $sectionId
- *
- * @return int
- */
- protected function getPreHeadingLevel(string $sectionId): int
- {
- $doc = new DOMDocument();
- libxml_use_internal_errors(true);
- $doc->loadHTML($this->page->html);
- $xpath = new DOMXPath($doc);
- $query = "//*[contains(text(),'$sectionId')]/preceding::*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6][1]";
- $nodeList = $xpath->query($query);
- // Falls kein vorheriges Heading gefunden wird, setze das Standard-Level auf 1
- $preHeadingLevel = 1;
- if ($nodeList->length > 0) {
- $preHeadingLevel = (int) filter_var($nodeList[0]->nodeName, FILTER_SANITIZE_NUMBER_INT);
- }
- return $preHeadingLevel;
- }
- /**
- * Renames a node in a DOM Document.
- *
- * @param DOMElement $node
- * @param string $name
- *
- * @return DOMNode
- */
- protected function changeTagName(DOMElement $node, string $name)
- {
- $childnodes = [];
- foreach ($node->childNodes as $child) {
- $childnodes[] = $child;
- }
- $newnode = $node->ownerDocument->createElement($name);
- foreach ($childnodes as $child) {
- $child2 = $node->ownerDocument->importNode($child, true);
- $newnode->appendChild($child2);
- }
- foreach ($node->attributes as $attrName => $attrNode) {
- $attrName = $attrNode->nodeName;
- $attrValue = $attrNode->nodeValue;
- $newnode->setAttribute($attrName, $attrValue);
- }
- $node->parentNode->replaceChild($newnode, $node);
- return $newnode;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment