[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/mod/wiki/parser/markups/ -> wikimarkup.php (source)

   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}&section=" . 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  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1