Whoops, forgot to edit WHATSNEW
[htmlpurifier.git] / docs / enduser-id.html
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6 <meta name="description" content="Explains various methods for allowing IDs in documents safely in HTML Purifier." />
7 <link rel="stylesheet" type="text/css" href="./style.css" />
9 <title>IDs - HTML Purifier</title>
11 </head><body>
13 <h1 class="subtitled">IDs</h1>
14 <div class="subtitle">What they are, why you should(n't) wear them, and how to deal with it</div>
16 <div id="filing">Filed under End-User</div>
17 <div id="index">Return to the <a href="index.html">index</a>.</div>
18 <div id="home"><a href="http://htmlpurifier.org/">HTML Purifier</a> End-User Documentation</div>
20 <p>Prior to HTML Purifier 1.2.0, this library blithely accepted user input that
21 looked like this:</p>
23 <pre>&lt;a id=&quot;fragment&quot;&gt;Anchor&lt;/a&gt;</pre>
25 <p>...presenting an attractive vector for those that would destroy standards
26 compliance: simply set the ID to one that is already used elsewhere in the
27 document and voila: validation breaks. There was a half-hearted attempt to
28 prevent this by allowing users to blacklist IDs, but I suspect that no one
29 really bothered, and thus, with the release of 1.2.0, IDs are now <em>removed</em>
30 by default.</p>
32 <p>IDs, however, are quite useful functionality to have, so if users start
33 complaining about broken anchors you'll probably want to turn them back on
34 with %Attr.EnableID. But before you go mucking around with the config
35 object, it's probably worth to take some precautions to keep your page
36 validating. Why?</p>
38 <ol>
39 <li>Standards-compliant pages are good</li>
40 <li>Duplicated IDs interfere with anchors. If there are two id="foobar"s in a
41 document, which spot does a browser presented with the fragment #foobar go
42 to? Most browsers opt for the first appearing ID, making it impossible
43 to references the second section. Similarly, duplicated IDs can hijack
44 client-side scripting that relies on the IDs of elements.</li>
45 </ol>
47 <p>You have (currently) four ways of dealing with the problem.</p>
51 <h2 class="subtitled">Blacklisting IDs</h2>
52 <div class="subsubtitle">Good for pages with single content source and stable templates</div>
54 <p>Keeping in terms with the
55 <acronym title="Keep It Simple, Stupid">KISS</acronym> principle, let us
56 deal with the most obvious solution: preventing users from using any IDs that
57 appear elsewhere on the document. The method is simple:</p>
59 <pre>$config-&gt;set('Attr.EnableID', true);
60 $config-&gt;set('Attr.IDBlacklist' array(
61 'list', 'of', 'attribute', 'values', 'that', 'are', 'forbidden'
62 ));</pre>
64 <p>That being said, there are some notable drawbacks. First of all, you have to
65 know precisely which IDs are being used by the HTML surrounding the user code.
66 This is easier said than done: quite often the page designer and the system
67 coder work separately, so the designer has to constantly be talking with the
68 coder whenever he decides to add a new anchor. Miss one and you open yourself
69 to possible standards-compliance issues.</p>
71 <p>Furthermore, this position becomes untenable when a single web page must hold
72 multiple portions of user-submitted content. Since there's obviously no way
73 to find out before-hand what IDs users will use, the blacklist is helpless.
74 And since HTML Purifier validates each segment separately, perhaps doing
75 so at different times, it would be extremely difficult to dynamically update
76 the blacklist in between runs.</p>
78 <p>Finally, simply destroying the ID is extremely un-userfriendly behavior: after
79 all, they might have simply specified a duplicate ID by accident.</p>
81 <p>Thus, we get to our second method.</p>
85 <h2 class="subtitled">Namespacing IDs</h2>
86 <div class="subsubtitle">Lazy developer's way, but needs user education</div>
88 <p>This method, too, is quite simple: add a prefix to all user IDs. With this
89 code:</p>
91 <pre>$config-&gt;set('Attr.EnableID', true);
92 $config-&gt;set('Attr.IDPrefix', 'user_');</pre>
94 <p>...this:</p>
96 <pre>&lt;a id=&quot;foobar&quot;&gt;Anchor!&lt;/a&gt;</pre>
98 <p>...turns into:</p>
100 <pre>&lt;a id=&quot;user_foobar&quot;&gt;Anchor!&lt;/a&gt;</pre>
102 <p>As long as you don't have any IDs that start with user_, collisions are
103 guaranteed not to happen. The drawback is obvious: if a user submits
104 id=&quot;foobar&quot;, they probably expect to be able to reference their page with
105 #foobar. You'll have to tell them, &quot;No, that doesn't work, you have to add
106 user_ to the beginning.&quot;</p>
108 <p>And yes, things get hairier. Even with a nice prefix, we still have done
109 nothing about multiple HTML Purifier outputs on one page. Thus, we have
110 a second configuration value to piggy-back off of: %Attr.IDPrefixLocal:</p>
112 <pre>$config-&gt;set('Attr.IDPrefixLocal', 'comment' . $id . '_');</pre>
114 <p>This new attributes does nothing but append on to regular IDPrefix, but is
115 special in that it is volatile: it's value is determined at run-time and
116 cannot possibly be cordoned into, say, a .ini config file. As for what to
117 put into the directive, is up to you, but I would recommend the ID number
118 the text has been assigned in the database. Whatever you pick, however, it
119 has to be unique and stable for the text you are validating. Note, however,
120 that we require that %Attr.IDPrefix be set before you use this directive.</p>
122 <p>And also remember: the user has to know what this prefix is too!</p>
126 <h2>Abstinence</h2>
128 <p>You may not want to bother. That's okay too, just don't enable IDs.</p>
130 <p>Personally, I would take this road whenever user-submitted content would be
131 possibly be shown together on one page. Why a blog comment would need to use
132 anchors is beyond me.</p>
136 <h2>Denial</h2>
138 <p>To revert back to pre-1.2.0 behavior, simply:</p>
140 <pre>$config-&gt;set('Attr.EnableID', true);</pre>
142 <p>Don't come crying to me when your page mysteriously stops validating, though.</p>
144 </body>
145 </html>
147 <!-- vim: et sw=4 sts=4