]> BookStack Code Mirror - bookstack/commitdiff
Added "page_include_parse" theme event 3698/head
authorDan Brown <redacted>
Mon, 5 Sep 2022 15:40:42 +0000 (16:40 +0100)
committerDan Brown <redacted>
Mon, 5 Sep 2022 15:40:42 +0000 (16:40 +0100)
For custom control of include tag parsing.

app/Entities/Tools/PageContent.php
app/Theming/ThemeEvents.php
tests/ThemeTest.php

index ea6a185f161424f3cc85d0ef918860c980bc3227..17cd4a0ade501089b5bdd5b2be14684fd50e071f 100644 (file)
@@ -5,6 +5,8 @@ namespace BookStack\Entities\Tools;
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Tools\Markdown\MarkdownToHtml;
 use BookStack\Exceptions\ImageUploadException;
+use BookStack\Facades\Theme;
+use BookStack\Theming\ThemeEvents;
 use BookStack\Uploads\ImageRepo;
 use BookStack\Uploads\ImageService;
 use BookStack\Util\HtmlContentFilter;
@@ -372,23 +374,30 @@ class PageContent
                 continue;
             }
 
-            // Find page and skip this if page not found
+            // Find page to use, and default replacement to empty string for non-matches.
             /** @var ?Page $matchedPage */
             $matchedPage = Page::visible()->find($pageId);
-            if ($matchedPage === null) {
-                $html = str_replace($fullMatch, '', $html);
-                continue;
+            $replacement = '';
+
+            if ($matchedPage && count($splitInclude) === 1) {
+                // If we only have page id, just insert all page html and continue.
+                $replacement = $matchedPage->html;
+            } else if ($matchedPage && count($splitInclude) > 1) {
+                // Otherwise, if our include tag defines a section, load that specific content
+                $innerContent = $this->fetchSectionOfPage($matchedPage, $splitInclude[1]);
+                $replacement = trim($innerContent);
             }
 
-            // If we only have page id, just insert all page html and continue.
-            if (count($splitInclude) === 1) {
-                $html = str_replace($fullMatch, $matchedPage->html, $html);
-                continue;
-            }
+            $themeReplacement = Theme::dispatch(
+                ThemeEvents::PAGE_INCLUDE_PARSE,
+                $includeId,
+                $replacement,
+                clone $this->page,
+                $matchedPage ? (clone $matchedPage) : null,
+            );
 
-            // Create and load HTML into a document
-            $innerContent = $this->fetchSectionOfPage($matchedPage, $splitInclude[1]);
-            $html = str_replace($fullMatch, trim($innerContent), $html);
+            // Perform the content replacement
+            $html = str_replace($fullMatch, $themeReplacement ?? $replacement, $html);
         }
 
         return $html;
index 427147146140f74e081c9e872103814367e6429c..0a8efaee4c75093f1f63b301e1d6cd2201b03215 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace BookStack\Theming;
 
+use BookStack\Entities\Models\Page;
+
 /**
  * The ThemeEvents used within BookStack.
  *
@@ -60,8 +62,7 @@ class ThemeEvents
 
     /**
      * Commonmark environment configure.
-     * Provides the commonmark library environment for customization
-     * before it's used to render markdown content.
+     * Provides the commonmark library environment for customization before it's used to render markdown content.
      * If the listener returns a non-null value, that will be used as an environment instead.
      *
      * @param \League\CommonMark\ConfigurableEnvironmentInterface $environment
@@ -69,6 +70,21 @@ class ThemeEvents
      */
     const COMMONMARK_ENVIRONMENT_CONFIGURE = 'commonmark_environment_configure';
 
+    /**
+     * Page include parse event.
+     * Runs when a page include tag is being parsed, typically when page content is being processed for viewing.
+     * Provides the "include tag" reference string, the default BookStack replacement content for the tag,
+     * the current page being processed, and the page that's being referenced by the include tag.
+     * The referenced page may be null where the page does not exist or where permissions prevent visibility.
+     * If the listener returns a non-null value, that will be used as the replacement HTML content instead.
+     *
+     * @param string $tagReference
+     * @param string $replacementHTML
+     * @param Page   $currentPage
+     * @param ?Page  $referencedPage
+     */
+    const PAGE_INCLUDE_PARSE = 'page_include_parse';
+
     /**
      * Web before middleware action.
      * Runs before the request is handled but after all other middleware apart from those
index c90625e2b076505a5ae2a849ba03058f3598f26f..689c274888841713f21a37a8c6c3960a9e95eaf4 100644 (file)
@@ -214,6 +214,36 @@ class ThemeTest extends TestCase
         $this->assertEquals($book->id, $args[1]->id);
     }
 
+    public function test_event_page_include_parse()
+    {
+        /** @var Page $page */
+        /** @var Page $otherPage */
+        $page = Page::query()->first();
+        $otherPage = Page::query()->where('id', '!=', $page->id)->first();
+        $otherPage->html = '<p id="bkmrk-cool">This is a really cool section</p>';
+        $page->html = "<p>{{@{$otherPage->id}#bkmrk-cool}}</p>";
+        $page->save();
+        $otherPage->save();
+
+        $args = [];
+        $callback = function (...$eventArgs) use (&$args) {
+            $args = $eventArgs;
+            return '<strong>Big &amp; content replace surprise!</strong>';
+        };
+
+        Theme::listen(ThemeEvents::PAGE_INCLUDE_PARSE, $callback);
+        $resp = $this->asEditor()->get($page->getUrl());
+        $this->withHtml($resp)->assertElementContains('.page-content strong', 'Big & content replace surprise!');
+
+        $this->assertCount(4, $args);
+        $this->assertEquals($otherPage->id . '#bkmrk-cool', $args[0]);
+        $this->assertEquals('This is a really cool section', $args[1]);
+        $this->assertTrue($args[2] instanceof Page);
+        $this->assertTrue($args[3] instanceof Page);
+        $this->assertEquals($page->id, $args[2]->id);
+        $this->assertEquals($otherPage->id, $args[3]->id);
+    }
+
     public function test_add_social_driver()
     {
         Theme::addSocialDriver('catnet', [