3 include_once('Tokenizer.php');
7 # Converts wikitext to HTML.
10 # objects: $wgLang, $wgDateFormatter, $wgLinkCache, $wgCurOut
12 # NOT $wgArticle, $wgUser or $wgTitle. Keep them away!
14 # settings: $wgUseTex*, $wgUseCategoryMagic*, $wgUseDynamicDates*, $wgInterwikiMagic*,
15 # $wgNamespacesWithSubpages, $wgLanguageCode, $wgAllowExternalImages*,
18 # * only within ParserOptions
22 # Cleared with clearState():
23 var $mOutput, $mAutonumber, $mLastSection, $mDTopen, $mStripState;
26 var $mOptions, $mTitle;
35 $this->mOutput
= new ParserOutput
;
36 $this->mAutonumber
= 0;
37 $this->mLastSection
= "";
38 $this->mDTopen
= false;
39 $this->mStripState
= false;
42 # First pass--just handle <nowiki> sections, pass the rest off
43 # to doWikiPass2() which does all the real work.
45 # Returns a ParserOutput
47 function parse( $text, &$title, $options, $linestart = true, $clearState = true )
49 $fname = "Parser::parse";
50 wfProfileIn( $fname );
56 $this->mOptions
= $options;
57 $this->mTitle
=& $title;
60 $text = $this->strip( $text, $this->mStripState
, true );
61 $text = $this->doWikiPass2( $text, $linestart );
62 $text = $this->unstrip( $text, $this->mStripState
);
64 $this->mOutput
->setText( $text );
65 wfProfileOut( $fname );
66 return $this->mOutput
;
69 /* static */ function getRandomString()
71 return dechex(mt_rand(0, 0x7fffffff)) . dechex(mt_rand(0, 0x7fffffff));
74 # Strips <nowiki>, <pre> and <math>
75 # Returns the text, and fills an array with data needed in unstrip()
77 function strip( $text, &$state, $render = true )
82 'nwunq' => Parser
::getRandomString(),
83 'mathlist' => array(),
85 'mathunq' => Parser
::getRandomString(),
88 'preunq' => Parser
::getRandomString()
95 # Replace any instances of the placeholders
96 $text = str_replace( $state['nwunq'], wfHtmlEscapeFirst( $state['nwunq'] ), $text );
97 $text = str_replace( $state['mathunq'], wfHtmlEscapeFirst( $state['mathunq'] ), $text );
98 $text = str_replace( $state['preunq'], wfHtmlEscapeFirst( $state['preunq'] ), $text );
100 while ( "" != $text ) {
101 $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
103 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) {
106 $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
110 $state['nwlist'][$state['nwsecs']] = wfEscapeHTMLTagsOnly($q[0]);
112 $state['nwlist'][$state['nwsecs']] = "<nowiki>{$q[0]}</nowiki>";
115 $stripped .= $state['nwunq'] . sprintf("%08X", $state['nwsecs']);
120 if( $this->mOptions
->getUseTeX() ) {
121 while ( "" != $stripped ) {
122 $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 );
124 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) {
127 $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 );
128 ++
$state['mathsecs'];
131 $state['mathlist'][$state['mathsecs']] = renderMath($q[0]);
133 $state['mathlist'][$state['mathsecs']] = "<math>{$q[0]}</math>";
136 $stripped2 .= $state['mathunq'] . sprintf("%08X", $state['mathsecs']);
141 $stripped2 = $stripped;
144 while ( "" != $stripped2 ) {
145 $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 );
147 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) {
150 $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 );
154 $state['prelist'][$state['presecs']] = "<pre>". wfEscapeHTMLTagsOnly($q[0]). "</pre>\n";
156 $state['prelist'][$state['presecs']] = "<pre>{$q[0]}</pre>";
159 $stripped3 .= $state['preunq'] . sprintf("%08X", $state['presecs']);
166 function unstrip( $text, &$state )
168 for ( $i = 1; $i <= $state['presecs']; ++
$i ) {
169 $text = str_replace( $state['preunq'] . sprintf("%08X", $i), $state['prelist'][$i], $text );
172 for ( $i = 1; $i <= $state['mathsecs']; ++
$i ) {
173 $text = str_replace( $state['mathunq'] . sprintf("%08X", $i), $state['mathlist'][$i], $text );
176 for ( $i = 1; $i <= $state['nwsecs']; ++
$i ) {
177 $text = str_replace( $state['nwunq'] . sprintf("%08X", $i), $state['nwlist'][$i], $text );
182 function categoryMagic ()
184 global $wgLang , $wgUser ;
185 if ( !$this->mOptions
->getUseCategoryMagic() ) return ;
186 $id = $this->mTitle
->getArticleID() ;
187 $cat = ucfirst ( wfMsg ( "category" ) ) ;
188 $ti = $this->mTitle
->getText() ;
189 $ti = explode ( ":" , $ti , 2 ) ;
190 if ( $cat != $ti[0] ) return "" ;
191 $r = "<br break=all>\n" ;
193 $articles = array() ;
194 $parents = array () ;
195 $children = array() ;
198 # $sk =& $this->mGetSkin();
199 $sk =& $wgUser->getSkin() ;
203 $sql = "SELECT cur_title,cur_namespace FROM cur,links WHERE l_to={$id} AND l_from=cur_id";
205 $sql = "SELECT cur_title,cur_namespace FROM cur,brokenlinks WHERE bl_to={$id} AND bl_from=cur_id" ;
208 $res = wfQuery ( $sql, DB_READ
) ;
209 while ( $x = wfFetchObject ( $res ) )
212 # $t->newFromDBkey ( $x->l_from ) ;
213 # $t = $t->getText() ;
214 $t = $wgLang->getNsText ( $x->cur_namespace
) ;
215 if ( $t != "" ) $t .= ":" ;
216 $t .= $x->cur_title
;
218 $y = explode ( ":" , $t , 2 ) ;
219 if ( count ( $y ) == 2 && $y[0] == $cat ) {
220 array_push ( $children , $sk->makeLink ( $t , $y[1] ) ) ;
222 array_push ( $articles , $sk->makeLink ( $t ) ) ;
225 wfFreeResult ( $res ) ;
228 if ( count ( $children ) > 0 )
230 asort ( $children ) ;
231 $r .= "<h2>".wfMsg("subcategories")."</h2>\n" ;
232 $r .= implode ( ", " , $children ) ;
236 if ( count ( $articles ) > 0 )
238 asort ( $articles ) ;
239 $h = wfMsg( "category_header", $ti[1] );
240 $r .= "<h2>{$h}</h2>\n" ;
241 $r .= implode ( ", " , $articles ) ;
248 function getHTMLattrs ()
250 $htmlattrs = array( # Allowed attributes--no scripting, etc.
251 "title", "align", "lang", "dir", "width", "height",
252 "bgcolor", "clear", /* BR */ "noshade", /* HR */
253 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
254 /* FONT */ "type", "start", "value", "compact",
255 /* For various lists, mostly deprecated but safe */
256 "summary", "width", "border", "frame", "rules",
257 "cellspacing", "cellpadding", "valign", "char",
258 "charoff", "colgroup", "col", "span", "abbr", "axis",
259 "headers", "scope", "rowspan", "colspan", /* Tables */
260 "id", "class", "name", "style" /* For CSS */
265 function fixTagAttributes ( $t )
267 if ( trim ( $t ) == "" ) return "" ; # Saves runtime ;-)
268 $htmlattrs = $this->getHTMLattrs() ;
270 # Strip non-approved attributes from the tag
272 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
273 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
275 # Strip javascript "expression" from stylesheets. Brute force approach:
276 # If anythin offensive is found, all attributes of the HTML tag are dropped
279 "/style\\s*=.*(expression|tps*:\/\/|url\\s*\().*/is",
280 wfMungeToUtf8( $t ) ) )
288 function doTableStuff ( $t )
290 $t = explode ( "\n" , $t ) ;
291 $td = array () ; # Is currently a td tag open?
292 $ltd = array () ; # Was it TD or TH?
293 $tr = array () ; # Is currently a tr tag open?
294 $ltr = array () ; # tr attributes
295 foreach ( $t AS $k => $x )
298 $fc = substr ( $x , 0 , 1 ) ;
299 if ( "{|" == substr ( $x , 0 , 2 ) )
301 $t[$k] = "<table " . $this->fixTagAttributes ( substr ( $x , 3 ) ) . ">" ;
302 array_push ( $td , false ) ;
303 array_push ( $ltd , "" ) ;
304 array_push ( $tr , false ) ;
305 array_push ( $ltr , "" ) ;
307 else if ( count ( $td ) == 0 ) { } # Don't do any of the following
308 else if ( "|}" == substr ( $x , 0 , 2 ) )
311 $l = array_pop ( $ltd ) ;
312 if ( array_pop ( $tr ) ) $z = "</tr>" . $z ;
313 if ( array_pop ( $td ) ) $z = "</{$l}>" . $z ;
317 /* else if ( "|_" == substr ( $x , 0 , 2 ) ) # Caption
319 $z = trim ( substr ( $x , 2 ) ) ;
320 $t[$k] = "<caption>{$z}</caption>\n" ;
322 else if ( "|-" == substr ( $x , 0 , 2 ) ) # Allows for |---------------
324 $x = substr ( $x , 1 ) ;
325 while ( $x != "" && substr ( $x , 0 , 1 ) == '-' ) $x = substr ( $x , 1 ) ;
327 $l = array_pop ( $ltd ) ;
328 if ( array_pop ( $tr ) ) $z = "</tr>" . $z ;
329 if ( array_pop ( $td ) ) $z = "</{$l}>" . $z ;
332 array_push ( $tr , false ) ;
333 array_push ( $td , false ) ;
334 array_push ( $ltd , "" ) ;
335 array_push ( $ltr , $this->fixTagAttributes ( $x ) ) ;
337 else if ( "|" == $fc ||
"!" == $fc ||
"|+" == substr ( $x , 0 , 2 ) ) # Caption
339 if ( "|+" == substr ( $x , 0 , 2 ) )
342 $x = substr ( $x , 1 ) ;
344 $after = substr ( $x , 1 ) ;
345 if ( $fc == "!" ) $after = str_replace ( "!!" , "||" , $after ) ;
346 $after = explode ( "||" , $after ) ;
348 foreach ( $after AS $theline )
353 $tra = array_pop ( $ltr ) ;
354 if ( !array_pop ( $tr ) ) $z = "<tr {$tra}>\n" ;
355 array_push ( $tr , true ) ;
356 array_push ( $ltr , "" ) ;
359 $l = array_pop ( $ltd ) ;
360 if ( array_pop ( $td ) ) $z = "</{$l}>" . $z ;
361 if ( $fc == "|" ) $l = "TD" ;
362 else if ( $fc == "!" ) $l = "TH" ;
363 else if ( $fc == "+" ) $l = "CAPTION" ;
365 array_push ( $ltd , $l ) ;
366 $y = explode ( "|" , $theline , 2 ) ;
367 if ( count ( $y ) == 1 ) $y = "{$z}<{$l}>{$y[0]}" ;
368 else $y = $y = "{$z}<{$l} ".$this->fixTagAttributes($y[0]).">{$y[1]}" ;
370 array_push ( $td , true ) ;
375 # Closing open td, tr && table
376 while ( count ( $td ) > 0 )
378 if ( array_pop ( $td ) ) $t[] = "</td>" ;
379 if ( array_pop ( $tr ) ) $t[] = "</tr>" ;
383 $t = implode ( "\n" , $t ) ;
384 # $t = $this->removeHTMLtags( $t );
388 # Well, OK, it's actually about 14 passes. But since all the
389 # hard lifting is done inside PHP's regex code, it probably
390 # wouldn't speed things up much to add a real parser.
392 function doWikiPass2( $text, $linestart )
394 $fname = "OutputPage::doWikiPass2";
395 wfProfileIn( $fname );
397 $text = $this->removeHTMLtags( $text );
398 $text = $this->replaceVariables( $text );
400 # $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
401 $text = str_replace ( "<HR>", "<hr>", $text );
403 $text = $this->doHeadings( $text );
404 $text = $this->doBlockLevels( $text, $linestart );
406 if($this->mOptions
->getUseDynamicDates()) {
407 global $wgDateFormatter;
408 $text = $wgDateFormatter->reformat( $this->mOptions
->getDateFormat(), $text );
411 $text = $this->replaceExternalLinks( $text );
412 $text = $this->replaceInternalLinks ( $text );
413 $text = $this->doTableStuff ( $text ) ;
415 $text = $this->formatHeadings( $text );
417 $sk =& $this->mOptions
->getSkin();
418 $text = $sk->transformContent( $text );
419 $text .= $this->categoryMagic () ;
421 wfProfileOut( $fname );
426 /* private */ function doHeadings( $text )
428 for ( $i = 6; $i >= 1; --$i ) {
429 $h = substr( "======", 0, $i );
430 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
431 "<h{$i}>\\1</h{$i}>\\2", $text );
436 # Note: we have to do external links before the internal ones,
437 # and otherwise take great care in the order of things here, so
438 # that we don't end up interpreting some URLs twice.
440 /* private */ function replaceExternalLinks( $text )
442 $fname = "OutputPage::replaceExternalLinks";
443 wfProfileIn( $fname );
444 $text = $this->subReplaceExternalLinks( $text, "http", true );
445 $text = $this->subReplaceExternalLinks( $text, "https", true );
446 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
447 $text = $this->subReplaceExternalLinks( $text, "irc", false );
448 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
449 $text = $this->subReplaceExternalLinks( $text, "news", false );
450 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
451 wfProfileOut( $fname );
455 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
457 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
458 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
460 # this is the list of separators that should be ignored if they
461 # are the last character of an URL but that should be included
462 # if they occur within the URL, e.g. "go to www.foo.com, where .."
463 # in this case, the last comma should not become part of the URL,
464 # but in "www.foo.com/123,2342,32.htm" it should.
466 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
467 $images = "gif|png|jpg|jpeg";
469 # PLEASE NOTE: The curly braces { } are not part of the regex,
470 # they are interpreted as part of the string (used to tell PHP
471 # that the content of the string should be inserted there).
472 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
473 "((?i){$images})([^{$uc}]|$)/";
475 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
476 $sk =& $this->mOptions
->getSkin();
478 if ( $autonumber and $this->mOptions
->getAllowExternalImages() ) { # Use img tags only for HTTP urls
479 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
480 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
482 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
483 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
484 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
486 $s = str_replace( $unique, $protocol, $s );
488 $a = explode( "[{$protocol}:", " " . $s );
489 $s = array_shift( $a );
490 $s = substr( $s, 1 );
492 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
493 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
495 foreach ( $a as $line ) {
496 if ( preg_match( $e1, $line, $m ) ) {
497 $link = "{$protocol}:{$m[1]}";
499 if ( $autonumber ) { $text = "[" . ++
$this->mAutonumber
. "]"; }
500 else { $text = wfEscapeHTML( $link ); }
501 } else if ( preg_match( $e2, $line, $m ) ) {
502 $link = "{$protocol}:{$m[1]}";
506 $s .= "[{$protocol}:" . $line;
509 if ( $this->mOptions
->getPrintable() ) $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
511 $la = $sk->getExternalLinkAttributes( $link, $text );
512 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
518 /* private */ function handle3Quotes( &$state, $token )
520 if ( $state["strong"] ) {
521 if ( $state["em"] && $state["em"] > $state["strong"] )
523 # ''' lala ''lala '''
524 $s = "</em></strong><em>";
528 $state["strong"] = FALSE;
531 $state["strong"] = $token["pos"];
536 /* private */ function handle2Quotes( &$state, $token )
538 if ( $state["em"] ) {
539 if ( $state["strong"] && $state["strong"] > $state["em"] )
541 # ''lala'''lala'' ....'''
542 $s = "</strong></em><strong>";
546 $state["em"] = FALSE;
549 $state["em"] = $token["pos"];
554 /* private */ function handle5Quotes( &$state, $token )
556 if ( $state["em"] && $state["strong"] ) {
557 if ( $state["em"] < $state["strong"] ) {
558 $s .= "</strong></em>";
560 $s .= "</em></strong>";
562 $state["strong"] = $state["em"] = FALSE;
563 } elseif ( $state["em"] ) {
564 $s .= "</em><strong>";
565 $state["em"] = FALSE;
566 $state["strong"] = $token["pos"];
567 } elseif ( $state["strong"] ) {
568 $s .= "</strong><em>";
569 $state["strong"] = FALSE;
570 $state["em"] = $token["pos"];
571 } else { # not $em and not $strong
572 $s .= "<strong><em>";
573 $state["strong"] = $state["em"] = $token["pos"];
578 /* private */ function replaceInternalLinks( $str )
580 global $wgLang; # for language specific parser hook
582 $tokenizer=Tokenizer
::newFromString( $str );
583 $tokenStack = array();
586 $state["em"] = FALSE;
587 $state["strong"] = FALSE;
590 # The tokenizer splits the text into tokens and returns them one by one.
591 # Every call to the tokenizer returns a new token.
592 while ( $token = $tokenizer->nextToken() )
594 switch ( $token["type"] )
597 # simple text with no further markup
598 $txt = $token["text"];
602 # FIXME : Treat orphaned open tags (stack not empty when text is over)
604 array_push( $tokenStack, $token );
609 # get text from stack, glue it together, and call the code to handle a
611 if ( count( $tokenStack ) == 0 )
613 # stack empty. Found a ]] without an opening [[
617 $lastToken = array_pop( $tokenStack );
618 while ( $lastToken["type"] != "[[" )
620 if( !empty( $lastToken["text"] ) ) {
621 $linkText = $lastToken["text"] . $linkText;
623 $lastToken = array_pop( $tokenStack );
625 $txt = $linkText ."]]";
626 if( isset( $lastToken["text"] ) ) {
627 $prefix = $lastToken["text"];
631 $nextToken = $tokenizer->previewToken();
632 if ( $nextToken["type"] == "text" )
634 # Preview just looks at it. Now we have to fetch it.
635 $nextToken = $tokenizer->nextToken();
636 $txt .= $nextToken["text"];
638 $txt = $this->handleInternalLink( $txt, $prefix );
640 $tagIsOpen = (count( $tokenStack ) != 0);
646 # This and the three next ones handle quotes
647 $txt = $this->handle3Quotes( $state, $token );
650 $txt = $this->handle2Quotes( $state, $token );
653 $txt = $this->handle5Quotes( $state, $token );
663 $txt = $this->doMagicRFC( $tokenizer );
670 $txt = $this->doMagicISBN( $tokenizer );
674 # Call language specific Hook.
675 $txt = $wgLang->processToken( $token, $tokenStack );
676 if ( NULL == $txt ) {
677 # An unkown token. Highlight.
678 $txt = "<font color=\"#FF0000\"><b>".$token["type"]."</b></font>";
679 $txt .= "<font color=\"#FFFF00\"><b>".$token["text"]."</b></font>";
683 # If we're parsing the interior of a link, don't append the interior to $s,
684 # but push it to the stack so it can be processed when a ]] token is found.
685 if ( $tagIsOpen && $txt != "" ) {
686 $token["type"] = "text";
687 $token["text"] = $txt;
688 array_push( $tokenStack, $token );
693 if ( count( $tokenStack ) != 0 )
695 # still objects on stack. opened [[ tag without closing ]] tag.
697 while ( $lastToken = array_pop( $tokenStack ) )
699 if ( $lastToken["type"] == "text" )
701 $txt = $lastToken["text"] . $txt;
703 $txt = $lastToken["type"] . $txt;
711 /* private */ function handleInternalLink( $line, $prefix )
713 global $wgLang, $wgLinkCache;
714 global $wgNamespacesWithSubpages, $wgLanguageCode;
715 static $fname = "OutputPage::replaceInternalLinks" ;
716 wfProfileIn( $fname );
718 wfProfileIn( "$fname-setup" );
720 if ( !$tc ) { $tc = Title
::legalChars() . "#"; }
721 $sk =& $this->mOptions
->getSkin();
723 # Match a link having the form [[namespace:link|alternate]]trail
725 if ( !$e1 ) { $e1 = "/^([{$tc}]+)(?:\\|([^]]+))?]](.*)\$/sD"; }
726 # Match the end of a line for a word that's not followed by whitespace,
727 # e.g. in the case of 'The Arab al[[Razi]]', 'al' will be matched
728 #$e2 = "/^(.*)\\b(\\w+)\$/suD";
729 #$e2 = "/^(.*\\s)(\\S+)\$/suD";
730 static $e2 = '/^(.*\s)([a-zA-Z\x80-\xff]+)$/sD';
733 # Special and Media are pseudo-namespaces; no pages actually exist in them
734 static $image = FALSE;
735 static $special = FALSE;
736 static $media = FALSE;
737 static $category = FALSE;
738 if ( !$image ) { $image = Namespace::getImage(); }
739 if ( !$special ) { $special = Namespace::getSpecial(); }
740 if ( !$media ) { $media = Namespace::getMedia(); }
741 if ( !$category ) { $category = wfMsg ( "category" ) ; }
743 $nottalk = !Namespace::isTalk( $this->mTitle
->getNamespace() );
745 wfProfileOut( "$fname-setup" );
748 if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt
751 } else { # Invalid form; output directly
752 $s .= $prefix . "[[" . $line ;
758 :Foobar -- override special treatment of prefix (images, language links)
759 /Foobar -- convert to CurrentPage/Foobar
760 /Foobar/ -- convert to CurrentPage/Foobar, strip the initial / from text
762 $c = substr($m[1],0,1);
763 $noforce = ($c != ":");
764 if( $c == "/" ) { # subpage
765 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
766 $m[1]=substr($m[1],1,strlen($m[1])-2);
769 $noslash=substr($m[1],1);
771 if($wgNamespacesWithSubpages[$this->mTitle
->getNamespace()]) { # subpages allowed here
772 $link = $this->mTitle
->getPrefixedText(). "/" . trim($noslash);
775 } # this might be changed for ugliness reasons
777 $link = $noslash; # no subpage allowed, use standard link
779 } elseif( $noforce ) { # no subpage
782 $link = substr( $m[1], 1 );
787 $nt = Title
::newFromText( $link );
789 $s .= $prefix . "[[" . $line;
792 $ns = $nt->getNamespace();
793 $iw = $nt->getInterWiki();
795 if( $iw && $this->mOptions
->getInterwikiMagic() && $nottalk && $wgLang->getLanguageName( $iw ) ) {
796 array_push( $this->mOutput
->mLanguageLinks
, $nt->getPrefixedText() );
797 $s .= $prefix . $trail;
800 if( $ns == $image ) {
801 $s .= $prefix . $sk->makeImageLinkObj( $nt, $text ) . $trail;
802 $wgLinkCache->addImageLinkObj( $nt );
806 if( ( $nt->getPrefixedText() == $this->mTitle
->getPrefixedText() ) &&
807 ( strpos( $link, "#" ) == FALSE ) ) {
808 $s .= $prefix . "<strong>" . $text . "</strong>" . $trail;
811 if ( $ns == $category && $this->mOptions
->getUseCategoryMagic() ) {
812 $t = explode ( ":" , $nt->getText() ) ;
814 $t = implode ( ":" , $t ) ;
815 $t = $wgLang->ucFirst ( $t ) ;
816 # $t = $sk->makeKnownLink( $category.":".$t, $t, "", $trail , $prefix );
817 $nnt = Title
::newFromText ( $category.":".$t ) ;
818 $t = $sk->makeLinkObj( $nnt, $t, "", $trail , $prefix );
819 $this->mCategoryLinks
[] = $t ;
820 $s .= $prefix . $trail ;
823 if( $ns == $media ) {
824 $s .= $prefix . $sk->makeMediaLinkObj( $nt, $text ) . $trail;
825 $wgLinkCache->addImageLinkObj( $nt );
827 } elseif( $ns == $special ) {
828 $s .= $prefix . $sk->makeKnownLinkObj( $nt, $text, "", $trail );
831 $s .= $sk->makeLinkObj( $nt, $text, "", $trail , $prefix );
833 wfProfileOut( $fname );
837 # Some functions here used by doBlockLevels()
839 /* private */ function closeParagraph()
842 if ( 0 != strcmp( "p", $this->mLastSection
) &&
843 0 != strcmp( "", $this->mLastSection
) ) {
844 $result = "</" . $this->mLastSection
. ">";
846 $this->mLastSection
= "";
849 # getCommon() returns the length of the longest common substring
850 # of both arguments, starting at the beginning of both.
852 /* private */ function getCommon( $st1, $st2 )
854 $fl = strlen( $st1 );
855 $shorter = strlen( $st2 );
856 if ( $fl < $shorter ) { $shorter = $fl; }
858 for ( $i = 0; $i < $shorter; ++
$i ) {
859 if ( $st1{$i} != $st2{$i} ) { break; }
863 # These next three functions open, continue, and close the list
864 # element appropriate to the prefix character passed into them.
866 /* private */ function openList( $char )
868 $result = $this->closeParagraph();
870 if ( "*" == $char ) { $result .= "<ul><li>"; }
871 else if ( "#" == $char ) { $result .= "<ol><li>"; }
872 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
873 else if ( ";" == $char ) {
874 $result .= "<dl><dt>";
875 $this->mDTopen
= true;
877 else { $result = "<!-- ERR 1 -->"; }
882 /* private */ function nextItem( $char )
884 if ( "*" == $char ||
"#" == $char ) { return "</li><li>"; }
885 else if ( ":" == $char ||
";" == $char ) {
887 if ( $this->mDTopen
) { $close = "</dt>"; }
888 if ( ";" == $char ) {
889 $this->mDTopen
= true;
890 return $close . "<dt>";
892 $this->mDTopen
= false;
893 return $close . "<dd>";
896 return "<!-- ERR 2 -->";
899 /* private */function closeList( $char )
901 if ( "*" == $char ) { $text = "</li></ul>"; }
902 else if ( "#" == $char ) { $text = "</li></ol>"; }
903 else if ( ":" == $char ) {
904 if ( $this->mDTopen
) {
905 $this->mDTopen
= false;
906 $text = "</dt></dl>";
908 $text = "</dd></dl>";
911 else { return "<!-- ERR 3 -->"; }
915 /* private */ function doBlockLevels( $text, $linestart )
917 $fname = "OutputPage::doBlockLevels";
918 wfProfileIn( $fname );
919 # Parsing through the text line by line. The main thing
920 # happening here is handling of block-level elements p, pre,
921 # and making lists from lines starting with * # : etc.
923 $a = explode( "\n", $text );
924 $text = $lastPref = "";
925 $this->mDTopen
= $inBlockElem = false;
927 if ( ! $linestart ) { $text .= array_shift( $a ); }
928 foreach ( $a as $t ) {
929 if ( "" != $text ) { $text .= "\n"; }
932 $opl = strlen( $lastPref );
933 $npl = strspn( $t, "*#:;" );
934 $pref = substr( $t, 0, $npl );
935 $pref2 = str_replace( ";", ":", $pref );
936 $t = substr( $t, $npl );
938 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
939 $text .= $this->nextItem( substr( $pref, -1 ) );
941 if ( ";" == substr( $pref, -1 ) ) {
942 $cpos = strpos( $t, ":" );
943 if ( ! ( false === $cpos ) ) {
944 $term = substr( $t, 0, $cpos );
945 $text .= $term . $this->nextItem( ":" );
946 $t = substr( $t, $cpos +
1 );
949 } else if (0 != $npl ||
0 != $opl) {
950 $cpl = $this->getCommon( $pref, $lastPref );
952 while ( $cpl < $opl ) {
953 $text .= $this->closeList( $lastPref{$opl-1} );
956 if ( $npl <= $cpl && $cpl > 0 ) {
957 $text .= $this->nextItem( $pref{$cpl-1} );
959 while ( $npl > $cpl ) {
960 $char = substr( $pref, $cpl, 1 );
961 $text .= $this->openList( $char );
963 if ( ";" == $char ) {
964 $cpos = strpos( $t, ":" );
965 if ( ! ( false === $cpos ) ) {
966 $term = substr( $t, 0, $cpos );
967 $text .= $term . $this->nextItem( ":" );
968 $t = substr( $t, $cpos +
1 );
975 if ( 0 == $npl ) { # No prefix--go to paragraph mode
977 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
978 $text .= $this->closeParagraph();
981 if ( ! $inBlockElem ) {
982 if ( " " == $t{0} ) {
984 # $t = wfEscapeHTML( $t );
986 else { $newSection = "p"; }
988 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
989 $text .= $this->closeParagraph();
990 $text .= "<" . $newSection . ">";
991 } else if ( 0 != strcmp( $this->mLastSection
,
993 $text .= $this->closeParagraph();
994 if ( 0 != strcmp( "p", $newSection ) ) {
995 $text .= "<" . $newSection . ">";
998 $this->mLastSection
= $newSection;
1000 if ( $inBlockElem &&
1001 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1002 $inBlockElem = false;
1008 $text .= $this->closeList( $pref2{$npl-1} );
1011 if ( "" != $this->mLastSection
) {
1012 if ( "p" != $this->mLastSection
) {
1013 $text .= "</" . $this->mLastSection
. ">";
1015 $this->mLastSection
= "";
1017 wfProfileOut( $fname );
1021 /* private */ function replaceVariables( $text )
1023 global $wgLang, $wgCurOut;
1024 $fname = "OutputPage::replaceVariables";
1025 wfProfileIn( $fname );
1030 # See Language.php for the definition of each magic word
1031 # As with sigs, this uses the server's local time -- ensure
1032 # this is appropriate for your audience!
1034 $magic[MAG_CURRENTMONTH
] = date( "m" );
1035 $magic[MAG_CURRENTMONTHNAME
] = $wgLang->getMonthName( date("n") );
1036 $magic[MAG_CURRENTMONTHNAMEGEN
] = $wgLang->getMonthNameGen( date("n") );
1037 $magic[MAG_CURRENTDAY
] = date("j");
1038 $magic[MAG_CURRENTDAYNAME
] = $wgLang->getWeekdayName( date("w")+
1 );
1039 $magic[MAG_CURRENTYEAR
] = date( "Y" );
1040 $magic[MAG_CURRENTTIME
] = $wgLang->time( wfTimestampNow(), false );
1042 $this->mOutput
->mContainsOldMagic +
= MagicWord
::replaceMultiple($magic, $text, $text);
1044 $mw =& MagicWord
::get( MAG_NUMBEROFARTICLES
);
1045 if ( $mw->match( $text ) ) {
1046 $v = wfNumberOfArticles();
1047 $text = $mw->replace( $v, $text );
1048 if( $mw->getWasModified() ) { $this->mOutput
->mContainsOldMagic++
; }
1051 # "Variables" with an additional parameter e.g. {{MSG:wikipedia}}
1052 # The callbacks are at the bottom of this file
1054 $mw =& MagicWord
::get( MAG_MSG
);
1055 $text = $mw->substituteCallback( $text, "wfReplaceMsgVar" );
1056 if( $mw->getWasModified() ) { $this->mContainsNewMagic++
; }
1058 $mw =& MagicWord
::get( MAG_MSGNW
);
1059 $text = $mw->substituteCallback( $text, "wfReplaceMsgnwVar" );
1060 if( $mw->getWasModified() ) { $this->mContainsNewMagic++
; }
1062 wfProfileOut( $fname );
1066 # Cleans up HTML, removes dangerous tags and attributes
1067 /* private */ function removeHTMLtags( $text )
1069 $fname = "OutputPage::removeHTMLtags";
1070 wfProfileIn( $fname );
1071 $htmlpairs = array( # Tags that must be closed
1072 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1073 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1074 "strike", "strong", "tt", "var", "div", "center",
1075 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1076 "ruby", "rt" , "rb" , "rp"
1078 $htmlsingle = array(
1079 "br", "p", "hr", "li", "dt", "dd"
1081 $htmlnest = array( # Tags that can be nested--??
1082 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1083 "dl", "font", "big", "small", "sub", "sup"
1085 $tabletags = array( # Can only appear inside table
1089 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1090 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1092 $htmlattrs = $this->getHTMLattrs () ;
1094 # Remove HTML comments
1095 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1097 $bits = explode( "<", $text );
1098 $text = array_shift( $bits );
1099 $tagstack = array(); $tablestack = array();
1101 foreach ( $bits as $x ) {
1102 $prev = error_reporting( E_ALL
& ~
( E_NOTICE | E_WARNING
) );
1103 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1105 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1106 error_reporting( $prev );
1109 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1113 if ( ! in_array( $t, $htmlsingle ) &&
1114 ( $ot = array_pop( $tagstack ) ) != $t ) {
1115 array_push( $tagstack, $ot );
1118 if ( $t == "table" ) {
1119 $tagstack = array_pop( $tablestack );
1124 # Keep track for later
1125 if ( in_array( $t, $tabletags ) &&
1126 ! in_array( "table", $tagstack ) ) {
1128 } else if ( in_array( $t, $tagstack ) &&
1129 ! in_array ( $t , $htmlnest ) ) {
1131 } else if ( ! in_array( $t, $htmlsingle ) ) {
1132 if ( $t == "table" ) {
1133 array_push( $tablestack, $tagstack );
1134 $tagstack = array();
1136 array_push( $tagstack, $t );
1138 # Strip non-approved attributes from the tag
1139 $newparams = $this->fixTagAttributes($params);
1143 $rest = str_replace( ">", ">", $rest );
1144 $text .= "<$slash$t $newparams$brace$rest";
1148 $text .= "<" . str_replace( ">", ">", $x);
1150 # Close off any remaining tags
1151 while ( $t = array_pop( $tagstack ) ) {
1153 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1155 wfProfileOut( $fname );
1161 * This function accomplishes several tasks:
1162 * 1) Auto-number headings if that option is enabled
1163 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1164 * 3) Add a Table of contents on the top for users who have enabled the option
1165 * 4) Auto-anchor headings
1167 * It loops through all headlines, collects the necessary data, then splits up the
1168 * string and re-inserts the newly formatted headlines.
1171 /* private */ function formatHeadings( $text )
1173 $nh=$this->mOptions
->getNumberHeadings();
1174 $st=$this->mOptions
->getShowToc();
1175 if(!$this->mTitle
->userCanEdit()) {
1179 $es=$this->mOptions
->getEditSection();
1180 $esr=$this->mOptions
->getEditSectionOnRightClick();
1183 # Inhibit editsection links if requested in the page
1184 $esw =& MagicWord
::get( MAG_NOEDITSECTION
);
1185 if ($esw->matchAndRemove( $text )) {
1188 # if the string __NOTOC__ (not case-sensitive) occurs in the HTML,
1190 $mw =& MagicWord
::get( MAG_NOTOC
);
1191 if ($mw->matchAndRemove( $text ))
1196 # never add the TOC to the Main Page. This is an entry page that should not
1197 # be more than 1-2 screens large anyway
1198 if($this->mTitle
->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1200 # We need this to perform operations on the HTML
1201 $sk =& $this->mOptions
->getSkin();
1203 # Get all headlines for numbering them and adding funky stuff like [edit]
1205 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1210 # Ugh .. the TOC should have neat indentation levels which can be
1211 # passed to the skin functions. These are determined here
1216 foreach($matches[3] as $headline) {
1217 if($level) { $prevlevel=$level;}
1218 $level=$matches[1][$c];
1219 if(($nh||
$st) && $prevlevel && $level>$prevlevel) {
1221 $h[$level]=0; // reset when we enter a new level
1222 $toc.=$sk->tocIndent($level-$prevlevel);
1223 $toclevel+
=$level-$prevlevel;
1226 if(($nh||
$st) && $level<$prevlevel) {
1227 $h[$level+
1]=0; // reset when we step back a level
1228 $toc.=$sk->tocUnindent($prevlevel-$level);
1229 $toclevel-=$prevlevel-$level;
1232 $h[$level]++
; // count number of headlines for each level
1235 for($i=1;$i<=$level;$i++
) {
1237 if($dot) {$numbering.=".";}
1244 // The canonized header is a version of the header text safe to use for links
1245 // Avoid insertion of weird stuff like <math> by expanding the relevant sections
1246 $canonized_headline=Parser
::unstrip( $headline, $this->mStripState
);
1247 $canonized_headline=preg_replace("/<.*?>/","",$canonized_headline); // strip out HTML
1248 $tocline = trim( $canonized_headline );
1249 $canonized_headline=str_replace('"',"",$canonized_headline);
1250 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1251 $refer[$c]=$canonized_headline;
1252 $refers[$canonized_headline]++
; // count how many in assoc. array so we can track dupes in anchors
1253 $refcount[$c]=$refers[$canonized_headline];
1255 // Prepend the number to the heading text
1258 $tocline=$numbering ." ". $tocline;
1260 // Don't number the heading if it is the only one (looks silly)
1261 if($nh && count($matches[3]) > 1) {
1262 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1266 // Create the anchor for linking from the TOC to the section
1267 $anchor=$canonized_headline;
1268 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1270 $toc.=$sk->tocLine($anchor,$tocline,$toclevel);
1273 $head[$c].=$sk->editSectionLink($c+
1);
1276 // Put it all together
1278 $head[$c].="<h".$level.$matches[2][$c]
1279 ."<a name=\"".$anchor."\">"
1284 // Add the edit section link
1287 $head[$c]=$sk->editSectionScript($c+
1,$head[$c]);
1297 $toc.=$sk->tocUnindent($toclevel);
1298 $toc=$sk->tocTable($toc);
1301 // split up and insert constructed headlines
1303 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1306 foreach($blocks as $block) {
1307 if(($es) && $c>0 && $i==0) {
1308 # This is the [edit] link that appears for the top block of text when
1309 # section editing is enabled
1310 $full.=$sk->editSectionLink(0);
1313 if($st && $toclines>3 && !$i) {
1314 # Let's add a top anchor just in case we want to link to the top of the page
1315 $full="<a name=\"top\"></a>".$full.$toc;
1318 if( !empty( $head[$i] ) ) {
1327 /* private */ function doMagicISBN( &$tokenizer )
1331 # Check whether next token is a text token
1332 # If yes, fetch it and convert the text into a
1333 # Special::BookSources link
1334 $token = $tokenizer->previewToken();
1335 while ( $token["type"] == "" )
1337 $tokenizer->nextToken();
1338 $token = $tokenizer->previewToken();
1340 if ( $token["type"] == "text" )
1342 $token = $tokenizer->nextToken();
1343 $x = $token["text"];
1344 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1346 $isbn = $blank = "" ;
1347 while ( " " == $x{0} ) {
1349 $x = substr( $x, 1 );
1351 while ( strstr( $valid, $x{0} ) != false ) {
1353 $x = substr( $x, 1 );
1355 $num = str_replace( "-", "", $isbn );
1356 $num = str_replace( " ", "", $num );
1359 $text = "ISBN $blank$x";
1361 $titleObj = Title
::makeTitle( NS_SPECIAL
, "Booksources" );
1362 $text = "<a href=\"" .
1363 $titleObj->escapeLocalUrl( "isbn={$num}" ) .
1364 "\" class=\"internal\">ISBN $isbn</a>";
1372 /* private */ function doMagicRFC( &$tokenizer )
1376 # Check whether next token is a text token
1377 # If yes, fetch it and convert the text into a
1378 # link to an RFC source
1379 $token = $tokenizer->previewToken();
1380 while ( $token["type"] == "" )
1382 $tokenizer->nextToken();
1383 $token = $tokenizer->previewToken();
1385 if ( $token["type"] == "text" )
1387 $token = $tokenizer->nextToken();
1388 $x = $token["text"];
1389 $valid = "0123456789";
1391 $rfc = $blank = "" ;
1392 while ( " " == $x{0} ) {
1394 $x = substr( $x, 1 );
1396 while ( strstr( $valid, $x{0} ) != false ) {
1398 $x = substr( $x, 1 );
1402 $text .= "RFC $blank$x";
1404 $url = wfmsg( "rfcurl" );
1405 $url = str_replace( "$1", $rfc, $url);
1406 $sk =& $this->mOptions
->getSkin();
1407 $la = $sk->getExternalLinkAttributes( $url, "RFC {$rfc}" );
1408 $text = "<a href='{$url}'{$la}>RFC {$rfc}</a>{$x}";
1416 function preSaveTransform( $text, &$title, &$user, $options, $clearState = true )
1418 $this->mOptions
= $options;
1419 $this->mTitle
= $title;
1420 if ( $clearState ) {
1421 $this->clearState();
1424 $stripState = false;
1425 $text = str_replace("\r\n", "\n", $text);
1426 $text = $this->strip( $text, $stripState, false );
1427 $text = $this->pstPass2( $text, $user );
1428 $text = $this->unstrip( $text, $stripState );
1432 /* private */ function pstPass2( $text, &$user )
1434 global $wgLang, $wgLocaltimezone;
1438 $n = $user->getName();
1439 $k = $user->getOption( "nickname" );
1440 if ( "" == $k ) { $k = $n; }
1441 if(isset($wgLocaltimezone)) {
1442 $oldtz = getenv("TZ"); putenv("TZ=$wgLocaltimezone");
1444 /* Note: this is an ugly timezone hack for the European wikis */
1445 $d = $wgLang->timeanddate( date( "YmdHis" ), false ) .
1446 " (" . date( "T" ) . ")";
1447 if(isset($wgLocaltimezone)) putenv("TZ=$oldtz");
1449 $text = preg_replace( "/~~~~/", "[[" . $wgLang->getNsText(
1450 Namespace::getUser() ) . ":$n|$k]] $d", $text );
1451 $text = preg_replace( "/~~~/", "[[" . $wgLang->getNsText(
1452 Namespace::getUser() ) . ":$n|$k]]", $text );
1454 # Context links: [[|name]] and [[name (context)|]]
1456 $tc = "[&;%\\-,.\\(\\)' _0-9A-Za-z\\/:\\x80-\\xff]";
1457 $np = "[&;%\\-,.' _0-9A-Za-z\\/:\\x80-\\xff]"; # No parens
1458 $namespacechar = '[ _0-9A-Za-z\x80-\xff]'; # Namespaces can use non-ascii!
1459 $conpat = "/^({$np}+) \\(({$tc}+)\\)$/";
1461 $p1 = "/\[\[({$np}+) \\(({$np}+)\\)\\|]]/"; # [[page (context)|]]
1462 $p2 = "/\[\[\\|({$tc}+)]]/"; # [[|page]]
1463 $p3 = "/\[\[($namespacechar+):({$np}+)\\|]]/"; # [[namespace:page|]]
1464 $p4 = "/\[\[($namespacechar+):({$np}+) \\(({$np}+)\\)\\|]]/";
1465 # [[ns:page (cont)|]]
1467 $t = $this->mTitle
->getText();
1468 if ( preg_match( $conpat, $t, $m ) ) {
1471 $text = preg_replace( $p4, "[[\\1:\\2 (\\3)|\\2]]", $text );
1472 $text = preg_replace( $p1, "[[\\1 (\\2)|\\1]]", $text );
1473 $text = preg_replace( $p3, "[[\\1:\\2|\\2]]", $text );
1475 if ( "" == $context ) {
1476 $text = preg_replace( $p2, "[[\\1]]", $text );
1478 $text = preg_replace( $p2, "[[\\1 ({$context})|\\1]]", $text );
1481 # {{SUBST:xxx}} variables
1483 $mw =& MagicWord
::get( MAG_SUBST
);
1484 $text = $mw->substituteCallback( $text, "wfReplaceSubstVar" );
1486 # Trim trailing whitespace
1487 # MAG_END (__END__) tag allows for trailing
1488 # whitespace to be deliberately included
1489 $text = rtrim( $text );
1490 $mw =& MagicWord
::get( MAG_END
);
1491 $mw->matchAndRemove( $text );
1501 var $mText, $mLanguageLinks, $mCategoryLinks, $mContainsOldMagic;
1503 function ParserOutput( $text = "", $languageLinks = array(), $categoryLinks = array(),
1504 $containsOldMagic = false )
1506 $this->mText
= $text;
1507 $this->mLanguageLinks
= $languageLinks;
1508 $this->mCategoryLinks
= $categoryLinks;
1509 $this->mContainsOldMagic
= $containsOldMagic;
1512 function getText() { return $this->mText
; }
1513 function getLanguageLinks() { return $this->mLanguageLinks
; }
1514 function getCategoryLinks() { return $this->mCategoryLinks
; }
1515 function containsOldMagic() { return $this->mContainsOldMagic
; }
1516 function setText( $text ) { return wfSetVar( $this->mText
, $text ); }
1517 function setLanguageLinks( $ll ) { return wfSetVar( $this->mLanguageLinks
, $ll ); }
1518 function setCategoryLinks( $cl ) { return wfSetVar( $this->mCategoryLinks
, $cl ); }
1519 function setContainsOldMagic( $com ) { return wfSetVar( $this->mContainsOldMagic
, $com ); }
1524 # All variables are private
1525 var $mUseTeX; # Use texvc to expand <math> tags
1526 var $mUseCategoryMagic; # Treat [[Category:xxxx]] tags specially
1527 var $mUseDynamicDates; # Use $wgDateFormatter to format dates
1528 var $mInterwikiMagic; # Interlanguage links are removed and returned in an array
1529 var $mAllowExternalImages; # Allow external images inline
1530 var $mSkin; # Reference to the preferred skin
1531 var $mDateFormat; # Date format index
1532 var $mEditSection; # Create "edit section" links
1533 var $mEditSectionOnRightClick; # Generate JavaScript to edit section on right click
1534 var $mPrintable; # Generate printable output
1535 var $mNumberHeadings; # Automatically number headings
1536 var $mShowToc; # Show table of contents
1538 function getUseTeX() { return $this->mUseTeX
; }
1539 function getUseCategoryMagic() { return $this->mUseCategoryMagic
; }
1540 function getUseDynamicDates() { return $this->mUseDynamicDates
; }
1541 function getInterwikiMagic() { return $this->mInterwikiMagic
; }
1542 function getAllowExternalImages() { return $this->mAllowExternalImages
; }
1543 function getSkin() { return $this->mSkin
; }
1544 function getDateFormat() { return $this->mDateFormat
; }
1545 function getEditSection() { return $this->mEditSection
; }
1546 function getEditSectionOnRightClick() { return $this->mEditSectionOnRightClick
; }
1547 function getPrintable() { return $this->mPrintable
; }
1548 function getNumberHeadings() { return $this->mNumberHeadings
; }
1549 function getShowToc() { return $this->mShowToc
; }
1551 function setUseTeX( $x ) { return wfSetVar( $this->mUseTeX
, $x ); }
1552 function setUseCategoryMagic( $x ) { return wfSetVar( $this->mUseCategoryMagic
, $x ); }
1553 function setUseDynamicDates( $x ) { return wfSetVar( $this->mUseDynamicDates
, $x ); }
1554 function setInterwikiMagic( $x ) { return wfSetVar( $this->mInterwikiMagic
, $x ); }
1555 function setAllowExternalImages( $x ) { return wfSetVar( $this->mAllowExternalImages
, $x ); }
1556 function setSkin( $x ) { return wfSetRef( $this->mSkin
, $x ); }
1557 function setDateFormat( $x ) { return wfSetVar( $this->mDateFormat
, $x ); }
1558 function setEditSection( $x ) { return wfSetVar( $this->mEditSection
, $x ); }
1559 function setEditSectionOnRightClick( $x ) { return wfSetVar( $this->mEditSectionOnRightClick
, $x ); }
1560 function setPrintable( $x ) { return wfSetVar( $this->mPrintable
, $x ); }
1561 function setNumberHeadings( $x ) { return wfSetVar( $this->mNumberHeadings
, $x ); }
1562 function setShowToc( $x ) { return wfSetVar( $this->mShowToc
, $x ); }
1564 /* static */ function newFromUser( &$user )
1566 $popts = new ParserOptions
;
1567 $popts->initialiseFromUser( &$user );
1571 function initialiseFromUser( &$userInput )
1573 global $wgUseTeX, $wgUseCategoryMagic, $wgUseDynamicDates, $wgInterwikiMagic, $wgAllowExternalImages;
1575 if ( !$userInput ) {
1578 $user =& $userInput;
1581 $this->mUseTeX
= $wgUseTeX;
1582 $this->mUseCategoryMagic
= $wgUseCategoryMagic;
1583 $this->mUseDynamicDates
= $wgUseDynamicDates;
1584 $this->mInterwikiMagic
= $wgInterwikiMagic;
1585 $this->mAllowExternalImages
= $wgAllowExternalImages;
1586 $this->mSkin
=& $user->getSkin();
1587 $this->mDateFormat
= $user->getOption( "date" );
1588 $this->mEditSection
= $user->getOption( "editsection" );
1589 $this->mEditSectionOnRightClick
= $user->getOption( "editsectiononrightclick" );
1590 $this->mPrintable
= false;
1591 $this->mNumberHeadings
= $user->getOption( "numberheadings" );
1592 $this->mShowToc
= $user->getOption( "showtoc" );
1598 # Regex callbacks, used in OutputPage::replaceVariables
1600 # Just get rid of the dangerous stuff
1601 # Necessary because replaceVariables is called after removeHTMLtags,
1602 # and message text can come from any user
1603 function wfReplaceMsgVar( $matches ) {
1604 global $wgCurOut, $wgLinkCache;
1605 $text = $wgCurOut->removeHTMLtags( wfMsg( $matches[1] ) );
1606 $wgLinkCache->suspend();
1607 $text = $wgCurOut->replaceInternalLinks( $text );
1608 $wgLinkCache->resume();
1609 $wgLinkCache->addLinkObj( Title
::makeTitle( NS_MEDIAWIKI
, $matches[1] ) );
1613 # Effective <nowiki></nowiki>
1614 # Not real <nowiki> because this is called after nowiki sections are processed
1615 function wfReplaceMsgnwVar( $matches ) {
1616 global $wgCurOut, $wgLinkCache;
1617 $text = wfEscapeWikiText( wfMsg( $matches[1] ) );
1618 $wgLinkCache->addLinkObj( Title
::makeTitle( NS_MEDIAWIKI
, $matches[1] ) );