HTMLPurifier 4.4.0
|
00001 <?php 00002 00026 class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition 00027 { 00028 00029 // FULLY-PUBLIC VARIABLES --------------------------------------------- 00030 00034 public $info = array(); 00035 00039 public $info_global_attr = array(); 00040 00044 public $info_parent = 'div'; 00045 00050 public $info_parent_def; 00051 00056 public $info_block_wrapper = 'p'; 00057 00061 public $info_tag_transform = array(); 00062 00066 public $info_attr_transform_pre = array(); 00067 00071 public $info_attr_transform_post = array(); 00072 00077 public $info_content_sets = array(); 00078 00082 public $info_injector = array(); 00083 00087 public $doctype; 00088 00089 00090 00091 // RAW CUSTOMIZATION STUFF -------------------------------------------- 00092 00102 public function addAttribute($element_name, $attr_name, $def) { 00103 $module = $this->getAnonymousModule(); 00104 if (!isset($module->info[$element_name])) { 00105 $element = $module->addBlankElement($element_name); 00106 } else { 00107 $element = $module->info[$element_name]; 00108 } 00109 $element->attr[$attr_name] = $def; 00110 } 00111 00117 public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) { 00118 $module = $this->getAnonymousModule(); 00119 // assume that if the user is calling this, the element 00120 // is safe. This may not be a good idea 00121 $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); 00122 return $element; 00123 } 00124 00131 public function addBlankElement($element_name) { 00132 $module = $this->getAnonymousModule(); 00133 $element = $module->addBlankElement($element_name); 00134 return $element; 00135 } 00136 00142 public function getAnonymousModule() { 00143 if (!$this->_anonModule) { 00144 $this->_anonModule = new HTMLPurifier_HTMLModule(); 00145 $this->_anonModule->name = 'Anonymous'; 00146 } 00147 return $this->_anonModule; 00148 } 00149 00150 private $_anonModule = null; 00151 00152 00153 // PUBLIC BUT INTERNAL VARIABLES -------------------------------------- 00154 00155 public $type = 'HTML'; 00156 public $manager; 00161 public function __construct() { 00162 $this->manager = new HTMLPurifier_HTMLModuleManager(); 00163 } 00164 00165 protected function doSetup($config) { 00166 $this->processModules($config); 00167 $this->setupConfigStuff($config); 00168 unset($this->manager); 00169 00170 // cleanup some of the element definitions 00171 foreach ($this->info as $k => $v) { 00172 unset($this->info[$k]->content_model); 00173 unset($this->info[$k]->content_model_type); 00174 } 00175 } 00176 00180 protected function processModules($config) { 00181 00182 if ($this->_anonModule) { 00183 // for user specific changes 00184 // this is late-loaded so we don't have to deal with PHP4 00185 // reference wonky-ness 00186 $this->manager->addModule($this->_anonModule); 00187 unset($this->_anonModule); 00188 } 00189 00190 $this->manager->setup($config); 00191 $this->doctype = $this->manager->doctype; 00192 00193 foreach ($this->manager->modules as $module) { 00194 foreach($module->info_tag_transform as $k => $v) { 00195 if ($v === false) unset($this->info_tag_transform[$k]); 00196 else $this->info_tag_transform[$k] = $v; 00197 } 00198 foreach($module->info_attr_transform_pre as $k => $v) { 00199 if ($v === false) unset($this->info_attr_transform_pre[$k]); 00200 else $this->info_attr_transform_pre[$k] = $v; 00201 } 00202 foreach($module->info_attr_transform_post as $k => $v) { 00203 if ($v === false) unset($this->info_attr_transform_post[$k]); 00204 else $this->info_attr_transform_post[$k] = $v; 00205 } 00206 foreach ($module->info_injector as $k => $v) { 00207 if ($v === false) unset($this->info_injector[$k]); 00208 else $this->info_injector[$k] = $v; 00209 } 00210 } 00211 00212 $this->info = $this->manager->getElements(); 00213 $this->info_content_sets = $this->manager->contentSets->lookup; 00214 00215 } 00216 00220 protected function setupConfigStuff($config) { 00221 00222 $block_wrapper = $config->get('HTML.BlockWrapper'); 00223 if (isset($this->info_content_sets['Block'][$block_wrapper])) { 00224 $this->info_block_wrapper = $block_wrapper; 00225 } else { 00226 trigger_error('Cannot use non-block element as block wrapper', 00227 E_USER_ERROR); 00228 } 00229 00230 $parent = $config->get('HTML.Parent'); 00231 $def = $this->manager->getElement($parent, true); 00232 if ($def) { 00233 $this->info_parent = $parent; 00234 $this->info_parent_def = $def; 00235 } else { 00236 trigger_error('Cannot use unrecognized element as parent', 00237 E_USER_ERROR); 00238 $this->info_parent_def = $this->manager->getElement($this->info_parent, true); 00239 } 00240 00241 // support template text 00242 $support = "(for information on implementing this, see the ". 00243 "support forums) "; 00244 00245 // setup allowed elements ----------------------------------------- 00246 00247 $allowed_elements = $config->get('HTML.AllowedElements'); 00248 $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early 00249 00250 if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { 00251 $allowed = $config->get('HTML.Allowed'); 00252 if (is_string($allowed)) { 00253 list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); 00254 } 00255 } 00256 00257 if (is_array($allowed_elements)) { 00258 foreach ($this->info as $name => $d) { 00259 if(!isset($allowed_elements[$name])) unset($this->info[$name]); 00260 unset($allowed_elements[$name]); 00261 } 00262 // emit errors 00263 foreach ($allowed_elements as $element => $d) { 00264 $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful! 00265 trigger_error("Element '$element' is not supported $support", E_USER_WARNING); 00266 } 00267 } 00268 00269 // setup allowed attributes --------------------------------------- 00270 00271 $allowed_attributes_mutable = $allowed_attributes; // by copy! 00272 if (is_array($allowed_attributes)) { 00273 00274 // This actually doesn't do anything, since we went away from 00275 // global attributes. It's possible that userland code uses 00276 // it, but HTMLModuleManager doesn't! 00277 foreach ($this->info_global_attr as $attr => $x) { 00278 $keys = array($attr, "*@$attr", "*.$attr"); 00279 $delete = true; 00280 foreach ($keys as $key) { 00281 if ($delete && isset($allowed_attributes[$key])) { 00282 $delete = false; 00283 } 00284 if (isset($allowed_attributes_mutable[$key])) { 00285 unset($allowed_attributes_mutable[$key]); 00286 } 00287 } 00288 if ($delete) unset($this->info_global_attr[$attr]); 00289 } 00290 00291 foreach ($this->info as $tag => $info) { 00292 foreach ($info->attr as $attr => $x) { 00293 $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr"); 00294 $delete = true; 00295 foreach ($keys as $key) { 00296 if ($delete && isset($allowed_attributes[$key])) { 00297 $delete = false; 00298 } 00299 if (isset($allowed_attributes_mutable[$key])) { 00300 unset($allowed_attributes_mutable[$key]); 00301 } 00302 } 00303 if ($delete) { 00304 if ($this->info[$tag]->attr[$attr]->required) { 00305 trigger_error("Required attribute '$attr' in element '$tag' was not allowed, which means '$tag' will not be allowed either", E_USER_WARNING); 00306 } 00307 unset($this->info[$tag]->attr[$attr]); 00308 } 00309 } 00310 } 00311 // emit errors 00312 foreach ($allowed_attributes_mutable as $elattr => $d) { 00313 $bits = preg_split('/[.@]/', $elattr, 2); 00314 $c = count($bits); 00315 switch ($c) { 00316 case 2: 00317 if ($bits[0] !== '*') { 00318 $element = htmlspecialchars($bits[0]); 00319 $attribute = htmlspecialchars($bits[1]); 00320 if (!isset($this->info[$element])) { 00321 trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support"); 00322 } else { 00323 trigger_error("Attribute '$attribute' in element '$element' not supported $support", 00324 E_USER_WARNING); 00325 } 00326 break; 00327 } 00328 // otherwise fall through 00329 case 1: 00330 $attribute = htmlspecialchars($bits[0]); 00331 trigger_error("Global attribute '$attribute' is not ". 00332 "supported in any elements $support", 00333 E_USER_WARNING); 00334 break; 00335 } 00336 } 00337 00338 } 00339 00340 // setup forbidden elements --------------------------------------- 00341 00342 $forbidden_elements = $config->get('HTML.ForbiddenElements'); 00343 $forbidden_attributes = $config->get('HTML.ForbiddenAttributes'); 00344 00345 foreach ($this->info as $tag => $info) { 00346 if (isset($forbidden_elements[$tag])) { 00347 unset($this->info[$tag]); 00348 continue; 00349 } 00350 foreach ($info->attr as $attr => $x) { 00351 if ( 00352 isset($forbidden_attributes["$tag@$attr"]) || 00353 isset($forbidden_attributes["*@$attr"]) || 00354 isset($forbidden_attributes[$attr]) 00355 ) { 00356 unset($this->info[$tag]->attr[$attr]); 00357 continue; 00358 } // this segment might get removed eventually 00359 elseif (isset($forbidden_attributes["$tag.$attr"])) { 00360 // $tag.$attr are not user supplied, so no worries! 00361 trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING); 00362 } 00363 } 00364 } 00365 foreach ($forbidden_attributes as $key => $v) { 00366 if (strlen($key) < 2) continue; 00367 if ($key[0] != '*') continue; 00368 if ($key[1] == '.') { 00369 trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING); 00370 } 00371 } 00372 00373 // setup injectors ----------------------------------------------------- 00374 foreach ($this->info_injector as $i => $injector) { 00375 if ($injector->checkNeeded($config) !== false) { 00376 // remove injector that does not have it's required 00377 // elements/attributes present, and is thus not needed. 00378 unset($this->info_injector[$i]); 00379 } 00380 } 00381 } 00382 00392 public function parseTinyMCEAllowedList($list) { 00393 00394 $list = str_replace(array(' ', "\t"), '', $list); 00395 00396 $elements = array(); 00397 $attributes = array(); 00398 00399 $chunks = preg_split('/(,|[\n\r]+)/', $list); 00400 foreach ($chunks as $chunk) { 00401 if (empty($chunk)) continue; 00402 // remove TinyMCE element control characters 00403 if (!strpos($chunk, '[')) { 00404 $element = $chunk; 00405 $attr = false; 00406 } else { 00407 list($element, $attr) = explode('[', $chunk); 00408 } 00409 if ($element !== '*') $elements[$element] = true; 00410 if (!$attr) continue; 00411 $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ] 00412 $attr = explode('|', $attr); 00413 foreach ($attr as $key) { 00414 $attributes["$element.$key"] = true; 00415 } 00416 } 00417 00418 return array($elements, $attributes); 00419 00420 } 00421 00422 00423 } 00424 00425 // vim: et sw=4 sts=4