From ef51f8681a0428aec1b01f42e6bd962dc1cc483e Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Mon, 28 May 2007 02:20:55 +0000 Subject: [PATCH] [1.7.0] Create ConfigForm printer classes - Extend hash to convert strings from form key,value,key,value - Hack up configdoc to accommodate configForm.php smoketest git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1101 48356398-32a2-884e-a903-53898d9a118a --- NEWS | 4 + TODO | 1 - configdoc/generate.php | 32 +++- configdoc/styles/plain.xsl | 20 +- library/HTMLPurifier/Config.php | 8 + library/HTMLPurifier/ConfigSchema.php | 10 + library/HTMLPurifier/Printer.php | 16 +- library/HTMLPurifier/Printer/ConfigForm.css | 7 + library/HTMLPurifier/Printer/ConfigForm.js | 3 + library/HTMLPurifier/Printer/ConfigForm.php | 202 ++++++++++++++++++++ smoketests/all.php | 1 + smoketests/configForm.php | 66 +++++++ smoketests/testSchema.php | 44 +++++ tests/HTMLPurifier/ConfigSchemaTest.php | 2 + 14 files changed, 398 insertions(+), 18 deletions(-) create mode 100644 library/HTMLPurifier/Printer/ConfigForm.css create mode 100644 library/HTMLPurifier/Printer/ConfigForm.js create mode 100644 library/HTMLPurifier/Printer/ConfigForm.php create mode 100644 smoketests/configForm.php create mode 100644 smoketests/testSchema.php diff --git a/NEWS b/NEWS index 8d28f6ac..2413cb92 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,10 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier # New compact syntax for AttrDef objects that can be used to instantiate new objects via make() ! HTML Purifier now works in PHP 4.3.2. +! Configuration form-editing API makes tweaking HTMLPurifier_Config a + breeze! +! Configuration directives that accept hashes now allow new string + format: key1,value1,key2,value2 - Deprecated and removed EnableRedundantUTF8Cleaning. It didn't even work! . Unit test for ElementDef created, ElementDef behavior modified to be more flexible diff --git a/TODO b/TODO index acf2e7d8..e9fb016a 100644 --- a/TODO +++ b/TODO @@ -20,7 +20,6 @@ TODO List # Reorganize Unit Tests - Refactor loop tests (esp. AttrDef_URI) - Parse TinyMCE-style whitelist into our %HTML.Allow* whitelists - ? HTML interface for tweaking configuration to see changes 1.8 release [Refactor, refactor!] # URI validation routines tighter (see docs/dev-code-quality.html) (COMPLEX) diff --git a/configdoc/generate.php b/configdoc/generate.php index a5b06e96..d01878b8 100644 --- a/configdoc/generate.php +++ b/configdoc/generate.php @@ -14,6 +14,11 @@ TODO: - factor out code into classes */ +// there are several hacks for the configForm.php smoketest +// - load copies (override default schema) +// - file/line server information hack +// - post-processing, base-name change hack + // --------------------------------------------------------------------------- // Check and configure environment @@ -47,7 +52,15 @@ function appendHTMLDiv($document, $node, $html) { // --------------------------------------------------------------------------- // Load copies of HTMLPurifier_ConfigDef and HTMLPurifier -$schema = HTMLPurifier_ConfigSchema::instance(); +// hack +if (defined('HTMLPURIFIER_CUSTOM_SCHEMA')) { + // included from somewhere else + $var = HTMLPURIFIER_CUSTOM_SCHEMA; + $schema = $$var; + chdir(dirname(__FILE__)); +} else { + $schema = HTMLPurifier_ConfigSchema::instance(); +} $purifier = new HTMLPurifier(); @@ -153,8 +166,11 @@ foreach($schema->info as $namespace_name => $namespace_info) { foreach ($info->descriptions as $file => $file_descriptions) { foreach ($file_descriptions as $line => $description) { $dom_description = $dom_document->createElement('description'); - $dom_description->setAttribute('file', $file); - $dom_description->setAttribute('line', $line); + // hack + if (!defined('HTMLPURIFIER_CUSTOM_SCHEMA')) { + $dom_description->setAttribute('file', $file); + $dom_description->setAttribute('line', $line); + } appendHTMLDiv($dom_document, $dom_description, $description); $dom_descriptions->appendChild($dom_description); } @@ -203,9 +219,13 @@ if (class_exists('Tidy')) { $html_output = (string) $tidy; } -// write it to a file (todo: parse into seperate pages) -file_put_contents("$xsl_stylesheet_name.html", $html_output); - +// hack +if (!defined('HTMLPURIFIER_CUSTOM_SCHEMA')) { + // write it to a file (todo: parse into seperate pages) + file_put_contents("$xsl_stylesheet_name.html", $html_output); +} else { + $html_output = str_replace('styles/plain.css', HTMLPURIFIER_SCRIPT_LOCATION . 'styles/plain.css', $html_output); +} // --------------------------------------------------------------------------- // Output for instant feedback diff --git a/configdoc/styles/plain.xsl b/configdoc/styles/plain.xsl index cfd5a7ca..eb118778 100644 --- a/configdoc/styles/plain.xsl +++ b/configdoc/styles/plain.xsl @@ -76,15 +76,17 @@ - - - - + + + + + +
Used by: - - , - - -
Used by: + + , + + +
diff --git a/library/HTMLPurifier/Config.php b/library/HTMLPurifier/Config.php index 3c8d0f52..033f069e 100644 --- a/library/HTMLPurifier/Config.php +++ b/library/HTMLPurifier/Config.php @@ -147,6 +147,14 @@ class HTMLPurifier_Config return $this->conf[$namespace]; } + /** + * Retrieves all directives, organized by namespace + */ + function getAll() { + if (!$this->finalized && $this->autoFinalize) $this->finalize(); + return $this->conf; + } + /** * Sets a value to configuration. * @param $namespace String namespace diff --git a/library/HTMLPurifier/ConfigSchema.php b/library/HTMLPurifier/ConfigSchema.php index 1e58f196..f97ddf05 100644 --- a/library/HTMLPurifier/ConfigSchema.php +++ b/library/HTMLPurifier/ConfigSchema.php @@ -8,6 +8,7 @@ require_once 'HTMLPurifier/ConfigDef/DirectiveAlias.php'; /** * Configuration definition, defines directives and their defaults. + * @note If you update this, please update Printer_ConfigForm * @todo The ability to define things multiple times is confusing and should * be factored out to its own function named registerDependency() or * addNote(), where only the namespace.name and an extra descriptions @@ -304,6 +305,7 @@ class HTMLPurifier_ConfigSchema { if ($allow_null && $var === null) return null; switch ($type) { case 'mixed': + //if (is_string($var)) $var = unserialize($var); return $var; case 'istring': case 'string': @@ -344,6 +346,14 @@ class HTMLPurifier_ConfigSchema { $var = explode(',',$var); // remove spaces foreach ($var as $i => $j) $var[$i] = trim($j); + if ($type === 'hash') { + // key,value,key,value + $nvar = array(); + for ($i = 0, $c = count($var); $i + 1 < $c; $i += 2) { + $nvar[$var[$i]] = $var[$i + 1]; + } + $var = $nvar; + } } if (!is_array($var)) break; $keys = array_keys($var); diff --git a/library/HTMLPurifier/Printer.php b/library/HTMLPurifier/Printer.php index 8325ff45..95be17a2 100644 --- a/library/HTMLPurifier/Printer.php +++ b/library/HTMLPurifier/Printer.php @@ -28,9 +28,9 @@ class HTMLPurifier_Printer /** * Main function that renders object or aspect of that object - * @param $config Configuration object + * @note Parameters vary depending on printer */ - function render($config) {} + // function render() {} /** * Returns a start tag @@ -66,6 +66,18 @@ class HTMLPurifier_Printer $this->end($tag); } + function elementEmpty($tag, $attr = array()) { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_Empty($tag, $attr) + ); + } + + function text($text) { + return $this->generator->generateFromToken( + new HTMLPurifier_Token_Text($text) + ); + } + /** * Prints a simple key/value row in a table. * @param $name Key diff --git a/library/HTMLPurifier/Printer/ConfigForm.css b/library/HTMLPurifier/Printer/ConfigForm.css new file mode 100644 index 00000000..f9c9c936 --- /dev/null +++ b/library/HTMLPurifier/Printer/ConfigForm.css @@ -0,0 +1,7 @@ + +.hp-config {} + +.hp-config tbody th {text-align:right;} +.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;} +.hp-config .namespace th {text-align:center;} +.hp-config .verbose {display:none;} diff --git a/library/HTMLPurifier/Printer/ConfigForm.js b/library/HTMLPurifier/Printer/ConfigForm.js new file mode 100644 index 00000000..119ca4a0 --- /dev/null +++ b/library/HTMLPurifier/Printer/ConfigForm.js @@ -0,0 +1,3 @@ +function toggleWriteability(id_of_patient, checked) { + document.getElementById(id_of_patient).disabled = checked; +} \ No newline at end of file diff --git a/library/HTMLPurifier/Printer/ConfigForm.php b/library/HTMLPurifier/Printer/ConfigForm.php new file mode 100644 index 00000000..c579dd99 --- /dev/null +++ b/library/HTMLPurifier/Printer/ConfigForm.php @@ -0,0 +1,202 @@ +docURL = $doc_url; + $this->fields['default'] = new HTMLPurifier_Printer_ConfigForm_default(); + $this->fields['bool'] = new HTMLPurifier_Printer_ConfigForm_bool(); + } + + function render($config) { + $this->config = $config; + $all = $config->getAll(); + $ret = ''; + $ret .= $this->start('table', array('class' => 'hp-config')); + $ret .= $this->start('thead'); + $ret .= $this->start('tr'); + $ret .= $this->element('th', 'Directive'); + $ret .= $this->element('th', 'Value'); + $ret .= $this->end('tr'); + $ret .= $this->end('thead'); + foreach ($all as $ns => $directives) { + $ret .= $this->renderNamespace($ns, $directives); + } + $ret .= $this->end('table'); + return $ret; + } + + function renderNamespace($ns, $directives) { + $ret = ''; + $ret .= $this->start('tbody', array('class' => 'namespace')); + $ret .= $this->start('tr'); + $ret .= $this->element('th', $ns, array('colspan' => 2)); + $ret .= $this->end('tr'); + $ret .= $this->end('tbody'); + $ret .= $this->start('tbody'); + foreach ($directives as $directive => $value) { + $ret .= $this->start('tr'); + $ret .= $this->start('th'); + if ($this->docURL) $ret .= $this->start('a', array('href' => $this->docURL . "#$ns.$directive")); + $ret .= $this->element( + 'label', + "%$ns.$directive", + array('for' => "$ns.$directive") + ); + if ($this->docURL) $ret .= $this->end('a'); + $ret .= $this->end('th'); + + $ret .= $this->start('td'); + $def = $this->config->def->info[$ns][$directive]; + $type = $def->type; + if (!isset($this->fields[$type])) $type = 'default'; + $type_obj = $this->fields[$type]; + if ($def->allow_null) { + $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); + } + $ret .= $type_obj->render($ns, $directive, $value, $this->config); + $ret .= $this->end('td'); + $ret .= $this->end('tr'); + } + $ret .= $this->end('tbody'); + return $ret; + } + +} + +class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer { + var $obj; + function HTMLPurifier_Printer_ConfigForm_NullDecorator($obj) { + parent::HTMLPurifier_Printer(); + $this->obj = $obj; + } + function render($ns, $directive, $value, $config) { + $ret = ''; + $ret .= $this->start('label', array('for' => "Null_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' Null/Disabled'); + $ret .= $this->end('label'); + $attr = array( + 'type' => 'checkbox', + 'value' => '1', + 'class' => 'null-toggle', + 'name' => "Null_$ns.$directive", + 'id' => "Null_$ns.$directive", + 'onclick' => "toggleWriteability('$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! + ); + if ($value === null) $attr['checked'] = 'checked'; + $ret .= $this->elementEmpty('input', $attr); + $ret .= $this->text(' or '); + $ret .= $this->elementEmpty('br'); + $ret .= $this->obj->render($ns, $directive, $value, $config); + return $ret; + } +} + +class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { + function render($ns, $directive, $value, $config) { + $ret = ''; + $def = $config->def->info[$ns][$directive]; + if (is_array($value)) { + switch ($def->type) { + case 'lookup': + $array = $value; + $value = array(); + foreach ($array as $val => $b) { + $value[] = $val; + } + case 'list': + $value = implode(',', $value); + break; + case 'hash': + $nvalue = ''; + foreach ($value as $i => $v) { + $nvalue .= "$i,$v,"; + } + $value = $nvalue; + break; + default: + $value = ''; + } + } + if ($def->type === 'mixed') { + return 'Not supported'; + $value = serialize($value); + } + $attr = array( + 'type' => 'text', + 'name' => "$ns.$directive", + 'id' => "$ns.$directive" + ); + if ($value === null) $attr['disabled'] = 'disabled'; + if (is_array($def->allowed)) { + $ret .= $this->start('select', $attr); + foreach ($def->allowed as $val => $b) { + $attr = array(); + if ($value == $val) $attr['selected'] = 'selected'; + $ret .= $this->element('option', $val, $attr); + } + $ret .= $this->end('select'); + } else { + $attr['value'] = $value; + $ret .= $this->elementEmpty('input', $attr); + } + return $ret; + } +} + +class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer { + function render($ns, $directive, $value, $config) { + $ret = ''; + + $ret .= $this->start('div', array('id' => "$ns.$directive")); + + $ret .= $this->start('label', array('for' => "Yes_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' Yes'); + $ret .= $this->end('label'); + + $attr = array( + 'type' => 'radio', + 'name' => "Yes_$ns.$directive", + 'id' => "Yes_$ns.$directive", + 'value' => '1' + ); + if ($value) $attr['checked'] = 'checked'; + $ret .= $this->elementEmpty('input', $attr); + + $ret .= $this->start('label', array('for' => "No_$ns.$directive")); + $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); + $ret .= $this->text(' No'); + $ret .= $this->end('label'); + + $attr = array( + 'type' => 'radio', + 'name' => "No_$ns.$directive", + 'id' => "No_$ns.$directive", + 'value' => '0' + ); + if (!$value) $attr['checked'] = 'checked'; + $ret .= $this->elementEmpty('input', $attr); + + $ret .= $this->end('div'); + + return $ret; + } +} + +?> \ No newline at end of file diff --git a/smoketests/all.php b/smoketests/all.php index 743440be..de09412c 100644 --- a/smoketests/all.php +++ b/smoketests/all.php @@ -29,6 +29,7 @@ while (false !== ($filename = readdir($dh))) { if (strpos($filename, '.php') === false) continue; if ($filename == 'common.php') continue; if ($filename == 'all.php') continue; + if ($filename == 'testSchema.php') continue; ?> + + + HTML Purifier Config Form Smoketest + + + + + +

