ILIAS  Release_5_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
HTMLDefinition.php
Go to the documentation of this file.
1 <?php
2 
27 {
28 
29  // FULLY-PUBLIC VARIABLES ---------------------------------------------
30 
35  public $info = array();
36 
41  public $info_global_attr = array();
42 
47  public $info_parent = 'div';
48 
55 
61  public $info_block_wrapper = 'p';
62 
67  public $info_tag_transform = array();
68 
73  public $info_attr_transform_pre = array();
74 
79  public $info_attr_transform_post = array();
80 
86  public $info_content_sets = array();
87 
92  public $info_injector = array();
93 
98  public $doctype;
99 
100 
101 
102  // RAW CUSTOMIZATION STUFF --------------------------------------------
103 
113  public function addAttribute($element_name, $attr_name, $def)
114  {
115  $module = $this->getAnonymousModule();
116  if (!isset($module->info[$element_name])) {
117  $element = $module->addBlankElement($element_name);
118  } else {
119  $element = $module->info[$element_name];
120  }
121  $element->attr[$attr_name] = $def;
122  }
123 
129  public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array())
130  {
131  $module = $this->getAnonymousModule();
132  // assume that if the user is calling this, the element
133  // is safe. This may not be a good idea
134  $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes);
135  return $element;
136  }
137 
146  public function addBlankElement($element_name)
147  {
148  $module = $this->getAnonymousModule();
149  $element = $module->addBlankElement($element_name);
150  return $element;
151  }
152 
159  public function getAnonymousModule()
160  {
161  if (!$this->_anonModule) {
162  $this->_anonModule = new HTMLPurifier_HTMLModule();
163  $this->_anonModule->name = 'Anonymous';
164  }
165  return $this->_anonModule;
166  }
167 
168  private $_anonModule = null;
169 
170  // PUBLIC BUT INTERNAL VARIABLES --------------------------------------
171 
175  public $type = 'HTML';
176 
180  public $manager;
181 
185  public function __construct()
186  {
187  $this->manager = new HTMLPurifier_HTMLModuleManager();
188  }
189 
193  protected function doSetup($config)
194  {
195  $this->processModules($config);
196  $this->setupConfigStuff($config);
197  unset($this->manager);
198 
199  // cleanup some of the element definitions
200  foreach ($this->info as $k => $v) {
201  unset($this->info[$k]->content_model);
202  unset($this->info[$k]->content_model_type);
203  }
204  }
205 
210  protected function processModules($config)
211  {
212  if ($this->_anonModule) {
213  // for user specific changes
214  // this is late-loaded so we don't have to deal with PHP4
215  // reference wonky-ness
216  $this->manager->addModule($this->_anonModule);
217  unset($this->_anonModule);
218  }
219 
220  $this->manager->setup($config);
221  $this->doctype = $this->manager->doctype;
222 
223  foreach ($this->manager->modules as $module) {
224  foreach ($module->info_tag_transform as $k => $v) {
225  if ($v === false) {
226  unset($this->info_tag_transform[$k]);
227  } else {
228  $this->info_tag_transform[$k] = $v;
229  }
230  }
231  foreach ($module->info_attr_transform_pre as $k => $v) {
232  if ($v === false) {
233  unset($this->info_attr_transform_pre[$k]);
234  } else {
235  $this->info_attr_transform_pre[$k] = $v;
236  }
237  }
238  foreach ($module->info_attr_transform_post as $k => $v) {
239  if ($v === false) {
240  unset($this->info_attr_transform_post[$k]);
241  } else {
242  $this->info_attr_transform_post[$k] = $v;
243  }
244  }
245  foreach ($module->info_injector as $k => $v) {
246  if ($v === false) {
247  unset($this->info_injector[$k]);
248  } else {
249  $this->info_injector[$k] = $v;
250  }
251  }
252  }
253  $this->info = $this->manager->getElements();
254  $this->info_content_sets = $this->manager->contentSets->lookup;
255  }
256 
261  protected function setupConfigStuff($config)
262  {
263  $block_wrapper = $config->get('HTML.BlockWrapper');
264  if (isset($this->info_content_sets['Block'][$block_wrapper])) {
265  $this->info_block_wrapper = $block_wrapper;
266  } else {
267  trigger_error(
268  'Cannot use non-block element as block wrapper',
269  E_USER_ERROR
270  );
271  }
272 
273  $parent = $config->get('HTML.Parent');
274  $def = $this->manager->getElement($parent, true);
275  if ($def) {
276  $this->info_parent = $parent;
277  $this->info_parent_def = $def;
278  } else {
279  trigger_error(
280  'Cannot use unrecognized element as parent',
281  E_USER_ERROR
282  );
283  $this->info_parent_def = $this->manager->getElement($this->info_parent, true);
284  }
285 
286  // support template text
287  $support = "(for information on implementing this, see the support forums) ";
288 
289  // setup allowed elements -----------------------------------------
290 
291  $allowed_elements = $config->get('HTML.AllowedElements');
292  $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early
293 
294  if (!is_array($allowed_elements) && !is_array($allowed_attributes)) {
295  $allowed = $config->get('HTML.Allowed');
296  if (is_string($allowed)) {
297  list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed);
298  }
299  }
300 
301  if (is_array($allowed_elements)) {
302  foreach ($this->info as $name => $d) {
303  if (!isset($allowed_elements[$name])) {
304  unset($this->info[$name]);
305  }
306  unset($allowed_elements[$name]);
307  }
308  // emit errors
309  foreach ($allowed_elements as $element => $d) {
310  $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful!
311  trigger_error("Element '$element' is not supported $support", E_USER_WARNING);
312  }
313  }
314 
315  // setup allowed attributes ---------------------------------------
316 
317  $allowed_attributes_mutable = $allowed_attributes; // by copy!
318  if (is_array($allowed_attributes)) {
319  // This actually doesn't do anything, since we went away from
320  // global attributes. It's possible that userland code uses
321  // it, but HTMLModuleManager doesn't!
322  foreach ($this->info_global_attr as $attr => $x) {
323  $keys = array($attr, "*@$attr", "*.$attr");
324  $delete = true;
325  foreach ($keys as $key) {
326  if ($delete && isset($allowed_attributes[$key])) {
327  $delete = false;
328  }
329  if (isset($allowed_attributes_mutable[$key])) {
330  unset($allowed_attributes_mutable[$key]);
331  }
332  }
333  if ($delete) {
334  unset($this->info_global_attr[$attr]);
335  }
336  }
337 
338  foreach ($this->info as $tag => $info) {
339  foreach ($info->attr as $attr => $x) {
340  $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr");
341  $delete = true;
342  foreach ($keys as $key) {
343  if ($delete && isset($allowed_attributes[$key])) {
344  $delete = false;
345  }
346  if (isset($allowed_attributes_mutable[$key])) {
347  unset($allowed_attributes_mutable[$key]);
348  }
349  }
350  if ($delete) {
351  if ($this->info[$tag]->attr[$attr]->required) {
352  trigger_error(
353  "Required attribute '$attr' in element '$tag' " .
354  "was not allowed, which means '$tag' will not be allowed either",
355  E_USER_WARNING
356  );
357  }
358  unset($this->info[$tag]->attr[$attr]);
359  }
360  }
361  }
362  // emit errors
363  foreach ($allowed_attributes_mutable as $elattr => $d) {
364  $bits = preg_split('/[.@]/', $elattr, 2);
365  $c = count($bits);
366  switch ($c) {
367  case 2:
368  if ($bits[0] !== '*') {
369  $element = htmlspecialchars($bits[0]);
370  $attribute = htmlspecialchars($bits[1]);
371  if (!isset($this->info[$element])) {
372  trigger_error(
373  "Cannot allow attribute '$attribute' if element " .
374  "'$element' is not allowed/supported $support"
375  );
376  } else {
377  trigger_error(
378  "Attribute '$attribute' in element '$element' not supported $support",
379  E_USER_WARNING
380  );
381  }
382  break;
383  }
384  // otherwise fall through
385  case 1:
386  $attribute = htmlspecialchars($bits[0]);
387  trigger_error(
388  "Global attribute '$attribute' is not ".
389  "supported in any elements $support",
390  E_USER_WARNING
391  );
392  break;
393  }
394  }
395  }
396 
397  // setup forbidden elements ---------------------------------------
398 
399  $forbidden_elements = $config->get('HTML.ForbiddenElements');
400  $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');
401 
402  foreach ($this->info as $tag => $info) {
403  if (isset($forbidden_elements[$tag])) {
404  unset($this->info[$tag]);
405  continue;
406  }
407  foreach ($info->attr as $attr => $x) {
408  if (isset($forbidden_attributes["$tag@$attr"]) ||
409  isset($forbidden_attributes["*@$attr"]) ||
410  isset($forbidden_attributes[$attr])
411  ) {
412  unset($this->info[$tag]->attr[$attr]);
413  continue;
414  } elseif (isset($forbidden_attributes["$tag.$attr"])) { // this segment might get removed eventually
415  // $tag.$attr are not user supplied, so no worries!
416  trigger_error(
417  "Error with $tag.$attr: tag.attr syntax not supported for " .
418  "HTML.ForbiddenAttributes; use tag@attr instead",
419  E_USER_WARNING
420  );
421  }
422  }
423  }
424  foreach ($forbidden_attributes as $key => $v) {
425  if (strlen($key) < 2) {
426  continue;
427  }
428  if ($key[0] != '*') {
429  continue;
430  }
431  if ($key[1] == '.') {
432  trigger_error(
433  "Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead",
434  E_USER_WARNING
435  );
436  }
437  }
438 
439  // setup injectors -----------------------------------------------------
440  foreach ($this->info_injector as $i => $injector) {
441  if ($injector->checkNeeded($config) !== false) {
442  // remove injector that does not have it's required
443  // elements/attributes present, and is thus not needed.
444  unset($this->info_injector[$i]);
445  }
446  }
447  }
448 
458  public function parseTinyMCEAllowedList($list)
459  {
460  $list = str_replace(array(' ', "\t"), '', $list);
461 
462  $elements = array();
463  $attributes = array();
464 
465  $chunks = preg_split('/(,|[\n\r]+)/', $list);
466  foreach ($chunks as $chunk) {
467  if (empty($chunk)) {
468  continue;
469  }
470  // remove TinyMCE element control characters
471  if (!strpos($chunk, '[')) {
472  $element = $chunk;
473  $attr = false;
474  } else {
475  list($element, $attr) = explode('[', $chunk);
476  }
477  if ($element !== '*') {
478  $elements[$element] = true;
479  }
480  if (!$attr) {
481  continue;
482  }
483  $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ]
484  $attr = explode('|', $attr);
485  foreach ($attr as $key) {
486  $attributes["$element.$key"] = true;
487  }
488  }
489  return array($elements, $attributes);
490  }
491 }
492 
493 // vim: et sw=4 sts=4