1: <?php
2:
3: class HTMLPurifier_HTMLModuleManager
4: {
5:
6: 7: 8:
9: public $doctypes;
10:
11: 12: 13:
14: public $doctype;
15:
16: 17: 18:
19: public $attrTypes;
20:
21: 22: 23: 24:
25: public $modules = array();
26:
27: 28: 29: 30: 31:
32: public $registeredModules = array();
33:
34: 35: 36: 37: 38:
39: public $userModules = array();
40:
41: 42: 43: 44:
45: public $elementLookup = array();
46:
47:
48: public $prefixes = array('HTMLPurifier_HTMLModule_');
49:
50: public $contentSets;
51: public $attrCollections;
52:
53:
54: public $trusted = false;
55:
56: public function __construct() {
57:
58:
59: $this->attrTypes = new HTMLPurifier_AttrTypes();
60: $this->doctypes = new HTMLPurifier_DoctypeRegistry();
61:
62:
63: $common = array(
64: 'CommonAttributes', 'Text', 'Hypertext', 'List',
65: 'Presentation', 'Edit', 'Bdo', 'Tables', 'Image',
66: 'StyleAttribute',
67:
68: 'Scripting', 'Object', 'Forms',
69:
70: 'Name',
71: );
72: $transitional = array('Legacy', 'Target');
73: $xml = array('XMLCommonAttributes');
74: $non_xml = array('NonXMLCommonAttributes');
75:
76:
77: $this->doctypes->register(
78: 'HTML 4.01 Transitional', false,
79: array_merge($common, $transitional, $non_xml),
80: array('Tidy_Transitional', 'Tidy_Proprietary'),
81: array(),
82: '-//W3C//DTD HTML 4.01 Transitional//EN',
83: 'http://www.w3.org/TR/html4/loose.dtd'
84: );
85:
86: $this->doctypes->register(
87: 'HTML 4.01 Strict', false,
88: array_merge($common, $non_xml),
89: array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
90: array(),
91: '-//W3C//DTD HTML 4.01//EN',
92: 'http://www.w3.org/TR/html4/strict.dtd'
93: );
94:
95: $this->doctypes->register(
96: 'XHTML 1.0 Transitional', true,
97: array_merge($common, $transitional, $xml, $non_xml),
98: array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'),
99: array(),
100: '-//W3C//DTD XHTML 1.0 Transitional//EN',
101: 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
102: );
103:
104: $this->doctypes->register(
105: 'XHTML 1.0 Strict', true,
106: array_merge($common, $xml, $non_xml),
107: array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'),
108: array(),
109: '-//W3C//DTD XHTML 1.0 Strict//EN',
110: 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
111: );
112:
113: $this->doctypes->register(
114: 'XHTML 1.1', true,
115: array_merge($common, $xml, array('Ruby')),
116: array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'),
117: array(),
118: '-//W3C//DTD XHTML 1.1//EN',
119: 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
120: );
121:
122: }
123:
124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144:
145: public function registerModule($module, $overload = false) {
146: if (is_string($module)) {
147:
148: $original_module = $module;
149: $ok = false;
150: foreach ($this->prefixes as $prefix) {
151: $module = $prefix . $original_module;
152: if (class_exists($module)) {
153: $ok = true;
154: break;
155: }
156: }
157: if (!$ok) {
158: $module = $original_module;
159: if (!class_exists($module)) {
160: trigger_error($original_module . ' module does not exist',
161: E_USER_ERROR);
162: return;
163: }
164: }
165: $module = new $module();
166: }
167: if (empty($module->name)) {
168: trigger_error('Module instance of ' . get_class($module) . ' must have name');
169: return;
170: }
171: if (!$overload && isset($this->registeredModules[$module->name])) {
172: trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING);
173: }
174: $this->registeredModules[$module->name] = $module;
175: }
176:
177: 178: 179: 180:
181: public function addModule($module) {
182: $this->registerModule($module);
183: if (is_object($module)) $module = $module->name;
184: $this->userModules[] = $module;
185: }
186:
187: 188: 189: 190:
191: public function addPrefix($prefix) {
192: $this->prefixes[] = $prefix;
193: }
194:
195: 196: 197: 198: 199:
200: public function setup($config) {
201:
202: $this->trusted = $config->get('HTML.Trusted');
203:
204:
205: $this->doctype = $this->doctypes->make($config);
206: $modules = $this->doctype->modules;
207:
208:
209: $lookup = $config->get('HTML.AllowedModules');
210: $special_cases = $config->get('HTML.CoreModules');
211:
212: if (is_array($lookup)) {
213: foreach ($modules as $k => $m) {
214: if (isset($special_cases[$m])) continue;
215: if (!isset($lookup[$m])) unset($modules[$k]);
216: }
217: }
218:
219:
220: if ($config->get('HTML.Proprietary')) {
221: $modules[] = 'Proprietary';
222: }
223: if ($config->get('HTML.SafeObject')) {
224: $modules[] = 'SafeObject';
225: }
226: if ($config->get('HTML.SafeEmbed')) {
227: $modules[] = 'SafeEmbed';
228: }
229: if ($config->get('HTML.Nofollow')) {
230: $modules[] = 'Nofollow';
231: }
232:
233:
234: $modules = array_merge($modules, $this->userModules);
235:
236: foreach ($modules as $module) {
237: $this->processModule($module);
238: $this->modules[$module]->setup($config);
239: }
240:
241: foreach ($this->doctype->tidyModules as $module) {
242: $this->processModule($module);
243: $this->modules[$module]->setup($config);
244: }
245:
246:
247: foreach ($this->modules as $module) {
248: $n = array();
249: foreach ($module->info_injector as $i => $injector) {
250: if (!is_object($injector)) {
251: $class = "HTMLPurifier_Injector_$injector";
252: $injector = new $class;
253: }
254: $n[$injector->name] = $injector;
255: }
256: $module->info_injector = $n;
257: }
258:
259:
260: foreach ($this->modules as $module) {
261: foreach ($module->info as $name => $def) {
262: if (!isset($this->elementLookup[$name])) {
263: $this->elementLookup[$name] = array();
264: }
265: $this->elementLookup[$name][] = $module->name;
266: }
267: }
268:
269:
270: $this->contentSets = new HTMLPurifier_ContentSets(
271:
272:
273: $this->modules
274: );
275: $this->attrCollections = new HTMLPurifier_AttrCollections(
276: $this->attrTypes,
277:
278:
279:
280: $this->modules
281: );
282: }
283:
284: 285: 286: 287:
288: public function processModule($module) {
289: if (!isset($this->registeredModules[$module]) || is_object($module)) {
290: $this->registerModule($module);
291: }
292: $this->modules[$module] = $this->registeredModules[$module];
293: }
294:
295: 296: 297: 298:
299: public function getElements() {
300:
301: $elements = array();
302: foreach ($this->modules as $module) {
303: if (!$this->trusted && !$module->safe) continue;
304: foreach ($module->info as $name => $v) {
305: if (isset($elements[$name])) continue;
306: $elements[$name] = $this->getElement($name);
307: }
308: }
309:
310:
311:
312: foreach ($elements as $n => $v) {
313: if ($v === false) unset($elements[$n]);
314: }
315:
316: return $elements;
317:
318: }
319:
320: 321: 322: 323: 324: 325: 326: 327: 328: 329:
330: public function getElement($name, $trusted = null) {
331:
332: if (!isset($this->elementLookup[$name])) {
333: return false;
334: }
335:
336:
337: $def = false;
338: if ($trusted === null) $trusted = $this->trusted;
339:
340:
341:
342: foreach($this->elementLookup[$name] as $module_name) {
343:
344: $module = $this->modules[$module_name];
345:
346:
347:
348: if (!$trusted && !$module->safe) {
349: continue;
350: }
351:
352:
353:
354:
355: $new_def = clone $module->info[$name];
356:
357: if (!$def && $new_def->standalone) {
358: $def = $new_def;
359: } elseif ($def) {
360:
361:
362: $def->mergeIn($new_def);
363: } else {
364:
365:
366:
367: continue;
368: }
369:
370:
371: $this->attrCollections->performInclusions($def->attr);
372: $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes);
373:
374:
375: if (is_string($def->content_model) &&
376: strpos($def->content_model, 'Inline') !== false) {
377: if ($name != 'del' && $name != 'ins') {
378:
379: $def->descendants_are_inline = true;
380: }
381: }
382:
383: $this->contentSets->generateChildDef($def, $module);
384: }
385:
386:
387:
388: if (!$def) return false;
389:
390:
391: foreach ($def->attr as $attr_name => $attr_def) {
392: if ($attr_def->required) {
393: $def->required_attr[] = $attr_name;
394: }
395: }
396:
397: return $def;
398:
399: }
400:
401: }
402:
403:
404: