Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  
   3  /**
   4   * HTML parser implementation. It only implements links.
   5   *
   6   * @author Josep ArĂºs
   7   *
   8   * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
   9   * @package mod_wiki
  10   */
  11  include_once ("nwiki.php");
  12  
  13  class html_parser extends nwiki_parser {
  14      protected $blockrules = array();
  15  
  16      protected $section_editing = true;
  17  
  18      /** @var int Minimum level of the headers on the page (usually tinymce uses <h1> and atto <h3>)  */
  19      protected $minheaderlevel = null;
  20  
  21      public function __construct() {
  22          parent::__construct();
  23          // The order is important, headers should be parsed before links.
  24          $this->tagrules = array(
  25              // Headers are considered tags here.
  26              'header' => array(
  27                  'expression' => "/<\s*h([1-6])\s*>(.+?)<\/h[1-6]>/is"
  28              ),
  29              'link' => $this->tagrules['link'],
  30              'url' => $this->tagrules['url']
  31          );
  32      }
  33  
  34      /**
  35       * Find minimum header level used on the page (<h1>, <h3>, ...)
  36       *
  37       * @param string $text
  38       * @return int
  39       */
  40      protected function find_min_header_level($text) {
  41          preg_match_all($this->tagrules['header']['expression'], $text, $matches);
  42          return !empty($matches[1]) ? min($matches[1]) : 1;
  43      }
  44  
  45      protected function before_parsing() {
  46          parent::before_parsing();
  47  
  48          $this->minheaderlevel = $this->find_min_header_level($this->string);
  49  
  50          // Protect all explicit links from further wiki parsing. The link text may contain another URL which would get
  51          // converted into another link via {@see nwiki_parser::$tagrules} 'url' element.
  52          if (preg_match_all('/<a\s[^>]+?>(.*?)<\/a>/is', $this->string, $matches)) {
  53              foreach (array_unique($matches[0]) as $match) {
  54                  $this->string = str_replace($match, $this->protect($match), $this->string);
  55              }
  56          }
  57  
  58          $this->rules($this->string);
  59      }
  60  
  61      /**
  62       * Header tag rule
  63       * @param array $match Header regex match
  64       * @return string
  65       */
  66      protected function header_tag_rule($match) {
  67          return $this->generate_header($match[2], (int)$match[1] - $this->minheaderlevel + 1);
  68      }
  69  
  70      /**
  71       * Section editing: Special for HTML Parser (It parses <h1></h1>)
  72       */
  73  
  74      public function get_section($header, $text, $clean = false) {
  75          if ($clean) {
  76              $text = preg_replace('/\r\n/', "\n", $text);
  77              $text = preg_replace('/\r/', "\n", $text);
  78              $text .= "\n\n";
  79          }
  80  
  81          $minheaderlevel = $this->find_min_header_level($text);
  82  
  83          $h1 = array("<\s*h{$minheaderlevel}\s*>", "<\/h{$minheaderlevel}>");
  84  
  85          $regex = "/(.*?)({$h1[0]}\s*".preg_quote($header, '/')."\s*{$h1[1]}.*?)((?:{$h1[0]}.*)|$)/is";
  86          preg_match($regex, $text, $match);
  87  
  88          if (!empty($match)) {
  89              return array($match[1], $match[2], $match[3]);
  90          } else {
  91              return false;
  92          }
  93      }
  94  
  95      protected function get_repeated_sections(&$text, $repeated = array()) {
  96          $this->repeated_sections = $repeated;
  97          return preg_replace_callback($this->tagrules['header'], array($this, 'get_repeated_sections_callback'), $text);
  98      }
  99  
 100      protected function get_repeated_sections_callback($match) {
 101          $text = trim($match[2]);
 102  
 103          if (in_array($text, $this->repeated_sections)) {
 104              $this->returnvalues['repeated_sections'][] = $text;
 105              return parser_utils::h('p', $text);
 106          } else {
 107              $this->repeated_sections[] = $text;
 108          }
 109  
 110          return $match[0];
 111      }
 112  }