Escape story fields and clean story HTML with HTML Purifier.

Add HTML Purifier 4.19.1. Stories are purified when they are saved and
when they are shown.
This commit is contained in:
tim
2026-09-25 21:34:10 +02:00
parent e881ddb458
commit 00e1829f8b
385 changed files with 33174 additions and 12 deletions
@@ -0,0 +1,112 @@
<?php
class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
{
/**
* @type HTMLPurifier_Context
*/
private $context;
/**
* @type HTMLPurifier_Config
*/
private $config;
/**
* @type HTMLPurifier_AttrValidator
*/
private $attrValidator;
/**
* @type bool
*/
private $removeNbsp;
/**
* @type bool
*/
private $removeNbspExceptions;
/**
* Cached contents of %AutoFormat.RemoveEmpty.Predicate
* @type array
*/
private $exclude;
/**
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return void
*/
public function prepare($config, $context)
{
parent::prepare($config, $context);
$this->config = $config;
$this->context = $context;
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
$this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
foreach ($this->exclude as $key => $attrs) {
if (!is_array($attrs)) {
// HACK, see HTMLPurifier/Printer/ConfigForm.php
$this->exclude[$key] = explode(';', $attrs);
}
}
$this->attrValidator = new HTMLPurifier_AttrValidator();
}
/**
* @param HTMLPurifier_Token $token
*/
public function handleElement(&$token)
{
if (!$token instanceof HTMLPurifier_Token_Start) {
return;
}
$next = false;
$deleted = 1; // the current tag
for ($i = count($this->inputZipper->back) - 1; $i >= 0; $i--, $deleted++) {
$next = $this->inputZipper->back[$i];
if ($next instanceof HTMLPurifier_Token_Text) {
if ($next->is_whitespace) {
continue;
}
if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) {
$plain = str_replace("\xC2\xA0", "", $next->data);
$isWsOrNbsp = $plain === '' || ctype_space($plain);
if ($isWsOrNbsp) {
continue;
}
}
}
break;
}
if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) {
$this->attrValidator->validateToken($token, $this->config, $this->context);
$token->armor['ValidateAttributes'] = true;
if (isset($this->exclude[$token->name])) {
$r = true;
foreach ($this->exclude[$token->name] as $elem) {
if (!isset($token->attr[$elem])) $r = false;
}
if ($r) return;
}
if (isset($token->attr['id']) || isset($token->attr['name'])) {
return;
}
$token = $deleted + 1;
for ($b = 0, $c = count($this->inputZipper->front); $b < $c; $b++) {
$prev = $this->inputZipper->front[$b];
if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) {
continue;
}
break;
}
// This is safe because we removed the token that triggered this.
$this->rewindOffset($b+$deleted);
return;
}
}
}
// vim: et sw=4 sts=4