[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Generic & abstract parser functions & skeleton. It has some functions & generic stuff. 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 abstract class wiki_markup_parser extends generic_parser { 13 14 protected $pretty_print = false; 15 protected $printable = false; 16 17 //page id 18 protected $wiki_page_id; 19 20 //sections 21 protected $repeated_sections; 22 23 protected $section_editing = true; 24 25 //header & ToC 26 protected $toc = array(); 27 protected $maxheaderdepth = 3; 28 29 /** 30 * function wiki_parser_link_callback($link = "") 31 * 32 * Returns array('content' => "Inside the link", 'url' => "http://url.com/Wiki/Entry", 'new' => false). 33 */ 34 private $linkgeneratorcallback = array('parser_utils', 'wiki_parser_link_callback'); 35 private $linkgeneratorcallbackargs = array(); 36 37 /** 38 * Table generator callback 39 */ 40 41 private $tablegeneratorcallback = array('parser_utils', 'wiki_parser_table_callback'); 42 43 /** 44 * Get real path from relative path 45 */ 46 private $realpathcallback = array('parser_utils', 'wiki_parser_real_path'); 47 private $realpathcallbackargs = array(); 48 49 /** 50 * Before and after parsing... 51 */ 52 53 protected function before_parsing() { 54 $this->toc = array(); 55 56 $this->string = preg_replace('/\r\n/', "\n", $this->string); 57 $this->string = preg_replace('/\r/', "\n", $this->string); 58 59 $this->string .= "\n\n"; 60 61 if (!$this->printable && $this->section_editing) { 62 $this->returnvalues['unparsed_text'] = $this->string; 63 $this->string = $this->get_repeated_sections($this->string); 64 } 65 } 66 67 protected function after_parsing() { 68 if (!$this->printable) { 69 $this->returnvalues['repeated_sections'] = array_unique($this->returnvalues['repeated_sections']); 70 } 71 72 $this->process_toc(); 73 74 $this->string = preg_replace("/\n\s/", "\n", $this->string); 75 $this->string = preg_replace("/\n{2,}/", "\n", $this->string); 76 $this->string = trim($this->string); 77 $this->string .= "\n"; 78 } 79 80 /** 81 * Set options 82 */ 83 84 protected function set_options($options) { 85 parent::set_options($options); 86 87 $this->returnvalues['link_count'] = array(); 88 $this->returnvalues['repeated_sections'] = array(); 89 $this->returnvalues['toc'] = ""; 90 91 foreach ($options as $name => $o) { 92 switch ($name) { 93 case 'link_callback': 94 $callback = explode(':', $o); 95 96 global $CFG; 97 require_once($CFG->dirroot . $callback[0]); 98 99 if (function_exists($callback[1])) { 100 $this->linkgeneratorcallback = $callback[1]; 101 } 102 break; 103 case 'link_callback_args': 104 if (is_array($o)) { 105 $this->linkgeneratorcallbackargs = $o; 106 } 107 break; 108 case 'real_path_callback': 109 $callback = explode(':', $o); 110 111 global $CFG; 112 require_once($CFG->dirroot . $callback[0]); 113 114 if (function_exists($callback[1])) { 115 $this->realpathcallback = $callback[1]; 116 } 117 break; 118 case 'real_path_callback_args': 119 if (is_array($o)) { 120 $this->realpathcallbackargs = $o; 121 } 122 break; 123 case 'table_callback': 124 $callback = explode(':', $o); 125 126 global $CFG; 127 require_once($CFG->dirroot . $callback[0]); 128 129 if (function_exists($callback[1])) { 130 $this->tablegeneratorcallback = $callback[1]; 131 } 132 break; 133 case 'pretty_print': 134 if ($o) { 135 $this->pretty_print = true; 136 } 137 break; 138 case 'pageid': 139 $this->wiki_page_id = $o; 140 break; 141 case 'printable': 142 if ($o) { 143 $this->printable = true; 144 } 145 break; 146 } 147 } 148 } 149 150 /** 151 * Generic block rules 152 */ 153 154 protected function line_break_block_rule($match) { 155 return '<hr />'; 156 } 157 158 protected function list_block_rule($match) { 159 preg_match_all("/^\ *([\*\#]{1,5})\ *((?:[^\n]|\n(?!(?:\ *[\*\#])|\n))+)/im", $match[1], $listitems, PREG_SET_ORDER); 160 161 return $this->process_block_list($listitems) . $match[2]; 162 } 163 164 protected function nowiki_block_rule($match) { 165 return parser_utils::h('pre', $this->protect($match[1])); 166 } 167 168 /** 169 * Generic tag rules 170 */ 171 172 protected function nowiki_tag_rule($match) { 173 return parser_utils::h('tt', $this->protect($match[1])); 174 } 175 176 /** 177 * Header generation 178 */ 179 180 protected function generate_header($text, $level) { 181 $text = trim($text); 182 183 if (!$this->pretty_print && $level == 1) { 184 $text .= ' ' . parser_utils::h('a', '['.get_string('editsection', 'wiki').']', 185 array('href' => "edit.php?pageid={$this->wiki_page_id}§ion=" . urlencode($text), 186 'class' => 'wiki_edit_section')); 187 } 188 189 if ($level <= $this->maxheaderdepth) { 190 $this->toc[] = array($level, $text); 191 $num = count($this->toc); 192 $text = parser_utils::h('a', "", array('name' => "toc-$num")) . $text; 193 } 194 195 return parser_utils::h('h' . $level, $text) . "\n\n"; 196 } 197 198 /** 199 * Table of contents processing after parsing 200 */ 201 protected function process_toc() { 202 if (empty($this->toc)) { 203 return; 204 } 205 206 $toc = ""; 207 $currentsection = array(0, 0, 0); 208 $i = 1; 209 foreach ($this->toc as & $header) { 210 switch ($header[0]) { 211 case 1: 212 $currentsection = array($currentsection[0] + 1, 0, 0); 213 break; 214 case 2: 215 $currentsection[1]++; 216 $currentsection[2] = 0; 217 if ($currentsection[0] == 0) { 218 $currentsection[0]++; 219 } 220 break; 221 case 3: 222 $currentsection[2]++; 223 if ($currentsection[1] == 0) { 224 $currentsection[1]++; 225 } 226 if ($currentsection[0] == 0) { 227 $currentsection[0]++; 228 } 229 break; 230 default: 231 continue; 232 } 233 $number = "$currentsection[0]"; 234 if (!empty($currentsection[1])) { 235 $number .= ".$currentsection[1]"; 236 if (!empty($currentsection[2])) { 237 $number .= ".$currentsection[2]"; 238 } 239 } 240 $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")); 241 $i++; 242 } 243 244 $this->returnvalues['toc'] = "<div class=\"wiki-toc\"><p class=\"wiki-toc-title\">" . get_string('tableofcontents', 'wiki') . "</p>$toc</div>"; 245 } 246 247 /** 248 * List helpers 249 */ 250 251 private function process_block_list($listitems) { 252 $list = array(); 253 foreach ($listitems as $li) { 254 $text = str_replace("\n", "", $li[2]); 255 $this->rules($text); 256 257 if ($li[1][0] == '*') { 258 $type = 'ul'; 259 } else { 260 $type = 'ol'; 261 } 262 263 $list[] = array(strlen($li[1]), $text, $type); 264 } 265 $type = $list[0][2]; 266 return "<$type>" . "\n" . $this->generate_list($list) . "\n" . "</$type>" . "\n"; 267 } 268 269 /** 270 * List generation function from an array of array(level, text) 271 */ 272 273 protected function generate_list($listitems) { 274 $list = ""; 275 $current_depth = 1; 276 $next_depth = 1; 277 $liststack = array(); 278 for ($lc = 0; $lc < count($listitems) && $next_depth; $lc++) { 279 $cli = $listitems[$lc]; 280 $nli = isset($listitems[$lc + 1]) ? $listitems[$lc + 1] : null; 281 282 $text = $cli[1]; 283 284 $current_depth = $next_depth; 285 $next_depth = $nli ? $nli[0] : null; 286 287 if ($next_depth == $current_depth || $next_depth == null) { 288 $list .= parser_utils::h('li', $text) . "\n"; 289 } else if ($next_depth > $current_depth) { 290 $next_depth = $current_depth + 1; 291 292 $list .= "<li>" . $text . "\n"; 293 $list .= "<" . $nli[2] . ">" . "\n"; 294 $liststack[] = $nli[2]; 295 } else { 296 $list .= parser_utils::h('li', $text) . "\n"; 297 298 for ($lv = $next_depth; $lv < $current_depth; $lv++) { 299 $type = array_pop($liststack); 300 $list .= "</$type>" . "\n" . "</li>" . "\n"; 301 } 302 } 303 } 304 305 for ($lv = 1; $lv < $current_depth; $lv++) { 306 $type = array_pop($liststack); 307 $list .= "</$type>" . "\n" . "</li>" . "\n"; 308 } 309 310 return $list; 311 } 312 313 /** 314 * Table generation functions 315 */ 316 317 protected function generate_table($table) { 318 $table_html = call_user_func_array($this->tablegeneratorcallback, array($table)); 319 320 return $table_html; 321 } 322 323 protected function format_image($src, $alt, $caption = "", $align = 'left') { 324 $src = $this->real_path($src); 325 return parser_utils::h('div', parser_utils::h('p', $caption) . '<img src="' . $src . '" alt="' . $alt . '" />', array('class' => 'wiki_image_' . $align)); 326 } 327 328 protected function real_path($url) { 329 $callbackargs = array_merge(array($url), $this->realpathcallbackargs); 330 return call_user_func_array($this->realpathcallback, $callbackargs); 331 } 332 333 /** 334 * Link internal callback 335 */ 336 337 protected function link($link, $anchor = "") { 338 $link = trim($link); 339 if (preg_match("/^(https?|s?ftp):\/\/.+$/i", $link)) { 340 $link = trim($link, ",.?!"); 341 return array('content' => $link, 'url' => $link); 342 } else { 343 $callbackargs = $this->linkgeneratorcallbackargs; 344 $callbackargs['anchor'] = $anchor; 345 $link = call_user_func_array($this->linkgeneratorcallback, array($link, $callbackargs)); 346 347 if (isset($link['link_info'])) { 348 $l = $link['link_info']['link']; 349 unset($link['link_info']['link']); 350 $this->returnvalues['link_count'][$l] = $link['link_info']; 351 } 352 return $link; 353 } 354 } 355 356 /** 357 * Format links 358 */ 359 360 protected function format_link($text) { 361 $matches = array(); 362 if (preg_match("/^([^\|]+)\|(.+)$/i", $text, $matches)) { 363 $link = $matches[1]; 364 $content = trim($matches[2]); 365 if (preg_match("/(.+)#(.*)/is", $link, $matches)) { 366 $link = $this->link($matches[1], $matches[2]); 367 } else if ($link[0] == '#') { 368 $link = array('url' => "#" . urlencode(substr($link, 1))); 369 } else { 370 $link = $this->link($link); 371 } 372 373 $link['content'] = $content; 374 } else { 375 $link = $this->link($text); 376 } 377 378 if (isset($link['new']) && $link['new']) { 379 $options = array('class' => 'wiki_newentry'); 380 } else { 381 $options = array(); 382 } 383 384 $link['content'] = $this->protect($link['content']); 385 $link['url'] = $this->protect($link['url']); 386 387 $options['href'] = $link['url']; 388 389 if ($this->printable) { 390 $options['href'] = '#'; //no target for the link 391 } 392 return array($link['content'], $options); 393 } 394 395 /** 396 * Section editing 397 */ 398 399 public function get_section($header, $text, $clean = false) { 400 if ($clean) { 401 $text = preg_replace('/\r\n/', "\n", $text); 402 $text = preg_replace('/\r/', "\n", $text); 403 $text .= "\n\n"; 404 } 405 406 $regex = "/(.*?)(=\ *".preg_quote($header, '/')."\ *=*\n.*?)((?:\n=[^=]+.*)|$)/is"; 407 preg_match($regex, $text, $match); 408 409 if (!empty($match)) { 410 return array($match[1], $match[2], $match[3]); 411 } else { 412 return false; 413 } 414 } 415 416 protected function get_repeated_sections(&$text, $repeated = array()) { 417 $this->repeated_sections = $repeated; 418 return preg_replace_callback($this->blockrules['header']['expression'], array($this, 'get_repeated_sections_callback'), $text); 419 } 420 421 protected function get_repeated_sections_callback($match) { 422 $num = strlen($match[1]); 423 $text = trim($match[2]); 424 if ($num == 1) { 425 if (in_array($text, $this->repeated_sections)) { 426 $this->returnvalues['repeated_sections'][] = $text; 427 return $text . "\n"; 428 } else { 429 $this->repeated_sections[] = $text; 430 } 431 } 432 433 return $match[0]; 434 } 435 436 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |