For custom control of include tag parsing.
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;
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;
namespace BookStack\Theming;
+use BookStack\Entities\Models\Page;
+
/**
* The ThemeEvents used within BookStack.
*
/**
* 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
*/
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
$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 & 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', [