ILIAS  release_4-3 Revision
 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 
34  public $info = array();
35 
39  public $info_global_attr = array();
40 
44  public $info_parent = 'div';
45 
51 
56  public $info_block_wrapper = 'p';
57 
61  public $info_tag_transform = array();
62 
66  public $info_attr_transform_pre = array();
67 
71  public $info_attr_transform_post = array();
72 
77  public $info_content_sets = array();
78 
82  public $info_injector = array();
83 
87  public $doctype;
88 
89 
90 
91  // RAW CUSTOMIZATION STUFF --------------------------------------------
92 
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 
117  public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) {
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 
131  public function addBlankElement($element_name) {
132  $module = $this->getAnonymousModule();
133  $element = $module->addBlankElement($element_name);
134  return $element;
135  }
136 
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 = null;
151 
152 
153  // PUBLIC BUT INTERNAL VARIABLES --------------------------------------
154 
155  public $type = 'HTML';
156  public $manager;
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 
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 
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) {
304  if ($this->info[$tag]->attr[$attr]->required) {
305  trigger_error("Required attribute '$attr' in element '$tag' was not allowed, which means '$tag' will not be allowed either", E_USER_WARNING);
306  }
307  unset($this->info[$tag]->attr[$attr]);
308  }
309  }
310  }
311  // emit errors
312  foreach ($allowed_attributes_mutable as $elattr => $d) {
313  $bits = preg_split('/[.@]/', $elattr, 2);
314  $c = count($bits);
315  switch ($c) {
316  case 2:
317  if ($bits[0] !== '*') {
318  $element = htmlspecialchars($bits[0]);
319  $attribute = htmlspecialchars($bits[1]);
320  if (!isset($this->info[$element])) {
321  trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support");
322  } else {
323  trigger_error("Attribute '$attribute' in element '$element' not supported $support",
324  E_USER_WARNING);
325  }
326  break;
327  }
328  // otherwise fall through
329  case 1:
330  $attribute = htmlspecialchars($bits[0]);
331  trigger_error("Global attribute '$attribute' is not ".
332  "supported in any elements $support",
333  E_USER_WARNING);
334  break;
335  }
336  }
337 
338  }
339 
340  // setup forbidden elements ---------------------------------------
341 
342  $forbidden_elements = $config->get('HTML.ForbiddenElements');
343  $forbidden_attributes = $config->get('HTML.ForbiddenAttributes');
344 
345  foreach ($this->info as $tag => $info) {
346  if (isset($forbidden_elements[$tag])) {
347  unset($this->info[$tag]);
348  continue;
349  }
350  foreach ($info->attr as $attr => $x) {
351  if (
352  isset($forbidden_attributes["$tag@$attr"]) ||
353  isset($forbidden_attributes["*@$attr"]) ||
354  isset($forbidden_attributes[$attr])
355  ) {
356  unset($this->info[$tag]->attr[$attr]);
357  continue;
358  } // this segment might get removed eventually
359  elseif (isset($forbidden_attributes["$tag.$attr"])) {
360  // $tag.$attr are not user supplied, so no worries!
361  trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING);
362  }
363  }
364  }
365  foreach ($forbidden_attributes as $key => $v) {
366  if (strlen($key) < 2) continue;
367  if ($key[0] != '*') continue;
368  if ($key[1] == '.') {
369  trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING);
370  }
371  }
372 
373  // setup injectors -----------------------------------------------------
374  foreach ($this->info_injector as $i => $injector) {
375  if ($injector->checkNeeded($config) !== false) {
376  // remove injector that does not have it's required
377  // elements/attributes present, and is thus not needed.
378  unset($this->info_injector[$i]);
379  }
380  }
381  }
382 
392  public function parseTinyMCEAllowedList($list) {
393 
394  $list = str_replace(array(' ', "\t"), '', $list);
395 
396  $elements = array();
397  $attributes = array();
398 
399  $chunks = preg_split('/(,|[\n\r]+)/', $list);
400  foreach ($chunks as $chunk) {
401  if (empty($chunk)) continue;
402  // remove TinyMCE element control characters
403  if (!strpos($chunk, '[')) {
404  $element = $chunk;
405  $attr = false;
406  } else {
407  list($element, $attr) = explode('[', $chunk);
408  }
409  if ($element !== '*') $elements[$element] = true;
410  if (!$attr) continue;
411  $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ]
412  $attr = explode('|', $attr);
413  foreach ($attr as $key) {
414  $attributes["$element.$key"] = true;
415  }
416  }
417 
418  return array($elements, $attributes);
419 
420  }
421 
422 
423 }
424 
425 // vim: et sw=4 sts=4