first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+218
View File
@@ -0,0 +1,218 @@
<?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, 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, 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]);
}
}
+112
View File
@@ -0,0 +1,112 @@
<?php
/**
* HTML parser implementation. It only implements links.
*
* @author Josep Arús
*
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mod_wiki
*/
include_once("nwiki.php");
class html_parser extends nwiki_parser {
protected $blockrules = array();
protected $section_editing = true;
/** @var int Minimum level of the headers on the page (usually tinymce uses <h1> and atto <h3>) */
protected $minheaderlevel = null;
public function __construct() {
parent::__construct();
// The order is important, headers should be parsed before links.
$this->tagrules = array(
// Headers are considered tags here.
'header' => array(
'expression' => "/<\s*h([1-6])\s*>(.+?)<\/h[1-6]>/is"
),
'link' => $this->tagrules['link'],
'url' => $this->tagrules['url']
);
}
/**
* Find minimum header level used on the page (<h1>, <h3>, ...)
*
* @param string $text
* @return int
*/
protected function find_min_header_level($text) {
preg_match_all($this->tagrules['header']['expression'], $text, $matches);
return !empty($matches[1]) ? min($matches[1]) : 1;
}
protected function before_parsing() {
parent::before_parsing();
$this->minheaderlevel = $this->find_min_header_level($this->string);
// Protect all explicit links from further wiki parsing. The link text may contain another URL which would get
// converted into another link via {@see nwiki_parser::$tagrules} 'url' element.
if (preg_match_all('/<a\s[^>]+?>(.*?)<\/a>/is', $this->string, $matches)) {
foreach (array_unique($matches[0]) as $match) {
$this->string = str_replace($match, $this->protect($match), $this->string);
}
}
$this->rules($this->string);
}
/**
* Header tag rule
* @param array $match Header regex match
* @return string
*/
protected function header_tag_rule($match) {
return $this->generate_header($match[2], (int)$match[1] - $this->minheaderlevel + 1);
}
/**
* Section editing: Special for HTML Parser (It parses <h1></h1>)
*/
public function get_section($header, $text, $clean = false) {
if ($clean) {
$text = preg_replace('/\r\n/', "\n", $text);
$text = preg_replace('/\r/', "\n", $text);
$text .= "\n\n";
}
$minheaderlevel = $this->find_min_header_level($text);
$h1 = array("<\s*h{$minheaderlevel}\s*>", "<\/h{$minheaderlevel}>");
$regex = "/(.*?)({$h1[0]}\s*".preg_quote($header, '/')."\s*{$h1[1]}.*?)((?:{$h1[0]}.*)|$)/is";
preg_match($regex, $text, $match);
if (!empty($match)) {
return array($match[1], $match[2], $match[3]);
} else {
return false;
}
}
protected function get_repeated_sections(&$text, $repeated = array()) {
$this->repeated_sections = $repeated;
return preg_replace_callback($this->tagrules['header'], array($this, 'get_repeated_sections_callback'), $text);
}
protected function get_repeated_sections_callback($match) {
$text = trim($match[2]);
if (in_array($text, $this->repeated_sections)) {
$this->returnvalues['repeated_sections'][] = $text;
return parser_utils::h('p', $text);
} else {
$this->repeated_sections[] = $text;
}
return $match[0];
}
}
+279
View File
@@ -0,0 +1,279 @@
<?php
/**
* NWiki 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 nwiki_parser extends wiki_markup_parser {
protected $blockrules = array(
'nowiki' => array(
'expression' => "/^<nowiki>(.*?)<\/nowiki>/ims",
'tags' => array(),
'token' => array('<nowiki>', '</nowiki>')
),
'header' => array(
'expression' => "/^\ *(={1,6})\ *(.+?)(={1,6})\ *$/ims",
'tags' => array(), //none
'token' => '='
),
'line_break' => array(
'expression' => "/^-{3,4}\s*$/im",
'tags' => array(),
'token' => '---'
),
'desc_list' => array(
'expression' => "/(?:^.+?\:.+?\;\n)+/ims",
'tags' => array(),
'token' => array(':', ';'),
'tag' => 'dl'
),
'table' => array(
'expression' => "/\{\|(.+?)\|\}/ims"
),
'tab_paragraph' => array(
'expression' => "/^(\:+)(.+?)$/ims",
'tag' => 'p'
),
'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' => "/<nowiki>(.*?)<\/nowiki>/is",
'token' => array('<nowiki>', '</nowiki>')
),
'image' => array(
'expression' => "/\[\[image:(.+?)\|(.+?)\]\]/is",
'token' => array("[[image:", "|alt]]")
),
'attach' => array(
'expression' => "/\[\[attach:(.+?)\]\]/is",
'token' => array("[[attach:", "|name]]")
),
'link' => array(
'expression' => "/\[\[(.+?)\]\]/is",
'tag' => 'a',
'token' => array("[[", "]]")
),
'url_tag' => array(
'expression' => "/\[(.+?)\]/is",
'tag' => 'a',
'token' => array("[", "]")
),
'url' => array(
'expression' => "/(?<!=\")((?:https?|ftp):\/\/[^\s\n]+[^,\.\?!:;\"\'\n\ ])/i",
'tag' => 'a',
'token' => 'http://'
),
'italic' => array(
'expression' => "/\'{3}(.+?)(\'{3}(?:\'{2})?)/is",
'tag' => 'em',
'token' => array("'''", "'''")
),
'bold' => array(
'expression' => "/\'{2}(.+?)\'{2}/is",
'tag' => 'strong',
'token' => array("''", "''")
)
);
protected function after_parsing() {
parent::after_parsing();
}
/**
* Block hooks
*/
protected function header_block_rule($match) {
if($match[1] != $match[3]) {
return $match[0];
}
$num = strlen($match[1]);
return $this->generate_header($match[2], $num);
}
protected function table_block_rule($match) {
$rows = explode("\n|-", $match[1]);
$table = array();
foreach($rows as $r) {
$colsendline = explode("\n", $r);
$cols = array();
foreach($colsendline as $ce) {
$cols = array_merge($cols, $this->get_table_cells($ce));
}
if(!empty($cols)) {
$table[] = $cols;
}
}
return $this->generate_table($table);
}
private function get_table_cells($string) {
$string = ltrim($string);
$type = (!empty($string) && $string[0] == "!") ? 'header' : 'normal';
$string = substr($string, 1);
if(empty($string)) {
$normalcells = array();
}
else {
$normalcells = explode("||", $string);
}
$cells = array();
foreach($normalcells as $nc) {
$headercells = explode("!!", $nc);
$countheadercells = count($headercells);
for($i = 0; $i < $countheadercells; $i++) {
$cells[] = array($type, $headercells[$i]);
$type = 'header';
}
$type = 'normal';
}
return $cells;
}
protected function tab_paragraph_block_rule($match) {
$num = strlen($match[1]);
$text = $match[2];
$html = "";
for($i = 0; $i < $num - 1; $i++) {
$html = parser_utils::h('p', $html, array('class' => 'wiki_tab_paragraph'));
}
return parser_utils::h('p', $text, array('class' => 'wiki_tab_paragraph'));
}
protected function desc_list_block_rule($match) {
preg_match_all("/^(.+?)\:(.+?)\;$/ims", $match[0], $listitems, PREG_SET_ORDER);
$list = "";
foreach($listitems as $li) {
$term = $li[1];
$this->rules($term);
$description = $li[2];
$this->rules($description);
$list .= parser_utils::h('dt', $term).parser_utils::h('dd', $description);
}
return $list;
}
/**
* Tag functions
*/
/**
* Bold and italic similar to creole...
*/
protected function italic_tag_rule($match) {
$text = $match[1];
if(strlen($match[2]) == 5) {
$text .= "''";
}
$this->rules($text, array('only' => array('bold')));
if(strpos($text, "''") !== false) {
$text = str_replace("''", $this->protect("''"), $text);
}
return array($text, array());
}
/**
* Link tag functions
*/
protected function link_tag_rule($match) {
return $this->format_link($match[1]);
}
protected function url_tag_tag_rule($match) {
$text = trim($match[1]);
if(preg_match("/(.+?)\|(.+)/is", $text, $matches)) {
$link = $matches[1];
$text = $matches[2];
}
else if(preg_match("/(.+?)\ (.+)/is", $text, $matches)) {
$link = $matches[1];
$text = $matches[2];
}
else {
$link = $text;
}
return array($this->protect($text), array('href' => $this->protect($link)));
}
protected function url_tag_rule($match) {
$url = $this->protect($match[1]);
$options = array('href' => $url);
return array($url, $options);
}
/**
* Attachments & images
*/
protected function image_tag_rule($match) {
return $this->format_image($match[1], $match[2]);
}
protected function attach_tag_rule($match) {
$parts = explode("|", $match[1]);
$url = array_shift($parts);
if(count($parts) > 0) {
$text = array_shift($parts);
}
$extension = substr($url, strrpos($url, "."));
$text = empty($text) ? $url : $text;
$imageextensions = array('jpg', 'jpeg', 'png', 'bmp', 'gif', 'tif');
if(in_array($extension, $imageextensions)) {
$align = 'left';
if(count($parts) > 0) {
switch(strtolower($text)) {
case 'right':
$align = 'right';
break;
case 'center':
$align = 'center';
break;
default:
$align = 'left';
}
$text = $parts[0];
}
return $this->format_image($url, $text, $text, $align);
}
else {
$url = $this->real_path($url);
return parser_utils::h('a', $text, array('href' => $url, 'class' => 'wiki-attachment'));
}
}
}
+441
View File
@@ -0,0 +1,441 @@
<?php
/**
* Generic & abstract parser functions & skeleton. It has some functions & generic stuff.
*
* @author Josep Arús
*
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mod_wiki
*/
abstract class wiki_markup_parser extends generic_parser {
protected $pretty_print = false;
protected $printable = false;
//page id
protected $wiki_page_id;
//sections
protected $repeated_sections;
protected $section_editing = true;
//header & ToC
protected $toc = array();
protected $maxheaderdepth = 3;
/**
* function wiki_parser_link_callback($link = "")
*
* Returns array('content' => "Inside the link", 'url' => "http://url.com/Wiki/Entry", 'new' => false).
*/
private $linkgeneratorcallback = array('parser_utils', 'wiki_parser_link_callback');
private $linkgeneratorcallbackargs = array();
/**
* Table generator callback
*/
private $tablegeneratorcallback = array('parser_utils', 'wiki_parser_table_callback');
/**
* Get real path from relative path
*/
private $realpathcallback = array('parser_utils', 'wiki_parser_real_path');
private $realpathcallbackargs = array();
/**
* Before and after parsing...
*/
protected function before_parsing() {
$this->toc = array();
$this->string = preg_replace('/\r\n/', "\n", $this->string);
$this->string = preg_replace('/\r/', "\n", $this->string);
$this->string .= "\n\n";
if (!$this->printable && $this->section_editing) {
$this->returnvalues['unparsed_text'] = $this->string;
$this->string = $this->get_repeated_sections($this->string);
}
}
protected function after_parsing() {
if (!$this->printable) {
$this->returnvalues['repeated_sections'] = array_unique($this->returnvalues['repeated_sections']);
}
$this->process_toc();
$this->string = preg_replace("/\n\s/", "\n", $this->string);
$this->string = preg_replace("/\n{2,}/", "\n", $this->string);
$this->string = trim($this->string);
$this->string .= "\n";
}
/**
* Set options
*/
protected function set_options($options) {
parent::set_options($options);
$this->returnvalues['link_count'] = array();
$this->returnvalues['repeated_sections'] = array();
$this->returnvalues['toc'] = "";
foreach ($options as $name => $o) {
switch ($name) {
case 'link_callback':
$callback = explode(':', $o);
global $CFG;
require_once($CFG->dirroot . $callback[0]);
if (function_exists($callback[1])) {
$this->linkgeneratorcallback = $callback[1];
}
break;
case 'link_callback_args':
if (is_array($o)) {
$this->linkgeneratorcallbackargs = $o;
}
break;
case 'real_path_callback':
$callback = explode(':', $o);
global $CFG;
require_once($CFG->dirroot . $callback[0]);
if (function_exists($callback[1])) {
$this->realpathcallback = $callback[1];
}
break;
case 'real_path_callback_args':
if (is_array($o)) {
$this->realpathcallbackargs = $o;
}
break;
case 'table_callback':
$callback = explode(':', $o);
global $CFG;
require_once($CFG->dirroot . $callback[0]);
if (function_exists($callback[1])) {
$this->tablegeneratorcallback = $callback[1];
}
break;
case 'pretty_print':
if ($o) {
$this->pretty_print = true;
}
break;
case 'pageid':
$this->wiki_page_id = $o;
break;
case 'printable':
if ($o) {
$this->printable = true;
}
break;
}
}
}
/**
* Generic block rules
*/
protected function line_break_block_rule($match) {
return '<hr />';
}
protected function list_block_rule($match) {
preg_match_all("/^\ *([\*\#]{1,5})\ *((?:[^\n]|\n(?!(?:\ *[\*\#])|\n))+)/im", $match[1], $listitems, PREG_SET_ORDER);
return $this->process_block_list($listitems) . $match[2];
}
protected function nowiki_block_rule($match) {
return parser_utils::h('pre', $this->protect($match[1]));
}
/**
* Generic tag rules
*/
protected function nowiki_tag_rule($match) {
return parser_utils::h('tt', $this->protect($match[1]));
}
/**
* Header generation
*/
protected function generate_header($text, $level) {
$toctext = $text = trim($text);
if (!$this->pretty_print && $level == 1) {
$editlink = '[' . get_string('editsection', 'wiki') . ']';
$url = array('href' => "edit.php?pageid={$this->wiki_page_id}&section=" . urlencode($text),
'class' => 'wiki_edit_section');
$text .= ' ' . parser_utils::h('a', $this->protect($editlink), $url);
$toctext .= ' ' . parser_utils::h('a', $editlink, $url);
}
if ($level <= $this->maxheaderdepth) {
$this->toc[] = array($level, $toctext);
$num = count($this->toc);
$text = parser_utils::h('a', "", array('name' => "toc-$num")) . $text;
}
// Display headers as <h3> and lower for accessibility.
return parser_utils::h('h' . min(6, $level + 2), $text) . "\n\n";
}
/**
* Table of contents processing after parsing
*/
protected function process_toc() {
if (empty($this->toc)) {
return;
}
$toc = "";
$currentsection = array(0, 0, 0);
$i = 1;
foreach ($this->toc as & $header) {
switch ($header[0]) {
case 1:
$currentsection = array($currentsection[0] + 1, 0, 0);
break;
case 2:
$currentsection[1]++;
$currentsection[2] = 0;
if ($currentsection[0] == 0) {
$currentsection[0]++;
}
break;
case 3:
$currentsection[2]++;
if ($currentsection[1] == 0) {
$currentsection[1]++;
}
if ($currentsection[0] == 0) {
$currentsection[0]++;
}
break;
default:
continue 2;
}
$number = "$currentsection[0]";
if (!empty($currentsection[1])) {
$number .= ".$currentsection[1]";
if (!empty($currentsection[2])) {
$number .= ".$currentsection[2]";
}
}
$toc .= parser_utils::h('p', $number . ". " .
parser_utils::h('a', str_replace(array('[[', ']]'), '', $header[1]), array('href' => "#toc-$i")),
array('class' => 'wiki-toc-section-' . $header[0] . " wiki-toc-section"));
$i++;
}
$this->returnvalues['toc'] = "<div class=\"wiki-toc\"><p class=\"wiki-toc-title\">" . get_string('tableofcontents', 'wiki') . "</p>$toc</div>";
}
/**
* List helpers
*/
private function process_block_list($listitems) {
$list = array();
foreach ($listitems as $li) {
$text = str_replace("\n", "", $li[2]);
$this->rules($text);
if ($li[1][0] == '*') {
$type = 'ul';
} else {
$type = 'ol';
}
$list[] = array(strlen($li[1]), $text, $type);
}
$type = $list[0][2];
return "<$type>" . "\n" . $this->generate_list($list) . "\n" . "</$type>" . "\n";
}
/**
* List generation function from an array of array(level, text)
*/
protected function generate_list($listitems) {
$list = "";
$current_depth = 1;
$next_depth = 1;
$liststack = array();
for ($lc = 0; $lc < count($listitems) && $next_depth; $lc++) {
$cli = $listitems[$lc];
$nli = isset($listitems[$lc + 1]) ? $listitems[$lc + 1] : null;
$text = $cli[1];
$current_depth = $next_depth;
$next_depth = $nli ? $nli[0] : null;
if ($next_depth == $current_depth || $next_depth == null) {
$list .= parser_utils::h('li', $text) . "\n";
} else if ($next_depth > $current_depth) {
$next_depth = $current_depth + 1;
$list .= "<li>" . $text . "\n";
$list .= "<" . $nli[2] . ">" . "\n";
$liststack[] = $nli[2];
} else {
$list .= parser_utils::h('li', $text) . "\n";
for ($lv = $next_depth; $lv < $current_depth; $lv++) {
$type = array_pop($liststack);
$list .= "</$type>" . "\n" . "</li>" . "\n";
}
}
}
for ($lv = 1; $lv < $current_depth; $lv++) {
$type = array_pop($liststack);
$list .= "</$type>" . "\n" . "</li>" . "\n";
}
return $list;
}
/**
* Table generation functions
*/
protected function generate_table($table) {
$table_html = call_user_func_array($this->tablegeneratorcallback, array($table));
return $table_html;
}
protected function format_image($src, $alt, $caption = "", $align = 'left') {
$src = $this->real_path($src);
return parser_utils::h('div', parser_utils::h('p', $caption) . '<img src="' . $src . '" alt="' . $alt . '" />', array('class' => 'wiki_image_' . $align));
}
protected function real_path($url) {
$callbackargs = array_merge(array($url), $this->realpathcallbackargs);
return call_user_func_array($this->realpathcallback, array_values($callbackargs));
}
/**
* Link internal callback
*/
protected function link($link, $anchor = "") {
$link = trim($link);
if (preg_match("/^(https?|s?ftp):\/\/.+$/i", $link)) {
$link = trim($link, ",.?!");
return array('content' => $link, 'url' => $link);
} else {
$callbackargs = $this->linkgeneratorcallbackargs;
$callbackargs['anchor'] = $anchor;
$link = call_user_func_array($this->linkgeneratorcallback, array($link, $callbackargs));
if (isset($link['link_info'])) {
$l = $link['link_info']['link'];
unset($link['link_info']['link']);
$this->returnvalues['link_count'][$l] = $link['link_info'];
}
return $link;
}
}
/**
* Format links
*/
protected function format_link($text) {
$matches = array();
if (preg_match("/^([^\|]+)\|(.+)$/i", $text, $matches)) {
$link = $matches[1];
$content = trim($matches[2]);
if (preg_match("/(.+)#(.*)/is", $link, $matches)) {
$link = $this->link($matches[1], $matches[2]);
} else if ($link[0] == '#') {
$link = array('url' => "#" . urlencode(substr($link, 1)));
} else {
$link = $this->link($link);
}
$link['content'] = $content;
} else {
$link = $this->link($text);
}
if (isset($link['new']) && $link['new']) {
$options = array('class' => 'wiki_newentry');
} else {
$options = array();
}
$link['content'] = $this->protect($link['content']);
$link['url'] = $this->protect($link['url']);
$options['href'] = $link['url'];
if ($this->printable) {
$options['href'] = '#'; //no target for the link
}
return array($link['content'], $options);
}
/**
* Section editing
*/
public function get_section($header, $text, $clean = false) {
if ($clean) {
$text = preg_replace('/\r\n/', "\n", $text);
$text = preg_replace('/\r/', "\n", $text);
$text .= "\n\n";
}
$regex = "/(.*?)(=\ *".preg_quote($header, '/')."\ *=*\n.*?)((?:\n=[^=]+.*)|$)/is";
preg_match($regex, $text, $match);
if (!empty($match)) {
return array($match[1], $match[2], $match[3]);
} else {
return false;
}
}
protected function get_repeated_sections(&$text, $repeated = array()) {
$this->repeated_sections = $repeated;
return preg_replace_callback($this->blockrules['header']['expression'], array($this, 'get_repeated_sections_callback'), $text);
}
protected function get_repeated_sections_callback($match) {
$num = strlen($match[1]);
$text = trim($match[2]);
if ($num == 1) {
if (in_array($text, $this->repeated_sections)) {
$this->returnvalues['repeated_sections'][] = $text;
return $text . "\n";
} else {
$this->repeated_sections[] = $text;
}
}
return $match[0];
}
}
+313
View File
@@ -0,0 +1,313 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Library of functions and constants for module wiki
*
* It contains the great majority of functions defined by Moodle
* that are mandatory to develop a module.
*
* @package mod_wiki
* @copyright 2009 Marc Alier, Jordi Piguillem marc.alier@upc.edu
* @copyright 2009 Universitat Politecnica de Catalunya http://www.upc.edu
*
* @author Jordi Piguillem
* @author Marc Alier
* @author David Jimenez
* @author Josep Arus
* @author Kenneth Riba
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Generic parser implementation
*
* @author Josep Arús
*
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mod_wiki
*/
class wiki_parser_proxy {
/**
* @var array $parsers Array of parser instances
*/
private static $parsers = [];
/**
* Parse a string using a specific parser
*
* @param string $string The string to parse
* @param string $type The parser type
* @param array $options The parser options
* @return mixed The parsed string or false if the parser type is not found
*/
public static function parse(&$string, $type, $options = []) {
$type = strtolower($type);
self::$parsers[$type] = null; // Reset the current parser because it may have other options.
if (self::create_parser_instance($type)) {
return self::$parsers[$type]->parse($string, $options);
} else {
return false;
}
}
public static function get_token($name, $type) {
if (self::create_parser_instance($type)) {
return self::$parsers[$type]->get_token($name);
} else {
return false;
}
}
public static function get_section(&$string, $type, $section, $allcontent = false) {
if (self::create_parser_instance($type)) {
$content = self::$parsers[$type]->get_section($section, $string, true);
if ($allcontent) {
return $content;
} else {
return is_array($content) ? $content[1] : null;
}
} else {
return false;
}
}
private static function create_parser_instance($type) {
global $CFG;
$type = clean_param($type, PARAM_ALPHA);
if (empty(self::$parsers[$type])) {
$path = "$CFG->dirroot/mod/wiki/parser/markups/$type.php";
if (!file_exists($path)) {
throw new moodle_exception("Parser type $type not found");
}
include_once($path);
$class = strtolower($type) . "_parser";
if (class_exists($class)) {
self::$parsers[$type] = new $class;
return true;
} else {
return false;
}
} else {
return true;
}
}
}
require_once('utils.php');
abstract class generic_parser {
protected $string;
protected $blockrules = array();
protected $tagrules = array();
private $rulestack = array();
protected $parserstatus = 'Before';
/**
* Dynamic return values
*/
protected $returnvalues = array();
private $nowikiindex = array();
protected $nowikitoken = "%!";
public function __construct() {
}
/**
* Parse function
*/
public function parse(&$string, $options = array()) {
if (!is_string($string)) {
return false;
}
$this->string =& $string;
$this->set_options(is_array($options) ? $options : array());
$this->initialize_nowiki_index();
if (method_exists($this, 'before_parsing')) {
$this->before_parsing();
}
$this->parserstatus = 'Parsing';
foreach ($this->blockrules as $name => $block) {
$this->process_block_rule($name, $block);
}
$this->commit_nowiki_index();
$this->parserstatus = 'After';
if (method_exists($this, 'after_parsing')) {
$this->after_parsing();
}
return array('parsed_text' => $this->string) + $this->returnvalues;
}
/**
* Initialize options
*/
protected function set_options($options) {
}
/**
* Block processing function & callbacks
*/
protected function process_block_rule($name, $block) {
$this->rulestack[] = array('callback' => method_exists($this, $name . "_block_rule") ? $name . "_block_rule" : null,
'rule' => $block);
$this->string = preg_replace_callback($block['expression'], array($this, 'block_callback'), $this->string);
array_pop($this->rulestack);
}
private function block_callback($match) {
$rule = end($this->rulestack);
if (!empty($rule['callback'])) {
$stuff = $this->{$rule['callback']}($match);
} else {
$stuff = $match[1];
}
if (is_array($stuff) && $rule['rule']['tag']) {
$this->rules($stuff[0], $rule['rule']['tags']);
$stuff = "\n" . parser_utils::h($rule['rule']['tag'], $stuff[0], $stuff[1]) . "\n";
} else {
if (!isset($rule['rule']['tags'])) {
$rule['rule']['tags'] = null;
}
$this->rules($stuff, $rule['rule']['tags']);
if (isset($rule['rule']['tag']) && is_string($rule['rule']['tag'])) {
$stuff = "\n" . parser_utils::h($rule['rule']['tag'], $stuff) . "\n";
}
}
return $stuff;
}
/**
* Rules processing function & callback
*/
final protected function rules(&$text, $rules = null) {
if ($rules === null) {
$rules = array('except' => array());
} else if (is_array($rules) && count($rules) > 1) {
$rules = array('only' => $rules);
}
if (isset($rules['only']) && is_array($rules['only'])) {
$rules = $rules['only'];
foreach ($rules as $r) {
if (!empty($this->tagrules[$r])) {
$this->process_tag_rule($r, $this->tagrules[$r], $text);
}
}
} else if (isset($rules['except']) && is_array($rules['except'])) {
$rules = $rules['except'];
foreach ($this->tagrules as $r => $tr) {
if (!in_array($r, $rules)) {
$this->process_tag_rule($r, $tr, $text);
}
}
}
}
private function process_tag_rule($name, $rule, &$text) {
if (method_exists($this, $name . "_tag_rule")) {
$this->rulestack[] = array('callback' => $name . "_tag_rule", 'rule' => $rule);
$text = preg_replace_callback($rule['expression'], array($this, 'tag_callback'), $text);
array_pop($this->rulestack);
} else {
if (isset($rule['simple'])) {
$replace = "<{$rule['tag']} />";
} else {
$replace = parser_utils::h($rule['tag'], "$1");
}
$text = preg_replace($rule['expression'], $replace, $text);
}
}
private function tag_callback($match) {
$rule = end($this->rulestack);
$stuff = $this->{$rule['callback']}($match);
if (is_array($stuff)) {
return parser_utils::h($rule['rule']['tag'], $stuff[0], $stuff[1]);
} else {
return $stuff;
}
}
/**
* Special nowiki parser index
*/
private function initialize_nowiki_index() {
$token = "\Q" . $this->nowikitoken . "\E";
$this->string = preg_replace_callback("/" . $token . "\d+" . $token . "/",
array($this, "initialize_nowiki_index_callback"), $this->string);
}
private function initialize_nowiki_index_callback($match) {
return $this->protect($match[0]);
}
protected function protect($text) {
$this->nowikiindex[] = $text;
return $this->nowikitoken . (count($this->nowikiindex) - 1) . $this->nowikitoken;
}
private function commit_nowiki_index() {
$token = "\Q" . $this->nowikitoken . "\E";
$this->string = preg_replace_callback("/" . $token . "(\d+)" . $token . "/",
array($this, "commit_nowiki_index_callback"), $this->string);
}
private function commit_nowiki_index_callback($match) {
return $this->nowikiindex[intval($match[1])];
}
/**
* Get token of the parsable element $name.
*/
public function get_token($name) {
foreach (array_merge($this->blockrules, $this->tagrules) as $n => $v) {
if ($name == $n && isset($v['token'])) {
return $v['token'] ? $v['token'] : false;
}
}
return false;
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
/**
* Parser utils and default callbacks.
*
* @author Josep Arús
*
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mod_wiki
*/
require_once($CFG->dirroot . "/lib/outputcomponents.php");
class parser_utils {
public static function h($tag, $text = null, $options = array(), $escape_text = false) {
$tag = htmlentities($tag, ENT_COMPAT, 'UTF-8');
if(!empty($text) && $escape_text) {
$text = htmlentities($text, ENT_COMPAT, 'UTF-8');
}
return html_writer::tag($tag, $text, $options);
}
/**
* Default link generator
*/
public static function wiki_parser_link_callback($link, $options) {
$l = urlencode($link);
if(!empty($options['anchor'])) {
$l .= "#".urlencode($options['anchor']);
}
return array('content' => $link, 'url' => "http://".$l);
}
/**
* Default table generator
*/
public static function wiki_parser_table_callback($table) {
$html = "";
$headers = $table[0];
$columncount = count($headers);
$headerhtml = "";
foreach($headers as $h) {
$text = trim($h[1]);
if($h[0] == 'header') {
$headerhtml .= "\n".parser_utils::h('th', $text)."\n";
$hasheaders = true;
}
else if($h[0] == 'normal'){
$headerhtml .= "\n".parser_utils::h("td", $text)."\n";
}
}
$headerhtml = "\n".parser_utils::h('tr', $headerhtml)."\n";
$bodyhtml = "";
if(isset($hasheaders)) {
$html = "\n".parser_utils::h('thead', $headerhtml)."\n";
}
else {
$bodyhtml .= $headerhtml;
}
array_shift($table);
foreach($table as $row) {
$htmlrow = "";
for($i = 0; $i < $columncount; $i++) {
$text = "";
if(!isset($row[$i])) {
$htmlrow .= "\n".parser_utils::h('td', $text)."\n";
}
else {
$text = trim($row[$i][1]);
if($row[$i][0] == 'header') {
$htmlrow .= "\n".parser_utils::h('th', $text)."\n";
}
else if($row[$i][0] == 'normal'){
$htmlrow .= "\n".parser_utils::h('td', $text)."\n";
}
}
}
$bodyhtml .= "\n".parser_utils::h('tr', $htmlrow)."\n";
}
$html .= "\n".parser_utils::h('tbody', $bodyhtml)."\n";
return "\n".parser_utils::h('table', $html)."\n";
}
/**
* Default path converter
*/
public static function wiki_parser_real_path($url) {
return $url;
}
}