From b81fb0af906cfb9259d0d99e5b8c8a4261d26257 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Tue, 8 May 2007 03:28:58 +0000 Subject: [PATCH] [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 --- library/HTMLPurifier/AttrTypes.php | 1 + library/HTMLPurifier/HTMLModule.php | 50 ++++++++++++++++--- library/HTMLPurifier/HTMLModule/Bdo.php | 2 +- .../HTMLModule/CommonAttributes.php | 6 +-- library/HTMLPurifier/HTMLModule/Edit.php | 36 ++++++------- library/HTMLPurifier/HTMLModule/Hypertext.php | 27 +++++----- tests/HTMLPurifier/HTMLModuleTest.php | 50 +++++++++++++++++++ 7 files changed, 127 insertions(+), 45 deletions(-) diff --git a/library/HTMLPurifier/AttrTypes.php b/library/HTMLPurifier/AttrTypes.php index e13d0d30..ea6c1912 100644 --- a/library/HTMLPurifier/AttrTypes.php +++ b/library/HTMLPurifier/AttrTypes.php @@ -32,6 +32,7 @@ class HTMLPurifier_AttrTypes $this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels(); $this->info['Text'] = new HTMLPurifier_AttrDef_Text(); $this->info['URI'] = new HTMLPurifier_AttrDef_URI(); + $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang(); // number is really a positive integer (one or more digits) $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true); diff --git a/library/HTMLPurifier/HTMLModule.php b/library/HTMLPurifier/HTMLModule.php index 2a0ec9f4..c38610c6 100644 --- a/library/HTMLPurifier/HTMLModule.php +++ b/library/HTMLPurifier/HTMLModule.php @@ -16,6 +16,9 @@ class HTMLPurifier_HTMLModule { + + // -- Overloadable ---------------------------------------------------- + /** * Short unique string identifier of the module */ @@ -120,13 +123,15 @@ class HTMLPurifier_HTMLModule */ function setup(&$definition) {} + // -- Convenience ----------------------------------------------------- + /** * Convenience function that sets up a new element * @param $element Name of element to add * @param $safe Is element safe for untrusted users to use? * @param $type What content set should element be registered to? * 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" * @param $attr_includes What attribute collections to register to * element? @@ -134,14 +139,12 @@ class HTMLPurifier_HTMLModule * @note See ElementDef for in-depth descriptions of these parameters. * @protected */ - function addElement($element, $safe, $type, $content_model, $attr_includes, $attr) { + function addElement($element, $safe, $type, $contents, $attr_includes, $attr) { $this->elements[] = $element; // parse content_model - list($content_model_type, $content_model) = explode(':', $content_model); - $content_model_type = strtolower(trim($content_model_type)); - $content_model = trim($content_model); + list($content_model_type, $content_model) = $this->parseContents($contents); // merge in attribute inclusions - $attr[0] = $attr_includes; + $this->mergeInAttrIncludes($attr, $attr_includes); // add element to content sets if ($type) $this->addElementToContentSet($element, $type); // create element @@ -163,6 +166,41 @@ class HTMLPurifier_HTMLModule $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; + } } ?> \ No newline at end of file diff --git a/library/HTMLPurifier/HTMLModule/Bdo.php b/library/HTMLPurifier/HTMLModule/Bdo.php index 5b280d64..e8f4349e 100644 --- a/library/HTMLPurifier/HTMLModule/Bdo.php +++ b/library/HTMLPurifier/HTMLModule/Bdo.php @@ -18,7 +18,7 @@ class HTMLPurifier_HTMLModule_Bdo extends HTMLPurifier_HTMLModule function HTMLPurifier_HTMLModule_Bdo() { $dir = new HTMLPurifier_AttrDef_Enum(array('ltr','rtl'), false); $this->addElement( - 'bdo', true, 'Inline', 'Optional: #PCDATA | Inline', array('Core', 'Lang'), + 'bdo', true, 'Inline', 'Inline', array('Core', 'Lang'), array( 'dir' => $dir, // required // The Abstract Module specification has the attribute diff --git a/library/HTMLPurifier/HTMLModule/CommonAttributes.php b/library/HTMLPurifier/HTMLModule/CommonAttributes.php index 8f17c2f0..cad3c6da 100644 --- a/library/HTMLPurifier/HTMLModule/CommonAttributes.php +++ b/library/HTMLPurifier/HTMLModule/CommonAttributes.php @@ -13,7 +13,7 @@ class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule 'title' => 'CDATA', ), 'Lang' => array( - 'xml:lang' => false, // see constructor + 'xml:lang' => 'LanguageCode', ), 'I18N' => array( 0 => array('Lang'), // proprietary, for xml:lang/lang @@ -22,10 +22,6 @@ class HTMLPurifier_HTMLModule_CommonAttributes extends HTMLPurifier_HTMLModule 0 => array('Core', 'I18N') ) ); - - function HTMLPurifier_HTMLModule_CommonAttributes() { - $this->attr_collections['Lang']['xml:lang'] = new HTMLPurifier_AttrDef_Lang(); - } } ?> \ No newline at end of file diff --git a/library/HTMLPurifier/HTMLModule/Edit.php b/library/HTMLPurifier/HTMLModule/Edit.php index c3dc0197..720e4927 100644 --- a/library/HTMLPurifier/HTMLModule/Edit.php +++ b/library/HTMLPurifier/HTMLModule/Edit.php @@ -11,28 +11,28 @@ class HTMLPurifier_HTMLModule_Edit extends HTMLPurifier_HTMLModule { var $name = 'Edit'; - var $elements = array('del', 'ins'); - var $content_sets = array('Inline' => 'del | ins'); function HTMLPurifier_HTMLModule_Edit() { - foreach ($this->elements as $element) { - $this->info[$element] = new HTMLPurifier_ElementDef(); - $this->info[$element]->attr = array( - 0 => array('Common'), - 'cite' => 'URI', - // 'datetime' => 'Datetime' // Datetime not implemented - ); - // Inline context ! Block context (exclamation mark is - // separator, see getChildDef for parsing) - $this->info[$element]->content_model = - '#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'; - } + $contents = 'Chameleon: #PCDATA | Inline ! #PCDATA | Flow'; + $attr = array( + 'cite' => 'URI', + // 'datetime' => 'Datetime', // not implemented + ); + $this->addElement( + 'del', true, 'Inline', $contents, 'Common', $attr + ); + $this->addElement( + 'ins', true, 'Inline', $contents, 'Common', $attr + ); } + // 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; function getChildDef($def) { if ($def->content_model_type != 'chameleon') return false; diff --git a/library/HTMLPurifier/HTMLModule/Hypertext.php b/library/HTMLPurifier/HTMLModule/Hypertext.php index baa20fd1..fd89d7df 100644 --- a/library/HTMLPurifier/HTMLModule/Hypertext.php +++ b/library/HTMLPurifier/HTMLModule/Hypertext.php @@ -10,24 +10,21 @@ class HTMLPurifier_HTMLModule_Hypertext extends HTMLPurifier_HTMLModule { var $name = 'Hypertext'; - var $elements = array('a'); - var $content_sets = array('Inline' => 'a'); function HTMLPurifier_HTMLModule_Hypertext() { - $this->info['a'] = new HTMLPurifier_ElementDef(); - $this->info['a']->attr = array( - 0 => array('Common'), - // 'accesskey' => 'Character', - // 'charset' => 'Charset', - 'href' => 'URI', - //'hreflang' => 'LanguageCode', - 'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'), - 'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'), - //'tabindex' => 'Number', - //'type' => 'ContentType', + $this->addElement( + 'a', true, 'Inline', 'Inline', 'Common', + array( + // 'accesskey' => 'Character', + // 'charset' => 'Charset', + 'href' => 'URI', + // 'hreflang' => 'LanguageCode', + 'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'), + 'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'), + // 'tabindex' => 'Number', + // 'type' => 'ContentType', + ) ); - $this->info['a']->content_model = '#PCDATA | Inline'; - $this->info['a']->content_model_type = 'optional'; $this->info['a']->excludes = array('a' => true); } diff --git a/tests/HTMLPurifier/HTMLModuleTest.php b/tests/HTMLPurifier/HTMLModuleTest.php index 9ea48de7..58ef183e 100644 --- a/tests/HTMLPurifier/HTMLModuleTest.php +++ b/tests/HTMLPurifier/HTMLModuleTest.php @@ -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'))); + + } + } ?> \ No newline at end of file