mirror of
https://github.com/ezyang/htmlpurifier.git
synced 2025-08-06 06:07:26 +02:00
Forms implementation for %HTML.Trusted. Some backend changes:
* Added Charsets and Character attribute types * Fix a heavily recursive form of ContentSets, this allows a content-set to include another content-set which includes another content-set, and so forth. Signed-off-by: Edward Z. Yang <edwardzyang@thewritingpot.com>
This commit is contained in:
@@ -115,6 +115,7 @@ require 'HTMLPurifier/AttrTransform/Border.php';
|
||||
require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
|
||||
require 'HTMLPurifier/AttrTransform/ImgRequired.php';
|
||||
require 'HTMLPurifier/AttrTransform/ImgSpace.php';
|
||||
require 'HTMLPurifier/AttrTransform/Input.php';
|
||||
require 'HTMLPurifier/AttrTransform/Lang.php';
|
||||
require 'HTMLPurifier/AttrTransform/Length.php';
|
||||
require 'HTMLPurifier/AttrTransform/Name.php';
|
||||
@@ -122,6 +123,7 @@ require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
|
||||
require 'HTMLPurifier/AttrTransform/SafeObject.php';
|
||||
require 'HTMLPurifier/AttrTransform/SafeParam.php';
|
||||
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
|
||||
require 'HTMLPurifier/AttrTransform/Textarea.php';
|
||||
require 'HTMLPurifier/ChildDef/Chameleon.php';
|
||||
require 'HTMLPurifier/ChildDef/Custom.php';
|
||||
require 'HTMLPurifier/ChildDef/Empty.php';
|
||||
@@ -137,6 +139,7 @@ require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
|
||||
require 'HTMLPurifier/HTMLModule/Bdo.php';
|
||||
require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
|
||||
require 'HTMLPurifier/HTMLModule/Edit.php';
|
||||
require 'HTMLPurifier/HTMLModule/Forms.php';
|
||||
require 'HTMLPurifier/HTMLModule/Hypertext.php';
|
||||
require 'HTMLPurifier/HTMLModule/Image.php';
|
||||
require 'HTMLPurifier/HTMLModule/Legacy.php';
|
||||
|
@@ -109,6 +109,7 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
|
||||
@@ -116,6 +117,7 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
|
||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
|
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
|
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
|
||||
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php';
|
||||
@@ -131,6 +133,7 @@ require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
|
||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php';
|
||||
|
39
library/HTMLPurifier/AttrTransform/Input.php
Normal file
39
library/HTMLPurifier/AttrTransform/Input.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Performs miscellaneous cross attribute validation and filtering for
|
||||
* input elements. This is meant to be a post-transform.
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform {
|
||||
|
||||
protected $pixels;
|
||||
|
||||
public function __construct() {
|
||||
$this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels();
|
||||
}
|
||||
|
||||
public function transform($attr, $config, $context) {
|
||||
if (!isset($attr['type'])) $t = 'text';
|
||||
else $t = strtolower($attr['type']);
|
||||
if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') {
|
||||
unset($attr['checked']);
|
||||
}
|
||||
if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') {
|
||||
unset($attr['maxlength']);
|
||||
}
|
||||
if (isset($attr['size']) && $t !== 'text' && $t !== 'password') {
|
||||
$result = $this->pixels->validate($attr['size'], $config, $context);
|
||||
if ($result === false) unset($attr['size']);
|
||||
else $attr['size'] = $result;
|
||||
}
|
||||
if (isset($attr['src']) && $t !== 'image') {
|
||||
unset($attr['src']);
|
||||
}
|
||||
if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {
|
||||
$attr['value'] = '';
|
||||
}
|
||||
return $attr;
|
||||
}
|
||||
|
||||
}
|
||||
|
16
library/HTMLPurifier/AttrTransform/Textarea.php
Normal file
16
library/HTMLPurifier/AttrTransform/Textarea.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Sets height/width defaults for <textarea>
|
||||
*/
|
||||
class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform
|
||||
{
|
||||
|
||||
public function transform($attr, $config, $context) {
|
||||
// Calculated from Firefox
|
||||
if (!isset($attr['cols'])) $attr['cols'] = '22';
|
||||
if (!isset($attr['rows'])) $attr['rows'] = '3';
|
||||
return $attr;
|
||||
}
|
||||
|
||||
}
|
@@ -32,6 +32,9 @@ class HTMLPurifier_AttrTypes
|
||||
|
||||
// unimplemented aliases
|
||||
$this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
|
||||
$this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text();
|
||||
$this->info['Charsets'] = new HTMLPurifier_AttrDef_Text();
|
||||
$this->info['Character'] = new HTMLPurifier_AttrDef_Text();
|
||||
|
||||
// number is really a positive integer (one or more digits)
|
||||
// FIXME: ^^ not always, see start and value of list items
|
||||
|
@@ -5,8 +5,6 @@
|
||||
*
|
||||
* @warning Currently this class is an all or nothing proposition, that is,
|
||||
* it will only give a bool return value.
|
||||
* @note This class is currently not used by any code, although it is unit
|
||||
* tested.
|
||||
*/
|
||||
class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
|
||||
{
|
||||
|
@@ -37,33 +37,35 @@ class HTMLPurifier_ContentSets
|
||||
// sorry, no way of overloading
|
||||
foreach ($modules as $module_i => $module) {
|
||||
foreach ($module->content_sets as $key => $value) {
|
||||
if (isset($this->info[$key])) {
|
||||
$temp = $this->convertToLookup($value);
|
||||
if (isset($this->lookup[$key])) {
|
||||
// add it into the existing content set
|
||||
$this->info[$key] = $this->info[$key] . ' | ' . $value;
|
||||
$this->lookup[$key] = array_merge($this->lookup[$key], $temp);
|
||||
} else {
|
||||
$this->info[$key] = $value;
|
||||
$this->lookup[$key] = $temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
// perform content_set expansions
|
||||
$this->keys = array_keys($this->info);
|
||||
foreach ($this->info as $i => $set) {
|
||||
// only performed once, so infinite recursion is not
|
||||
// a problem
|
||||
$this->info[$i] =
|
||||
str_replace(
|
||||
$this->keys,
|
||||
// must be recalculated each time due to
|
||||
// changing substitutions
|
||||
array_values($this->info),
|
||||
$set);
|
||||
$old_lookup = false;
|
||||
while ($old_lookup !== $this->lookup) {
|
||||
$old_lookup = $this->lookup;
|
||||
foreach ($this->lookup as $i => $set) {
|
||||
$add = array();
|
||||
foreach ($set as $element => $x) {
|
||||
if (isset($this->lookup[$element])) {
|
||||
$add += $this->lookup[$element];
|
||||
unset($this->lookup[$i][$element]);
|
||||
}
|
||||
}
|
||||
$this->lookup[$i] += $add;
|
||||
}
|
||||
}
|
||||
$this->values = array_values($this->info);
|
||||
|
||||
// generate lookup tables
|
||||
foreach ($this->info as $name => $set) {
|
||||
$this->lookup[$name] = $this->convertToLookup($set);
|
||||
foreach ($this->lookup as $key => $lookup) {
|
||||
$this->info[$key] = implode(' | ', array_keys($lookup));
|
||||
}
|
||||
$this->keys = array_keys($this->info);
|
||||
$this->values = array_values($this->info);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,12 +77,22 @@ class HTMLPurifier_ContentSets
|
||||
if (!empty($def->child)) return; // already done!
|
||||
$content_model = $def->content_model;
|
||||
if (is_string($content_model)) {
|
||||
$def->content_model = str_replace(
|
||||
$this->keys, $this->values, $content_model);
|
||||
// Assume that $this->keys is alphanumeric
|
||||
$def->content_model = preg_replace_callback(
|
||||
'/\b(' . implode('|', $this->keys) . ')\b/',
|
||||
array($this, 'generateChildDefCallback'),
|
||||
$content_model
|
||||
);
|
||||
//$def->content_model = str_replace(
|
||||
// $this->keys, $this->values, $content_model);
|
||||
}
|
||||
$def->child = $this->getChildDef($def, $module);
|
||||
}
|
||||
|
||||
public function generateChildDefCallback($matches) {
|
||||
return $this->info[$matches[0]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a ChildDef based on content_model and content_model_type
|
||||
* member variables in HTMLPurifier_ElementDef
|
||||
|
117
library/HTMLPurifier/HTMLModule/Forms.php
Normal file
117
library/HTMLPurifier/HTMLModule/Forms.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* XHTML 1.1 Forms module, defines all form-related elements found in HTML 4.
|
||||
*/
|
||||
class HTMLPurifier_HTMLModule_Forms extends HTMLPurifier_HTMLModule
|
||||
{
|
||||
public $name = 'Forms';
|
||||
public $safe = false;
|
||||
|
||||
public $content_sets = array(
|
||||
'Block' => 'Form',
|
||||
'Inline' => 'Formctrl',
|
||||
);
|
||||
|
||||
public function setup($config) {
|
||||
$form = $this->addElement('form', 'Form',
|
||||
'Required: Heading | List | Block | fieldset', 'Common', array(
|
||||
'accept' => 'ContentTypes',
|
||||
'accept-charset' => 'Charsets',
|
||||
'action*' => 'URI',
|
||||
'method' => 'Enum#get,post',
|
||||
// really ContentType, but these two are the only ones used today
|
||||
'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data',
|
||||
));
|
||||
$form->excludes = array('form' => true);
|
||||
|
||||
$input = $this->addElement('input', 'Formctrl', 'Empty', 'Common', array(
|
||||
'accept' => 'ContentTypes',
|
||||
'accesskey' => 'Character',
|
||||
'alt' => 'Text',
|
||||
'checked' => 'Bool#checked',
|
||||
'disabled' => 'Bool#disabled',
|
||||
'maxlength' => 'Number',
|
||||
'name' => 'CDATA',
|
||||
'readonly' => 'Bool#readonly',
|
||||
'size' => 'Number',
|
||||
'src' => 'URI#embeds',
|
||||
'tabindex' => 'Number',
|
||||
'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image',
|
||||
'value' => 'CDATA',
|
||||
));
|
||||
$input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input();
|
||||
|
||||
$this->addElement('select', 'Formctrl', 'Required: optgroup | option', 'Common', array(
|
||||
'disabled' => 'Bool#disabled',
|
||||
'multiple' => 'Bool#multiple',
|
||||
'name' => 'CDATA',
|
||||
'size' => 'Number',
|
||||
'tabindex' => 'Number',
|
||||
));
|
||||
|
||||
$this->addElement('option', false, 'Optional: #PCDATA', 'Common', array(
|
||||
'disabled' => 'Bool#disabled',
|
||||
'label' => 'Text',
|
||||
'selected' => 'Bool#selected',
|
||||
'value' => 'CDATA',
|
||||
));
|
||||
// It's illegal for there to be more than one selected, but not
|
||||
// be multiple. Also, no selected means undefined behavior. This might
|
||||
// be difficult to implement; perhaps an injector, or a context variable.
|
||||
|
||||
$textarea = $this->addElement('textarea', 'Formctrl', 'Optional: #PCDATA', 'Common', array(
|
||||
'accesskey' => 'Character',
|
||||
'cols*' => 'Number',
|
||||
'disabled' => 'Bool#disabled',
|
||||
'name' => 'CDATA',
|
||||
'readonly' => 'Bool#readonly',
|
||||
'rows*' => 'Number',
|
||||
'tabindex' => 'Number',
|
||||
));
|
||||
$textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea();
|
||||
|
||||
$button = $this->addElement('button', 'Formctrl', 'Optional: #PCDATA | Heading | List | Block | Inline', 'Common', array(
|
||||
'accesskey' => 'Character',
|
||||
'disabled' => 'Bool#disabled',
|
||||
'name' => 'CDATA',
|
||||
'tabindex' => 'Number',
|
||||
'type' => 'Enum#button,submit,reset',
|
||||
'value' => 'CDATA',
|
||||
));
|
||||
|
||||
// For exclusions, ideally we'd specify content sets, not literal elements
|
||||
$button->excludes = $this->makeLookup(
|
||||
'form', 'fieldset', // Form
|
||||
'input', 'select', 'textarea', 'label', 'button', // Formctrl
|
||||
'a' // as per HTML 4.01 spec, this is omitted by modularization
|
||||
);
|
||||
|
||||
// Extra exclusion: img usemap="" is not permitted within this element.
|
||||
// We'll omit this for now, since we don't have any good way of
|
||||
// indicating it yet.
|
||||
|
||||
// This is HIGHLY user-unfriendly; we need a custom child-def for this
|
||||
$this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common');
|
||||
|
||||
$label = $this->addElement('label', 'Formctrl', 'Optional: #PCDATA | Inline', 'Common', array(
|
||||
'accesskey' => 'Character',
|
||||
// 'for' => 'IDREF', // IDREF not implemented, cannot allow
|
||||
));
|
||||
$label->excludes = array('label' => true);
|
||||
|
||||
$this->addElement('legend', false, 'Optional: #PCDATA | Inline', 'Common', array(
|
||||
'accesskey' => 'Character',
|
||||
));
|
||||
|
||||
$this->addElement('optgroup', false, 'Required: option', 'Common', array(
|
||||
'disabled' => 'Bool#disabled',
|
||||
'label*' => 'Text',
|
||||
));
|
||||
|
||||
// Don't forget an injector for <isindex>. This one's a little complex
|
||||
// because it maps to multiple elements.
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -63,8 +63,11 @@ class HTMLPurifier_HTMLModuleManager
|
||||
$common = array(
|
||||
'CommonAttributes', 'Text', 'Hypertext', 'List',
|
||||
'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
|
||||
'StyleAttribute', 'Scripting', 'Object',
|
||||
'Name' // technically legacy, but present in all the specs
|
||||
'StyleAttribute',
|
||||
// Unsafe:
|
||||
'Scripting', 'Object', 'Forms',
|
||||
// Sorta legacy, but present in strict:
|
||||
'Name',
|
||||
);
|
||||
$transitional = array('Legacy', 'Target');
|
||||
$xml = array('XMLCommonAttributes');
|
||||
|
Reference in New Issue
Block a user