Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  
   3  /**
   4   * NWiki parser implementation
   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  
  12  include_once ("wikimarkup.php");
  13  
  14  class nwiki_parser extends wiki_markup_parser {
  15  
  16      protected $blockrules = array(
  17          'nowiki' => array(
  18              'expression' => "/^<nowiki>(.*?)<\/nowiki>/ims",
  19              'tags' => array(),
  20              'token' => array('<nowiki>', '</nowiki>')
  21          ),
  22          'header' => array(
  23              'expression' => "/^\ *(={1,6})\ *(.+?)(={1,6})\ *$/ims",
  24              'tags' => array(), //none
  25              'token' => '='
  26          ),
  27          'line_break' => array(
  28              'expression' => "/^-{3,4}\s*$/im",
  29              'tags' => array(),
  30              'token' => '---'
  31          ),
  32          'desc_list' => array(
  33              'expression' => "/(?:^.+?\:.+?\;\n)+/ims",
  34              'tags' => array(),
  35              'token' => array(':', ';'),
  36              'tag' => 'dl'
  37          ),
  38          'table' => array(
  39              'expression' => "/\{\|(.+?)\|\}/ims"
  40          ),
  41          'tab_paragraph' => array(
  42              'expression' => "/^(\:+)(.+?)$/ims",
  43              'tag' => 'p'
  44          ),
  45          'list' => array(
  46              'expression' => "/^((?:\ *[\*|#]{1,5}\ *.+?)+)(\n\s*(?:\n|<(?:h\d|pre|table|tbody|thead|tr|th|td|ul|li|ol|hr)\ *\/?>))/ims",
  47              'tags' => array(),
  48              'token' => array('*', '#')
  49          ),
  50          'paragraph' => array(
  51              'expression' => "/^\ *((?:<(?!\ *\/?(?:h\d|pre|table|tbody|thead|tr|th|td|ul|li|ol|hr)\ *\/?>)|[^<\s]).+?)\n\s*\n/ims",
  52              //not specified -> all tags (null or unset)
  53              'tag' => 'p'
  54          )
  55      );
  56  
  57      protected $tagrules = array(
  58          'nowiki' => array(
  59              'expression' => "/<nowiki>(.*?)<\/nowiki>/is",
  60              'token' => array('<nowiki>', '</nowiki>')
  61          ),
  62          'image' => array(
  63              'expression' => "/\[\[image:(.+?)\|(.+?)\]\]/is",
  64              'token' => array("[[image:", "|alt]]")
  65          ),
  66          'attach' => array(
  67              'expression' => "/\[\[attach:(.+?)\]\]/is",
  68              'token' => array("[[attach:", "|name]]")
  69          ),
  70          'link' => array(
  71              'expression' => "/\[\[(.+?)\]\]/is",
  72              'tag' => 'a',
  73              'token' => array("[[", "]]")
  74          ),
  75          'url_tag' => array(
  76              'expression' => "/\[(.+?)\]/is",
  77              'tag' => 'a',
  78              'token' => array("[", "]")
  79          ),
  80          'url' => array(
  81              'expression' => "/(?<!=\")((?:https?|ftp):\/\/[^\s\n]+[^,\.\?!:;\"\'\n\ ])/i",
  82              'tag' => 'a',
  83              'token' => 'http://'
  84          ),
  85          'italic' => array(
  86              'expression' => "/\'{3}(.+?)(\'{3}(?:\'{2})?)/is",
  87              'tag' => 'em',
  88              'token' => array("'''", "'''")
  89          ),
  90          'bold' => array(
  91              'expression' => "/\'{2}(.+?)\'{2}/is",
  92              'tag' => 'strong',
  93              'token' => array("''", "''")
  94          )
  95      );
  96  
  97      protected function after_parsing() {
  98          parent::after_parsing();
  99      }
 100  
 101      /**
 102       * Block hooks
 103       */
 104  
 105      protected function header_block_rule($match) {
 106          if($match[1] != $match[3]) {
 107              return $match[0];
 108          }
 109  
 110          $num = strlen($match[1]);
 111  
 112          return $this->generate_header($match[2], $num);
 113      }
 114  
 115      protected function table_block_rule($match) {
 116          $rows = explode("\n|-", $match[1]);
 117          $table = array();
 118          foreach($rows as $r) {
 119              $colsendline = explode("\n", $r);
 120              $cols = array();
 121              foreach($colsendline as $ce) {
 122                  $cols = array_merge($cols, $this->get_table_cells($ce));
 123              }
 124  
 125              if(!empty($cols)) {
 126                  $table[] = $cols;
 127              }
 128          }
 129          return $this->generate_table($table);
 130      }
 131  
 132      private function get_table_cells($string) {
 133          $string = ltrim($string);
 134          $type = (!empty($string) && $string[0] == "!") ? 'header' : 'normal';
 135          $string = substr($string, 1);
 136          if(empty($string)) {
 137              $normalcells = array();
 138          }
 139          else {
 140              $normalcells = explode("||", $string);
 141          }
 142          $cells = array();
 143          foreach($normalcells as $nc) {
 144              $headercells = explode("!!", $nc);
 145              $countheadercells = count($headercells);
 146              for($i = 0; $i < $countheadercells; $i++) {
 147                  $cells[] = array($type, $headercells[$i]);
 148                  $type = 'header';
 149              }
 150              $type = 'normal';
 151          }
 152  
 153          return $cells;
 154      }
 155  
 156      protected function tab_paragraph_block_rule($match) {
 157          $num = strlen($match[1]);
 158          $text = $match[2];
 159          $html = "";
 160          for($i = 0; $i < $num - 1; $i++) {
 161              $html = parser_utils::h('p', $html, array('class' => 'wiki_tab_paragraph'));
 162          }
 163  
 164          return parser_utils::h('p', $text, array('class' => 'wiki_tab_paragraph'));
 165      }
 166  
 167      protected function desc_list_block_rule($match) {
 168          preg_match_all("/^(.+?)\:(.+?)\;$/ims", $match[0], $listitems, PREG_SET_ORDER);
 169  
 170          $list = "";
 171          foreach($listitems as $li) {
 172              $term = $li[1];
 173              $this->rules($term);
 174  
 175              $description = $li[2];
 176              $this->rules($description);
 177  
 178              $list .= parser_utils::h('dt', $term).parser_utils::h('dd', $description);
 179          }
 180  
 181          return $list;
 182      }
 183  
 184      /**
 185       * Tag functions
 186       */
 187  
 188      /**
 189       * Bold and italic similar to creole...
 190       */
 191      protected function italic_tag_rule($match) {
 192          $text = $match[1];
 193          if(strlen($match[2]) == 5) {
 194              $text .= "''";
 195          }
 196  
 197          $this->rules($text, array('only' => array('bold')));
 198          if(strpos($text, "''") !== false) {
 199              $text = str_replace("''", $this->protect("''"), $text);
 200          }
 201  
 202          return array($text, array());
 203      }
 204  
 205      /**
 206       * Link tag functions
 207       */
 208  
 209      protected function link_tag_rule($match) {
 210          return $this->format_link($match[1]);
 211      }
 212  
 213      protected function url_tag_tag_rule($match) {
 214          $text = trim($match[1]);
 215          if(preg_match("/(.+?)\|(.+)/is", $text, $matches)) {
 216              $link = $matches[1];
 217              $text = $matches[2];
 218          }
 219          else if(preg_match("/(.+?)\ (.+)/is", $text, $matches)) {
 220              $link = $matches[1];
 221              $text = $matches[2];
 222          }
 223          else {
 224              $link = $text;
 225          }
 226          return array($this->protect($text), array('href' => $this->protect($link)));
 227      }
 228  
 229      protected function url_tag_rule($match) {
 230          $url = $this->protect($match[1]);
 231          $options = array('href' => $url);
 232  
 233          return array($url, $options);
 234      }
 235  
 236      /**
 237       * Attachments & images
 238       */
 239  
 240      protected function image_tag_rule($match) {
 241          return $this->format_image($match[1], $match[2]);
 242      }
 243  
 244      protected function attach_tag_rule($match) {
 245          $parts = explode("|", $match[1]);
 246  
 247          $url = array_shift($parts);
 248  
 249          if(count($parts) > 0) {
 250              $text = array_shift($parts);
 251          }
 252  
 253          $extension = substr($url, strrpos($url, "."));
 254          $text = empty($text) ? $url : $text;
 255  
 256          $imageextensions = array('jpg', 'jpeg', 'png', 'bmp', 'gif', 'tif');
 257          if(in_array($extension, $imageextensions)) {
 258              $align = 'left';
 259              if(count($parts) > 0) {
 260                  switch(strtolower($text)) {
 261                      case 'right':
 262                          $align = 'right';
 263                          break;
 264                      case 'center':
 265                          $align = 'center';
 266                          break;
 267                      default:
 268                          $align = 'left';
 269                  }
 270                  $text = $parts[0];
 271              }
 272              return $this->format_image($url, $text, $text, $align);
 273          }
 274          else {
 275              $url = $this->real_path($url);
 276              return parser_utils::h('a', $text, array('href' => $url, 'class' => 'wiki-attachment'));
 277          }
 278      }
 279  }