0
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2024-12-22 08:21:52 +00:00

[3.1.0] Feature parity with configdoc rewrite

- Abolish most classes in ConfigDoc except for HTMLXSLTProcessor
- Implement Builder_Xml using XmlWriter
- Add some convenience functions

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1665 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang 2008-04-22 01:58:06 +00:00
parent 50aa0ea714
commit 949f605857
18 changed files with 182 additions and 269 deletions

4
TODO
View File

@ -27,10 +27,6 @@ IMPORTANT FEATURES
- Figure out autoload and PEAR
CONFIGDOC
- Properly integrate new ConfigSchema system into configdoc. DESCRIPTIONS
ARE CURRENTLY BROKEN AND NEED TO BE FIXED!!! (Configdoc
should directly read the configuration files, or at the very least should
not use static functions)
- Have configdoc use version and deprecated information (hide deprecated
info, for example)
- Implement source code sniffing for configdoc, so we can easily figure out

View File

@ -18,8 +18,7 @@ TODO:
if (version_compare(PHP_VERSION, '5.2.0', '<')) exit('PHP 5.2.0 or greater required.');
error_reporting(E_ALL | E_STRICT);
echo 'Currently broken!';
exit;
chdir(dirname(__FILE__));
// load dual-libraries
require_once '../extras/HTMLPurifierExtras.auto.php';
@ -30,10 +29,19 @@ HTMLPurifier::getInstance(array(
'AutoFormat.PurifierLinkify' => true
));
$schema = HTMLPurifier_ConfigSchema::instance();
$style = 'plain'; // use $_GET in the future
$configdoc = new ConfigDoc();
$output = $configdoc->generate($schema, $style);
$interchange = HTMLPurifier_ConfigSchema_InterchangeBuilder::buildFromDirectory();
$interchange->validate();
$style = 'plain'; // use $_GET in the future, careful to validate!
$configdoc_xml = 'configdoc.xml';
$xml_builder = new HTMLPurifier_ConfigSchema_Builder_Xml();
$xml_builder->openURI($configdoc_xml);
$xml_builder->build($interchange);
$xslt = new ConfigDoc_HTMLXSLTProcessor();
$xslt->importStylesheet(dirname(__FILE__) . "/styles/$style.xsl");
$output = $xslt->transformToHTML($configdoc_xml);
if (!$output) {
echo "Error in generating files\n";

View File

@ -64,7 +64,7 @@
</xsl:template>
<xsl:template match="namespace/description">
<div class="description">
<xsl:copy-of select="div/node()" />
<xsl:copy-of xmlns:xhtml="http://www.w3.org/1999/xhtml" select="xhtml:div/node()" />
</div>
</xsl:template>
@ -91,17 +91,19 @@
</table>
</xsl:template>
<xsl:template match="directive/aliases" mode="constraints">
<th>Aliases:</th>
<td>
<xsl:for-each select="alias">
<xsl:if test="position()&gt;1">, </xsl:if>
<xsl:value-of select="." />
</xsl:for-each>
</td>
<tr>
<th>Aliases:</th>
<td>
<xsl:for-each select="alias">
<xsl:if test="position()&gt;1">, </xsl:if>
<xsl:value-of select="." />
</xsl:for-each>
</td>
</tr>
</xsl:template>
<xsl:template match="directive/description">
<div class="description">
<xsl:copy-of select="div/node()" />
<xsl:copy-of xmlns:xhtml="http://www.w3.org/1999/xhtml" select="xhtml:div/node()" />
</div>
</xsl:template>

14
configdoc/types.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<types>
<type id="string">String</type>
<type id="istring">Case-insensitive string</type>
<type id="text">Text</type>
<type id="itext">Case-insensitive text</type>
<type id="int">Integer</type>
<type id="float">Float</type>
<type id="bool">Boolean</type>
<type id="lookup">Lookup array</type>
<type id="list">Array list</type>
<type id="hash">Associative array</type>
<type id="mixed">Mixed</type>
</types>

View File

@ -1,46 +0,0 @@
<?php
/**
* Facade class for configuration documentation system
*/
class ConfigDoc
{
/**
* Generates configuration documentation based on a HTMLPurifier_ConfigSchema
* object and styleshet name
* @param $schema Instance of HTMLPurifier_ConfigSchema to document
* @param $xsl_stylesheet_name Name of XSL stylesheet in ../styles/ directory to use
* @param $parameters Extra parameters to pass to the stylesheet
* @return string HTML output
*/
public function generate($schema, $xsl_stylesheet_name = 'plain', $parameters = array()) {
// generate types document, describing type constraints
$types_serializer = new ConfigDoc_XMLSerializer_Types();
$types_document = $types_serializer->serialize($schema);
$types_document->save(dirname(__FILE__) . '/../types.xml'); // only ONE
// generate configdoc.xml, documents configuration directives
$schema_serializer = new ConfigDoc_XMLSerializer_ConfigSchema();
$schema_document = $schema_serializer->serialize($schema);
$schema_document->save('configdoc.xml');
// setup transformation
$xsl_stylesheet = dirname(__FILE__) . "/../styles/$xsl_stylesheet_name.xsl";
$xslt_processor = new ConfigDoc_HTMLXSLTProcessor();
$xslt_processor->setParameters($parameters);
$xslt_processor->importStylesheet($xsl_stylesheet);
return $xslt_processor->transformToHTML($schema_document);
}
/**
* Remove any generated files
* @return boolean Success?
*/
public function cleanup() {
return unlink('configdoc.xml');
}
}

View File

@ -1,13 +0,0 @@
<?php
class ConfigDoc_DOM_Document extends DOMDocument
{
/**
* Register our classes
*/
public function __construct($version = "1.0", $encoding = "UTF-8") {
parent::__construct($version, $encoding);
parent::registerNodeClass('DOMDocument', 'ConfigDoc_DOM_Document');
parent::registerNodeClass('DOMElement', 'ConfigDoc_DOM_Element');
}
}

View File

@ -1,27 +0,0 @@
<?php
class ConfigDoc_DOM_Element extends DOMElement
{
/**
* Appends an HTML div to this node
*/
public function appendHTMLDiv($html) {
$this->appendChild($this->generateHTMLDiv($html));
}
/**
* Generates an HTML div that can contain arbitrary markup
*/
protected function generateHTMLDiv($html) {
$purifier = HTMLPurifier::getInstance();
$html = $purifier->purify($html);
$dom_html = $this->ownerDocument->createDocumentFragment();
$dom_html->appendXML($html);
$dom_div = $this->ownerDocument->createElement('div');
$dom_div->setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
$dom_div->appendChild($dom_html);
return $dom_div;
}
}

View File

@ -30,18 +30,23 @@ class ConfigDoc_HTMLXSLTProcessor
/**
* Transforms an XML file into compatible XHTML based on the stylesheet
* @param $xml XML DOM tree
* @param $xml XML DOM tree, or string filename
* @return string HTML output
* @todo Rename to transformToXHTML, as transformToHTML is misleading
*/
public function transformToHTML($xml) {
$out = $this->xsltProcessor->transformToXML($xml);
if (is_string($xml)) {
$dom = new DOMDocument();
$dom->load($xml);
} else {
$dom = $xml;
}
$out = $this->xsltProcessor->transformToXML($dom);
// fudges for HTML backwards compatibility
// assumes that document is XHTML
$out = str_replace('/>', ' />', $out); // <br /> not <br/>
$out = str_replace(' xmlns=""', '', $out); // rm unnecessary xmlns
$out = str_replace(' xmlns="http://www.w3.org/1999/xhtml"', '', $out); // rm unnecessary xmlns
if (class_exists('Tidy')) {
// cleanup output

View File

@ -1,11 +0,0 @@
<?php
/**
* The XMLSerializer hierarchy of classes consist of classes that take
* objects and convert them to XML form.
*/
class ConfigDoc_XMLSerializer
{
}

View File

@ -1,110 +0,0 @@
<?php
class ConfigDoc_XMLSerializer_ConfigSchema extends ConfigDoc_XMLSerializer
{
/**
* Serializes a schema into DOM form
* @todo Split into sub-serializers
* @param $schema HTMLPurifier_ConfigSchema to serialize
* @return DOMDocument representation of schema
*/
public function serialize($schema) {
$dom_document = new DOMDocument('1.0', 'UTF-8');
$dom_root = $dom_document->createElement('configdoc');
$dom_document->appendChild($dom_root);
$dom_document->formatOutput = true;
// add the name of the application
$dom_root->appendChild($dom_document->createElement('title', 'HTML Purifier'));
/*
TODO for XML format:
- create a definition (DTD or other) once interface stabilizes
*/
foreach($schema->info as $namespace_name => $namespace_info) {
$dom_namespace = $dom_document->createElement('namespace');
$dom_root->appendChild($dom_namespace);
$dom_namespace->setAttribute('id', $namespace_name);
$dom_namespace->appendChild(
$dom_document->createElement('name', $namespace_name)
);
$dom_namespace_description = $dom_document->createElement('description');
$dom_namespace->appendChild($dom_namespace_description);
$this->appendHTMLDiv($dom_document, $dom_namespace_description,
$schema->info_namespace[$namespace_name]->description);
foreach ($namespace_info as $name => $info) {
if ($info->class == 'alias') continue;
$dom_directive = $dom_document->createElement('directive');
$dom_namespace->appendChild($dom_directive);
$dom_directive->setAttribute('id', $namespace_name . '.' . $name);
$dom_directive->appendChild(
$dom_document->createElement('name', $name)
);
$dom_aliases = $dom_document->createElement('aliases');
$dom_directive->appendChild($dom_aliases);
foreach ($info->directiveAliases as $alias) {
$dom_aliases->appendChild($dom_document->createElement('alias', $alias));
}
$dom_constraints = $dom_document->createElement('constraints');
$dom_directive->appendChild($dom_constraints);
$dom_type = $dom_document->createElement('type', $info->type);
if ($info->allow_null) {
$dom_type->setAttribute('allow-null', 'yes');
}
$dom_constraints->appendChild($dom_type);
if ($info->allowed !== true) {
$dom_allowed = $dom_document->createElement('allowed');
$dom_constraints->appendChild($dom_allowed);
foreach ($info->allowed as $allowed => $bool) {
$dom_allowed->appendChild(
$dom_document->createElement('value', $allowed)
);
}
}
$raw_default = $schema->defaults[$namespace_name][$name];
if (is_bool($raw_default)) {
$default = $raw_default ? 'true' : 'false';
} elseif (is_string($raw_default)) {
$default = "\"$raw_default\"";
} elseif (is_null($raw_default)) {
$default = 'null';
} else {
$default = print_r(
$schema->defaults[$namespace_name][$name], true
);
}
$dom_default = $dom_document->createElement('default', $default);
// remove this once we get a DTD
$dom_default->setAttribute('xml:space', 'preserve');
$dom_constraints->appendChild($dom_default);
$dom_description = $dom_document->createElement('description');
$this->appendHTMLDiv($dom_document, $dom_description, $info->description);
$dom_directive->appendChild($dom_description);
}
}
return $dom_document;
}
}

View File

@ -1,25 +0,0 @@
<?php
class ConfigDoc_XMLSerializer_Types extends ConfigDoc_XMLSerializer
{
/**
* Serializes the types in a schema into DOM form
* @param $schema HTMLPurifier_ConfigSchema owner of types to serialize
* @return DOMDocument representing schema types
*/
public function serialize($schema) {
$types_document = new DOMDocument('1.0', 'UTF-8');
$types_root = $types_document->createElement('types');
$types_document->appendChild($types_root);
$types_document->formatOutput = true;
foreach ($schema->types as $name => $expanded_name) {
$types_type = $types_document->createElement('type', $expanded_name);
$types_type->setAttribute('id', $name);
$types_root->appendChild($types_type);
}
return $types_document;
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* Converts HTMLPurifier_ConfigSchema_Interchange to an XML format,
* which can be further processed to generate documentation.
*/
class HTMLPurifier_ConfigSchema_Builder_Xml extends XMLWriter
{
protected $interchange;
protected function writeHTMLDiv($html) {
$this->startElement('div');
$purifier = HTMLPurifier::getInstance();
$html = $purifier->purify($html);
$this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
$this->writeRaw($html);
$this->endElement(); // div
}
protected function export($var) {
if ($var === array()) return 'array()';
return var_export($var, true);
}
public function build($interchange) {
// global access, only use as last resort
$this->interchange = $interchange;
$this->setIndent(true);
$this->startDocument('1.0', 'UTF-8');
$this->startElement('configdoc');
$this->writeElement('title', $interchange->name);
foreach ($interchange->namespaces as $namespace) {
$this->buildNamespace($namespace);
}
$this->endElement(); // configdoc
$this->flush();
}
public function buildNamespace($namespace) {
$this->startElement('namespace');
$this->writeAttribute('id', $namespace->namespace);
$this->writeElement('name', $namespace->namespace);
$this->startElement('description');
$this->writeHTMLDiv($namespace->description);
$this->endElement(); // description
foreach ($this->interchange->directives as $directive) {
if ($directive->id->namespace !== $namespace->namespace) continue;
$this->buildDirective($directive);
}
$this->endElement(); // namespace
}
public function buildDirective($directive) {
$this->startElement('directive');
$this->writeAttribute('id', $directive->id->toString());
$this->writeElement('name', $directive->id->directive);
$this->startElement('aliases');
foreach ($directive->aliases as $alias) $this->writeElement('alias', $alias->toString());
$this->endElement(); // aliases
$this->startElement('constraints');
$this->writeElement('type', $directive->type);
if ($directive->typeAllowsNull) $this->writeAttribute('allow-null', 'yes');
if ($directive->allowed) {
$this->startElement('allowed');
foreach ($directive->allowed as $value => $x) $this->writeElement('value', $value);
$this->endElement(); // allowed
}
$this->writeElement('default', $this->export($directive->default));
$this->writeAttribute('xml:space', 'preserve');
$this->endElement(); // constraints
$this->startElement('description');
$this->writeHTMLDiv($directive->description);
$this->endElement(); // description
$this->endElement(); // directive
}
}

View File

@ -8,6 +8,11 @@
class HTMLPurifier_ConfigSchema_Interchange
{
/**
* Name of the application this schema is describing.
*/
public $name;
/**
* Array of Namespace ID => array(namespace info)
*/
@ -38,4 +43,13 @@ class HTMLPurifier_ConfigSchema_Interchange
$this->directives[$i] = $directive;
}
/**
* Convenience function to perform standard validation. Throws exception
* on failed validation.
*/
public function validate() {
$validator = new HTMLPurifier_ConfigSchema_Validator();
return $validator->validate($this);
}
}

View File

@ -12,6 +12,30 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder
$this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native();
}
public static function buildFromDirectory($dir = null) {
$parser = new HTMLPurifier_StringHashParser();
$builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
$interchange = new HTMLPurifier_ConfigSchema_Interchange();
if (!$dir) $dir = realpath(dirname(__FILE__) . '/schema/');
$info = parse_ini_file($dir . 'info.ini');
$interchange->name = $info['name'];
$dh = opendir($dir);
while (false !== ($file = readdir($dh))) {
if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') {
continue;
}
$builder->build(
$interchange,
new HTMLPurifier_StringHash( $parser->parseFile($dir . $file) )
);
}
closedir($dh);
return $interchange;
}
/**
* Builds an interchange object based on a hash.
* @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build

View File

@ -48,6 +48,7 @@ class HTMLPurifier_ConfigSchema_Validator
if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'");
$this->validateDirective($directive);
}
return true;
}
/**

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
name = "HTML Purifier"

View File

@ -8,7 +8,7 @@ assertCli();
/**
* @file
* Generates a schema cache file from the contents of
* Generates a schema cache file, saving it to
* library/HTMLPurifier/ConfigSchema/schema.ser.
*
* This should be run when new configuration options are added to
@ -17,20 +17,9 @@ assertCli();
*/
$target = '../library/HTMLPurifier/ConfigSchema/schema.ser';
$FS = new FSTools();
$files = $FS->globr('../library/HTMLPurifier/ConfigSchema/schema', '*.txt');
if (!$files) throw new Exception('Did not find any schema files');
$parser = new HTMLPurifier_StringHashParser();
$builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
$interchange = new HTMLPurifier_ConfigSchema_Interchange();
foreach ($files as $file) {
$builder->build($interchange, new HTMLPurifier_StringHash($parser->parseFile($file)));
}
$validator = new HTMLPurifier_ConfigSchema_Validator();
$validator->validate($interchange);
$interchange = HTMLPurifier_ConfigSchema_InterchangeBuilder::buildFromDirectory();
$interchange->validate();
$schema_builder = new HTMLPurifier_ConfigSchema_Builder_ConfigSchema();
$schema = $schema_builder->build($interchange);