HTML Purifier Config Form Smoketest

+

This file outputs the configuration form for every single type +of directive possible.

+
+ $value) { + if (!strncmp($key, 'Null_', 5) && !empty($value)) { + unset($get[substr($key, 5)]); + unset($get[$key]); + } + if ($mq) $get[$key] = stripslashes($value); +} +$config = @HTMLPurifier_Config::create($get); + +$printer = new HTMLPurifier_Printer_ConfigForm('?doc'); +echo $printer->render($config); + +?> +
+ + [Reset] +
+
+
+getAll());
+?>
+
+ + \ No newline at end of file diff --git a/smoketests/testSchema.php b/smoketests/testSchema.php new file mode 100644 index 00000000..1295566e --- /dev/null +++ b/smoketests/testSchema.php @@ -0,0 +1,44 @@ + true, '2' => true, '3' => true), 'lookup', + 'What numbers of neutrons for this element have been observed?'); +CS::define('Element', 'Traits', array('nonmetallic', 'odorless', 'flammable'), 'list', + 'What are general properties of the element?'); +CS::define('Element', 'IsotopeNames', array('1' => 'protium', '2' => 'deuterium', '3' => 'tritium'), 'hash', + 'Lookup hash of neutron counts to formal names.'); + +CS::defineNamespace('Instrument', 'Of the musical type.'); + +CS::define('Instrument', 'Manufacturer', 'Yamaha', 'string', 'Who made it?'); +CS::defineAllowedValues('Instrument', 'Manufacturer', array( + 'Yamaha', 'Conn-Selmer', 'Vandoren', 'Laubin', 'Buffet', 'other')); +CS::defineValueAliases('Instrument', 'Manufacturer', array( + 'Selmer' => 'Conn-Selmer')); + +CS::define('Instrument', 'Family', 'woodwind', 'istring', 'What family is it?'); +CS::defineAllowedValues('Instrument', 'Family', array( + 'brass', 'woodwind', 'percussion', 'string', 'keyboard', 'electronic')); +CS::defineValueAliases('Instrument', 'Family', array( + 'synth' => 'electronic')); + +CS::defineNamespace('ReportCard', 'It is for grades.'); +CS::define('ReportCard', 'English', null, 'string/null', 'Grade from English class.'); +CS::define('ReportCard', 'Absences', 0, 'int', 'How many times missing from school?'); + +?> \ No newline at end of file diff --git a/tests/HTMLPurifier/ConfigSchemaTest.php b/tests/HTMLPurifier/ConfigSchemaTest.php index 8a20988f..56f23b76 100644 --- a/tests/HTMLPurifier/ConfigSchemaTest.php +++ b/tests/HTMLPurifier/ConfigSchemaTest.php @@ -288,6 +288,8 @@ class HTMLPurifier_ConfigSchemaTest extends UnitTestCase $this->assertValid(array(1 => 'moo'), 'hash'); $this->assertInvalid(array(0 => 'moo'), 'hash'); $this->assertValid('', 'hash', array()); + $this->assertValid('foo,bar,too,two', 'hash', array('foo' => 'bar', 'too' => 'two')); + $this->assertValid('foo,bar,too', 'hash', array('foo' => 'bar')); $this->assertValid(23, 'mixed');