ILIAS  Release_4_1_x_branch Revision 61804
 All Data Structures Namespaces Files Functions Variables Groups Pages
ExtractStyleBlocks.php
Go to the documentation of this file.
1 <?php
2 
18 {
19 
20  public $name = 'ExtractStyleBlocks';
21  private $_styleMatches = array();
22  private $_tidy;
23 
24  public function __construct() {
25  $this->_tidy = new csstidy();
26  }
27 
32  protected function styleCallback($matches) {
33  $this->_styleMatches[] = $matches[1];
34  }
35 
40  public function preFilter($html, $config, $context) {
41  $tidy = $config->get('Filter.ExtractStyleBlocks.TidyImpl');
42  if ($tidy !== null) $this->_tidy = $tidy;
43  $html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
44  $style_blocks = $this->_styleMatches;
45  $this->_styleMatches = array(); // reset
46  $context->register('StyleBlocks', $style_blocks); // $context must not be reused
47  if ($this->_tidy) {
48  foreach ($style_blocks as &$style) {
49  $style = $this->cleanCSS($style, $config, $context);
50  }
51  }
52  return $html;
53  }
54 
63  public function cleanCSS($css, $config, $context) {
64  // prepare scope
65  $scope = $config->get('Filter.ExtractStyleBlocks.Scope');
66  if ($scope !== null) {
67  $scopes = array_map('trim', explode(',', $scope));
68  } else {
69  $scopes = array();
70  }
71  // remove comments from CSS
72  $css = trim($css);
73  if (strncmp('<!--', $css, 4) === 0) {
74  $css = substr($css, 4);
75  }
76  if (strlen($css) > 3 && substr($css, -3) == '-->') {
77  $css = substr($css, 0, -3);
78  }
79  $css = trim($css);
80  $this->_tidy->parse($css);
81  $css_definition = $config->getDefinition('CSS');
82  foreach ($this->_tidy->css as $k => $decls) {
83  // $decls are all CSS declarations inside an @ selector
84  $new_decls = array();
85  foreach ($decls as $selector => $style) {
86  $selector = trim($selector);
87  if ($selector === '') continue; // should not happen
88  if ($selector[0] === '+') {
89  if ($selector !== '' && $selector[0] === '+') continue;
90  }
91  if (!empty($scopes)) {
92  $new_selector = array(); // because multiple ones are possible
93  $selectors = array_map('trim', explode(',', $selector));
94  foreach ($scopes as $s1) {
95  foreach ($selectors as $s2) {
96  $new_selector[] = "$s1 $s2";
97  }
98  }
99  $selector = implode(', ', $new_selector); // now it's a string
100  }
101  foreach ($style as $name => $value) {
102  if (!isset($css_definition->info[$name])) {
103  unset($style[$name]);
104  continue;
105  }
106  $def = $css_definition->info[$name];
107  $ret = $def->validate($value, $config, $context);
108  if ($ret === false) unset($style[$name]);
109  else $style[$name] = $ret;
110  }
111  $new_decls[$selector] = $style;
112  }
113  $this->_tidy->css[$k] = $new_decls;
114  }
115  // remove stuff that shouldn't be used, could be reenabled
116  // after security risks are analyzed
117  $this->_tidy->import = array();
118  $this->_tidy->charset = null;
119  $this->_tidy->namespace = null;
120  $css = $this->_tidy->print->plain();
121  // we are going to escape any special characters <>& to ensure
122  // that no funny business occurs (i.e. </style> in a font-family prop).
123  if ($config->get('Filter.ExtractStyleBlocks.Escaping')) {
124  $css = str_replace(
125  array('<', '>', '&'),
126  array('\3C ', '\3E ', '\26 '),
127  $css
128  );
129  }
130  return $css;
131  }
132 
133 }
134 
135 // vim: et sw=4 sts=4