ILIAS  eassessment Revision 61809
 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;
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) 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 
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