1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-07-31 03:10:09 +02:00

Release 2.0.0, merged in 1026 to HEAD.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/branches/strict@1179 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang
2007-06-21 00:36:12 +00:00
parent c35eb3e95f
commit 0101311193
172 changed files with 7713 additions and 2520 deletions

View File

@@ -17,9 +17,12 @@
<div id="home"><a href="http://htmlpurifier.org/">HTML Purifier</a> End-User Documentation</div>
<p>HTML Purifier currently natively supports only a subset of HTML's
allowed elements, attributes, and behavior. This is by design,
but as the user is always right, they'll need some method to overload
these behaviors.</p>
allowed elements, attributes, and behavior; specifically, this subset
is the set of elements that are safe for untrusted users to use.
However, HTML Purifier is often utilized to ensure standards-compliance
from input that is trusted (making it a sort of Tidy substitute),
and often users need to define new elements or attributes. The
advanced API is oriented specifically for these use-cases.</p>
<p>Our goals are to let the user:</p>
@@ -27,20 +30,15 @@ these behaviors.</p>
<dt>Select</dt>
<dd><ul>
<li>Doctype</li>
<li>Mode: Lenient / Correctional</li>
<!-- <li>Filterset</li> -->
<li>Elements / Attributes / Modules</li>
<li>Filterset</li>
<li>Tidy</li>
</ul></dd>
<dt>Customize</dt>
<dd><ul>
<li>Attributes</li>
<li>Elements</li>
</ul></dd>
<dt>Internals</dt>
<dd><ul>
<li>Modules / Elements / Attributes / Attribute Types</li>
<li>Filtersets</li>
<li>Doctype</li>
<!--<li>Doctypes</li>-->
</ul></dd>
</dl>
@@ -68,136 +66,64 @@ Transitional, however, we really shouldn't be guessing what the user's
doctype is. Fortunantely, people who can't be bothered to set this won't
be bothered when their pages stop validating.</p>
<h3>Selecting Mode</h3>
<p>Within doctypes, there are various <strong>modes</strong> of operation.
These indicate variant behaviors that, while not strictly changing the
allowed set of elements and attributes, definitely affect the output.
Currently, we have two modes, which may be used together:</p>
<dl>
<dt>Lenient</dt>
<dd>
<p>Deprecated elements and attributes will be transformed into
standards-compliant alternatives when explicitly disallowed.</p>
<p>For example, in the XHTML 1.0 Strict doctype, a <code>center</code>
element would be turned into a <code>div</code> with the CSS property
<code>text-align:center;</code>, but in XHTML 1.0 Transitional
the element would be preserved.</p>
<p>This mode is on by default.</p>
</dd>
<dt>Correctional[items to correct]</dt>
<dd>
<p>Deprecated elements and attributes will be transformed into
standards-compliant alternatives whenever possible.
It may have various levels of operation.</p>
<p>Referring back to the previous example, the <code>center</code> element would
be transformed in both cases. However, elements without a
reasonable standards-compliant alternative will be preserved
in their form.</p>
<p>A user may want to correct certain deprecated attributes, but
not others. For example, the <code>bgcolor</code> attribute may be
acceptable, but the <code>center</code> element not; also, possibly,
an HTML Purifier transformation may be buggy, so the user wants
to forgo it. Thus, correctional accepts an array defining which
elements and attributes to cleanup, or no parameter at all, which
means everything gets corrected. This also means that each
correction needs to be given a unique ID that can be referenced
in this manner. (We may also allow globbing, like *.name or a.*
for mass-enabling correction, and subtractive mode, where things
specified stop correction.) This array gets passed into the
constructor of the mode's module.</p>
<p>This mode is on by default.</p>
</dd>
</dl>
<p>A possible call to select modes would be:</p>
<pre>$config->set('HTML', 'Mode', array('correctional', 'lenient'));</pre>
<p>If modes have extra parameters, a hash is necessary:</p>
<pre>$config->set('HTML', 'Mode', array(
'correctional' => 'center,a.name',
'lenient' => true // this one's just boolean
));</pre>
<p>Modes may be specified along with the doctype declaration (we may want
to get a better set of separator characters):</p>
<pre>$config->setDoctype('XHTML Transitional 1.0', '+correctional[center,a.name] -lenient');</pre>
<p>
With regards to the various levels of operation conjectured in the
Correctional mode, this is prompted by the fact that a user may want to
correct certain problems but not others, for example, fix the <code>center</code>
element but not the <code>u</code> element, both of which are deprecated.
Having an integer <q>level</q> will not work very well for such fine
grained tweaking, but an array of specific settings might.</p>
<h3>Selecting Elements / Attributes / Modules</h3>
<p></p>
<p>HTML Purifier will, by default, allow as many elements and attributes
as possible. However, a user may decide to roll their own filterset by
selecting modules, elements and attributes to allow for their own
specific use-case. This can be done using %HTML.Allowed:</p>
<p>If this cookie cutter approach doesn't appeal to a user, they may
decide to roll their own filterset by selecting modules, elements and
attributes to allow.</p>
<pre>$config->set('HTML', 'Allowed', 'a[href|title],em,p,blockquote');</pre>
<p class="technical">This would make use of the same facilities
as a filterset author would use, except that it would go under an
<q>anonymous</q> filterset that would be auto-selected if any of the
relevant module/elements/attribute selection configuration directives were
non-null.</p>
<p class="technical">The directive %HTML.Allowed is a convenience feature
that may be fully expressed with the legacy interface.</p>
<p>In practice, this is the most commonly demanded feature. Most users are
perfectly happy defining a filterset that looks like:</p>
<pre>$config->setAllowedHTML('a[href,title];em;p;blockquote');</pre>
<p class="technical">The directive %HTML.Allowed is a convenience function
that may be fully expressed with the legacy interface, and thus is
given its own setter.</p>
<p>We currently support a separated interface, which also must be preserved:</p>
<p>We currently support another interface from older versions:</p>
<pre>$config->set('HTML', 'AllowedElements', 'a,em,p,blockquote');
$config->set('HTML', 'AllowedAttributes', 'a.href,a.title');</pre>
<p>A user may also choose to allow modules:</p>
<p>A user may also choose to allow modules using a specialized
directive:</p>
<pre>$config->set('HTML', 'AllowedModules', 'Hypertext,Text,Lists'); // or
$config->setAllowedHTML('Hypertext,Text,Lists');</pre>
<pre>$config->set('HTML', 'AllowedModules', 'Hypertext,Text,Lists');</pre>
<p>But it is not expected that this feature will be widely used.</p>
<p class="fixme">The granularity of these modules is too coarse for
the average user (for example, the core module loads everything from
the essential <code>p</code> element to the not-so-safe <code>h1</code>
element). How do we make this still a viable solution? Possible answers
may be sub-modules or module parameters. This may not even be a problem,
considering that most people won't be selecting modules.</p>
<p class="technical">Module selection will work slightly differently
from the other AllowedElements and AllowedAttributes directives by
directly modifying the doctype you are operating in, in the spirit of
XHTML 1.1's modularization. We stop users from shooting themselves in the
foot by mandating the modules in %HTML.CoreModules be used.</p>
<p class="technical">Modules are distinguished from regular elements by the
case of their first letter. While XML distinguishes between and allows
lower and uppercase letters in element names, most well-known XML
languages use only lower-case
lower and uppercase letters in element names, XHTML uses only lower-case
element names for sake of consistency.</p>
<p class="technical">Considering that, internally speaking, as mandated by
the XHTML 1.1 Modularization specification, we have organized our
elements around modules, considerable gymnastics will be needed to
get this sort of functionality working.</p>
<h3>Selecting Tidy</h3>
<p>The name of this segment of functionality is inspired off of Dave
Ragget's program HTML Tidy, which purported to help clean up HTML. In
HTML Purifier, Tidy functionality involves turning unsupported and
deprecated elements into standards-compliant ones, maintaining
backwards compatibility, and enforcing best practices.</p>
<p>This is a complicated feature, and is explained more in depth at
<a href="enduser-tidy.html">the Tidy documentation page</a>.</p>
<!--
<h3>Unified selector</h3>
<p>Because selecting each and every one of these configuration options
is a chore, we may wish to offer a specialized configuration method
for selecting a filterset. Possibility:</p>
<pre>function selectFilter($doctype, $filterset, $mode)</pre>
<pre>function selectFilter($doctype, $filterset, $tidy)</pre>
<p>...which is simply a light wrapper over the individual configuration
calls. A custom config file format or text format could also be adopted.</p>
-->
<h2>Customize</h2>
@@ -209,38 +135,34 @@ use-cases.</p>
<p>Note that the functions described here are only available if
a raw copy of <code>HTMLPurifier_HTMLDefinition</code> was retrieved.
<code>addAttribute</code> may work on a processed copy, but for
consistency's sake we will mandate this for everything.</p>
Furthermore, caching may prevent your changes from immediately
being seen: consult <a href="enduser-customize.html">enduser-customize.html</a> on how
to work around this.</p>
<h3>Attributes</h3>
<p>An attribute is bound to an element by a name and has a specific
<code>AttrDef</code> that validates it. Thus, the interface should
be:</p>
<code>AttrDef</code> that validates it. The interface is therefore:</p>
<pre>function addAttribute($element, $attribute, $attribute_def);</pre>
<p>With a use-case that looks like:</p>
<p>Example of the functionality in action:</p>
<pre>$def->addAttribute('a', 'rel', new HTMLPurifier_AttrDef_Enum(array('nofollow')));</pre>
<pre>$def->addAttribute('a', 'rel', 'Enum#nofollow');</pre>
<p>The <code>$attribute_def</code> value can be a little flexible,
to make things simpler. We'll let it also be:</p>
<p>The <code>$attribute_def</code> value is flexible,
to make things simpler. It can be a literal object or:</p>
<ul>
<li>Class name: We'll instantiate it for you</li>
<!--<li>Class name: We'll instantiate it for you</li>
<li>Function name: We'll create an <code>HTMLPurifier_AttrDef_Anonymous</code>
class with that function registered as a callback.</li>
class with that function registered as a callback.</li>-->
<li>String attribute type: We'll use <code>HTMLPurifier_AttrTypes</code>
</li>
<li>String starting with <code>enum(</code>: We'll explode it and stuff it in an
<code>HTMLPurifier_AttrDef_Enum</code> for you.</li>
to resolve it for you. Any data that follows a hash mark (#) will
be used to customize the attribute type: in the example above,
we specify which values for Enum to allow.</li>
</ul>
<p>Making the previous example written as:</p>
<pre>$def->addAttribute('a', 'rel', 'enum(nofollow)');</pre>
<h3>Elements</h3>
<p>An element requires certain information as specified by
@@ -255,7 +177,8 @@ the usual things required are:</p>
<p>This suggests an API like this:</p>
<pre>function addElement($element, $type, $content_model, $attributes = array());</pre>
<pre>function addElement($element, $type, $contents,
$attr_collections = array(); $attributes = array());</pre>
<p>Each parameter explained in depth:</p>
@@ -264,11 +187,15 @@ the usual things required are:</p>
<dd>Element name, ex. 'label'</dd>
<dt><code>$type</code></dt>
<dd>Content set to register in, ex. 'Inline' or 'Flow'</dd>
<dt><code>$content_model</code></dt>
<dt><code>$contents</code></dt>
<dd>Description of allowed children. This is a merged form of
<code>HTMLPurifier_ElementDef</code>'s member variables
<code>$content_model</code> and <code>$content_model_type</code>,
where the form is <q>Type: Model</q>, ex. 'Optional: Inline'.</dd>
where the form is <q>Type: Model</q>, ex. 'Optional: Inline'.
There are also a number of predefined templates one may use.</dd>
<dt><code>$attr_collections</code></dt>
<dd>Array (or string if only one) of attribute collection(s) to
merge into the attributes array.</dd>
<dt><code>$attributes</code></dt>
<dd>Array of attribute names to attribute definitions, much like
the above-described attribute customization.</dd>
@@ -276,11 +203,10 @@ the usual things required are:</p>
<p>A possible usage:</p>
<pre>$def->addElement('font', 'Inline', 'Optional: Inline',
array(0 => array('Common'), 'color' => 'Color'));</pre>
<pre>$def->addElement('font', 'Inline', 'Optional: Inline', 'Common',
array('color' => 'Color'));</pre>
<p>We may want to Common attribute collection inclusion to be added
by default.</p>
<p>See <code>HTMLPurifier/HTMLModule.php</code> for details.</p>
<div id="version">$Id$</div>

791
docs/enduser-customize.html Normal file
View File

@@ -0,0 +1,791 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Tutorial for customizing HTML Purifier's tag and attribute sets." />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>Customize - HTML Purifier</title>
</head><body>
<h1 class="subtitled">Customize!</h1>
<div class="subtitle">HTML Purifier is a Swiss-Army Knife</div>
<div id="filing">Filed under End-User</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://htmlpurifier.org/">HTML Purifier</a> End-User Documentation</div>
<div id="applicability">
This document covers currently unreleased functionality and
only applies to recent SVN checkouts.
</div>
<p>
You may have heard of the <a href="dev-advanced-api.html">Advanced API</a>.
If you're interested in reading dry prose and boring functional
specifications, feel free to click that link to get a no-nonsense overview
on the Advanced API. For the rest of us, there's this tutorial. By the time
you're finished reading this, you should have a pretty good idea on
how to implement custom tags and attributes that HTML Purifier may not have.
</p>
<h2>Is it necessary?</h2>
<p>
Before we even write any code, it is paramount to consider whether or
not the code we're writing is necessary or not. HTML Purifier, by default,
contains a large set of elements and attributes: large enough so that
<em>any</em> element or attribute in XHTML 1.0 (and its HTML variant)
that can be safely used by the general public is implemented.
</p>
<p>
So what needs to be implemented? (Feel free to skip this section if
you know what you want).
</p>
<h3>XHTML 1.0</h3>
<p>
All of the modules listed below are based off of the
<a href="http://www.w3.org/TR/2001/REC-xhtml-modularization-20010410/abstract_modules.html#sec_5.2.">modularization of
XHTML</a>, which, while technically for XHTML 1.1, is quite a useful
resource.
</p>
<ul>
<li>Structure</li>
<li>Frames</li>
<li>Applets (deprecated)</li>
<li>Forms</li>
<li>Image maps</li>
<li>Objects</li>
<li>Frames</li>
<li>Events</li>
<li>Meta-information</li>
<li>Style sheets</li>
<li>Link (not hypertext)</li>
<li>Base</li>
<li>Name</li>
</ul>
<p>
If you don't recognize it, you probably don't need it. But the curious
can look all of these modules up in the above-mentioned document. Note
that inline scripting comes packaged with HTML Purifier (more on this
later).
</p>
<h3>XHTML 1.1</h3>
<p>
We have not implemented the
<a href="http://www.w3.org/TR/2001/REC-ruby-20010531/">Ruby module</a>,
which defines a set of tags
for publishing short annotations for text, used mostly in Japanese
and Chinese school texts.
</p>
<h3>XHTML 2.0</h3>
<p>
<a href="http://www.w3.org/TR/xhtml2/">XHTML 2.0</a> is still a
working draft, so any elements introduced in the
specification have not been implemented and will not be implemented
until we get a recommendation or proposal. Because XHTML 2.0 is
an entirely new markup language, implementing rules for it will be
no easy task.
</p>
<h3>HTML 5</h3>
<p>
<a href="http://www.whatwg.org/specs/web-apps/current-work/">HTML 5</a>
is a fork of HTML 4.01 by WHATWG, who believed that XHTML 2.0 was headed
in the wrong direction. It too is a working draft, and may change
drastically before publication, but it should be noted that the
<code>canvas</code> tag has been implemented by many browser vendors.
</p>
<h3>Proprietary</h3>
<p>
There are a number of proprietary tags still in the wild. Many of them
have been documented in <a href="ref-proprietary-tags.txt">ref-proprietary-tags.txt</a>,
but there is currently no implementation for any of them.
</p>
<h3>Extensions</h3>
<p>
There are also a number of other XML languages out there that can
be embedded in HTML documents: two of the most popular are MathML and
SVG, and I frequently get requests to implement these. But they are
expansive, comprehensive specifications, and it would take far too long
to implement them <em>correctly</em> (most systems I've seen go as far
as whitelisting tags and no further; come on, what about nesting!)
</p>
<p>
Word of warning: HTML Purifier is currently <em>not</em> namespace
aware.
</p>
<h2>Giving back</h2>
<p>
As you may imagine from the details above (don't be abashed if you didn't
read it all: a glance over would have done), there's quite a bit that
HTML Purifier doesn't implement. Recent architectural changes have
allowed HTML Purifier to implement elements and attributes that are not
safe! Don't worry, they won't be activated unless you set %HTML.Trusted
to true, but they certainly help out users who need to put, say, forms
on their page and don't want to go through the trouble of reading this
and implementing it themself.
</p>
<p>
So any of the above that you implement for your own application could
help out some other poor sap on the other side of the globe. Help us
out, and send back code so that it can be hammered into a module and
released with the core. Any code would be greatly appreciated!
</p>
<h2>And now...</h2>
<p>
Enough philosophical talk, time for some code:
</p>
<pre>$config = HTMLPurifier_Config::createDefault();
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1);
$def =& $config->getHTMLDefinition(true);</pre>
<p>
Assuming that HTML Purifier has already been properly loaded (hint:
include <code>HTMLPurifier.auto.php</code>), this code will set up
the environment that you need to start customizing the HTML definition.
What's going on?
</p>
<ul>
<li>
The first three lines are regular configuration code:
<ul>
<li>
%HTML.DefinitionID is set to a unique identifier for your
custom HTML definition. This prevents it from clobbering
other custom definitions on the same installation.
</li>
<li>
%HTML.DefinitionRev is a revision integer of your HTML
definition. Because HTML definitions are cached, you'll need
to increment this whenever you make a change in order to flush
the cache.
</li>
</ul>
</li>
<li>
The fourth line retrieves a raw <code>HTMLPurifier_HTMLDefinition</code>
object that we will be tweaking. If the parameter was removed, we
would be retrieving a fully formed definition object, which is somewhat
useless for customization purposes.
</li>
</ul>
<h3>Broken backwards-compatibility</h3>
<p>
Those of you who have already been twiddling around with the raw
HTML definition object, you'll be noticing that you're getting an error
when you attempt to retrieve the raw definition object without specifying
a DefinitionID. It is vital to caching (see below) that you make a unique
name for your customized definition, so make up something right now and
things will operate again.
</p>
<h2>Turn off caching</h2>
<p>
To make development easier, we're going to temporarily turn off
definition caching:
</p>
<pre>$config = HTMLPurifier_Config::createDefault();
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1);
<strong>$config->set('Core', 'DefinitionCache', null); // remove this later!</strong>
$def =& $config->getHTMLDefinition(true);</pre>
<p>
A few things should be mentioned about the caching mechanism before
we move on. For performance reasons, HTML Purifier caches generated
<code>HTMLPurifier_Definition</code> objects in serialized files
stored (by default) in <code>library/HTMLPurifier/DefinitionCache/Serializer</code>.
A lot of processing is done in order to create these objects, so it
makes little sense to repeat the same processing over and over again
whenever HTML Purifier is called.
</p>
<p>
In order to identify a cache entry, HTML Purifier uses three variables:
the library's version number, the value of %HTML.DefinitionRev and
a serial of relevant configuration. Whenever any of these changes,
a new HTML definition is generated. Notice that there is no way
for the definition object to track changes to customizations: here, it
is up to you to supply appropriate information to DefinitionID and
DefinitionRev.
</p>
<h2 id="addAttribute">Add an attribute</h2>
<p>
For this example, we're going to implement the <code>target</code> attribute found
on <code>a</code> elements. To implement an attribute, we have to
ask a few questions:
</p>
<ol>
<li>What element is it found on?</li>
<li>What is its name?</li>
<li>Is it required or optional?</li>
<li>What are valid values for it?</li>
</ol>
<p>
The first three are easy: the element is <code>a</code>, the attribute
is <code>target</code>, and it is not a required attribute. (If it
was required, we'd need to append an asterisk to the attribute name,
you'll see an example of this in the addElement() example).
</p>
<p>
The last question is a little trickier.
Lets allow the special values: _blank, _self, _target and _top.
The form of this is called an <strong>enumeration</strong>, a list of
valid values, although only one can be used at a time. To translate
this into code form, we write:
</p>
<pre>$config = HTMLPurifier_Config::createDefault();
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1);
$config->set('Core', 'DefinitionCache', null); // remove this later!
$def =& $config->getHTMLDefinition(true);
<strong>$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');</strong></pre>
<p>
The <code>Enum#_blank,_self,_target,_top</code> does all the magic.
The string is split into two parts, separated by a hash mark (#):
</p>
<ol>
<li>The first part is the name of what we call an <code>AttrDef</code></li>
<li>The second part is the parameter of the above-mentioned <code>AttrDef</code></li>
</ol>
<p>
If that sounds vague and generic, it's because it is! HTML Purifier defines
an assortment of different attribute types one can use, and each of these
has their own specialized parameter format. Here are some of the more useful
ones:
</p>
<table class="table">
<thead>
<tr>
<th>Type</th>
<th>Format</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<th>Enum</th>
<td><em>[s:]</em>value1,value2,...</td>
<td>
Attribute with a number of valid values, one of which may be used. When
s: is present, the enumeration is case sensitive.
</td>
</tr>
<tr>
<th>Bool</th>
<td>attribute_name</td>
<td>
Boolean attribute, with only one valid value: the name
of the attribute.
</td>
</tr>
<tr>
<th>CDATA</th>
<td></td>
<td>
Attribute of arbitrary text. Can also be referred to as <strong>Text</strong>
(the specification makes a semantic distinction between the two).
</td>
</tr>
<tr>
<th>ID</th>
<td></td>
<td>
Attribute that specifies a unique ID
</td>
</tr>
<tr>
<th>Pixels</th>
<td></td>
<td>
Attribute that specifies an integer pixel length
</td>
</tr>
<tr>
<th>Length</th>
<td></td>
<td>
Attribute that specifies a pixel or percentage length
</td>
</tr>
<tr>
<th>NMTOKENS</th>
<td></td>
<td>
Attribute that specifies a number of name tokens, example: the
<code>class</code> attribute
</td>
</tr>
<tr>
<th>URI</th>
<td></td>
<td>
Attribute that specifies a URI, example: the <code>href</code>
attribute
</td>
</tr>
<tr>
<th>Number</th>
<td></td>
<td>
Attribute that specifies an positive integer number
</td>
</tr>
</tbody>
</table>
<p>
For a complete list, consult
<a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/AttrTypes.php"><code>library/HTMLPurifier/AttrTypes.php</code></a>;
more information on attributes that accept parameters can be found on their
respective includes in
<a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/AttrDef/"><code>library/HTMLPurifier/AttrDef</code></a>.
</p>
<p>
Sometimes, the restrictive list in AttrTypes just doesn't cut it. Don't
sweat: you can also use a fully instantiated object as the value. The
equivalent, verbose form of the above example is:
</p>
<pre>$config = HTMLPurifier_Config::createDefault();
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1);
$config->set('Core', 'DefinitionCache', null); // remove this later!
$def =& $config->getHTMLDefinition(true);
<strong>$def->addAttribute('a', 'target', new HTMLPurifier_AttrDef_Enum(
array('_blank','_self','_target','_top')
));</strong></pre>
<p>
Trust me, you'll learn to love the shorthand.
</p>
<h2>Add an element</h2>
<p>
Adding attributes is really small-fry stuff, though, and it was possible
to add them (albeit a bit more wordy) prior to 2.0. The real gem of
the Advanced API is adding elements. There are five questions to
ask when adding a new element:
</p>
<ol>
<li>What is the element's name?</li>
<li>What content set does this element belong to?</li>
<li>What are the allowed children of this element?</li>
<li>What attributes does the element allow that are general?</li>
<li>What attributes does the element allow that are specific to this element?</li>
</ol>
<p>
It's a mouthful, and you'll be slightly lost if your not familiar with
the HTML specification, so let's explain them step by step.
</p>
<h3>Content set</h3>
<p>
The HTML specification defines two major content sets: Inline
and Block. Each of these
content sets contain a list of elements: Inline contains things like
<code>span</code> and <code>b</code> while Block contains things like
<code>div</code> and <code>blockquote</code>.
</p>
<p>
These content sets amount to a macro mechanism for HTML definition. Most
elements in HTML are organized into one of these two sets, and most
elements in HTML allow elements from one of these sets. If we had
to write each element verbatim into each other element's allowed
children, we would have ridiculously large lists; instead we use
content sets to compactify the declaration.
</p>
<p>
Practically speaking, there are several useful values you can use here:
</p>
<table class="table">
<thead>
<tr>
<th>Content set</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<th>Inline</th>
<td>Character level elements, text</td>
</tr>
<tr>
<th>Block</th>
<td>Block-like elements, like paragraphs and lists</td>
</tr>
<tr>
<th><em>false</em></th>
<td>
Any element that doesn't fit into the mold, for example <code>li</code>
or <code>tr</code>
</td>
</tr>
</tbody>
</table>
<p>
By specifying a valid value here, all other elements that use that
content set will also allow your element, without you having to do
anything. If you specify <em>false</em>, you'll have to register
your element manually.
</p>
<h3>Allowed children</h3>
<p>
Allowed children defines the elements that this element can contain.
The allowed values may range from none to a complex regexp depending on
your element.
</p>
<p>
If you've ever taken a look at the HTML DTD's before, you may have
noticed declarations like this:
</p>
<pre>&lt;!ELEMENT LI - O (%flow;)* -- list item --&gt;</pre>
<p>
The <code>(%flow;)*</code> indicates the allowed children of the
<code>li</code> tag: <code>li</code> allows any number of flow
elements as its children. In HTML Purifier, we'd write it like
<code>Flow</code> (here's where the content sets we were
discussing earlier come into play). There are three shorthand content models you
can specify:
</p>
<table class="table">
<thead>
<tr>
<th>Content model</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<th>Empty</th>
<td>No children allowed, like <code>br</code> or <code>hr</code></td>
</tr>
<tr>
<th>Inline</th>
<td>Any number of inline elements and text, like <code>span</code></td>
</tr>
<tr>
<th>Flow</th>
<td>Any number of inline elements, block elements and text, like <code>div</code></td>
</tr>
</tbody>
</table>
<p>
This covers 90% of all the cases out there, but what about elements that
break the mold like <code>ul</code>? This guy requires at least one
child, and the only valid children for it are <code>li</code>. The
content model is: <code>Required: li</code>. There are two parts: the
first type determines what <code>ChildDef</code> will be used to validate
content models. The most common values are:
</p>
<table class="table">
<thead>
<tr>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<th>Required</th>
<td>Children must be one or more of the valid elements</td>
</tr>
<tr>
<th>Optional</th>
<td>Children can be any number of the valid elements</td>
</tr>
<tr>
<th>Custom</th>
<td>Children must follow the DTD-style regex</td>
</tr>
</tbody>
</table>
<p>
You can also implement your own <code>ChildDef</code>: this was done
for a few special cases in HTML Purifier such as <code>Chameleon</code>
(for <code>ins</code> and <code>del</code>), <code>StrictBlockquote</code>
and <code>Table</code>.
</p>
<p>
The second part specifies either valid elements or a regular expression.
Valid elements are separated with horizontal bars (|), i.e.
"<code>a | b | c</code>". Use #PCDATA to represent plain text.
Regular expressions are based off of DTD's style:
</p>
<ul>
<li>Parentheses () are used for grouping</li>
<li>Commas (,) separate elements that should come one after another</li>
<li>Horizontal bars (|) indicate one or the other elements should be used</li>
<li>Plus signs (+) are used for a one or more match</li>
<li>Asterisks (*) are used for a zero or more match</li>
<li>Question marks (?) are used for a zero or one match</li>
</ul>
<p>
For example, "<code>a, b?, (c | d), e+, f*</code>" means "In this order,
one <code>a</code> element, at most one <code>b</code> element,
one <code>c</code> or <code>d</code> element (but not both), one or more
<code>e</code> elements, and any number of <code>f</code> elements."
Regex veterans should be able to jump right in, and those not so savvy
can always copy-paste W3C's content model definitions into HTML Purifier
and hope for the best.
</p>
<p>
A word of warning: while the regex format is extremely flexible on
the developer's side, it is
quite unforgiving on the user's side. If the user input does not <em>exactly</em>
match the specification, the entire contents of the element will
be nuked. This is why there is are specific content model types like
Optional and Required: while they could be implemented as <code>Custom:
(valid | elements)*</code>, the custom classes contain special recovery
measures that make sure as much of the user's original content gets
through. HTML Purifier's core, as a rule, does not use Custom.
</p>
<p>
One final note: you can also use Content Sets inside your valid elements
lists or regular expressions. In fact, the three shorthand content models
mentioned above are just that: abbreviations:
</p>
<table class="table">
<thead>
<tr>
<th>Content model</th>
<th>Implementation</th>
</tr>
</thead>
<tbody>
<tr>
<th>Inline</th>
<td>Optional: Inline | #PCDATA</td>
</tr>
<tr>
<th>Flow</th>
<td>Optional: Flow | #PCDATA</td>
</tr>
</tbody>
</table>
<p>
When the definition is compiled, Inline will be replaced with a
horizontal-bar separated list of inline elements. Also, notice that
it does not contain text: you have to specify that yourself.
</p>
<h3>Common attributes</h3>
<p>
Congratulations: you have just gotten over the proverbial hump (Allowed
children). Common attributes is much simpler, and boils down to
one question: does your element have the <code>id</code>, <code>style</code>,
<code>class</code>, <code>title</code> and <code>lang</code> attributes?
If so, you'll want to specify the <code>Common</code> attribute collection,
which contains these five attributes that are found on almost every
HTML element in the specification.
</p>
<p>
There are a few more collections, but they're really edge cases:
</p>
<table class="table">
<thead>
<tr>
<th>Collection</th>
<th>Attributes</th>
</tr>
</thead>
<tbody>
<tr>
<th>I18N</th>
<td><code>lang</code>, possibly <code>xml:lang</code></td>
</tr>
<tr>
<th>Core</th>
<td><code>style</code>, <code>class</code>, <code>id</code> and <code>title</code></td>
</tr>
</tbody>
</table>
<p>
Common is a combination of the above-mentioned collections.
</p>
<h3>Attributes</h3>
<p>
If you didn't read the <a href="#addAttribute">previous section on
adding attributes</a>, read it now. The last parameter is simply
array of attribute names to attribute implementations, in the exact
same format as <code>addAttribute()</code>.
</p>
<h3>Putting it all together</h3>
<p>
We're going to implement <code>form</code>. Before we embark, lets
grab a reference implementation from over at the
<a href="http://www.w3.org/TR/html4/sgml/loosedtd.html">transitional DTD</a>:
</p>
<pre>&lt;!ELEMENT FORM - - (%flow;)* -(FORM) -- interactive form --&gt;
&lt;!ATTLIST FORM
%attrs; -- %coreattrs, %i18n, %events --
action %URI; #REQUIRED -- server-side form handler --
method (GET|POST) GET -- HTTP method used to submit the form--
enctype %ContentType; &quot;application/x-www-form-urlencoded&quot;
accept %ContentTypes; #IMPLIED -- list of MIME types for file upload --
name CDATA #IMPLIED -- name of form for scripting --
onsubmit %Script; #IMPLIED -- the form was submitted --
onreset %Script; #IMPLIED -- the form was reset --
target %FrameTarget; #IMPLIED -- render in this frame --
accept-charset %Charsets; #IMPLIED -- list of supported charsets --
&gt;</pre>
<p>
Juicy! With just this, we can answer four of our five questions:
</p>
<ol>
<li>What is the element's name? <strong>form</strong></li>
<li>What content set does this element belong to? <strong>Block</strong>
(this needs a little sleuthing, I find the easiest way is to search
the DTD for <code>FORM</code> and determine which set it is in.)</li>
<li>What are the allowed children of this element? <strong>One
or more flow elements, but no nested <code>form</code>s</strong></li>
<li>What attributes does the element allow that are general? <strong>Common</strong></li>
<li>What attributes does the element allow that are specific to this element? <strong>A whole bunch, see ATTLIST;
we're going to the vital ones: <code>action</code>, <code>method</code> and <code>name</code></strong></li>
</ol>
<p>
Time for some code:
</p>
<pre>$config = HTMLPurifier_Config::createDefault();
$config->set('HTML', 'DefinitionID', 'enduser-customize.html tutorial');
$config->set('HTML', 'DefinitionRev', 1);
$config->set('Core', 'DefinitionCache', null); // remove this later!
$def =& $config->getHTMLDefinition(true);
$def->addAttribute('a', 'target', new HTMLPurifier_AttrDef_Enum(
array('_blank','_self','_target','_top')
));
<strong>$form =& $def->addElement(
'form', // name
'Block', // content set
'Flow', // allowed children
'Common', // attribute collection
array( // attributes
'action*' => 'URI',
'method' => 'Enum#get|post',
'name' => 'ID'
)
);
$form->excludes = array('form' => true);</strong></pre>
<p>
Each of the parameters corresponds to one of the questions we asked.
Notice that we added an asterisk to the end of the <code>action</code>
attribute to indicate that it is required. If someone specifies a
<code>form</code> without that attribute, the tag will be axed.
Also, the extra line at the end is a special extra declaration that
prevents forms from being nested within each other.
</p>
<p>
And that's all there is to it! Implementing the rest of the form
module is left as an exercise to the user; to see more examples
check the <a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/HTMLModule/"><code>library/HTMLPurifier/HTMLModule/</code></a> directory
in your local HTML Purifier installation.
</p>
<h2>And beyond...</h2>
<p>
Perceptive users may have realized that, to a certain extent, we
have simply re-implemented the facilities of XML Schema or the
Document Type Definition. What you are seeing here, however, is
not just an XML Schema or Document Type Definition: it is a fully
expressive method of specifying the definition of HTML that is
a portable superset of the capabilities of the two above-mentioned schema
languages. What makes HTMLDefinition so powerful is the fact that
if we don't have an implementation for a content model or an attribute
definition, you can supply it yourself by writing a PHP class.
</p>
<p>
There are many facets of HTMLDefinition beyond the Advanced API I have
walked you through today. To find out more about these, you can
check out these source files:
</p>
<ul>
<li><a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/HTMLModule.php"><code>library/HTMLPurifier/HTMLModule.php</code></a></li>
<li><a href="http://htmlpurifier.org/svnroot/htmlpurifier/trunk/library/HTMLPurifier/ElementDef.php"><code>library/HTMLPurifier/ElementDef.php</code></a></li>
</ul>
<div id="version">$Id: enduser-tidy.html 1158 2007-06-18 19:26:29Z Edward $</div>
</body></html>

