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.
   1  <?php
   2  
   3  /**
   4   * Error collection class that enables HTML Purifier to report HTML
   5   * problems back to the user
   6   */
   7  class HTMLPurifier_ErrorCollector
   8  {
   9  
  10      /**
  11       * Identifiers for the returned error array. These are purposely numeric
  12       * so list() can be used.
  13       */
  14      const LINENO   = 0;
  15      const SEVERITY = 1;
  16      const MESSAGE  = 2;
  17      const CHILDREN = 3;
  18  
  19      /**
  20       * @type array
  21       */
  22      protected $errors;
  23  
  24      /**
  25       * @type array
  26       */
  27      protected $_current;
  28  
  29      /**
  30       * @type array
  31       */
  32      protected $_stacks = array(array());
  33  
  34      /**
  35       * @type HTMLPurifier_Language
  36       */
  37      protected $locale;
  38  
  39      /**
  40       * @type HTMLPurifier_Generator
  41       */
  42      protected $generator;
  43  
  44      /**
  45       * @type HTMLPurifier_Context
  46       */
  47      protected $context;
  48  
  49      /**
  50       * @type array
  51       */
  52      protected $lines = array();
  53  
  54      /**
  55       * @param HTMLPurifier_Context $context
  56       */
  57      public function __construct($context)
  58      {
  59          $this->locale    =& $context->get('Locale');
  60          $this->context   = $context;
  61          $this->_current  =& $this->_stacks[0];
  62          $this->errors    =& $this->_stacks[0];
  63      }
  64  
  65      /**
  66       * Sends an error message to the collector for later use
  67       * @param int $severity Error severity, PHP error style (don't use E_USER_)
  68       * @param string $msg Error message text
  69       */
  70      public function send($severity, $msg)
  71      {
  72          $args = array();
  73          if (func_num_args() > 2) {
  74              $args = func_get_args();
  75              array_shift($args);
  76              unset($args[0]);
  77          }
  78  
  79          $token = $this->context->get('CurrentToken', true);
  80          $line  = $token ? $token->line : $this->context->get('CurrentLine', true);
  81          $col   = $token ? $token->col  : $this->context->get('CurrentCol', true);
  82          $attr  = $this->context->get('CurrentAttr', true);
  83  
  84          // perform special substitutions, also add custom parameters
  85          $subst = array();
  86          if (!is_null($token)) {
  87              $args['CurrentToken'] = $token;
  88          }
  89          if (!is_null($attr)) {
  90              $subst['$CurrentAttr.Name'] = $attr;
  91              if (isset($token->attr[$attr])) {
  92                  $subst['$CurrentAttr.Value'] = $token->attr[$attr];
  93              }
  94          }
  95  
  96          if (empty($args)) {
  97              $msg = $this->locale->getMessage($msg);
  98          } else {
  99              $msg = $this->locale->formatMessage($msg, $args);
 100          }
 101  
 102          if (!empty($subst)) {
 103              $msg = strtr($msg, $subst);
 104          }
 105  
 106          // (numerically indexed)
 107          $error = array(
 108              self::LINENO   => $line,
 109              self::SEVERITY => $severity,
 110              self::MESSAGE  => $msg,
 111              self::CHILDREN => array()
 112          );
 113          $this->_current[] = $error;
 114  
 115          // NEW CODE BELOW ...
 116          // Top-level errors are either:
 117          //  TOKEN type, if $value is set appropriately, or
 118          //  "syntax" type, if $value is null
 119          $new_struct = new HTMLPurifier_ErrorStruct();
 120          $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN;
 121          if ($token) {
 122              $new_struct->value = clone $token;
 123          }
 124          if (is_int($line) && is_int($col)) {
 125              if (isset($this->lines[$line][$col])) {
 126                  $struct = $this->lines[$line][$col];
 127              } else {
 128                  $struct = $this->lines[$line][$col] = $new_struct;
 129              }
 130              // These ksorts may present a performance problem
 131              ksort($this->lines[$line], SORT_NUMERIC);
 132          } else {
 133              if (isset($this->lines[-1])) {
 134                  $struct = $this->lines[-1];
 135              } else {
 136                  $struct = $this->lines[-1] = $new_struct;
 137              }
 138          }
 139          ksort($this->lines, SORT_NUMERIC);
 140  
 141          // Now, check if we need to operate on a lower structure
 142          if (!empty($attr)) {
 143              $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr);
 144              if (!$struct->value) {
 145                  $struct->value = array($attr, 'PUT VALUE HERE');
 146              }
 147          }
 148          if (!empty($cssprop)) {
 149              $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop);
 150              if (!$struct->value) {
 151                  // if we tokenize CSS this might be a little more difficult to do
 152                  $struct->value = array($cssprop, 'PUT VALUE HERE');
 153              }
 154          }
 155  
 156          // Ok, structs are all setup, now time to register the error
 157          $struct->addError($severity, $msg);
 158      }
 159  
 160      /**
 161       * Retrieves raw error data for custom formatter to use
 162       */
 163      public function getRaw()
 164      {
 165          return $this->errors;
 166      }
 167  
 168      /**
 169       * Default HTML formatting implementation for error messages
 170       * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature
 171       * @param array $errors Errors array to display; used for recursion.
 172       * @return string
 173       */
 174      public function getHTMLFormatted($config, $errors = null)
 175      {
 176          $ret = array();
 177  
 178          $this->generator = new HTMLPurifier_Generator($config, $this->context);
 179          if ($errors === null) {
 180              $errors = $this->errors;
 181          }
 182  
 183          // 'At line' message needs to be removed
 184  
 185          // generation code for new structure goes here. It needs to be recursive.
 186          foreach ($this->lines as $line => $col_array) {
 187              if ($line == -1) {
 188                  continue;
 189              }
 190              foreach ($col_array as $col => $struct) {
 191                  $this->_renderStruct($ret, $struct, $line, $col);
 192              }
 193          }
 194          if (isset($this->lines[-1])) {
 195              $this->_renderStruct($ret, $this->lines[-1]);
 196          }
 197  
 198          if (empty($errors)) {
 199              return '<p>' . $this->locale->getMessage('ErrorCollector: No errors') . '</p>';
 200          } else {
 201              return '<ul><li>' . implode('</li><li>', $ret) . '</li></ul>';
 202          }
 203  
 204      }
 205  
 206      private function _renderStruct(&$ret, $struct, $line = null, $col = null)
 207      {
 208          $stack = array($struct);
 209          $context_stack = array(array());
 210          while ($current = array_pop($stack)) {
 211              $context = array_pop($context_stack);
 212              foreach ($current->errors as $error) {
 213                  list($severity, $msg) = $error;
 214                  $string = '';
 215                  $string .= '<div>';
 216                  // W3C uses an icon to indicate the severity of the error.
 217                  $error = $this->locale->getErrorName($severity);
 218                  $string .= "<span class=\"error e$severity\"><strong>$error</strong></span> ";
 219                  if (!is_null($line) && !is_null($col)) {
 220                      $string .= "<em class=\"location\">Line $line, Column $col: </em> ";
 221                  } else {
 222                      $string .= '<em class="location">End of Document: </em> ';
 223                  }
 224                  $string .= '<strong class="description">' . $this->generator->escape($msg) . '</strong> ';
 225                  $string .= '</div>';
 226                  // Here, have a marker for the character on the column appropriate.
 227                  // Be sure to clip extremely long lines.
 228                  //$string .= '<pre>';
 229                  //$string .= '';
 230                  //$string .= '</pre>';
 231                  $ret[] = $string;
 232              }
 233              foreach ($current->children as $array) {
 234                  $context[] = $current;
 235                  $stack = array_merge($stack, array_reverse($array, true));
 236                  for ($i = count($array); $i > 0; $i--) {
 237                      $context_stack[] = $context;
 238                  }
 239              }
 240          }
 241      }
 242  }
 243  
 244  // vim: et sw=4 sts=4