|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 class HTMLPurifier_HTMLModuleManager 00004 { 00005 00009 public $doctypes; 00010 00014 public $doctype; 00015 00019 public $attrTypes; 00020 00025 public $modules = array(); 00026 00032 public $registeredModules = array(); 00033 00039 public $userModules = array(); 00040 00045 public $elementLookup = array(); 00046 00048 public $prefixes = array('HTMLPurifier_HTMLModule_'); 00049 00050 public $contentSets; 00051 public $attrCollections; 00054 public $trusted = false; 00055 00056 public function __construct() { 00057 00058 // editable internal objects 00059 $this->attrTypes = new HTMLPurifier_AttrTypes(); 00060 $this->doctypes = new HTMLPurifier_DoctypeRegistry(); 00061 00062 // setup basic modules 00063 $common = array( 00064 'CommonAttributes', 'Text', 'Hypertext', 'List', 00065 'Presentation', 'Edit', 'Bdo', 'Tables', 'Image', 00066 'StyleAttribute', 00067 // Unsafe: 00068 'Scripting', 'Object', 'Forms', 00069 // Sorta legacy, but present in strict: 00070 'Name', 00071 ); 00072 $transitional = array('Legacy', 'Target'); 00073 $xml = array('XMLCommonAttributes'); 00074 $non_xml = array('NonXMLCommonAttributes'); 00075 00076 // setup basic doctypes 00077 $this->doctypes->register( 00078 'HTML 4.01 Transitional', false, 00079 array_merge($common, $transitional, $non_xml), 00080 array('Tidy_Transitional', 'Tidy_Proprietary'), 00081 array(), 00082 '-//W3C//DTD HTML 4.01 Transitional//EN', 00083 'http://www.w3.org/TR/html4/loose.dtd' 00084 ); 00085 00086 $this->doctypes->register( 00087 'HTML 4.01 Strict', false, 00088 array_merge($common, $non_xml), 00089 array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), 00090 array(), 00091 '-//W3C//DTD HTML 4.01//EN', 00092 'http://www.w3.org/TR/html4/strict.dtd' 00093 ); 00094 00095 $this->doctypes->register( 00096 'XHTML 1.0 Transitional', true, 00097 array_merge($common, $transitional, $xml, $non_xml), 00098 array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'), 00099 array(), 00100 '-//W3C//DTD XHTML 1.0 Transitional//EN', 00101 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd' 00102 ); 00103 00104 $this->doctypes->register( 00105 'XHTML 1.0 Strict', true, 00106 array_merge($common, $xml, $non_xml), 00107 array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), 00108 array(), 00109 '-//W3C//DTD XHTML 1.0 Strict//EN', 00110 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd' 00111 ); 00112 00113 $this->doctypes->register( 00114 'XHTML 1.1', true, 00115 array_merge($common, $xml, array('Ruby')), 00116 array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1 00117 array(), 00118 '-//W3C//DTD XHTML 1.1//EN', 00119 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd' 00120 ); 00121 00122 } 00123 00145 public function registerModule($module, $overload = false) { 00146 if (is_string($module)) { 00147 // attempt to load the module 00148 $original_module = $module; 00149 $ok = false; 00150 foreach ($this->prefixes as $prefix) { 00151 $module = $prefix . $original_module; 00152 if (class_exists($module)) { 00153 $ok = true; 00154 break; 00155 } 00156 } 00157 if (!$ok) { 00158 $module = $original_module; 00159 if (!class_exists($module)) { 00160 trigger_error($original_module . ' module does not exist', 00161 E_USER_ERROR); 00162 return; 00163 } 00164 } 00165 $module = new $module(); 00166 } 00167 if (empty($module->name)) { 00168 trigger_error('Module instance of ' . get_class($module) . ' must have name'); 00169 return; 00170 } 00171 if (!$overload && isset($this->registeredModules[$module->name])) { 00172 trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING); 00173 } 00174 $this->registeredModules[$module->name] = $module; 00175 } 00176 00181 public function addModule($module) { 00182 $this->registerModule($module); 00183 if (is_object($module)) $module = $module->name; 00184 $this->userModules[] = $module; 00185 } 00186 00191 public function addPrefix($prefix) { 00192 $this->prefixes[] = $prefix; 00193 } 00194 00200 public function setup($config) { 00201 00202 $this->trusted = $config->get('HTML.Trusted'); 00203 00204 // generate 00205 $this->doctype = $this->doctypes->make($config); 00206 $modules = $this->doctype->modules; 00207 00208 // take out the default modules that aren't allowed 00209 $lookup = $config->get('HTML.AllowedModules'); 00210 $special_cases = $config->get('HTML.CoreModules'); 00211 00212 if (is_array($lookup)) { 00213 foreach ($modules as $k => $m) { 00214 if (isset($special_cases[$m])) continue; 00215 if (!isset($lookup[$m])) unset($modules[$k]); 00216 } 00217 } 00218 00219 // custom modules 00220 if ($config->get('HTML.Proprietary')) { 00221 $modules[] = 'Proprietary'; 00222 } 00223 if ($config->get('HTML.SafeObject')) { 00224 $modules[] = 'SafeObject'; 00225 } 00226 if ($config->get('HTML.SafeEmbed')) { 00227 $modules[] = 'SafeEmbed'; 00228 } 00229 if ($config->get('HTML.Nofollow')) { 00230 $modules[] = 'Nofollow'; 00231 } 00232 00233 // merge in custom modules 00234 $modules = array_merge($modules, $this->userModules); 00235 00236 foreach ($modules as $module) { 00237 $this->processModule($module); 00238 $this->modules[$module]->setup($config); 00239 } 00240 00241 foreach ($this->doctype->tidyModules as $module) { 00242 $this->processModule($module); 00243 $this->modules[$module]->setup($config); 00244 } 00245 00246 // prepare any injectors 00247 foreach ($this->modules as $module) { 00248 $n = array(); 00249 foreach ($module->info_injector as $i => $injector) { 00250 if (!is_object($injector)) { 00251 $class = "HTMLPurifier_Injector_$injector"; 00252 $injector = new $class; 00253 } 00254 $n[$injector->name] = $injector; 00255 } 00256 $module->info_injector = $n; 00257 } 00258 00259 // setup lookup table based on all valid modules 00260 foreach ($this->modules as $module) { 00261 foreach ($module->info as $name => $def) { 00262 if (!isset($this->elementLookup[$name])) { 00263 $this->elementLookup[$name] = array(); 00264 } 00265 $this->elementLookup[$name][] = $module->name; 00266 } 00267 } 00268 00269 // note the different choice 00270 $this->contentSets = new HTMLPurifier_ContentSets( 00271 // content set assembly deals with all possible modules, 00272 // not just ones deemed to be "safe" 00273 $this->modules 00274 ); 00275 $this->attrCollections = new HTMLPurifier_AttrCollections( 00276 $this->attrTypes, 00277 // there is no way to directly disable a global attribute, 00278 // but using AllowedAttributes or simply not including 00279 // the module in your custom doctype should be sufficient 00280 $this->modules 00281 ); 00282 } 00283 00288 public function processModule($module) { 00289 if (!isset($this->registeredModules[$module]) || is_object($module)) { 00290 $this->registerModule($module); 00291 } 00292 $this->modules[$module] = $this->registeredModules[$module]; 00293 } 00294 00299 public function getElements() { 00300 00301 $elements = array(); 00302 foreach ($this->modules as $module) { 00303 if (!$this->trusted && !$module->safe) continue; 00304 foreach ($module->info as $name => $v) { 00305 if (isset($elements[$name])) continue; 00306 $elements[$name] = $this->getElement($name); 00307 } 00308 } 00309 00310 // remove dud elements, this happens when an element that 00311 // appeared to be safe actually wasn't 00312 foreach ($elements as $n => $v) { 00313 if ($v === false) unset($elements[$n]); 00314 } 00315 00316 return $elements; 00317 00318 } 00319 00330 public function getElement($name, $trusted = null) { 00331 00332 if (!isset($this->elementLookup[$name])) { 00333 return false; 00334 } 00335 00336 // setup global state variables 00337 $def = false; 00338 if ($trusted === null) $trusted = $this->trusted; 00339 00340 // iterate through each module that has registered itself to this 00341 // element 00342 foreach($this->elementLookup[$name] as $module_name) { 00343 00344 $module = $this->modules[$module_name]; 00345 00346 // refuse to create/merge from a module that is deemed unsafe-- 00347 // pretend the module doesn't exist--when trusted mode is not on. 00348 if (!$trusted && !$module->safe) { 00349 continue; 00350 } 00351 00352 // clone is used because, ideally speaking, the original 00353 // definition should not be modified. Usually, this will 00354 // make no difference, but for consistency's sake 00355 $new_def = clone $module->info[$name]; 00356 00357 if (!$def && $new_def->standalone) { 00358 $def = $new_def; 00359 } elseif ($def) { 00360 // This will occur even if $new_def is standalone. In practice, 00361 // this will usually result in a full replacement. 00362 $def->mergeIn($new_def); 00363 } else { 00364 // :TODO: 00365 // non-standalone definitions that don't have a standalone 00366 // to merge into could be deferred to the end 00367 continue; 00368 } 00369 00370 // attribute value expansions 00371 $this->attrCollections->performInclusions($def->attr); 00372 $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes); 00373 00374 // descendants_are_inline, for ChildDef_Chameleon 00375 if (is_string($def->content_model) && 00376 strpos($def->content_model, 'Inline') !== false) { 00377 if ($name != 'del' && $name != 'ins') { 00378 // this is for you, ins/del 00379 $def->descendants_are_inline = true; 00380 } 00381 } 00382 00383 $this->contentSets->generateChildDef($def, $module); 00384 } 00385 00386 // This can occur if there is a blank definition, but no base to 00387 // mix it in with 00388 if (!$def) return false; 00389 00390 // add information on required attributes 00391 foreach ($def->attr as $attr_name => $attr_def) { 00392 if ($attr_def->required) { 00393 $def->required_attr[] = $attr_name; 00394 } 00395 } 00396 00397 return $def; 00398 00399 } 00400 00401 } 00402 00403 // vim: et sw=4 sts=4