View File

@@ -8,15 +8,11 @@ to be effective. Things to remember:
1. Character Encoding: see enduser-utf8.html for more info.
2. Doctype: document pending feature completion
Not strictly necessary, actually. More in-depth discussion once we figure
out how to get strict loose mode working.
2. IDs: see enduser-id.html for more info
3. IDs: see enduser-id.html for more info
4. Links: document pending feature completion
3. Links: document pending feature completion
Rudimentary blacklisting, we should also allow only relative URIs. We
need a doc to explain the stuff.
5. CSS: document pending
4. CSS: document pending
Explain which CSS styles we blocked and why.

235
docs/enduser-tidy.html Normal file
View File

@@ -0,0 +1,235 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="description" content="Tutorial for tweaking HTML Purifier's Tidy-like behavior." />
<link rel="stylesheet" type="text/css" href="style.css" />
<title>Tidy - HTML Purifier</title>
</head><body>
<h1>Tidy</h1>
<div id="filing">Filed under Development</div>
<div id="index">Return to the <a href="index.html">index</a>.</div>
<div id="home"><a href="http://htmlpurifier.org/">HTML Purifier</a> End-User Documentation</div>
<div id="applicability">
This document covers currently unreleased functionality and
only applies to recent SVN checkouts.
</div>
<p>You've probably heard of HTML Tidy, Dave Raggett's little piece
of software that cleans up poorly written HTML. Let me say it straight
out:</p>
<p class="emphasis">This ain't HTML Tidy!</p>
<p>Rather, Tidy stands for a cool set of Tidy-inspired in HTML Purifier
that allows users to submit deprecated elements and attributes and get
valid strict markup back. For example:</p>
<pre>&lt;center&gt;Centered&lt;/center&gt;</pre>
<p>...becomes:</p>
<pre>&lt;div style=&quot;text-align:center;&quot;&gt;Centered&lt;/div&gt;</pre>
<p>...when this particular fix is run on the HTML. This tutorial will give
you down the lowdown of what exactly HTML Purifier will do when Tidy
is on, and how to fine tune this behavior. Once again, <strong>you do
not need Tidy installed on your PHP to use these features!</strong></p>
<h2>What does it do?</h2>
<p>Tidy will do several things to your HTML:</p>
<ul>
<li>Convert deprecated elements and attributes to standards-compliant
alternatives</li>
<li>Enforce XHTML compatibility guidelines and other best practices</li>
<li>Preserve data that would normally be removed as per W3C</li>
</ul>
<h2>What are levels?</h2>
<p>Levels describe how aggressive the Tidy module should be when
cleaning up HTML. There are four levels to pick: none, light, medium
and heavy. Each of these levels has a well-defined set of behavior
associated with it, although it may change depending on your doctype.</p>
<dl>
<dt>light</dt>
<dd>This is the <strong>lenient</strong> level. If a tag or attribute
is about to be removed because it isn't supported by the
doctype, Tidy will step in and change into an alternative that
is supported.</dd>
<dt>medium</dt>
<dd>This is the <strong>correctional</strong> level. At this level,
all the functions of light are performed, as well as some extra,
non-essential best practices enforcement. Changes made on this
level are very benign and are unlikely to cause problems.</dd>
<dt>heavy</dt>
<dd>This is the <strong>aggressive</strong> level. If a tag or
attribute is deprecated, it will be converted into a non-deprecated
version, no ifs ands or buts.</dd>
</dl>
<p>By default, Tidy operates on the <strong>medium</strong> level. You can
change the level of cleaning by setting the %HTML.TidyLevel configuration
directive:</p>
<pre>$config->set('HTML', 'TidyLevel', 'heavy'); // burn baby burn!</pre>
<h2>Is the light level really light?</h2>
<p>It depends on what doctype you're using. If your documents are HTML
4.01 <em>Transitional</em>, HTML Purifier will be lazy
and won't clean up your <code>center</code>
or <code>font</code> tags. But if you're using HTML 4.01 <em>Strict</em>,
HTML Purifier has no choice: it has to convert them, or they will
be nuked out of existence. So while light on Transitional will result
in little to no changes, light on Strict will still result in quite
a lot of fixes.</p>
<p>This is different behavior from 1.6 or before, where deprecated
tags in transitional documents would
always be cleaned up regardless. This is also better behavior.</p>
<h2>My pages look different!</h2>
<p>HTML Purifier is tasked with converting deprecated tags and
attributes to standards-compliant alternatives, which usually
need copious amounts of CSS. It's also not foolproof: sometimes
things do get lost in the translation. This is why when HTML Purifier
can get away with not doing cleaning, it won't; this is why
the default value is <strong>medium</strong> and not heavy.</p>
<p>Fortunately, only a few attributes have problems with the switch
over. They are described below:</p>
<table class="table">
<thead><tr>
<th>Element@Attr</th>
<th>Changes</th>
</tr></thead>
<tbody>
<tr>
<td>caption@align</td>
<td>Firefox supports stuffing the caption on the
left and right side of the table, a feature that
Internet Explorer, understandably, does not have.
When align equals right or left, the text will simply
be aligned on the left or right side.</td>
</tr>
<tr>
<td>img@align</td>
<td>The implementation for align bottom is good, but not
perfect. There are a few pixel differences.</td>
</tr>
<tr>
<td>br@clear</td>
<td>Clear both gets a little wonky in Internet Explorer. Haven't
really been able to figure out why.</td>
</tr>
<tr>
<td>hr@noshade</td>
<td>All browsers implement this slightly differently: we've
chosen to make noshade horizontal rules gray.</td>
</tr>
</tbody>
</table>
<p>There are a few more minor, although irritating, bugs.
Some older browsers support deprecated attributes,
but not CSS. Transformed elements and attributes will look unstyled
to said browsers. Also, CSS precedence is slightly different for
inline styles versus presentational markup. In increasing precedence:</p>
<ol>
<li>Presentational attributes</li>
<li>External style sheets</li>
<li>Inline styling</li>
</ol>
<p>This means that styling that may have been masked by external CSS
declarations will start showing up (a good thing, perhaps). Finally,
if you've turned off the style attribute, almost all of
these transformations will not work. Sorry mates.</p>
<p>You can review the rendering before and after of these transformations
by consulting the <a
href="http://htmlpurifier.org/live/smoketests/attrTransform.php">attrTransform.php
smoketest</a>.</p>
<h2>I like the general idea, but the specifics bug me!</h2>
<p>So you want HTML Purifier to clean up your HTML, but you're not
so happy about the br@clear implementation. That's perfectly fine!
HTML Purifier will make accomodations:</p>
<pre>$config->set('HTML', 'Doctype', 'XHTML 1.0 Transitional');
$config->set('HTML', 'TidyLevel', 'heavy'); // all changes, minus...
<strong>$config->set('HTML', 'TidyRemove', 'br@clear');</strong></pre>
<p>That third line does the magic, removing the br@clear fix
from the module, ensuring that <code>&lt;br clear="both" /&gt;</code>
will pass through unharmed. The reverse is possible too:</p>
<pre>$config->set('HTML', 'Doctype', 'XHTML 1.0 Transitional');
$config->set('HTML', 'TidyLevel', 'none'); // no changes, plus...
<strong>$config->set('HTML', 'TidyAdd', 'p@align');</strong></pre>
<p>In this case, all transformations are shut off, except for the p@align
one, which you found handy.</p>
<p>To find out what the names of fixes you want to turn on or off are,
you'll have to consult the source code, specifically the files in
<code>HTMLPurifier/HTMLModule/Tidy/</code>. There is, however, a
general syntax:</p>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Example</th>
<th>Interpretation</th>
</tr>
</thead>
<tbody>
<tr>
<td>element</td>
<td>font</td>
<td>Tag transform for <em>element</em></td>
</tr>
<tr>
<td>element@attr</td>
<td>br@clear</td>
<td>Attribute transform for <em>attr</em> on <em>element</em></td>
</tr>
<tr>
<td>@attr</td>
<td>@lang</td>
<td>Global attribute transform for <em>attr</em></td>
</tr>
<tr>
<td>e#content_model_type</td>
<td>blockquote#content_model_type</td>
<td>Change of child processing implementation for <em>e</em></td>
</tr>
</tbody>
</table>
<h2>So... what's the lowdown?</h2>
<p>The lowdown is, quite frankly, HTML Purifier's default settings are
probably good enough. The next step is to bump the level up to heavy,
and if that still doesn't satisfy your appetite, do some fine tuning.
Other than that, don't worry about it: this all works silently and
effectively in the background.</p>
<div id="version">$Id$</div>
</body></html>

View File

@@ -8,8 +8,8 @@ require_once '../../library/HTMLPurifier.auto.php';
$config = HTMLPurifier_Config::createDefault();
// configuration goes here:
$config->set('Core', 'Encoding', 'ISO-8859-1'); //replace with your encoding
$config->set('Core', 'XHTML', true); // set to false if HTML 4.01
$config->set('Core', 'Encoding', 'UTF-8'); // replace with your encoding
$config->set('HTML', 'Doctype', 'XHTML 1.0 Transitional'); // replace with your doctype
$purifier = new HTMLPurifier($config);

View File

@@ -34,6 +34,12 @@ information for casual developers using HTML Purifier.</p>
<dt><a href="enduser-utf8.html">UTF-8: The Secret of Character Encoding</a></dt>
<dd>Describes the rationale for using UTF-8, the ramifications otherwise, and how to make the switch.</dd>
<dt><a href="enduser-tidy.html">Tidy</a></dt>
<dd>Tutorial for tweaking HTML Purifier's Tidy-like behavior.</dd>
<dt><a href="enduser-customize.html">Customize</a></dt>
<dd>Tutorial for customizing HTML Purifier's tag and attribute sets.</dd>
</dl>
<h2>Development</h2>
@@ -128,8 +134,8 @@ the code. They may be upgraded to HTML files or stay as TXT scratchpads.</p>
<tr>
<td>Reference</td>
<td><a href="ref-loose-vs-strict.txt">Loose vs.Strict</a></td>
<td>Differences between HTML Strict and Transitional versions.</td>
<td><a href="ref-content-models.txt">Handling Content Model Changes</a></td>
<td>Discusses how to tidy up content model changes using custom ChildDef classes.</td>
</tr>
<tr>
@@ -140,14 +146,8 @@ the code. They may be upgraded to HTML files or stay as TXT scratchpads.</p>
<tr>
<td>Reference</td>
<td><a href="ref-strictness.txt">Strictness</a></td>
<td>Short essay on how loose definition isn't really loose.</td>
</tr>
<tr>
<td>Reference</td>
<td><a href="ref-xhtml-1.1.txt">XHTML 1.1</a></td>
<td>What we'd have to do to support XHTML 1.1.</td>
<td><a href="ref-html-modularization.txt">Modularization of HTMLDefinition</a></td>
<td>Provides a high-level overview of the concepts behind HTMLModules.</td>
</tr>
<tr>

