diff --git a/app/Entities/Tools/PageContent.php b/app/Entities/Tools/PageContent.php index 67c6e4cf6..436c4f0be 100644 --- a/app/Entities/Tools/PageContent.php +++ b/app/Entities/Tools/PageContent.php @@ -2,6 +2,7 @@ namespace BookStack\Entities\Tools; +use BookStack\App\AppVersion; use BookStack\Entities\Models\Page; use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Tools\Markdown\MarkdownToHtml; @@ -321,13 +322,12 @@ class PageContent $cacheKey = $this->getContentCacheKey($doc->getBodyInnerHtml()); $cached = cache()->get($cacheKey, null); if ($cached !== null) { -// return $cached; + return $cached; } $filterConfig = HtmlContentFilterConfig::fromConfigString(config('app.content_filtering')); $filter = new HtmlContentFilter($filterConfig); $filtered = $filter->filterDocument($doc); -// $filtered = $doc->getBodyInnerHtml(); $cacheTime = 86400 * 7; // 1 week cache()->put($cacheKey, $filtered, $cacheTime); @@ -340,7 +340,8 @@ class PageContent $contentHash = md5($html); $contentId = $this->page->id; $contentTime = $this->page->updated_at->timestamp; - return "page-content-cache::{$contentId}::{$contentTime}::{$contentHash}"; + $appVersion = AppVersion::get(); + return "page-content-cache::{$appVersion}::{$contentId}::{$contentTime}::{$contentHash}"; } /** diff --git a/app/Util/ConfiguredHtmlPurifier.php b/app/Util/ConfiguredHtmlPurifier.php index 5aab25b47..d63d2ad5f 100644 --- a/app/Util/ConfiguredHtmlPurifier.php +++ b/app/Util/ConfiguredHtmlPurifier.php @@ -2,19 +2,29 @@ namespace BookStack\Util; +use BookStack\App\AppVersion; use HTMLPurifier; use HTMLPurifier_Config; +use HTMLPurifier_DefinitionCache_Serializer; use HTMLPurifier_HTML5Config; use HTMLPurifier_HTMLDefinition; +/** + * Provides a configured HTML Purifier instance. + * https://github.com/ezyang/htmlpurifier + * Also uses this to extend support to HTML5 elements: + * https://github.com/xemlock/htmlpurifier-html5 + */ class ConfiguredHtmlPurifier { protected HTMLPurifier $purifier; + protected static bool $cachedChecked = false; public function __construct() { $config = HTMLPurifier_HTML5Config::createDefault(); $this->setConfig($config); + $this->resetCacheIfNeeded($config); $htmlDef = $config->getDefinition('HTML', true, true); if ($htmlDef instanceof HTMLPurifier_HTMLDefinition) { @@ -24,9 +34,29 @@ class ConfiguredHtmlPurifier $this->purifier = new HTMLPurifier($config); } + protected function resetCacheIfNeeded(HTMLPurifier_Config $config): void + { + if (self::$cachedChecked) { + return; + } + + $cachedForVersion = cache('htmlpurifier::cache-version'); + $appVersion = AppVersion::get(); + if ($cachedForVersion !== $appVersion) { + foreach (['HTML', 'CSS', 'URI'] as $name) { + $cache = new HTMLPurifier_DefinitionCache_Serializer($name); + $cache->flush($config); + } + cache()->set('htmlpurifier::cache-version', $appVersion); + } + + self::$cachedChecked = true; + } + protected function setConfig(HTMLPurifier_Config $config): void { - $config->set('Cache.SerializerPath', storage_path('purifier')); + $config->set('Cache.SerializerPath', storage_path('framework/purifier')); + $config->set('Core.AllowHostnameUnderscore', true); $config->set('CSS.AllowTricky', true); $config->set('HTML.SafeIframe', true); $config->set('Attr.EnableID', true); @@ -44,7 +74,7 @@ class ConfiguredHtmlPurifier 'file' => true, ]); - $config->set('Cache.DefinitionImpl', null); // Disable cache during testing + // $config->set('Cache.DefinitionImpl', null); // Disable cache during testing } public function configureDefinition(HTMLPurifier_HTMLDefinition $definition): void diff --git a/storage/purifier/.gitignore b/storage/framework/purifier/.gitignore similarity index 100% rename from storage/purifier/.gitignore rename to storage/framework/purifier/.gitignore