Content: Updated purifier and content caching

- Updated page content cache to use app version in cache key
- Moved purifier cache into framework to better work with existing
  expected folders.
- Added app version check to purifier so that it will reset its own
  cache on app version change.
This commit is contained in:
Dan Brown
2026-02-15 16:46:09 +00:00
parent 0f040fe8b1
commit 227027fc45
3 changed files with 36 additions and 5 deletions

View File

@@ -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}";
}
/**

View File

@@ -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