View File

@@ -12,29 +12,10 @@ the documentation in ConfigDef for more information on these namespaces.
Since configuration is dependant on context, internal classes require a
configuration object to be passed as a parameter. (They also require a
Context object).
Context object). A majority of classes do not need the config object,
but for those who do, it is a lifesaver.
In relation to HTMLDefinition and CSSDefinition, there could be a special class
of directives that influence the *construction* of the Definition object.
A theoretical call pattern would look like:
1. Client calls Config->getHTMLDefinition()
2. Config calls HTMLDefinition->createNew(this)
3. HTMLDefinition constructs itself with base configuration
4. HTMLDefinition calls Config->get('HTML')
5. Config returns array of directives
6. HTMLDefinition performs operations and changes specified by directives
7. HTMLPurifier returns constructed definition
8. Config caches definition so it doesn't have to be generated again
9. Config returns definition
You could also override Config's copy of the definition with your own
custom copy, which OVERRIDES all directives. Only the base, vanilla copy
is the Singleton, the object actually interfaced with is a operated-upon
clone of that object. Also, if an update to the directives would update
the definition, you'd have to force reconstruction.
In practice, the pulling directives from the config object are
solely need-based, and the flex points are littered throughout the
setup() function. Some sort of refactoring is likely in order. See
ref-xhtml-1.1.txt for more info.
Definition objects are complex datatypes influenced by their respective
directive namespaces (HTMLDefinition with HTML and CSSDefinition with CSS).
If any of these directives is updated, HTML Purifier forces the definition
to be regenerated.

