|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00012 abstract class wiki_markup_parser extends generic_parser { 00013 00014 protected $pretty_print = false; 00015 protected $printable = false; 00016 00017 //page id 00018 protected $wiki_page_id; 00019 00020 //sections 00021 protected $section_edit_text = "[edit]"; 00022 protected $repeated_sections; 00023 00024 protected $section_editing = true; 00025 00026 //header & ToC 00027 protected $toc = array(); 00028 00034 private $linkgeneratorcallback = array('parser_utils', 'wiki_parser_link_callback'); 00035 private $linkgeneratorcallbackargs = array(); 00036 00041 private $tablegeneratorcallback = array('parser_utils', 'wiki_parser_table_callback'); 00042 00046 private $realpathcallback = array('parser_utils', 'wiki_parser_real_path'); 00047 private $realpathcallbackargs = array(); 00048 00053 protected function before_parsing() { 00054 $this->toc = array(); 00055 00056 $this->string = preg_replace('/\r\n/', "\n", $this->string); 00057 $this->string = preg_replace('/\r/', "\n", $this->string); 00058 00059 $this->string .= "\n\n"; 00060 00061 if (!$this->printable && $this->section_editing) { 00062 $this->returnvalues['unparsed_text'] = $this->string; 00063 $this->string = $this->get_repeated_sections($this->string); 00064 } 00065 } 00066 00067 protected function after_parsing() { 00068 if (!$this->printable) { 00069 $this->returnvalues['repeated_sections'] = array_unique($this->returnvalues['repeated_sections']); 00070 } 00071 00072 $this->process_toc(); 00073 00074 $this->string = preg_replace("/\n\s/", "\n", $this->string); 00075 $this->string = preg_replace("/\n{2,}/", "\n", $this->string); 00076 $this->string = trim($this->string); 00077 $this->string .= "\n"; 00078 } 00079 00084 protected function set_options($options) { 00085 parent::set_options($options); 00086 00087 $this->returnvalues['link_count'] = array(); 00088 $this->returnvalues['repeated_sections'] = array(); 00089 $this->returnvalues['toc'] = ""; 00090 00091 foreach ($options as $name => $o) { 00092 switch ($name) { 00093 case 'link_callback': 00094 $callback = explode(':', $o); 00095 00096 global $CFG; 00097 require_once($CFG->dirroot . $callback[0]); 00098 00099 if (function_exists($callback[1])) { 00100 $this->linkgeneratorcallback = $callback[1]; 00101 } 00102 break; 00103 case 'link_callback_args': 00104 if (is_array($o)) { 00105 $this->linkgeneratorcallbackargs = $o; 00106 } 00107 break; 00108 case 'real_path_callback': 00109 $callback = explode(':', $o); 00110 00111 global $CFG; 00112 require_once($CFG->dirroot . $callback[0]); 00113 00114 if (function_exists($callback[1])) { 00115 $this->realpathcallback = $callback[1]; 00116 } 00117 break; 00118 case 'real_path_callback_args': 00119 if (is_array($o)) { 00120 $this->realpathcallbackargs = $o; 00121 } 00122 break; 00123 case 'table_callback': 00124 $callback = explode(':', $o); 00125 00126 global $CFG; 00127 require_once($CFG->dirroot . $callback[0]); 00128 00129 if (function_exists($callback[1])) { 00130 $this->tablegeneratorcallback = $callback[1]; 00131 } 00132 break; 00133 case 'pretty_print': 00134 if ($o) { 00135 $this->pretty_print = true; 00136 } 00137 break; 00138 case 'pageid': 00139 $this->wiki_page_id = $o; 00140 break; 00141 case 'printable': 00142 if ($o) { 00143 $this->printable = true; 00144 } 00145 break; 00146 } 00147 } 00148 } 00149 00154 protected function line_break_block_rule($match) { 00155 return '<hr />'; 00156 } 00157 00158 protected function list_block_rule($match) { 00159 preg_match_all("/^\ *([\*\#]{1,5})\ *((?:[^\n]|\n(?!(?:\ *[\*\#])|\n))+)/im", $match[1], $listitems, PREG_SET_ORDER); 00160 00161 return $this->process_block_list($listitems) . $match[2]; 00162 } 00163 00164 protected function nowiki_block_rule($match) { 00165 return parser_utils::h('pre', $this->protect($match[1])); 00166 } 00167 00172 protected function nowiki_tag_rule($match) { 00173 return parser_utils::h('tt', $this->protect($match[1])); 00174 } 00175 00180 protected function generate_header($text, $level) { 00181 $text = trim($text); 00182 00183 if (!$this->pretty_print && $level == 1) { 00184 $text .= parser_utils::h('a', $this->section_edit_text, array('href' => "edit.php?pageid={$this->wiki_page_id}§ion=" . urlencode($text), 'class' => 'wiki_edit_section')); 00185 } 00186 00187 if ($level < 4) { 00188 $this->toc[] = array($level, $text); 00189 $num = count($this->toc); 00190 $text = parser_utils::h('a', "", array('name' => "toc-$num")) . $text; 00191 } 00192 00193 return parser_utils::h('h' . $level, $text) . "\n\n"; 00194 } 00195 00199 protected function process_toc() { 00200 if (empty($this->toc)) { 00201 return; 00202 } 00203 00204 $toc = ""; 00205 $currentsection = array(0, 0, 0); 00206 $i = 1; 00207 foreach ($this->toc as & $header) { 00208 switch ($header[0]) { 00209 case 1: 00210 $currentsection = array($currentsection[0] + 1, 0, 0); 00211 break; 00212 case 2: 00213 $currentsection[1]++; 00214 $currentsection[2] = 0; 00215 if ($currentsection[0] == 0) { 00216 $currentsection[0]++; 00217 } 00218 break; 00219 case 3: 00220 $currentsection[2]++; 00221 if ($currentsection[1] == 0) { 00222 $currentsection[1]++; 00223 } 00224 if ($currentsection[0] == 0) { 00225 $currentsection[0]++; 00226 } 00227 break; 00228 default: 00229 continue; 00230 } 00231 $number = "$currentsection[0]"; 00232 if (!empty($currentsection[1])) { 00233 $number .= ".$currentsection[1]"; 00234 if (!empty($currentsection[2])) { 00235 $number .= ".$currentsection[2]"; 00236 } 00237 } 00238 $toc .= parser_utils::h('p', $number . ". " . parser_utils::h('a', $header[1], array('href' => "#toc-$i")), array('class' => 'wiki-toc-section-' . $header[0] . " wiki-toc-section")); 00239 $i++; 00240 } 00241 00242 $this->returnvalues['toc'] = "<div class=\"wiki-toc\"><p class=\"wiki-toc-title\">Table of contents</p>$toc</div>"; 00243 } 00244 00249 private function process_block_list($listitems) { 00250 $list = array(); 00251 foreach ($listitems as $li) { 00252 $text = str_replace("\n", "", $li[2]); 00253 $this->rules($text); 00254 00255 if ($li[1][0] == '*') { 00256 $type = 'ul'; 00257 } else { 00258 $type = 'ol'; 00259 } 00260 00261 $list[] = array(strlen($li[1]), $text, $type); 00262 } 00263 $type = $list[0][2]; 00264 return "<$type>" . "\n" . $this->generate_list($list) . "\n" . "</$type>" . "\n"; 00265 } 00266 00271 protected function generate_list($listitems) { 00272 $list = ""; 00273 $current_depth = 1; 00274 $next_depth = 1; 00275 $liststack = array(); 00276 for ($lc = 0; $lc < count($listitems) && $next_depth; $lc++) { 00277 $cli = $listitems[$lc]; 00278 $nli = isset($listitems[$lc + 1]) ? $listitems[$lc + 1] : null; 00279 00280 $text = $cli[1]; 00281 00282 $current_depth = $next_depth; 00283 $next_depth = $nli ? $nli[0] : null; 00284 00285 if ($next_depth == $current_depth || $next_depth == null) { 00286 $list .= parser_utils::h('li', $text) . "\n"; 00287 } else if ($next_depth > $current_depth) { 00288 $next_depth = $current_depth + 1; 00289 00290 $list .= "<li>" . $text . "\n"; 00291 $list .= "<" . $nli[2] . ">" . "\n"; 00292 $liststack[] = $nli[2]; 00293 } else { 00294 $list .= parser_utils::h('li', $text) . "\n"; 00295 00296 for ($lv = $next_depth; $lv < $current_depth; $lv++) { 00297 $type = array_pop($liststack); 00298 $list .= "</$type>" . "\n" . "</li>" . "\n"; 00299 } 00300 } 00301 } 00302 00303 for ($lv = 1; $lv < $current_depth; $lv++) { 00304 $type = array_pop($liststack); 00305 $list .= "</$type>" . "\n" . "</li>" . "\n"; 00306 } 00307 00308 return $list; 00309 } 00310 00315 protected function generate_table($table) { 00316 $table_html = call_user_func_array($this->tablegeneratorcallback, array($table)); 00317 00318 return $table_html; 00319 } 00320 00321 protected function format_image($src, $alt, $caption = "", $align = 'left') { 00322 $src = $this->real_path($src); 00323 return parser_utils::h('div', parser_utils::h('p', $caption) . '<img src="' . $src . '" alt="' . $alt . '" />', array('class' => 'wiki_image_' . $align)); 00324 } 00325 00326 protected function real_path($url) { 00327 $callbackargs = array_merge(array($url), $this->realpathcallbackargs); 00328 return call_user_func_array($this->realpathcallback, $callbackargs); 00329 } 00330 00335 protected function link($link, $anchor = "") { 00336 $link = trim($link); 00337 if (preg_match("/^(https?|s?ftp):\/\/.+$/i", $link)) { 00338 $link = trim($link, ",.?!"); 00339 return array('content' => $link, 'url' => $link); 00340 } else { 00341 $callbackargs = $this->linkgeneratorcallbackargs; 00342 $callbackargs['anchor'] = $anchor; 00343 $link = call_user_func_array($this->linkgeneratorcallback, array($link, $callbackargs)); 00344 00345 if (isset($link['link_info'])) { 00346 $l = $link['link_info']['link']; 00347 unset($link['link_info']['link']); 00348 $this->returnvalues['link_count'][$l] = $link['link_info']; 00349 } 00350 return $link; 00351 } 00352 } 00353 00358 protected function format_link($text) { 00359 $matches = array(); 00360 if (preg_match("/^([^\|]+)\|(.+)$/i", $text, $matches)) { 00361 $link = $matches[1]; 00362 $content = trim($matches[2]); 00363 if (preg_match("/(.+)#(.*)/is", $link, $matches)) { 00364 $link = $this->link($matches[1], $matches[2]); 00365 } else if ($link[0] == '#') { 00366 $link = array('url' => "#" . urlencode(substr($link, 1))); 00367 } else { 00368 $link = $this->link($link); 00369 } 00370 00371 $link['content'] = $content; 00372 } else { 00373 $link = $this->link($text); 00374 } 00375 00376 if (isset($link['new']) && $link['new']) { 00377 $options = array('class' => 'wiki_newentry'); 00378 } else { 00379 $options = array(); 00380 } 00381 00382 $link['content'] = $this->protect($link['content']); 00383 $link['url'] = $this->protect($link['url']); 00384 00385 $options['href'] = $link['url']; 00386 00387 if ($this->printable) { 00388 $options['href'] = '#'; //no target for the link 00389 } 00390 return array($link['content'], $options); 00391 } 00392 00397 public function get_section($header, $text, $clean = false) { 00398 if ($clean) { 00399 $text = preg_replace('/\r\n/', "\n", $text); 00400 $text = preg_replace('/\r/', "\n", $text); 00401 $text .= "\n\n"; 00402 } 00403 00404 preg_match("/(.*?)(=\ *\Q$header\E\ *=*\n.*?)((?:\n=[^=]+.*)|$)/is", $text, $match); 00405 00406 if (!empty($match)) { 00407 return array($match[1], $match[2], $match[3]); 00408 } else { 00409 return false; 00410 } 00411 } 00412 00413 protected function get_repeated_sections(&$text, $repeated = array()) { 00414 $this->repeated_sections = $repeated; 00415 return preg_replace_callback($this->blockrules['header']['expression'], array($this, 'get_repeated_sections_callback'), $text); 00416 } 00417 00418 protected function get_repeated_sections_callback($match) { 00419 $num = strlen($match[1]); 00420 $text = trim($match[2]); 00421 if ($num == 1) { 00422 if (in_array($text, $this->repeated_sections)) { 00423 $this->returnvalues['repeated_sections'][] = $text; 00424 return $text . "\n"; 00425 } else { 00426 $this->repeated_sections[] = $text; 00427 } 00428 } 00429 00430 return $match[0]; 00431 } 00432 00433 }