/ lib / htmlpurifier / Printer / ConfigForm.php
ConfigForm.php
  1  <?php
  2  
  3  /**
  4   * @todo Rewrite to use Interchange objects
  5   */
  6  class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer
  7  {
  8  
  9      /**
 10       * Printers for specific fields.
 11       * @type HTMLPurifier_Printer[]
 12       */
 13      protected $fields = array();
 14  
 15      /**
 16       * Documentation URL, can have fragment tagged on end.
 17       * @type string
 18       */
 19      protected $docURL;
 20  
 21      /**
 22       * Name of form element to stuff config in.
 23       * @type string
 24       */
 25      protected $name;
 26  
 27      /**
 28       * Whether or not to compress directive names, clipping them off
 29       * after a certain amount of letters. False to disable or integer letters
 30       * before clipping.
 31       * @type bool
 32       */
 33      protected $compress = false;
 34  
 35      /**
 36       * @param string $name Form element name for directives to be stuffed into
 37       * @param string $doc_url String documentation URL, will have fragment tagged on
 38       * @param bool $compress Integer max length before compressing a directive name, set to false to turn off
 39       */
 40      public function __construct(
 41          $name,
 42          $doc_url = null,
 43          $compress = false
 44      ) {
 45          parent::__construct();
 46          $this->docURL = $doc_url;
 47          $this->name = $name;
 48          $this->compress = $compress;
 49          // initialize sub-printers
 50          $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default();
 51          $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool();
 52      }
 53  
 54      /**
 55       * Sets default column and row size for textareas in sub-printers
 56       * @param $cols Integer columns of textarea, null to use default
 57       * @param $rows Integer rows of textarea, null to use default
 58       */
 59      public function setTextareaDimensions($cols = null, $rows = null)
 60      {
 61          if ($cols) {
 62              $this->fields['default']->cols = $cols;
 63          }
 64          if ($rows) {
 65              $this->fields['default']->rows = $rows;
 66          }
 67      }
 68  
 69      /**
 70       * Retrieves styling, in case it is not accessible by webserver
 71       */
 72      public static function getCSS()
 73      {
 74          return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css');
 75      }
 76  
 77      /**
 78       * Retrieves JavaScript, in case it is not accessible by webserver
 79       */
 80      public static function getJavaScript()
 81      {
 82          return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js');
 83      }
 84  
 85      /**
 86       * Returns HTML output for a configuration form
 87       * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array
 88       *        where [0] has an HTML namespace and [1] is being rendered.
 89       * @param array|bool $allowed Optional namespace(s) and directives to restrict form to.
 90       * @param bool $render_controls
 91       * @return string
 92       */
 93      public function render($config, $allowed = true, $render_controls = true)
 94      {
 95          if (is_array($config) && isset($config[0])) {
 96              $gen_config = $config[0];
 97              $config = $config[1];
 98          } else {
 99              $gen_config = $config;
100          }
101  
102          $this->config = $config;
103          $this->genConfig = $gen_config;
104          $this->prepareGenerator($gen_config);
105  
106          $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def);
107          $all = array();
108          foreach ($allowed as $key) {
109              list($ns, $directive) = $key;
110              $all[$ns][$directive] = $config->get($ns . '.' . $directive);
111          }
112  
113          $ret = '';
114          $ret .= $this->start('table', array('class' => 'hp-config'));
115          $ret .= $this->start('thead');
116          $ret .= $this->start('tr');
117          $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive'));
118          $ret .= $this->element('th', 'Value', array('class' => 'hp-value'));
119          $ret .= $this->end('tr');
120          $ret .= $this->end('thead');
121          foreach ($all as $ns => $directives) {
122              $ret .= $this->renderNamespace($ns, $directives);
123          }
124          if ($render_controls) {
125              $ret .= $this->start('tbody');
126              $ret .= $this->start('tr');
127              $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls'));
128              $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit'));
129              $ret .= '[<a href="?">Reset</a>]';
130              $ret .= $this->end('td');
131              $ret .= $this->end('tr');
132              $ret .= $this->end('tbody');
133          }
134          $ret .= $this->end('table');
135          return $ret;
136      }
137  
138      /**
139       * Renders a single namespace
140       * @param $ns String namespace name
141       * @param array $directives array of directives to values
142       * @return string
143       */
144      protected function renderNamespace($ns, $directives)
145      {
146          $ret = '';
147          $ret .= $this->start('tbody', array('class' => 'namespace'));
148          $ret .= $this->start('tr');
149          $ret .= $this->element('th', $ns, array('colspan' => 2));
150          $ret .= $this->end('tr');
151          $ret .= $this->end('tbody');
152          $ret .= $this->start('tbody');
153          foreach ($directives as $directive => $value) {
154              $ret .= $this->start('tr');
155              $ret .= $this->start('th');
156              if ($this->docURL) {
157                  $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL);
158                  $ret .= $this->start('a', array('href' => $url));
159              }
160              $attr = array('for' => "{$this->name}:$ns.$directive");
161  
162              // crop directive name if it's too long
163              if (!$this->compress || (strlen($directive) < $this->compress)) {
164                  $directive_disp = $directive;
165              } else {
166                  $directive_disp = substr($directive, 0, $this->compress - 2) . '...';
167                  $attr['title'] = $directive;
168              }
169  
170              $ret .= $this->element(
171                  'label',
172                  $directive_disp,
173                  // component printers must create an element with this id
174                  $attr
175              );
176              if ($this->docURL) {
177                  $ret .= $this->end('a');
178              }
179              $ret .= $this->end('th');
180  
181              $ret .= $this->start('td');
182              $def = $this->config->def->info["$ns.$directive"];
183              if (is_int($def)) {
184                  $allow_null = $def < 0;
185                  $type = abs($def);
186              } else {
187                  $type = $def->type;
188                  $allow_null = isset($def->allow_null);
189              }
190              if (!isset($this->fields[$type])) {
191                  $type = 0;
192              } // default
193              $type_obj = $this->fields[$type];
194              if ($allow_null) {
195                  $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj);
196              }
197              $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config));
198              $ret .= $this->end('td');
199              $ret .= $this->end('tr');
200          }
201          $ret .= $this->end('tbody');
202          return $ret;
203      }
204  
205  }
206  
207  /**
208   * Printer decorator for directives that accept null
209   */
210  class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer
211  {
212      /**
213       * Printer being decorated
214       * @type HTMLPurifier_Printer
215       */
216      protected $obj;
217  
218      /**
219       * @param HTMLPurifier_Printer $obj Printer to decorate
220       */
221      public function __construct($obj)
222      {
223          parent::__construct();
224          $this->obj = $obj;
225      }
226  
227      /**
228       * @param string $ns
229       * @param string $directive
230       * @param string $value
231       * @param string $name
232       * @param HTMLPurifier_Config|array $config
233       * @return string
234       */
235      public function render($ns, $directive, $value, $name, $config)
236      {
237          if (is_array($config) && isset($config[0])) {
238              $gen_config = $config[0];
239              $config = $config[1];
240          } else {
241              $gen_config = $config;
242          }
243          $this->prepareGenerator($gen_config);
244  
245          $ret = '';
246          $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive"));
247          $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
248          $ret .= $this->text(' Null/Disabled');
249          $ret .= $this->end('label');
250          $attr = array(
251              'type' => 'checkbox',
252              'value' => '1',
253              'class' => 'null-toggle',
254              'name' => "$name" . "[Null_$ns.$directive]",
255              'id' => "$name:Null_$ns.$directive",
256              'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!!
257          );
258          if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) {
259              // modify inline javascript slightly
260              $attr['onclick'] =
261                  "toggleWriteability('$name:Yes_$ns.$directive',checked);" .
262                  "toggleWriteability('$name:No_$ns.$directive',checked)";
263          }
264          if ($value === null) {
265              $attr['checked'] = 'checked';
266          }
267          $ret .= $this->elementEmpty('input', $attr);
268          $ret .= $this->text(' or ');
269          $ret .= $this->elementEmpty('br');
270          $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config));
271          return $ret;
272      }
273  }
274  
275  /**
276   * Swiss-army knife configuration form field printer
277   */
278  class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
279  {
280      /**
281       * @type int
282       */
283      public $cols = 18;
284  
285      /**
286       * @type int
287       */
288      public $rows = 5;
289  
290      /**
291       * @param string $ns
292       * @param string $directive
293       * @param string $value
294       * @param string $name
295       * @param HTMLPurifier_Config|array $config
296       * @return string
297       */
298      public function render($ns, $directive, $value, $name, $config)
299      {
300          if (is_array($config) && isset($config[0])) {
301              $gen_config = $config[0];
302              $config = $config[1];
303          } else {
304              $gen_config = $config;
305          }
306          $this->prepareGenerator($gen_config);
307          // this should probably be split up a little
308          $ret = '';
309          $def = $config->def->info["$ns.$directive"];
310          if (is_int($def)) {
311              $type = abs($def);
312          } else {
313              $type = $def->type;
314          }
315          if (is_array($value)) {
316              switch ($type) {
317                  case HTMLPurifier_VarParser::LOOKUP:
318                      $array = $value;
319                      $value = array();
320                      foreach ($array as $val => $b) {
321                          $value[] = $val;
322                      }
323                      //TODO does this need a break?
324                  case HTMLPurifier_VarParser::ALIST:
325                      $value = implode(PHP_EOL, $value);
326                      break;
327                  case HTMLPurifier_VarParser::HASH:
328                      $nvalue = '';
329                      foreach ($value as $i => $v) {
330                          $nvalue .= "$i:$v" . PHP_EOL;
331                      }
332                      $value = $nvalue;
333                      break;
334                  default:
335                      $value = '';
336              }
337          }
338          if ($type === HTMLPurifier_VarParser::MIXED) {
339              return 'Not supported';
340              $value = serialize($value);
341          }
342          $attr = array(
343              'name' => "$name" . "[$ns.$directive]",
344              'id' => "$name:$ns.$directive"
345          );
346          if ($value === null) {
347              $attr['disabled'] = 'disabled';
348          }
349          if (isset($def->allowed)) {
350              $ret .= $this->start('select', $attr);
351              foreach ($def->allowed as $val => $b) {
352                  $attr = array();
353                  if ($value == $val) {
354                      $attr['selected'] = 'selected';
355                  }
356                  $ret .= $this->element('option', $val, $attr);
357              }
358              $ret .= $this->end('select');
359          } elseif ($type === HTMLPurifier_VarParser::TEXT ||
360                  $type === HTMLPurifier_VarParser::ITEXT ||
361                  $type === HTMLPurifier_VarParser::ALIST ||
362                  $type === HTMLPurifier_VarParser::HASH ||
363                  $type === HTMLPurifier_VarParser::LOOKUP) {
364              $attr['cols'] = $this->cols;
365              $attr['rows'] = $this->rows;
366              $ret .= $this->start('textarea', $attr);
367              $ret .= $this->text($value);
368              $ret .= $this->end('textarea');
369          } else {
370              $attr['value'] = $value;
371              $attr['type'] = 'text';
372              $ret .= $this->elementEmpty('input', $attr);
373          }
374          return $ret;
375      }
376  }
377  
378  /**
379   * Bool form field printer
380   */
381  class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer
382  {
383      /**
384       * @param string $ns
385       * @param string $directive
386       * @param string $value
387       * @param string $name
388       * @param HTMLPurifier_Config|array $config
389       * @return string
390       */
391      public function render($ns, $directive, $value, $name, $config)
392      {
393          if (is_array($config) && isset($config[0])) {
394              $gen_config = $config[0];
395              $config = $config[1];
396          } else {
397              $gen_config = $config;
398          }
399          $this->prepareGenerator($gen_config);
400          $ret = '';
401          $ret .= $this->start('div', array('id' => "$name:$ns.$directive"));
402  
403          $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive"));
404          $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
405          $ret .= $this->text(' Yes');
406          $ret .= $this->end('label');
407  
408          $attr = array(
409              'type' => 'radio',
410              'name' => "$name" . "[$ns.$directive]",
411              'id' => "$name:Yes_$ns.$directive",
412              'value' => '1'
413          );
414          if ($value === true) {
415              $attr['checked'] = 'checked';
416          }
417          if ($value === null) {
418              $attr['disabled'] = 'disabled';
419          }
420          $ret .= $this->elementEmpty('input', $attr);
421  
422          $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive"));
423          $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose'));
424          $ret .= $this->text(' No');
425          $ret .= $this->end('label');
426  
427          $attr = array(
428              'type' => 'radio',
429              'name' => "$name" . "[$ns.$directive]",
430              'id' => "$name:No_$ns.$directive",
431              'value' => '0'
432          );
433          if ($value === false) {
434              $attr['checked'] = 'checked';
435          }
436          if ($value === null) {
437              $attr['disabled'] = 'disabled';
438          }
439          $ret .= $this->elementEmpty('input', $attr);
440  
441          $ret .= $this->end('div');
442  
443          return $ret;
444      }
445  }
446  
447  // vim: et sw=4 sts=4