Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

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