[ Index ] |
PHP Cross Reference of vtigercrm-6.1.0 |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Definition of the purified HTML that describes allowed children, 5 * attributes, and many other things. 6 * 7 * Conventions: 8 * 9 * All member variables that are prefixed with info 10 * (including the main $info array) are used by HTML Purifier internals 11 * and should not be directly edited when customizing the HTMLDefinition. 12 * They can usually be set via configuration directives or custom 13 * modules. 14 * 15 * On the other hand, member variables without the info prefix are used 16 * internally by the HTMLDefinition and MUST NOT be used by other HTML 17 * Purifier internals. Many of them, however, are public, and may be 18 * edited by userspace code to tweak the behavior of HTMLDefinition. 19 * 20 * @note This class is inspected by Printer_HTMLDefinition; please 21 * update that class if things here change. 22 * 23 * @warning Directives that change this object's structure must be in 24 * the HTML or Attr namespace! 25 */ 26 class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition 27 { 28 29 // FULLY-PUBLIC VARIABLES --------------------------------------------- 30 31 /** 32 * Associative array of element names to HTMLPurifier_ElementDef 33 */ 34 public $info = array(); 35 36 /** 37 * Associative array of global attribute name to attribute definition. 38 */ 39 public $info_global_attr = array(); 40 41 /** 42 * String name of parent element HTML will be going into. 43 */ 44 public $info_parent = 'div'; 45 46 /** 47 * Definition for parent element, allows parent element to be a 48 * tag that's not allowed inside the HTML fragment. 49 */ 50 public $info_parent_def; 51 52 /** 53 * String name of element used to wrap inline elements in block context 54 * @note This is rarely used except for BLOCKQUOTEs in strict mode 55 */ 56 public $info_block_wrapper = 'p'; 57 58 /** 59 * Associative array of deprecated tag name to HTMLPurifier_TagTransform 60 */ 61 public $info_tag_transform = array(); 62 63 /** 64 * Indexed list of HTMLPurifier_AttrTransform to be performed before validation. 65 */ 66 public $info_attr_transform_pre = array(); 67 68 /** 69 * Indexed list of HTMLPurifier_AttrTransform to be performed after validation. 70 */ 71 public $info_attr_transform_post = array(); 72 73 /** 74 * Nested lookup array of content set name (Block, Inline) to 75 * element name to whether or not it belongs in that content set. 76 */ 77 public $info_content_sets = array(); 78 79 /** 80 * Indexed list of HTMLPurifier_Injector to be used. 81 */ 82 public $info_injector = array(); 83 84 /** 85 * Doctype object 86 */ 87 public $doctype; 88 89 90 91 // RAW CUSTOMIZATION STUFF -------------------------------------------- 92 93 /** 94 * Adds a custom attribute to a pre-existing element 95 * @note This is strictly convenience, and does not have a corresponding 96 * method in HTMLPurifier_HTMLModule 97 * @param $element_name String element name to add attribute to 98 * @param $attr_name String name of attribute 99 * @param $def Attribute definition, can be string or object, see 100 * HTMLPurifier_AttrTypes for details 101 */ 102 public function addAttribute($element_name, $attr_name, $def) { 103 $module = $this->getAnonymousModule(); 104 if (!isset($module->info[$element_name])) { 105 $element = $module->addBlankElement($element_name); 106 } else { 107 $element = $module->info[$element_name]; 108 } 109 $element->attr[$attr_name] = $def; 110 } 111 112 /** 113 * Adds a custom element to your HTML definition 114 * @note See HTMLPurifier_HTMLModule::addElement for detailed 115 * parameter and return value descriptions. 116 */ 117 public function addElement($element_name, $type, $contents, $attr_collections, $attributes) { 118 $module = $this->getAnonymousModule(); 119 // assume that if the user is calling this, the element 120 // is safe. This may not be a good idea 121 $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); 122 return $element; 123 } 124 125 /** 126 * Adds a blank element to your HTML definition, for overriding 127 * existing behavior 128 * @note See HTMLPurifier_HTMLModule::addBlankElement for detailed 129 * parameter and return value descriptions. 130 */ 131 public function addBlankElement($element_name) { 132 $module = $this->getAnonymousModule(); 133 $element = $module->addBlankElement($element_name); 134 return $element; 135 } 136 137 /** 138 * Retrieves a reference to the anonymous module, so you can 139 * bust out advanced features without having to make your own 140 * module. 141 */ 142 public function getAnonymousModule() { 143 if (!$this->_anonModule) { 144 $this->_anonModule = new HTMLPurifier_HTMLModule(); 145 $this->_anonModule->name = 'Anonymous'; 146 } 147 return $this->_anonModule; 148 } 149 150 private $_anonModule; 151 152 153 // PUBLIC BUT INTERNAL VARIABLES -------------------------------------- 154 155 public $type = 'HTML'; 156 public $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */ 157 158 /** 159 * Performs low-cost, preliminary initialization. 160 */ 161 public function __construct() { 162 $this->manager = new HTMLPurifier_HTMLModuleManager(); 163 } 164 165 protected function doSetup($config) { 166 $this->processModules($config); 167 $this->setupConfigStuff($config); 168 unset($this->manager); 169 170 // cleanup some of the element definitions 171 foreach ($this->info as $k => $v) { 172 unset($this->info[$k]->content_model); 173 unset($this->info[$k]->content_model_type); 174 } 175 } 176 177 /** 178 * Extract out the information from the manager 179 */ 180 protected function processModules($config) { 181 182 if ($this->_anonModule) { 183 // for user specific changes 184 // this is late-loaded so we don't have to deal with PHP4 185 // reference wonky-ness 186 $this->manager->addModule($this->_anonModule); 187 unset($this->_anonModule); 188 } 189 190 $this->manager->setup($config); 191 $this->doctype = $this->manager->doctype; 192 193 foreach ($this->manager->modules as $module) { 194 foreach($module->info_tag_transform as $k => $v) { 195 if ($v === false) unset($this->info_tag_transform[$k]); 196 else $this->info_tag_transform[$k] = $v; 197 } 198 foreach($module->info_attr_transform_pre as $k => $v) { 199 if ($v === false) unset($this->info_attr_transform_pre[$k]); 200 else $this->info_attr_transform_pre[$k] = $v; 201 } 202 foreach($module->info_attr_transform_post as $k => $v) { 203 if ($v === false) unset($this->info_attr_transform_post[$k]); 204 else $this->info_attr_transform_post[$k] = $v; 205 } 206 foreach ($module->info_injector as $k => $v) { 207 if ($v === false) unset($this->info_injector[$k]); 208 else $this->info_injector[$k] = $v; 209 } 210 } 211 212 $this->info = $this->manager->getElements(); 213 $this->info_content_sets = $this->manager->contentSets->lookup; 214 215 } 216 217 /** 218 * Sets up stuff based on config. We need a better way of doing this. 219 */ 220 protected function setupConfigStuff($config) { 221 222 $block_wrapper = $config->get('HTML', 'BlockWrapper'); 223 if (isset($this->info_content_sets['Block'][$block_wrapper])) { 224 $this->info_block_wrapper = $block_wrapper; 225 } else { 226 trigger_error('Cannot use non-block element as block wrapper', 227 E_USER_ERROR); 228 } 229 230 $parent = $config->get('HTML', 'Parent'); 231 $def = $this->manager->getElement($parent, true); 232 if ($def) { 233 $this->info_parent = $parent; 234 $this->info_parent_def = $def; 235 } else { 236 trigger_error('Cannot use unrecognized element as parent', 237 E_USER_ERROR); 238 $this->info_parent_def = $this->manager->getElement($this->info_parent, true); 239 } 240 241 // support template text 242 $support = "(for information on implementing this, see the ". 243 "support forums) "; 244 245 // setup allowed elements ----------------------------------------- 246 247 $allowed_elements = $config->get('HTML', 'AllowedElements'); 248 $allowed_attributes = $config->get('HTML', 'AllowedAttributes'); // retrieve early 249 250 if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { 251 $allowed = $config->get('HTML', 'Allowed'); 252 if (is_string($allowed)) { 253 list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); 254 } 255 } 256 257 if (is_array($allowed_elements)) { 258 foreach ($this->info as $name => $d) { 259 if(!isset($allowed_elements[$name])) unset($this->info[$name]); 260 unset($allowed_elements[$name]); 261 } 262 // emit errors 263 foreach ($allowed_elements as $element => $d) { 264 $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful! 265 trigger_error("Element '$element' is not supported $support", E_USER_WARNING); 266 } 267 } 268 269 // setup allowed attributes --------------------------------------- 270 271 $allowed_attributes_mutable = $allowed_attributes; // by copy! 272 if (is_array($allowed_attributes)) { 273 274 // This actually doesn't do anything, since we went away from 275 // global attributes. It's possible that userland code uses 276 // it, but HTMLModuleManager doesn't! 277 foreach ($this->info_global_attr as $attr => $x) { 278 $keys = array($attr, "*@$attr", "*.$attr"); 279 $delete = true; 280 foreach ($keys as $key) { 281 if ($delete && isset($allowed_attributes[$key])) { 282 $delete = false; 283 } 284 if (isset($allowed_attributes_mutable[$key])) { 285 unset($allowed_attributes_mutable[$key]); 286 } 287 } 288 if ($delete) unset($this->info_global_attr[$attr]); 289 } 290 291 foreach ($this->info as $tag => $info) { 292 foreach ($info->attr as $attr => $x) { 293 $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr"); 294 $delete = true; 295 foreach ($keys as $key) { 296 if ($delete && isset($allowed_attributes[$key])) { 297 $delete = false; 298 } 299 if (isset($allowed_attributes_mutable[$key])) { 300 unset($allowed_attributes_mutable[$key]); 301 } 302 } 303 if ($delete) unset($this->info[$tag]->attr[$attr]); 304 } 305 } 306 // emit errors 307 foreach ($allowed_attributes_mutable as $elattr => $d) { 308 $bits = preg_split('/[.@]/', $elattr, 2); 309 $c = count($bits); 310 switch ($c) { 311 case 2: 312 if ($bits[0] !== '*') { 313 $element = htmlspecialchars($bits[0]); 314 $attribute = htmlspecialchars($bits[1]); 315 if (!isset($this->info[$element])) { 316 trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support"); 317 } else { 318 trigger_error("Attribute '$attribute' in element '$element' not supported $support", 319 E_USER_WARNING); 320 } 321 break; 322 } 323 // otherwise fall through 324 case 1: 325 $attribute = htmlspecialchars($bits[0]); 326 trigger_error("Global attribute '$attribute' is not ". 327 "supported in any elements $support", 328 E_USER_WARNING); 329 break; 330 } 331 } 332 333 } 334 335 // setup forbidden elements --------------------------------------- 336 337 $forbidden_elements = $config->get('HTML', 'ForbiddenElements'); 338 $forbidden_attributes = $config->get('HTML', 'ForbiddenAttributes'); 339 340 foreach ($this->info as $tag => $info) { 341 if (isset($forbidden_elements[$tag])) { 342 unset($this->info[$tag]); 343 continue; 344 } 345 foreach ($info->attr as $attr => $x) { 346 if ( 347 isset($forbidden_attributes["$tag@$attr"]) || 348 isset($forbidden_attributes["*@$attr"]) || 349 isset($forbidden_attributes[$attr]) 350 ) { 351 unset($this->info[$tag]->attr[$attr]); 352 continue; 353 } // this segment might get removed eventually 354 elseif (isset($forbidden_attributes["$tag.$attr"])) { 355 // $tag.$attr are not user supplied, so no worries! 356 trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING); 357 } 358 } 359 } 360 foreach ($forbidden_attributes as $key => $v) { 361 if (strlen($key) < 2) continue; 362 if ($key[0] != '*') continue; 363 if ($key[1] == '.') { 364 trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING); 365 } 366 } 367 368 // setup injectors ----------------------------------------------------- 369 foreach ($this->info_injector as $i => $injector) { 370 if ($injector->checkNeeded($config) !== false) { 371 // remove injector that does not have it's required 372 // elements/attributes present, and is thus not needed. 373 unset($this->info_injector[$i]); 374 } 375 } 376 } 377 378 /** 379 * Parses a TinyMCE-flavored Allowed Elements and Attributes list into 380 * separate lists for processing. Format is element[attr1|attr2],element2... 381 * @warning Although it's largely drawn from TinyMCE's implementation, 382 * it is different, and you'll probably have to modify your lists 383 * @param $list String list to parse 384 * @param array($allowed_elements, $allowed_attributes) 385 * @todo Give this its own class, probably static interface 386 */ 387 public function parseTinyMCEAllowedList($list) { 388 389 $list = str_replace(array(' ', "\t"), '', $list); 390 391 $elements = array(); 392 $attributes = array(); 393 394 $chunks = preg_split('/(,|[\n\r]+)/', $list); 395 foreach ($chunks as $chunk) { 396 if (empty($chunk)) continue; 397 // remove TinyMCE element control characters 398 if (!strpos($chunk, '[')) { 399 $element = $chunk; 400 $attr = false; 401 } else { 402 list($element, $attr) = explode('[', $chunk); 403 } 404 if ($element !== '*') $elements[$element] = true; 405 if (!$attr) continue; 406 $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ] 407 $attr = explode('|', $attr); 408 foreach ($attr as $key) { 409 $attributes["$element.$key"] = true; 410 } 411 } 412 413 return array($elements, $attributes); 414 415 } 416 417 418 } 419 420 // vim: et sw=4 sts=4
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:08:37 2014 | Cross-referenced by PHPXref 0.7.1 |