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.
<?php

/**
 * Creole parser implementation
 *
 * @author Josep ArĂºs
 *
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
 * @package mod_wiki
 */

include_once("wikimarkup.php");

class creole_parser extends wiki_markup_parser {

    protected $blockrules = array(
        'nowiki' => array(
            'expression' => "/^\{\{\{(.*?)\}\}\}/ims",
            'tags' => array(),
            'token' => array('{{{', '}}}')
        ),
        'header' => array(
            'expression' => "/^\ *(={1,6})\ *(.+?)=*\ *$/ims",
            'tags' => array(), //none
            'token' => '='
        ),
        'table' => array(
            'expression' => "/^(?:\|.*?\|\ *\n)+/ims"
        ),
        'line_break' => array(
            'expression' => "/^----\s*$/im",
            'token' => '----',
            'tags' => array()
        ),
        'list' => array(
            'expression' => "/((?:^\ *[\*#][^\*#]\ *.+?)(?:^\ *[\*#]{1,5}\ *.+?)*)(\n\s*(?:\n|<(?:h\d|pre|table|tbody|thead|tr|th|td|ul|li|ol|hr)))/ims",
            'tags' => array(),
            'token' => array('*', '#')
        ),
        'paragraph' => array(
            'expression' => "/^\ *((?:<(?!\ *\/?(?:h\d|pre|table|tbody|thead|tr|th|td|ul|li|ol|hr)\ *\/?>)|[^<\s]).+?)\n\s*\n/ims",
            //not specified -> all tags (null or unset)
            'tag' => 'p'
        )
    );

    protected $tagrules = array(
        'nowiki' => array(
            'expression' => "/\{\{\{(.*?)\}\}\}/is",
            'token' => array('{{{', '}}}')
        ),
        'image' => array(
            'expression' => "/\~?\{\{(.+)\|(.+)\}\}/i",
            'tags' => array(),
            'token' => array('{{', '|Alt}}')
        ),
        'link' => array(
            'expression' => "/\~?\[\[(.+?)\]\]/is",
            'tag' => 'a',
            'token' => array('[[', ']]')
        ),
        'url' => array(
            'expression' => "/\~?(?<!=\")((?:https?|ftp):\/\/[^\s\n]+[^,\.\?!:;\"\'\n\ ])/is",
            'tag' => 'a',
            'token' => "http://"
        ),
        'line_break' => array(
            'expression' => "/\\\\\\\\/",
            'tag' => 'br',
            'simple' => true,
            'token' => '----'
        ),
        'bold' => array(
            'expression' => "/\*\*(.*?)(?:\*\*|$)/is",
            'tag' => 'strong',
            'token' => array('**', '**')
        ),
        'italic' => array(
            'expression' => "#(?<!http:|https:|ftp:)//(.+?)(?<!http:|https:|ftp:)//#is",
            'tag' => 'em',
            'token' => array('//', '//')
        )
    );

    /**
     * Block hooks
     */

    protected function before_parsing() {
< $this->string = htmlspecialchars($this->string);
> $this->string = htmlspecialchars($this->string, ENT_COMPAT);
parent::before_parsing(); } public function get_section($header, $text, $clean = false) { // The requested header is likely to have been passed to htmlspecialchars in // self::before_parsing(), therefore we should decode it when looking for it.
< return parent::get_section(htmlspecialchars_decode($header), $text, $clean);
> return parent::get_section(htmlspecialchars_decode($header, ENT_COMPAT), $text, $clean);
} protected function header_block_rule($match) { $num = strlen($match[1]); $text = trim($match[2]); $text = preg_replace("/\s*={1,$num}$/im", "", $text); return $this->generate_header($text, $num); } /** * Table generation */ protected function table_block_rule($match) { $rows = explode("\n", $match[0]); $table = array(); foreach($rows as $r) { if(empty($r)) { continue; } $rawcells = explode("|", $r); $cells = array(); array_shift($rawcells); array_pop($rawcells); foreach($rawcells as $c) { if(!empty($c)) { if($c[0] == "=") { $type = 'header'; $c = substr($c, 1); } else { $type = 'normal'; } $this->rules($c); $cells[] = array($type, $c); } } $table[] = $cells; } return $this->generate_table($table); } protected function paragraph_block_rule($match) { $text = $match[1]; foreach($this->tagrules as $tr) { if(isset($tr['token'])) { if(is_array($tr['token'])) { $this->escape_token_string($text, $tr['token'][0]); $this->escape_token_string($text, $tr['token'][1]); } else { $this->escape_token_string($text, $tr['token']); } } } $this->escape_token_string($text, "~"); return $text; } /** * Escape token when it is "negated" */ private function escape_token_string(&$text, $token) { $text = str_replace("~".$token, $this->protect($token), $text); } /** * Tag functions */ protected function url_tag_rule($match) { if(strpos($match[0], "~") === 0) { return substr($match[0], 1); } else { $text = trim($match[0]); $options = array('href' => $text); return array($text, $options); } } protected function link_tag_rule($match) { $text = trim($match[1]); if(strpos($match[0], "~") === 0) { return substr($match[0], 1); } else { return $this->format_link($text); } } /** * Special treatment of // ** // ** // */ protected function bold_tag_rule($match) { $text = $match[1]; $this->rules($text, array('only' => array('italic'))); if(strpos($text, "//") !== false) { $text = str_replace("//", $this->protect("//"), $text); } return array($text, array()); } protected function image_tag_rule($match) { if(strpos($match[0], "~") === 0) { return substr($match[0], 1); } return $this->format_image($match[1], $match[2]); } }