View File

@@ -2,23 +2,16 @@
Filter Levels
When one size *does not* fit all
The more I think about it, the less sense it makes for maintaining one huge
monolithic HTMLDefinition class. There's simply so much variation that
could go into this definition: the set of HTML good for blog entries is
definitely too large for HTML that would be allowed in blog comments. Going
from Transitional to Strict requires changes to the definition.
It makes little sense to constrain users to one set of HTML elements and
attributes and tell them that they are not allowed to mold this in
any fashion. Many users demand to be able to custom-select which elements
and attributes they want. This is fine: because HTML Purifier keeps close
track of what elements are safe to use, there is no way for them to
accidently allow an XSS-able tag.
Allowing users to specify their own whitelists is one step (implemented, btw),
but I have doubts on only doing this. Simply put, the typical programmer is too
lazy to actually go through the trouble of investigating which tags, attributes
and properties to allow. HTMLDefinition makes a big part of what HTMLPurifier
is.
The idea, then, is to setup fundamentally different set of definitions, which
can further be customized using simpler configuration options. Alternatively,
they could be implemented as configuration profiles, which simply load
a set of recommended directives to acheive a desired affect (no simpler
config options though).
However, combing through the HTML spec to make your own whitelist can
be a daunting task. HTML Purifier ought to offer pre-canned filter levels
that amateur users can select based on what they think is their use-case.
Here are some fuzzy levels you could set:
@@ -46,6 +39,10 @@ make forbidden element to text transformations desirable (for example, images).
== Element Risk Analysis ==
Although none of the currently supported elements presents a security
threat per-say, some can cause problems for page layouts or be
extremely complicated.
Legend:
[danger level] - regular tags / uncommon tags ~ deprecated tags
[danger level]* - rare tags
@@ -114,6 +111,10 @@ Partially presentational - table.cellpadding, table.cellspacing,
== CSS Risk Analysis ==
Currently, there is no support for fine-grained "allowed CSS" specification,
mainly because I'm lazy, partially because no one has asked for it. However,
this will be added eventually.
There are certain CSS elements that are extremely useful inline, but then
as you get to more presentation oriented styling it may not always be
appropriate to inline them.
@@ -126,6 +127,7 @@ any CSS properties that are not currently implemented (such as position).
Dangerous, can go outside container - float
Easy to abuse - font-size, font-family (font), width
Colored - background-color (background), border-color (border), color
(see proposal-colors.html)
Dramatic - border, list-style-position (list-style), margin, padding,
text-align, text-indent, text-transform, vertical-align, line-height

