1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-08-02 04:10:25 +02:00

[1.7.0] Add more convenience functions to HTMLModule, wire Edit and Hypertext to use new functionality

- Added LanguageCode to AttrTypes. We should prefer string representations of attribute definitions.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1040 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang
2007-05-08 03:28:58 +00:00
parent 47fe34ad81
commit b81fb0af90
7 changed files with 127 additions and 45 deletions

View File

@@ -32,6 +32,7 @@ class HTMLPurifier_AttrTypes
$this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels(); $this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels();
$this->info['Text'] = new HTMLPurifier_AttrDef_Text(); $this->info['Text'] = new HTMLPurifier_AttrDef_Text();
$this->info['URI'] = new HTMLPurifier_AttrDef_URI(); $this->info['URI'] = new HTMLPurifier_AttrDef_URI();
$this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang();
// number is really a positive integer (one or more digits) // number is really a positive integer (one or more digits)
$this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true); $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true);

View File

@@ -16,6 +16,9 @@
class HTMLPurifier_HTMLModule class HTMLPurifier_HTMLModule
{ {
// -- Overloadable ----------------------------------------------------
/** /**
* Short unique string identifier of the module * Short unique string identifier of the module
*/ */
@@ -120,13 +123,15 @@ class HTMLPurifier_HTMLModule
*/ */
function setup(&$definition) {} function setup(&$definition) {}
// -- Convenience -----------------------------------------------------
/** /**
* Convenience function that sets up a new element * Convenience function that sets up a new element
* @param $element Name of element to add * @param $element Name of element to add
* @param $safe Is element safe for untrusted users to use? * @param $safe Is element safe for untrusted users to use?
* @param $type What content set should element be registered to? * @param $type What content set should element be registered to?
* Set as false to skip this step. * Set as false to skip this step.
* @param $content_model Content model definition in form of: * @param $contents Allowed children in form of:
* "$content_model_type: $content_model" * "$content_model_type: $content_model"
* @param $attr_includes What attribute collections to register to * @param $attr_includes What attribute collections to register to
* element? * element?
@@ -134,14 +139,12 @@ class HTMLPurifier_HTMLModule
* @note See ElementDef for in-depth descriptions of these parameters. * @note See ElementDef for in-depth descriptions of these parameters.
* @protected * @protected
*/ */
function addElement($element, $safe, $type, $content_model, $attr_includes, $attr) { function addElement($element, $safe, $type, $contents, $attr_includes, $attr) {
$this->elements[] = $element; $this->elements[] = $element;
// parse content_model // parse content_model
list($content_model_type, $content_model) = explode(':', $content_model); list($content_model_type, $content_model) = $this->parseContents($contents);
$content_model_type = strtolower(trim($content_model_type));
$content_model = trim($content_model);
// merge in attribute inclusions // merge in attribute inclusions
$attr[0] = $attr_includes; $this->mergeInAttrIncludes($attr, $attr_includes);
// add element to content sets // add element to content sets
if ($type) $this->addElementToContentSet($element, $type); if ($type) $this->addElementToContentSet($element, $type);
// create element // create element
@@ -163,6 +166,41 @@ class HTMLPurifier_HTMLModule
$this->content_sets[$type] .= $element; $this->content_sets[$type] .= $element;
} }
/**
* Convenience function that transforms single-string contents
* into separate content model and content model type
* @param $contents Allowed children in form of:
* "$content_model_type: $content_model"
*/
function parseContents($contents) {
switch ($contents) {
// check for shorthand content model forms
case 'Inline':
$contents = 'Optional: Inline | #PCDATA';
break;
case 'Flow':
$contents = 'Optional: Flow | #PCDATA';
break;
}
list($content_model_type, $content_model) = explode(':', $contents);
$content_model_type = strtolower(trim($content_model_type));
$content_model = trim($content_model);
return array($content_model_type, $content_model);
}
/**
* Convenience function that merges a list of attribute includes into
* an attribute array.
* @param $attr Reference to attr array to modify
* @param $attr_includes Array of includes / string include to merge in
*/
function mergeInAttrIncludes(&$attr, $attr_includes) {
if (!is_array($attr_includes)) {
if (empty($attr_includes)) $attr_includes = array();
else $attr_includes = array($attr_includes);
}
$attr[0] = $attr_includes;
}
} }
?> ?>

View File

