mirror of
https://github.com/ezyang/htmlpurifier.git
synced 2024-12-22 08:21:52 +00: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:
parent
dc28346677
commit
c9b6f125aa
1
NEWS
1
NEWS
@ -30,6 +30,7 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
|
|||||||
Users who are not performing inbound filtering, this may seem a little
|
Users who are not performing inbound filtering, this may seem a little
|
||||||
useless, but as a bonus, the test suite and handling of edge cases is also
|
useless, but as a bonus, the test suite and handling of edge cases is also
|
||||||
improved.
|
improved.
|
||||||
|
! Experimental implementation of forms for %HTML.Trusted
|
||||||
- Fix two bugs in %URI.MakeAbsolute; one involving empty paths in base URLs,
|
- Fix two bugs in %URI.MakeAbsolute; one involving empty paths in base URLs,
|
||||||
the other involving an undefined $is_folder error.
|
the other involving an undefined $is_folder error.
|
||||||
- Throw error when %Core.Encoding is set to a spurious value. Previously,
|
- Throw error when %Core.Encoding is set to a spurious value. Previously,
|
||||||
|
4
TODO
4
TODO
@ -22,7 +22,6 @@ FUTURE VERSIONS
|
|||||||
|
|
||||||
3.2 release [It's All About Trust] (floating)
|
3.2 release [It's All About Trust] (floating)
|
||||||
# Implement untrusted, dangerous elements/attributes
|
# Implement untrusted, dangerous elements/attributes
|
||||||
- Forms are especially wanted
|
|
||||||
# Implement IDREF support (harder than it seems, since you cannot have
|
# Implement IDREF support (harder than it seems, since you cannot have
|
||||||
IDREFs to non-existent IDs)
|
IDREFs to non-existent IDs)
|
||||||
# Frameset XHTML 1.0 and HTML 4.01 doctypes
|
# Frameset XHTML 1.0 and HTML 4.01 doctypes
|
||||||
@ -44,7 +43,6 @@ FUTURE VERSIONS
|
|||||||
contents should be dropped or not (currently, there's code that could do
|
contents should be dropped or not (currently, there's code that could do
|
||||||
something like this if it didn't drop the inner text too.)
|
something like this if it didn't drop the inner text too.)
|
||||||
- Remove <span> tags that don't do anything (no attributes)
|
- Remove <span> tags that don't do anything (no attributes)
|
||||||
- Remove empty inline tags<i></i>
|
|
||||||
- Append something to duplicate IDs so they're still usable (impl. note: the
|
- Append something to duplicate IDs so they're still usable (impl. note: the
|
||||||
dupe detector would also need to detect the suffix as well)
|
dupe detector would also need to detect the suffix as well)
|
||||||
- Externalize inline CSS to promote clean HTML, proposed by Sander Tekelenburg
|
- Externalize inline CSS to promote clean HTML, proposed by Sander Tekelenburg
|
||||||
@ -60,8 +58,6 @@ FUTURE VERSIONS
|
|||||||
Also, enable disabling of directionality
|
Also, enable disabling of directionality
|
||||||
|
|
||||||
5.0 release [To XML and Beyond]
|
5.0 release [To XML and Beyond]
|
||||||
- AllowedAttributes and ForbiddenAttributes step on the toes of XML by
|
|
||||||
using periods; this needs to be changed.
|
|
||||||
- Extended HTML capabilities based on namespacing and tag transforms (COMPLEX)
|
- Extended HTML capabilities based on namespacing and tag transforms (COMPLEX)
|
||||||
- Hooks for adding custom processors to custom namespaced tags and
|
- Hooks for adding custom processors to custom namespaced tags and
|
||||||
attributes, offer default implementation
|
attributes, offer default implementation
|
||||||
|
@ -151,7 +151,7 @@
|
|||||||
</directive>
|
</directive>
|
||||||
<directive id="HTML.Trusted">
|
<directive id="HTML.Trusted">
|
||||||
<file name="HTMLPurifier/HTMLModuleManager.php">
|
<file name="HTMLPurifier/HTMLModuleManager.php">
|
||||||
<line>199</line>
|
<line>202</line>
|
||||||
</file>
|
</file>
|
||||||
<file name="HTMLPurifier/Lexer.php">
|
<file name="HTMLPurifier/Lexer.php">
|
||||||
<line>233</line>
|
<line>233</line>
|
||||||
@ -168,27 +168,27 @@
|
|||||||
</directive>
|
</directive>
|
||||||
<directive id="HTML.AllowedModules">
|
<directive id="HTML.AllowedModules">
|
||||||
<file name="HTMLPurifier/HTMLModuleManager.php">
|
<file name="HTMLPurifier/HTMLModuleManager.php">
|
||||||
<line>206</line>
|
<line>209</line>
|
||||||
</file>
|
</file>
|
||||||
</directive>
|
</directive>
|
||||||
<directive id="HTML.CoreModules">
|
<directive id="HTML.CoreModules">
|
||||||
<file name="HTMLPurifier/HTMLModuleManager.php">
|
<file name="HTMLPurifier/HTMLModuleManager.php">
|
||||||
<line>207</line>
|
<line>210</line>
|
||||||
</file>
|
</file>
|
||||||
</directive>
|
</directive>
|
||||||
<directive id="HTML.Proprietary">
|
<directive id="HTML.Proprietary">
|
||||||
<file name="HTMLPurifier/HTMLModuleManager.php">
|
<file name="HTMLPurifier/HTMLModuleManager.php">
|
||||||
<line>221</line>
|
<line>224</line>
|
||||||
</file>
|
</file>
|
||||||
</directive>
|
</directive>
|
||||||
<directive id="HTML.SafeObject">
|
<directive id="HTML.SafeObject">
|
||||||
<file name="HTMLPurifier/HTMLModuleManager.php">
|
<file name="HTMLPurifier/HTMLModuleManager.php">
|
||||||
<line>226</line>
|
<line>229</line>
|
||||||
</file>
|
</file>
|
||||||
</directive>
|
</directive>
|
||||||
<directive id="HTML.SafeEmbed">
|
<directive id="HTML.SafeEmbed">
|
||||||
<file name="HTMLPurifier/HTMLModuleManager.php">
|
<file name="HTMLPurifier/HTMLModuleManager.php">
|
||||||
<line>229</line>
|
<line>232</line>
|
||||||
</file>
|
</file>
|
||||||
</directive>
|
</directive>
|
||||||
<directive id="Attr.IDBlacklist">
|
<directive id="Attr.IDBlacklist">
|
||||||
|
@ -115,6 +115,7 @@ require 'HTMLPurifier/AttrTransform/Border.php';
|
|||||||
require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
|
require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
|
||||||
require 'HTMLPurifier/AttrTransform/ImgRequired.php';
|
require 'HTMLPurifier/AttrTransform/ImgRequired.php';
|
||||||
require 'HTMLPurifier/AttrTransform/ImgSpace.php';
|
require 'HTMLPurifier/AttrTransform/ImgSpace.php';
|
||||||
|
require 'HTMLPurifier/AttrTransform/Input.php';
|
||||||
require 'HTMLPurifier/AttrTransform/Lang.php';
|
require 'HTMLPurifier/AttrTransform/Lang.php';
|
||||||
require 'HTMLPurifier/AttrTransform/Length.php';
|
require 'HTMLPurifier/AttrTransform/Length.php';
|
||||||
require 'HTMLPurifier/AttrTransform/Name.php';
|
require 'HTMLPurifier/AttrTransform/Name.php';
|
||||||
@ -122,6 +123,7 @@ require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
|
|||||||
require 'HTMLPurifier/AttrTransform/SafeObject.php';
|
require 'HTMLPurifier/AttrTransform/SafeObject.php';
|
||||||
require 'HTMLPurifier/AttrTransform/SafeParam.php';
|
require 'HTMLPurifier/AttrTransform/SafeParam.php';
|
||||||
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
|
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
|
||||||
|
require 'HTMLPurifier/AttrTransform/Textarea.php';
|
||||||
require 'HTMLPurifier/ChildDef/Chameleon.php';
|
require 'HTMLPurifier/ChildDef/Chameleon.php';
|
||||||
require 'HTMLPurifier/ChildDef/Custom.php';
|
require 'HTMLPurifier/ChildDef/Custom.php';
|
||||||
require 'HTMLPurifier/ChildDef/Empty.php';
|
require 'HTMLPurifier/ChildDef/Empty.php';
|
||||||
@ -137,6 +139,7 @@ require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
|
|||||||
require 'HTMLPurifier/HTMLModule/Bdo.php';
|
require 'HTMLPurifier/HTMLModule/Bdo.php';
|
||||||
require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
|
require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
|
||||||
require 'HTMLPurifier/HTMLModule/Edit.php';
|
require 'HTMLPurifier/HTMLModule/Edit.php';
|
||||||
|
require 'HTMLPurifier/HTMLModule/Forms.php';
|
||||||
require 'HTMLPurifier/HTMLModule/Hypertext.php';
|
require 'HTMLPurifier/HTMLModule/Hypertext.php';
|
||||||
require 'HTMLPurifier/HTMLModule/Image.php';
|
require 'HTMLPurifier/HTMLModule/Image.php';
|
||||||
require 'HTMLPurifier/HTMLModule/Legacy.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/EnumToCSS.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
|
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.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/Lang.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
|
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.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/SafeObject.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
|
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.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/Chameleon.php';
|
||||||
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
|
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
|
||||||
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.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/Bdo.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
|
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.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/Hypertext.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
|
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.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
|
// unimplemented aliases
|
||||||
$this->info['ContentType'] = new HTMLPurifier_AttrDef_Text();
|
$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)
|
// number is really a positive integer (one or more digits)
|
||||||
// FIXME: ^^ not always, see start and value of list items
|
// 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,
|
* @warning Currently this class is an all or nothing proposition, that is,
|
||||||
* it will only give a bool return value.
|
* 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
|
class HTMLPurifier_ChildDef_Custom extends HTMLPurifier_ChildDef
|
||||||
{
|
{
|
||||||
|
@ -37,33 +37,35 @@ class HTMLPurifier_ContentSets
|
|||||||
// sorry, no way of overloading
|
// sorry, no way of overloading
|
||||||
foreach ($modules as $module_i => $module) {
|
foreach ($modules as $module_i => $module) {
|
||||||
foreach ($module->content_sets as $key => $value) {
|
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
|
// add it into the existing content set
|
||||||
$this->info[$key] = $this->info[$key] . ' | ' . $value;
|
$this->lookup[$key] = array_merge($this->lookup[$key], $temp);
|
||||||
} else {
|
} else {
|
||||||
$this->info[$key] = $value;
|
$this->lookup[$key] = $temp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// perform content_set expansions
|
$old_lookup = false;
|
||||||
$this->keys = array_keys($this->info);
|
while ($old_lookup !== $this->lookup) {
|
||||||
foreach ($this->info as $i => $set) {
|
$old_lookup = $this->lookup;
|
||||||
// only performed once, so infinite recursion is not
|
foreach ($this->lookup as $i => $set) {
|
||||||
// a problem
|
$add = array();
|
||||||
$this->info[$i] =
|
foreach ($set as $element => $x) {
|
||||||
str_replace(
|
if (isset($this->lookup[$element])) {
|
||||||
$this->keys,
|
$add += $this->lookup[$element];
|
||||||
// must be recalculated each time due to
|
unset($this->lookup[$i][$element]);
|
||||||
// changing substitutions
|
}
|
||||||
array_values($this->info),
|
}
|
||||||
$set);
|
$this->lookup[$i] += $add;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->values = array_values($this->info);
|
|
||||||
|
|
||||||
// generate lookup tables
|
foreach ($this->lookup as $key => $lookup) {
|
||||||
foreach ($this->info as $name => $set) {
|
$this->info[$key] = implode(' | ', array_keys($lookup));
|
||||||
$this->lookup[$name] = $this->convertToLookup($set);
|
|
||||||
}
|
}
|
||||||
|
$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!
|
if (!empty($def->child)) return; // already done!
|
||||||
$content_model = $def->content_model;
|
$content_model = $def->content_model;
|
||||||
if (is_string($content_model)) {
|
if (is_string($content_model)) {
|
||||||
$def->content_model = str_replace(
|
// Assume that $this->keys is alphanumeric
|
||||||
$this->keys, $this->values, $content_model);
|
$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);
|
$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
|
* Instantiates a ChildDef based on content_model and content_model_type
|
||||||
* member variables in HTMLPurifier_ElementDef
|
* 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(
|
$common = array(
|
||||||
'CommonAttributes', 'Text', 'Hypertext', 'List',
|
'CommonAttributes', 'Text', 'Hypertext', 'List',
|
||||||
'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
|
'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
|
||||||
'StyleAttribute', 'Scripting', 'Object',
|
'StyleAttribute',
|
||||||
'Name' // technically legacy, but present in all the specs
|
// Unsafe:
|
||||||
|
'Scripting', 'Object', 'Forms',
|
||||||
|
// Sorta legacy, but present in strict:
|
||||||
|
'Name',
|
||||||
);
|
);
|
||||||
$transitional = array('Legacy', 'Target');
|
$transitional = array('Legacy', 'Target');
|
||||||
$xml = array('XMLCommonAttributes');
|
$xml = array('XMLCommonAttributes');
|
||||||
|
93
tests/HTMLPurifier/AttrTransform/InputTest.php
Normal file
93
tests/HTMLPurifier/AttrTransform/InputTest.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class HTMLPurifier_AttrTransform_InputTest extends HTMLPurifier_AttrTransformHarness
|
||||||
|
{
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->obj = new HTMLPurifier_AttrTransform_Input();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testEmptyInput() {
|
||||||
|
$this->assertResult(array());
|
||||||
|
}
|
||||||
|
|
||||||
|
function testInvalidCheckedWithEmpty() {
|
||||||
|
$this->assertResult(array('checked' => 'checked'), array());
|
||||||
|
}
|
||||||
|
|
||||||
|
function testInvalidCheckedWithPassword() {
|
||||||
|
$this->assertResult(array(
|
||||||
|
'checked' => 'checked',
|
||||||
|
'type' => 'password'
|
||||||
|
), array(
|
||||||
|
'type' => 'password'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testValidCheckedWithUcCheckbox() {
|
||||||
|
$this->assertResult(array(
|
||||||
|
'checked' => 'checked',
|
||||||
|
'type' => 'CHECKBOX',
|
||||||
|
'value' => 'bar',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testInvalidMaxlength() {
|
||||||
|
$this->assertResult(array(
|
||||||
|
'maxlength' => '10',
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'value' => 'foo',
|
||||||
|
), array(
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'value' => 'foo',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testValidMaxLength() {
|
||||||
|
$this->assertResult(array(
|
||||||
|
'maxlength' => '10',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// these two are really bad test-cases
|
||||||
|
|
||||||
|
function testSizeWithCheckbox() {
|
||||||
|
$this->assertResult(array(
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'value' => 'foo',
|
||||||
|
'size' => '100px',
|
||||||
|
), array(
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'value' => 'foo',
|
||||||
|
'size' => '100',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSizeWithText() {
|
||||||
|
$this->assertResult(array(
|
||||||
|
'type' => 'password',
|
||||||
|
'size' => '100px', // spurious value, to indicate no validation takes place
|
||||||
|
), array(
|
||||||
|
'type' => 'password',
|
||||||
|
'size' => '100px',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testInvalidSrc() {
|
||||||
|
$this->assertResult(array(
|
||||||
|
'src' => 'img.png',
|
||||||
|
), array());
|
||||||
|
}
|
||||||
|
|
||||||
|
function testMissingValue() {
|
||||||
|
$this->assertResult(array(
|
||||||
|
'type' => 'checkbox',
|
||||||
|
), array(
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'value' => '',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -69,5 +69,20 @@ class HTMLPurifier_ChildDef_CustomTest extends HTMLPurifier_ChildDefHarness
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testPcdata() {
|
||||||
|
$this->obj = new HTMLPurifier_ChildDef_Custom('#PCDATA,a');
|
||||||
|
$this->assertEqual($this->obj->elements, array('#PCDATA' => true, 'a' => true));
|
||||||
|
$this->assertResult('foo<a />');
|
||||||
|
$this->assertResult('<a />', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testWhitespace() {
|
||||||
|
$this->obj = new HTMLPurifier_ChildDef_Custom('a');
|
||||||
|
$this->assertEqual($this->obj->elements, array('a' => true));
|
||||||
|
$this->assertResult('foo<a />', false);
|
||||||
|
$this->assertResult('<a />');
|
||||||
|
$this->assertResult(' <a />');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +67,6 @@ class HTMLPurifier_ChildDef_RequiredTest extends HTMLPurifier_ChildDefHarness
|
|||||||
'Out <b>Bold text</b><img />',
|
'Out <b>Bold text</b><img />',
|
||||||
'Out <b>Bold text</b><img />'
|
'Out <b>Bold text</b><img />'
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +80,10 @@ class HTMLPurifier_ComplexHarness extends HTMLPurifier_Harness
|
|||||||
}
|
}
|
||||||
$this->assertIdentical($expect, $result);
|
$this->assertIdentical($expect, $result);
|
||||||
|
|
||||||
|
if ($expect !== $result) {
|
||||||
|
echo '<pre>' . htmlspecialchars($result) . '</pre>';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
137
tests/HTMLPurifier/HTMLModule/FormsTest.php
Normal file
137
tests/HTMLPurifier/HTMLModule/FormsTest.php
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class HTMLPurifier_HTMLModule_FormsTest extends HTMLPurifier_HTMLModuleHarness
|
||||||
|
{
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->config->set('HTML', 'Trusted', true);
|
||||||
|
$this->config->set('Attr', 'EnableID', true);
|
||||||
|
$this->config->set('Cache', 'DefinitionImpl', null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBasicUse() {
|
||||||
|
$this->assertResult( // need support for label for later
|
||||||
|
'
|
||||||
|
<form action="http://somesite.com/prog/adduser" method="post">
|
||||||
|
<p>
|
||||||
|
<label>First name: </label>
|
||||||
|
<input type="text" id="firstname" /><br />
|
||||||
|
<label>Last name: </label>
|
||||||
|
<input type="text" id="lastname" /><br />
|
||||||
|
<label>email: </label>
|
||||||
|
<input type="text" id="email" /><br />
|
||||||
|
<input type="radio" name="sex" value="Male" /> Male<br />
|
||||||
|
<input type="radio" name="sex" value="Female" /> Female<br />
|
||||||
|
<input type="submit" value="Send" /> <input type="reset" />
|
||||||
|
</p>
|
||||||
|
</form>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSelectOption() {
|
||||||
|
$this->assertResult('
|
||||||
|
<form action="http://somesite.com/prog/component-select" method="post">
|
||||||
|
<p>
|
||||||
|
<select multiple="multiple" size="4" name="component-select">
|
||||||
|
<option selected="selected" value="Component_1_a">Component_1</option>
|
||||||
|
<option selected="selected" value="Component_1_b">Component_2</option>
|
||||||
|
<option>Component_3</option>
|
||||||
|
<option>Component_4</option>
|
||||||
|
<option>Component_5</option>
|
||||||
|
<option>Component_6</option>
|
||||||
|
<option>Component_7</option>
|
||||||
|
</select>
|
||||||
|
<input type="submit" value="Send" /><input type="reset" />
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSelectOptgroup() {
|
||||||
|
$this->assertResult('
|
||||||
|
<form action="http://somesite.com/prog/someprog" method="post">
|
||||||
|
<p>
|
||||||
|
<select name="ComOS">
|
||||||
|
<option selected="selected" label="none" value="none">None</option>
|
||||||
|
<optgroup label="PortMaster 3">
|
||||||
|
<option label="3.7.1" value="pm3_3.7.1">PortMaster 3 with ComOS 3.7.1</option>
|
||||||
|
<option label="3.7" value="pm3_3.7">PortMaster 3 with ComOS 3.7</option>
|
||||||
|
<option label="3.5" value="pm3_3.5">PortMaster 3 with ComOS 3.5</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="PortMaster 2">
|
||||||
|
<option label="3.7" value="pm2_3.7">PortMaster 2 with ComOS 3.7</option>
|
||||||
|
<option label="3.5" value="pm2_3.5">PortMaster 2 with ComOS 3.5</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="IRX">
|
||||||
|
<option label="3.7R" value="IRX_3.7R">IRX with ComOS 3.7R</option>
|
||||||
|
<option label="3.5R" value="IRX_3.5R">IRX with ComOS 3.5R</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTextarea() {
|
||||||
|
$this->assertResult('
|
||||||
|
<form action="http://somesite.com/prog/text-read" method="post">
|
||||||
|
<p>
|
||||||
|
<textarea name="thetext" rows="20" cols="80">
|
||||||
|
First line of initial text.
|
||||||
|
Second line of initial text.
|
||||||
|
</textarea>
|
||||||
|
<input type="submit" value="Send" /><input type="reset" />
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
');
|
||||||
|
}
|
||||||
|
|
||||||
|
// label tests omitted
|
||||||
|
|
||||||
|
function testFieldset() {
|
||||||
|
$this->assertResult('
|
||||||
|
<form action="..." method="post">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Personal Information</legend>
|
||||||
|
Last Name: <input name="personal_lastname" type="text" tabindex="1" />
|
||||||
|
First Name: <input name="personal_firstname" type="text" tabindex="2" />
|
||||||
|
Address: <input name="personal_address" type="text" tabindex="3" />
|
||||||
|
...more personal information...
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Medical History</legend>
|
||||||
|
<input name="history_illness" type="checkbox" value="Smallpox" tabindex="20" />Smallpox
|
||||||
|
<input name="history_illness" type="checkbox" value="Mumps" tabindex="21" /> Mumps
|
||||||
|
<input name="history_illness" type="checkbox" value="Dizziness" tabindex="22" /> Dizziness
|
||||||
|
<input name="history_illness" type="checkbox" value="Sneezing" tabindex="23" /> Sneezing
|
||||||
|
...more medical history...
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Current Medication</legend>
|
||||||
|
Are you currently taking any medication?
|
||||||
|
<input name="medication_now" type="radio" value="Yes" tabindex="35" />Yes
|
||||||
|
<input name="medication_now" type="radio" value="No" tabindex="35" />No
|
||||||
|
|
||||||
|
If you are currently taking medication, please indicate
|
||||||
|
it in the space below:
|
||||||
|
<textarea name="current_medication" rows="20" cols="50" tabindex="40"></textarea>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testInputTransform() {
|
||||||
|
$this->assertResult('<input type="checkbox" />', '<input type="checkbox" value="" />');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTextareaTransform() {
|
||||||
|
$this->assertResult('<textarea></textarea>', '<textarea cols="22" rows="3"></textarea>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTextInFieldset() {
|
||||||
|
$this->assertResult('<fieldset> <legend></legend>foo</fieldset>');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user