View File

@@ -0,0 +1,48 @@
Handling Content Model Changes
1. Context
The distinction between Transitional and Strict document types is somewhat
of an anomaly in the lineage of XHTML document types (following 1.0, no
doctypes do not have flavors: instead, modularization is used to let
document authors vary their elements). This transition is usually quite
straight-forward, as W3C usually deprecates attributes or elements, which
are quite easily handled using tag and attribute transforms.
However, for two elements, <blockquote>, <body> and <address>, W3C elected
to also change the content model. <blockquote> and <body> originally
accepted both inline and block elements, but in the strict doctype they
only allow block elements. With <address>, the situation is inverted:
<p> tags were now forbidden from appearing within this tag.
2. Current situation
Currently, HTML Purifier treats <blockquote> specially during Tidy mode
using a custom ChildDef class StrictBlockquote. StrictBlockquote
operates similarly to Required, except that when it encounters an inline
element, it will wrap it in a block tag (as specified by
%HTML.BlockWrapper, the default is <p>). The naming suggests it can
only be used for <blockquote>s, although it may be possible to
genericize it to work on other cases of this nature (this would be of
little practical application, as no other element in XHTML 1.1 or earlier
has a block-only content model).
Tidy currently contains no custom, lenient implementation for <address>.
If one were to be written, it would likely operate on the principle that,
when a <p> tag were to be encountered, it would be replaced with a
leading and trailing <br /> tag (the contents of <p>, being inline, are
not an issue). There is no prior work with this sort of operation.
3. Outside applicability
There are a number of other elements that contain restrictive content
models, such as <ul> or <span> (the latter is restrictive in that it
does not allow block elements). In the former case, an errant node
is eliminated completely, in the latter case, the text of the node
would is preserved (as the parent node does allow PCDATA). Custom
content model implementations probably are not the best way of handling
these cases, instead, node bubbling should be implemented instead.