@@ -18,7 +18,7 @@ class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule
function HTMLPurifier_HTMLModule_Bdo() { function HTMLPurifier_HTMLModule_Bdo() {
$dir = new HTMLPurifier_AttrDef_Enum(array('ltr','rtl'), false); $dir = new HTMLPurifier_AttrDef_Enum(array('ltr','rtl'), false);
$this->addElement( $this->addElement(
'bdo', true, 'Inline', 'Optional: #PCDATA | Inline', array('Core', 'Lang'), 'bdo', true, 'Inline', 'Inline', array('Core', 'Lang'),
array( array(
'dir' => $dir, // required 'dir' => $dir, // required
// The Abstract Module specification has the attribute // The Abstract Module specification has the attribute

View File

@@ -13,7 +13,7 @@ class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
'title' => 'CDATA', 'title' => 'CDATA',
), ),
'Lang' => array( 'Lang' => array(
'xml:lang' => false, // see constructor 'xml:lang' => 'LanguageCode',
), ),
'I18N' => array( 'I18N' => array(
0 => array('Lang'), // proprietary, for xml:lang/lang 0 => array('Lang'), // proprietary, for xml:lang/lang
@@ -22,10 +22,6 @@ class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule
0 => array('Core', 'I18N') 0 => array('Core', 'I18N')
) )
); );
function HTMLPurifier_HTMLModule_CommonAttributes() {
$this->attr_collections['Lang']['xml:lang'] = new HTMLPurifier_AttrDef_Lang();
}
} }
?> ?>

View File

@@ -11,28 +11,28 @@ class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule
{ {
var $name = 'Edit'; var $name = 'Edit';
var $elements = array('del', 'ins');
var $content_sets = array('Inline' => 'del | ins');
function HTMLPurifier_HTMLModule_Edit() { function HTMLPurifier_HTMLModule_Edit() {
foreach ($this->elements as $element) { $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow';
$this->info[$element] = new HTMLPurifier_ElementDef(); $attr = array(
$this->info[$element]->attr = array( 'cite' => 'URI',
0 => array('Common'), // 'datetime' => 'Datetime', // not implemented
'cite' => 'URI', );
// 'datetime' => 'Datetime' // Datetime not implemented $this->addElement(
); 'del', true, 'Inline', $contents, 'Common', $attr
// Inline context ! Block context (exclamation mark is );
// separator, see getChildDef for parsing) $this->addElement(
$this->info[$element]->content_model = 'ins', true, 'Inline', $contents, 'Common', $attr
'#PCDATA | Inline ! #PCDATA | Flow'; );
// HTML 4.01 specifies that ins/del must not contain block
// elements when used in an inline context, chameleon is
// a complicated workaround to acheive this effect
$this->info[$element]->content_model_type = 'chameleon';
}
} }
// HTML 4.01 specifies that ins/del must not contain block
// elements when used in an inline context, chameleon is
// a complicated workaround to acheive this effect
// Inline context ! Block context (exclamation mark is
// separator, see getChildDef for parsing)
var $defines_child_def = true; var $defines_child_def = true;
function getChildDef($def) { function getChildDef($def) {
if ($def->content_model_type != 'chameleon') return false; if ($def->content_model_type != 'chameleon') return false;

View File

@@ -10,24 +10,21 @@ class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule
{ {
var $name = 'Hypertext'; var $name = 'Hypertext';
var $elements = array('a');
var $content_sets = array('Inline' => 'a');
function HTMLPurifier_HTMLModule_Hypertext() { function HTMLPurifier_HTMLModule_Hypertext() {
$this->info['a'] = new HTMLPurifier_ElementDef(); $this->addElement(
$this->info['a']->attr = array( 'a', true, 'Inline', 'Inline', 'Common',
0 => array('Common'), array(
// 'accesskey' => 'Character', // 'accesskey' => 'Character',
// 'charset' => 'Charset', // 'charset' => 'Charset',
'href' => 'URI', 'href' => 'URI',
//'hreflang' => 'LanguageCode', // 'hreflang' => 'LanguageCode',
'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'), 'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'),
'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'), 'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'),
//'tabindex' => 'Number', // 'tabindex' => 'Number',
//'type' => 'ContentType', // 'type' => 'ContentType',
)
); );
$this->info['a']->content_model = '#PCDATA | Inline';
$this->info['a']->content_model_type = 'optional';
$this->info['a']->excludes = array('a' => true); $this->info['a']->excludes = array('a' => true);
} }

View File

@@ -45,6 +45,56 @@ class HTMLPurifier_HTMLModuleTest extends UnitTestCase
} }
function test_parseContents() {
$module = new HTMLPurifier_HTMLModule();
// pre-defined templates
$this->assertIdentical(
$module->parseContents('Inline'),
array('optional', 'Inline | #PCDATA')
);
$this->assertIdentical(
$module->parseContents('Flow'),
array('optional', 'Flow | #PCDATA')
);
// normalization procedures
$this->assertIdentical(
$module->parseContents('optional: a'),
array('optional', 'a')
);
$this->assertIdentical(
$module->parseContents('OPTIONAL :a'),
array('optional', 'a')
);
$this->assertIdentical(
$module->parseContents('Optional: a'),
array('optional', 'a')
);
// others
$this->assertIdentical(
$module->parseContents('Optional: a | b | c'),
array('optional', 'a | b | c')
);
}
function test_mergeInAttrIncludes() {
$module = new HTMLPurifier_HTMLModule();
$attr = array();
$module->mergeInAttrIncludes($attr, 'Common');
$this->assertIdentical($attr, array(0 => array('Common')));
$attr = array('a' => 'b');
$module->mergeInAttrIncludes($attr, array('Common', 'Good'));
$this->assertIdentical($attr, array('a' => 'b', 0 => array('Common', 'Good')));
}
} }
?> ?>