View File

@@ -1,10 +1,8 @@
XHTML 1.1 and HTML Purifier
The Modularization of HTMLDefinition in HTML Purifier
Todo for XHTML 1.1 support <http://www.w3.org/TR/xhtml11/changes.html>
1. Scratch lang entirely in favor of xml:lang
2. Scratch name entirely in favor of id (partially-done)
3. Support Ruby <http://www.w3.org/TR/2001/REC-ruby-20010531/>
1. Support Ruby <http://www.w3.org/TR/2001/REC-ruby-20010531/>
HTML Purifier uses the modularization of XHTML
<http://www.w3.org/TR/xhtml-modularization/> to organize the internals
@@ -12,25 +10,10 @@ of HTMLDefinition into a more manageable and extensible fashion. Rather
than have one super-object, HTMLDefinition is split into HTMLModules,
each of which are responsible for defining elements, their attributes,
and other properties (for a more indepth coverage, see
/library/HTMLPurifier/HTMLModule.php's docblock comments).
/library/HTMLPurifier/HTMLModule.php's docblock comments). These modules
are managed by HTMLModuleManager.
The modules that W3C defines and we support are:
* 5.1. Attribute Collections (technically not a module
* 5.2. Core Modules
o 5.2.2. Text Module
o 5.2.3. Hypertext Module
o 5.2.4. List Module
* 5.4. Text Extension Modules
o 5.4.1. Presentation Module
o 5.4.2. Edit Module
o 5.4.3. Bi-directional Text Module
* 5.6. Table Modules
o 5.6.2. Tables Module
* 5.7. Image Module
* 5.18. Style Attribute Module
Modules that we don't support but coul support are:
Modules that we don't support but could support are:
* 5.6. Table Modules
o 5.6.1. Basic Tables Module [?]
@@ -38,10 +21,8 @@ Modules that we don't support but coul support are:
* 5.9. Server-side Image Map Module [?]
* 5.12. Target Module [?]
* 5.21. Name Identification Module [deprecated]
* 5.22. Legacy Module [deprecated]
These modules will not be implemented due to their dangerousness or
inapplicability as an XHTML fragment:
These modules would be implemented as "unsafe":
* 5.2. Core Modules
o 5.2.1. Structure Module
@@ -64,11 +45,7 @@ of robust tools for handling them (the main problem is that all the
current parsers are usually PHP 5 only and solely-validating, not
correcting).
The abstraction of the HTMLDefinition creation process will also
contribute to a need for a caching system. Cache invalidation would be
difficult, but could be done by comparing the HTML and Attr config
namespaces with a copy that was packaged along with the serialized
HTMLDefinition object.
This system may be generalized and ported over for CSS.
== General Use-Case ==
@@ -91,7 +68,7 @@ like this:
<?php
$config = HTMLPurifier_Config::createDefault();
$def =& $config->getHTMLDefinition(true); // reference to raw
unset($def->modules['Hypertext']); // rm ''a'' link
$def->addElement('marquee', 'Block', 'Flow', 'Common');
$purifier = new HTMLPurifier($config);
$purifier->purify($html); // now the definition is finalized
?>
@@ -184,4 +161,4 @@ Content sets can be altered using HTMLModule->content_sets, an associative
array of content set names to content set contents. If the content set
already exists, your values are appended on to it (great for, say,
registering the font tag as an inline element), otherwise it is
created. They are substituted into content_model.
created. They are substituted into content_model.

View File

@@ -1,37 +0,0 @@
Loose versus Strict
Changes from one doctype to another
There are changes. Wow, how insightful. Not everything changed is relevant
to HTML Purifier, though, so let's take a look:
== Major incompatibilities ==
[done] BLOCKQUOTE changes from 'flow' to 'block'
current behavior: inline inner contents should not be nuked, block-ify as necessary
[partially-done] U, S, STRIKE cut
current behavior: removed completely
projected behavior: replace with appropriate inline span + CSS
[done] ADDRESS from potpourri to Inline (removes p tags)
current behavior: block tags silently dropped
ideal behavior: replace tags with something like <br>. (not high priority)
== Things we can loosen up ==
Tags DIR, MENU, CENTER, ISINDEX, FONT, BASEFONT? allowed in loose
current behavior: transform to strict-valid forms
Attributes allowed in loose (see attribute transforms in 'dev-progress.html')
current behavior: projected to transform into strict-valid forms
== Periphery issues ==
A tag's attribute 'target' (for selecting frames) cut
current behavior: not allowed at all
projected behavior: use loose doctype if needed, needs valid values
[done] OL/LI tag's attribute 'start'/'value' (for renumbering lists) cut
current behavior: no substitute, just delete when in strict, allow in loose
Attribute 'name' deprecated in favor of 'id'
current behavior: dropped silently
projected behavior: create proper AttrTransform
[done] PRE tag allows SUB/SUP? (strict dtd comment vs syntax, loose disallows)
current behavior: disallow as usual

View File

@@ -18,5 +18,7 @@ HTML Purifier context.
<listing>, monospace pre-variant (extremely rare)
<plaintext>, escapes all tags to the end of document
<ruby> and friends, (more research needed, appears to be XHTML 1.1 markup)
<xmp>, monospace, replace with pre
These should be put into their own Tidy module, not loaded by default(?). These
all qualify as "lenient" transforms.

View File

@@ -1,37 +0,0 @@
Is HTML Purifier Strict or Transitional?
A little bit of helpful guidance
Despite the fact that HTML Purifier professes to support both transitional and
strict HTML, it rejects a lot of attributes and elements that are actually, indeed,
valid. You can investigate progress.html to find out precisely what we
are doing to these *deprecated* attributes.
However, users have found that Strict HTML imposes some quite unreasonable
restrictions on certain things. The start and value attributes in ol and
li (respectively) perhaps are the most contested. There's is currently no
widely supported browser method short of JavaScript that can replace these
two deprecated elements. It behooves us to allow these deprecated
attributes when the output is transitional.
Fortunantely, that's the only real bugger case. The others have near-perfect
CSS equivalents, and were presentational anyway. However, the other question
pops up: should we always convert these to the CSS forms when 1. the spec
allows them anyway and 2. older browsers support them better? After all, the
whole point about CSS is to seperate styling from content, so inline styling
doesn't solve that problem.
It's an icky question, and we'll have to deal with it as more and more
transforms get implemented. As of right now, however, we currently support
these loose-only constructs in loose mode:
- <ul start="1">, <li value="1"> attributes
- <u>, <strike>, <s> tags
- flow children in <blockquote>
- mixed children in <address>
The changed child definitions as well as the ul.start li.value are the most
compelling reasons why loose should be used. We may want offer disabling <u>,
<strike> and <s> by themselves. We may also want to offer no pre-emptive
deprecated conversions. This all must be unified.

View File

@@ -2,8 +2,23 @@
Web Hypertext Application Technology Working Group
WHATWG
I don't think we need to worry about them. Untrusted users shouldn't be
submitting applications, eh? But if some interesting attribute pops up in
their spec, and might be worth supporting, stick it here.
== HTML 5 ==
(none so far, as you can see)
URL: http://www.whatwg.org/specs/web-apps/current-work/
HTML 5 defines a kaboodle of new elements and attributes, as well as
some well-defined, "quirks mode" HTML parsing. Although WHATWG professes
to be targeted towards web applications, many of their semantic additions
would be quite useful in regular documents. Eventually, HTML
Purifier will need to audit their lists and figure out what changes need
to be made. This process is complicated by the fact that the WHATWG
doesn't buy into W3C's modularization of XHTML 1.1: we may need
to remodularize HTML 5 (probably done by section name). No sense in
committing ourselves till the spec stabilizes, though.
More immediately speaking though, however, is the well-defined parsing
behavior that HTML 5 adds. While I have little interest in writing
another DirectLex parser, other parsers like ph5p
<http://jero.net/lab/ph5p/> can be adapted to DOMLex to support much more
flexible HTML parsing (a cool feature I've seen is how they resolve
<b>bold<i>both</b>italic</i>).

View File

@@ -25,6 +25,7 @@ h4 {font-family:sans-serif; font-size:0.9em; font-weight:bold; }
.aside {margin-left:2em; font-family:sans-serif; font-size:0.9em; }
blockquote .label {font-weight:bold; font-size:1em; margin:0 0 .1em;
border-bottom:1px solid #CCC;}
.emphasis {font-weight:bold; text-align:center; font-size:1.3em;}
/* A regular table */
.table {border-collapse:collapse; border-bottom:2px solid #888; margin-left:2em; }
@@ -66,3 +67,5 @@ q:after {
/* Marks off sections that are lacking. */
.fixme {margin-left:2em; }
.fixme:before {content:"Fix me: "; font-weight:bold; color:#C00; }
#applicability {margin: 1em 5%; font-style:italic;}