4 if($wgUseTeX) include_once( "Math.php" );
7 var $mHeaders, $mCookies, $mMetatags, $mKeywords;
8 var $mLinktags, $mPagetitle, $mBodytext, $mDebugtext;
9 var $mHTMLtitle, $mRobotpolicy, $mIsarticle, $mPrintable;
10 var $mSubtitle, $mRedirect, $mAutonumber, $mHeadtext;
11 var $mLastModified, $mCategoryLinks;
13 var $mDTopen, $mLastSection; # Used for processing DL, PRE
14 var $mLanguageLinks, $mSupressQuickbar;
20 $this->mHeaders
= $this->mCookies
= $this->mMetatags
=
21 $this->mKeywords
= $this->mLinktags
= array();
22 $this->mHTMLtitle
= $this->mPagetitle
= $this->mBodytext
=
23 $this->mLastSection
= $this->mRedirect
= $this->mLastModified
=
24 $this->mSubtitle
= $this->mDebugtext
= $this->mRobotpolicy
=
25 $this->mOnloadHandler
= "";
26 $this->mIsarticle
= $this->mPrintable
= true;
27 $this->mSupressQuickbar
= $this->mDTopen
= $this->mPrintable
= false;
28 $this->mLanguageLinks
= array();
29 $this->mCategoryLinks
= array() ;
30 $this->mAutonumber
= 0;
31 $this->mDoNothing
= false;
34 function addHeader( $name, $val ) { array_push( $this->mHeaders
, "$name: $val" ) ; }
35 function addCookie( $name, $val ) { array_push( $this->mCookies
, array( $name, $val ) ); }
36 function redirect( $url ) { $this->mRedirect
= $url; }
38 # To add an http-equiv meta tag, precede the name with "http:"
39 function addMeta( $name, $val ) { array_push( $this->mMetatags
, array( $name, $val ) ); }
40 function addKeyword( $text ) { array_push( $this->mKeywords
, $text ); }
41 function addLink( $rel, $rev, $target ) { array_push( $this->mLinktags
, array( $rel, $rev, $target ) ); }
43 # checkLastModified tells the client to use the client-cached page if
44 # possible. If sucessful, the OutputPage is disabled so that
45 # any future call to OutputPage->output() have no effect. The method
46 # returns true iff cache-ok headers was sent.
47 function checkLastModified ( $timestamp )
49 global $wgLang, $wgCachePages, $wgUser;
50 if( !$wgCachePages ) {
51 wfDebug( "CACHE DISABLED\n", false );
54 if( preg_match( '/MSIE ([1-4]|5\.0)/', $_SERVER["HTTP_USER_AGENT"] ) ) {
55 # IE 5.0 has probs with our caching
56 wfDebug( "-- bad client, not caching\n", false );
59 if( $wgUser->getOption( "nocache" ) ) {
60 wfDebug( "USER DISABLED CACHE\n", false );
64 $lastmod = gmdate( "D, j M Y H:i:s", wfTimestamp2Unix(
65 max( $timestamp, $wgUser->mTouched
) ) ) . " GMT";
67 if( !empty( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) ) {
68 # IE sends sizes after the date like this:
69 # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
70 # this breaks strtotime().
71 $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
72 $ismodsince = wfUnix2Timestamp( strtotime( $modsince ) );
73 wfDebug( "-- client send If-Modified-Since: " . $modsince . "\n", false );
74 wfDebug( "-- we might send Last-Modified : $lastmod\n", false );
76 if( ($ismodsince >= $timestamp ) and $wgUser->validateCache( $ismodsince ) ) {
77 # Make sure you're in a place you can leave when you call us!
78 header( "HTTP/1.0 304 Not Modified" );
79 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
80 header( "Cache-Control: private, must-revalidate, max-age=0" );
81 header( "Last-Modified: {$lastmod}" );
82 wfDebug( "CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
86 wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
87 $this->mLastModified
= $lastmod;
90 wfDebug( "We're confused.\n", false );
91 $this->mLastModified
= $lastmod;
95 function setRobotpolicy( $str ) { $this->mRobotpolicy
= $str; }
96 function setHTMLtitle( $name ) { $this->mHTMLtitle
= $name; }
97 function setPageTitle( $name ) { $this->mPagetitle
= $name; }
98 function getPageTitle() { return $this->mPagetitle
; }
99 function setSubtitle( $str ) { $this->mSubtitle
= $str; }
100 function getSubtitle() { return $this->mSubtitle
; }
101 function setArticleFlag( $v ) { $this->mIsarticle
= $v; }
102 function isArticle() { return $this->mIsarticle
; }
103 function setPrintable() { $this->mPrintable
= true; }
104 function isPrintable() { return $this->mPrintable
; }
105 function setOnloadHandler( $js ) { $this->mOnloadHandler
= $js; }
106 function getOnloadHandler() { return $this->mOnloadHandler
; }
107 function disable() { $this->mDoNothing
= true; }
109 function getLanguageLinks() {
110 global $wgTitle, $wgLanguageCode;
111 global $wgDBconnection, $wgDBname;
112 return $this->mLanguageLinks
;
114 function supressQuickbar() { $this->mSupressQuickbar
= true; }
115 function isQuickbarSupressed() { return $this->mSupressQuickbar
; }
117 function addHTML( $text ) { $this->mBodytext
.= $text; }
118 function addHeadtext( $text ) { $this->mHeadtext
.= $text; }
119 function debug( $text ) { $this->mDebugtext
.= $text; }
121 # First pass--just handle <nowiki> sections, pass the rest off
122 # to doWikiPass2() which does all the real work.
125 function addWikiText( $text, $linestart = true )
128 $fname = "OutputPage::addWikiText";
129 wfProfileIn( $fname );
130 $unique = "3iyZiyA7iMwg5rhxP0Dcc9oTnj8qD1jm1Sfv4";
131 $unique2 = "4LIQ9nXtiYFPCSfitVwDw7EYwQlL4GeeQ7qSO";
132 $unique3 = "fPaA8gDfdLBqzj68Yjg9Hil3qEF8JGO0uszIp";
143 while ( "" != $text ) {
144 $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
146 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $text = ""; }
148 $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
150 $nwlist[$nwsecs] = wfEscapeHTMLTagsOnly($q[0]);
151 $stripped .= $unique . $nwsecs . "s";
157 while ( "" != $stripped ) {
158 $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 );
160 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $stripped = ""; }
162 $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 );
164 $mathlist[$mathsecs] = renderMath($q[0]);
165 $stripped2 .= $unique2 . $mathsecs . "s";
170 $stripped2 = $stripped;
173 while ( "" != $stripped2 ) {
174 $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 );
176 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $stripped2 = ""; }
178 $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 );
180 $prelist[$presecs] = "<pre>". wfEscapeHTMLTagsOnly($q[0]). "</pre>";
181 $stripped3 .= $unique3 . $presecs . "s";
186 $text = $this->doWikiPass2( $stripped3, $linestart );
188 $specialChars = array("\\", "$");
189 $escapedChars = array("\\\\", "\\$");
190 for ( $i = 1; $i <= $presecs; ++
$i ) {
191 $text = preg_replace( "/{$unique3}{$i}s/", str_replace( $specialChars,
192 $escapedChars, $prelist[$i] ), $text );
195 for ( $i = 1; $i <= $mathsecs; ++
$i ) {
196 $text = preg_replace( "/{$unique2}{$i}s/", str_replace( $specialChars,
197 $escapedChars, $mathlist[$i] ), $text );
200 for ( $i = 1; $i <= $nwsecs; ++
$i ) {
201 $text = preg_replace( "/{$unique}{$i}s/", str_replace( $specialChars,
202 $escapedChars, $nwlist[$i] ), $text );
204 $this->addHTML( $text );
205 wfProfileOut( $fname );
208 function sendCacheControl() {
210 if( $this->mLastModified
!= "" ) {
211 wfDebug( "** private caching; {$this->mLastModified} **\n", false );
212 header( "Cache-Control: private, must-revalidate, max-age=0" );
213 header( "Last-modified: {$this->mLastModified}" );
215 # We should put in Accept-Encoding, but IE chokes on anything but
216 # User-Agent in a Vary: header (at least through 6.0)
217 header( "Vary: User-Agent" );
220 wfDebug( "** no caching **\n", false );
221 header( "Cache-Control: no-cache" ); # Experimental - see below
222 header( "Pragma: no-cache" );
223 header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
225 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
228 # Finally, all the text has been munged and accumulated into
229 # the object, let's actually output it:
233 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
234 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
235 if( $this->mDoNothing
){
238 $fname = "OutputPage::output";
239 wfProfileIn( $fname );
241 $sk = $wgUser->getSkin();
243 $this->sendCacheControl();
245 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
246 header( "Content-language: {$wgLanguageCode}" );
248 if ( "" != $this->mRedirect
) {
249 if( substr( $this->mRedirect
, 0, 4 ) != "http" ) {
250 # Standards require redirect URLs to be absolute
252 $this->mRedirect
= $wgServer . $this->mRedirect
;
254 header( "Location: {$this->mRedirect}" );
258 $exp = time() +
$wgCookieExpiration;
259 foreach( $this->mCookies
as $name => $val ) {
260 setcookie( $name, $val, $exp, "/" );
263 $sk->outputPage( $this );
269 global $wgInputEncoding, $wgOutputEncoding, $wgLang;
270 if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
273 $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
274 if ( false === $outs ) { $outs = $ins; }
279 function setEncodings()
281 global $wgInputEncoding, $wgOutputEncoding;
282 global $wgUser, $wgLang;
284 $wgInputEncoding = strtolower( $wgInputEncoding );
286 if( $wgUser->getOption( 'altencoding' ) ) {
287 $wgLang->setAltEncoding();
291 if ( empty( $_SERVER['HTTP_ACCEPT_CHARSET'] ) ) {
292 $wgOutputEncoding = strtolower( $wgOutputEncoding );
297 # This code is unused anyway!
298 # Commenting out. --bv 2003-11-15
300 $a = explode( ",", $_SERVER['HTTP_ACCEPT_CHARSET'] );
304 foreach ( $a as $s ) {
305 if ( preg_match( "/(.*);q=(.*)/", $s, $m ) ) {
317 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
318 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
319 $wgOutputEncoding = strtolower( $bestset );
324 $wgOutputEncoding = $wgInputEncoding;
327 # Returns a HTML comment with the elapsed time since request.
328 # This method has no side effects.
329 function reportTime()
331 global $wgRequestTime;
333 list( $usec, $sec ) = explode( " ", microtime() );
334 $now = (float)$sec +
(float)$usec;
336 list( $usec, $sec ) = explode( " ", $wgRequestTime );
337 $start = (float)$sec +
(float)$usec;
338 $elapsed = $now - $start;
339 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
344 # Note: these arguments are keys into wfMsg(), not text!
346 function errorpage( $title, $msg )
350 $this->mDebugtext
.= "Original title: " .
351 $wgTitle->getPrefixedText() . "\n";
352 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
353 $this->setPageTitle( wfMsg( $title ) );
354 $this->setRobotpolicy( "noindex,nofollow" );
355 $this->setArticleFlag( false );
357 $this->mBodytext
= "";
358 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
359 $this->returnToMain( false );
365 function sysopRequired()
369 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
370 $this->setPageTitle( wfMsg( "sysoptitle" ) );
371 $this->setRobotpolicy( "noindex,nofollow" );
372 $this->setArticleFlag( false );
373 $this->mBodytext
= "";
375 $sk = $wgUser->getSkin();
376 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
377 $this->addHTML( wfMsg( "sysoptext", $ap ) );
378 $this->returnToMain();
381 function developerRequired()
385 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
386 $this->setPageTitle( wfMsg( "developertitle" ) );
387 $this->setRobotpolicy( "noindex,nofollow" );
388 $this->setArticleFlag( false );
389 $this->mBodytext
= "";
391 $sk = $wgUser->getSkin();
392 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
393 $this->addHTML( wfMsg( "developertext", $ap ) );
394 $this->returnToMain();
397 function databaseError( $fname )
399 global $wgUser, $wgCommandLineMode;
401 $this->setPageTitle( wfMsgNoDB( "databaseerror" ) );
402 $this->setRobotpolicy( "noindex,nofollow" );
403 $this->setArticleFlag( false );
405 if ( $wgCommandLineMode ) {
406 $msg = wfMsgNoDB( "dberrortextcl" );
408 $msg = wfMsgNoDB( "dberrortext" );
411 $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), $msg );
412 $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg );
413 $msg = str_replace( "$3", wfLastErrno(), $msg );
414 $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg );
416 if ( $wgCommandLineMode ) {
420 $sk = $wgUser->getSkin();
421 $shlink = $sk->makeKnownLink( wfMsgNoDB( "searchhelppage" ),
422 wfMsgNoDB( "searchingwikipedia" ) );
423 $msg = str_replace( "$5", $shlink, $msg );
425 $this->mBodytext
= $msg;
430 function readOnlyPage( $source = "", $protected = false )
432 global $wgUser, $wgReadOnlyFile;
434 $this->setRobotpolicy( "noindex,nofollow" );
435 $this->setArticleFlag( false );
438 $this->setPageTitle( wfMsg( "viewsource" ) );
439 $this->addWikiText( wfMsg( "protectedtext" ) );
441 $this->setPageTitle( wfMsg( "readonly" ) );
442 $reason = file_get_contents( $wgReadOnlyFile );
443 $this->addHTML( wfMsg( "readonlytext", $reason ) );
447 $rows = $wgUser->getOption( "rows" );
448 $cols = $wgUser->getOption( "cols" );
449 $text .= "</p>\n<textarea cols='$cols' rows='$rows' readonly>" .
450 htmlspecialchars( $source ) . "\n</textarea>";
451 $this->addHTML( $text );
454 $this->returnToMain( false );
457 function fatalError( $message )
459 $this->setPageTitle( wfMsg( "internalerror" ) );
460 $this->setRobotpolicy( "noindex,nofollow" );
461 $this->setArticleFlag( false );
463 $this->mBodytext
= $message;
468 function unexpectedValueError( $name, $val )
470 $this->fatalError( wfMsg( "unexpected", $name, $val ) );
473 function fileCopyError( $old, $new )
475 $this->fatalError( wfMsg( "filecopyerror", $old, $new ) );
478 function fileRenameError( $old, $new )
480 $this->fatalError( wfMsg( "filerenameerror", $old, $new ) );
483 function fileDeleteError( $name )
485 $this->fatalError( wfMsg( "filedeleteerror", $name ) );
488 function fileNotFoundError( $name )
490 $this->fatalError( wfMsg( "filenotfound", $name ) );
493 function returnToMain( $auto = true )
495 global $wgUser, $wgOut, $returnto;
497 $sk = $wgUser->getSkin();
498 if ( "" == $returnto ) {
499 $returnto = wfMsg( "mainpage" );
501 $link = $sk->makeKnownLink( $returnto, "" );
503 $r = wfMsg( "returnto", $link );
505 $wgOut->addMeta( "http:Refresh", "10;url=" .
506 wfLocalUrlE( wfUrlencode( $returnto ) ) );
508 $wgOut->addHTML( "\n<p>$r\n" );
512 function categoryMagic ()
514 global $wgTitle , $wgUseCategoryMagic ;
515 if ( !isset ( $wgUseCategoryMagic ) ||
!$wgUseCategoryMagic ) return ;
516 $id = $wgTitle->getArticleID() ;
517 $cat = ucfirst ( wfMsg ( "category" ) ) ;
518 $ti = $wgTitle->getText() ;
519 $ti = explode ( ":" , $ti , 2 ) ;
520 if ( $cat != $ti[0] ) return "" ;
521 $r = "<br break=all>\n" ;
523 $articles = array() ;
524 $parents = array () ;
525 $children = array() ;
529 $sk = $wgUser->getSkin() ;
530 $sql = "SELECT l_from FROM links WHERE l_to={$id}" ;
531 $res = wfQuery ( $sql, DB_READ
) ;
532 while ( $x = wfFetchObject ( $res ) )
535 # $t->newFromDBkey ( $x->l_from ) ;
536 # $t = $t->getText() ;
538 $y = explode ( ":" , $t , 2 ) ;
539 if ( count ( $y ) == 2 && $y[0] == $cat ) {
540 array_push ( $children , $sk->makeLink ( $t , $y[1] ) ) ;
542 array_push ( $articles , $sk->makeLink ( $t ) ) ;
545 wfFreeResult ( $res ) ;
548 if ( count ( $children ) > 0 )
550 asort ( $children ) ;
551 $r .= "<h2>".wfMsg("subcategories")."</h2>\n" ;
552 $r .= implode ( ", " , $children ) ;
556 if ( count ( $articles ) > 0 )
558 asort ( $articles ) ;
559 $h = wfMsg( "category_header", $ti[1] );
560 $r .= "<h2>{$h}</h2>\n" ;
561 $r .= implode ( ", " , $articles ) ;
568 function getHTMLattrs ()
570 $htmlattrs = array( # Allowed attributes--no scripting, etc.
571 "title", "align", "lang", "dir", "width", "height",
572 "bgcolor", "clear", /* BR */ "noshade", /* HR */
573 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
574 /* FONT */ "type", "start", "value", "compact",
575 /* For various lists, mostly deprecated but safe */
576 "summary", "width", "border", "frame", "rules",
577 "cellspacing", "cellpadding", "valign", "char",
578 "charoff", "colgroup", "col", "span", "abbr", "axis",
579 "headers", "scope", "rowspan", "colspan", /* Tables */
580 "id", "class", "name", "style" /* For CSS */
585 function fixTableTags ( $t )
587 if ( trim ( $t ) == "" ) return "" ; # Saves runtime ;-)
588 $htmlattrs = $this->getHTMLattrs() ;
590 # Strip non-approved attributes from the tag
592 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
593 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
599 function doTableStuff ( $t )
601 $t = explode ( "\n" , $t ) ;
602 $td = array () ; # Is currently a td tag open?
603 $ltd = array () ; # Was it TD or TH?
604 $tr = array () ; # Is currently a tr tag open?
605 $ltr = array () ; # tr attributes
606 foreach ( $t AS $k => $x )
609 $fc = substr ( $x , 0 , 1 ) ;
610 if ( "{|" == substr ( $x , 0 , 2 ) )
612 $t[$k] = "<table " . $this->fixTableTags ( substr ( $x , 3 ) ) . ">" ;
613 array_push ( $td , false ) ;
614 array_push ( $ltd , "" ) ;
615 array_push ( $tr , false ) ;
616 array_push ( $ltr , "" ) ;
618 else if ( count ( $td ) == 0 ) { } # Don't do any of the following
619 else if ( "|}" == substr ( $x , 0 , 2 ) )
622 $l = array_pop ( $ltd ) ;
623 if ( array_pop ( $tr ) ) $z = "</tr>" . $z ;
624 if ( array_pop ( $td ) ) $z = "</{$l}>" . $z ;
628 /* else if ( "|_" == substr ( $x , 0 , 2 ) ) # Caption
630 $z = trim ( substr ( $x , 2 ) ) ;
631 $t[$k] = "<caption>{$z}</caption>\n" ;
633 else if ( "|-" == substr ( $x , 0 , 2 ) ) # Allows for |---------------
635 $x = substr ( $x , 1 ) ;
636 while ( $x != "" && substr ( $x , 0 , 1 ) == '-' ) $x = substr ( $x , 1 ) ;
638 $l = array_pop ( $ltd ) ;
639 if ( array_pop ( $tr ) ) $z = "</tr>" . $z ;
640 if ( array_pop ( $td ) ) $z = "</{$l}>" . $z ;
643 array_push ( $tr , false ) ;
644 array_push ( $td , false ) ;
645 array_push ( $ltd , "" ) ;
646 array_push ( $ltr , $this->fixTableTags ( $x ) ) ;
648 else if ( "|" == $fc ||
"!" == $fc ||
"|+" == substr ( $x , 0 , 2 ) ) # Caption
650 if ( "|+" == substr ( $x , 0 , 2 ) )
653 $x = substr ( $x , 1 ) ;
655 $after = substr ( $x , 1 ) ;
656 if ( $fc == "!" ) $after = str_replace ( "!!" , "||" , $after ) ;
657 $after = explode ( "||" , $after ) ;
659 foreach ( $after AS $theline )
662 $tra = array_pop ( $ltr ) ;
663 if ( !array_pop ( $tr ) ) $z = "<tr {$tra}>\n" ;
664 array_push ( $tr , true ) ;
665 array_push ( $ltr , "" ) ;
667 $l = array_pop ( $ltd ) ;
668 if ( array_pop ( $td ) ) $z = "</{$l}>" . $z ;
669 if ( $fc == "|" ) $l = "TD" ;
670 else if ( $fc == "!" ) $l = "TH" ;
671 else if ( $fc == "+" ) $l = "CAPTION" ;
673 array_push ( $ltd , $l ) ;
674 $y = explode ( "|" , $theline , 2 ) ;
675 if ( count ( $y ) == 1 ) $y = "{$z}<{$l}>{$y[0]}" ;
676 else $y = $y = "{$z}<{$l} ".$this->fixTableTags($y[0]).">{$y[1]}" ;
678 array_push ( $td , true ) ;
683 # Closing open td, tr && table
684 while ( count ( $td ) > 0 )
686 if ( array_pop ( $td ) ) $t[] = "</td>" ;
687 if ( array_pop ( $tr ) ) $t[] = "</tr>" ;
691 $t = implode ( "\n" , $t ) ;
692 # $t = $this->removeHTMLtags( $t );
696 # Well, OK, it's actually about 14 passes. But since all the
697 # hard lifting is done inside PHP's regex code, it probably
698 # wouldn't speed things up much to add a real parser.
700 function doWikiPass2( $text, $linestart )
702 global $wgUser, $wgLang, $wgUseDynamicDates;
703 $fname = "OutputPage::doWikiPass2";
704 wfProfileIn( $fname );
706 $text = $this->removeHTMLtags( $text );
707 $text = $this->replaceVariables( $text );
709 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
710 $text = str_replace ( "<HR>", "<hr>", $text );
712 $text = $this->doAllQuotes( $text );
713 $text = $this->doHeadings( $text );
715 if($wgUseDynamicDates) {
716 global $wgDateFormatter;
717 $text = $wgDateFormatter->reformat( $wgUser->getOption("date"), $text );
720 $text = $this->replaceExternalLinks( $text );
721 $text = $this->replaceInternalLinks ( $text );
723 $text = $this->doTableStuff ( $text ) ;
724 $text = $this->doBlockLevels( $text, $linestart );
726 $text = $this->magicISBN( $text );
727 $text = $this->magicRFC( $text );
728 $text = $this->formatHeadings( $text );
730 $sk = $wgUser->getSkin();
731 $text = $sk->transformContent( $text );
732 $text .= $this->categoryMagic () ;
734 wfProfileOut( $fname );
738 /* private */ function doAllQuotes( $text )
741 $lines = explode( "\r\n", $text );
742 foreach ( $lines as $line ) {
743 $outtext .= $this->doQuotes ( "", $line, "" ) . "\r\n";
748 /* private */ function doQuotes( $pre, $text, $mode )
750 if ( preg_match( "/^(.*)''(.*)$/sU", $text, $m ) ) {
751 $m1_strong = ($m[1] == "") ?
"" : "<strong>{$m[1]}</strong>";
752 $m1_em = ($m[1] == "") ?
"" : "<em>{$m[1]}</em>";
753 if ( substr ($m[2], 0, 1) == "'" ) {
754 $m[2] = substr ($m[2], 1);
756 return $this->doQuotes ( $m[1], $m[2], ($m[1] == "") ?
"both" : "emstrong" );
757 } else if ($mode == "strong") {
758 return $m1_strong . $this->doQuotes ( "", $m[2], "" );
759 } else if (($mode == "emstrong") ||
($mode == "both")) {
760 return $this->doQuotes ( "", $pre.$m1_strong.$m[2], "em" );
761 } else if ($mode == "strongem") {
762 return "<strong>{$pre}{$m1_em}</strong>" . $this->doQuotes ( "", $m[2], "em" );
764 return $m[1] . $this->doQuotes ( "", $m[2], "strong" );
767 if ($mode == "strong") {
768 return $this->doQuotes ( $m[1], $m[2], ($m[1] == "") ?
"both" : "strongem" );
769 } else if ($mode == "em") {
770 return $m1_em . $this->doQuotes ( "", $m[2], "" );
771 } else if ($mode == "emstrong") {
772 return "<em>{$pre}{$m1_strong}</em>" . $this->doQuotes ( "", $m[2], "strong" );
773 } else if (($mode == "strongem") ||
($mode == "both")) {
774 return $this->doQuotes ( "", $pre.$m1_em.$m[2], "strong" );
776 return $m[1] . $this->doQuotes ( "", $m[2], "em" );
780 $text_strong = ($text == "") ?
"" : "<strong>{$text}</strong>";
781 $text_em = ($text == "") ?
"" : "<em>{$text}</em>";
784 } else if ($mode == "em") {
785 return $pre . $text_em;
786 } else if ($mode == "strong") {
787 return $pre . $text_strong;
788 } else if ($mode == "strongem") {
789 return (($pre == "") && ($text == "")) ?
"" : "<strong>{$pre}{$text_em}</strong>";
791 return (($pre == "") && ($text == "")) ?
"" : "<em>{$pre}{$text_strong}</em>";
796 /* private */ function doHeadings( $text )
798 for ( $i = 6; $i >= 1; --$i ) {
799 $h = substr( "======", 0, $i );
800 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
801 "<h{$i}>\\1</h{$i}>\\2", $text );
806 # Note: we have to do external links before the internal ones,
807 # and otherwise take great care in the order of things here, so
808 # that we don't end up interpreting some URLs twice.
810 /* private */ function replaceExternalLinks( $text )
812 $fname = "OutputPage::replaceExternalLinks";
813 wfProfileIn( $fname );
814 $text = $this->subReplaceExternalLinks( $text, "http", true );
815 $text = $this->subReplaceExternalLinks( $text, "https", true );
816 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
817 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
818 $text = $this->subReplaceExternalLinks( $text, "news", false );
819 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
820 wfProfileOut( $fname );
824 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
826 global $wgUser, $printable;
827 global $wgAllowExternalImages;
830 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
831 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
833 # this is the list of separators that should be ignored if they
834 # are the last character of an URL but that should be included
835 # if they occur within the URL, e.g. "go to www.foo.com, where .."
836 # in this case, the last comma should not become part of the URL,
837 # but in "www.foo.com/123,2342,32.htm" it should.
839 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
840 $images = "gif|png|jpg|jpeg";
842 # PLEASE NOTE: The curly braces { } are not part of the regex,
843 # they are interpreted as part of the string (used to tell PHP
844 # that the content of the string should be inserted there).
845 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
846 "((?i){$images})([^{$uc}]|$)/";
848 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
849 $sk = $wgUser->getSkin();
851 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
852 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
853 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
855 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
856 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
857 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
859 $s = str_replace( $unique, $protocol, $s );
861 $a = explode( "[{$protocol}:", " " . $s );
862 $s = array_shift( $a );
863 $s = substr( $s, 1 );
865 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
866 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
868 foreach ( $a as $line ) {
869 if ( preg_match( $e1, $line, $m ) ) {
870 $link = "{$protocol}:{$m[1]}";
872 if ( $autonumber ) { $text = "[" . ++
$this->mAutonumber
. "]"; }
873 else { $text = wfEscapeHTML( $link ); }
874 } else if ( preg_match( $e2, $line, $m ) ) {
875 $link = "{$protocol}:{$m[1]}";
879 $s .= "[{$protocol}:" . $line;
882 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
884 $la = $sk->getExternalLinkAttributes( $link, $text );
885 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
891 /* private */ function replaceInternalLinks( $s )
893 global $wgTitle, $wgUser, $wgLang;
894 global $wgLinkCache, $wgInterwikiMagic, $wgUseCategoryMagic;
895 global $wgNamespacesWithSubpages, $wgLanguageCode;
896 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
898 wfProfileIn( "$fname-setup" );
899 $tc = Title
::legalChars() . "#";
900 $sk = $wgUser->getSkin();
902 $a = explode( "[[", " " . $s );
903 $s = array_shift( $a );
904 $s = substr( $s, 1 );
906 $e1 = "/^([{$tc}]+)(?:\\|([^]]+))?]](.*)\$/sD";
908 # Special and Media are pseudo-namespaces; no pages actually exist in them
909 $image = Namespace::getImage();
910 $special = Namespace::getSpecial();
911 $media = Namespace::getMedia();
912 $nottalk = !Namespace::isTalk( $wgTitle->getNamespace() );
913 wfProfileOut( "$fname-setup" );
915 foreach ( $a as $line ) {
916 if ( preg_match( $e1, $line, $m ) ) { # page with normal text or alt
919 } else { # Invalid form; output directly
926 :Foobar -- override special treatment of prefix (images, language links)
927 /Foobar -- convert to CurrentPage/Foobar
928 /Foobar/ -- convert to CurrentPage/Foobar, strip the initial / from text
930 $c = substr($m[1],0,1);
931 $noforce = ($c != ":");
932 if( $c == "/" ) { # subpage
933 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
934 $m[1]=substr($m[1],1,strlen($m[1])-2);
937 $noslash=substr($m[1],1);
939 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
940 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
943 } # this might be changed for ugliness reasons
945 $link = $noslash; # no subpage allowed, use standard link
947 } elseif( $noforce ) { # no subpage
950 $link = substr( $m[1], 1 );
955 $nt = Title
::newFromText( $link );
960 $ns = $nt->getNamespace();
961 $iw = $nt->getInterWiki();
963 if( $iw && $wgInterwikiMagic && $nottalk && $wgLang->getLanguageName( $iw ) ) {
964 array_push( $this->mLanguageLinks
, $nt->getPrefixedText() );
967 } else if ( "media" == $pre ) {
968 $nt = Title::newFromText( $suf );
969 $name = $nt->getDBkey();
970 if ( "" == $text ) { $text = $nt->GetText(); }
972 $wgLinkCache->addImageLink( $name );
973 $s .= $sk->makeMediaLink( $name,
974 wfImageUrl( $name ), $text );
976 } else if ( isset($wgUseCategoryMagic) && $wgUseCategoryMagic && $pre == wfMsg ( "category" ) ) {
977 $l = $sk->makeLink ( $pre.":".ucfirst( $m[2] ), ucfirst ( $m[2] ) ) ;
978 array_push ( $this->mCategoryLinks , $l ) ;
981 $l = $wgLang->getLanguageName( $pre );
982 if ( "" == $l or !$wgInterwikiMagic or Namespace::isTalk( $wgTitle->getNamespace() ) ) {
986 $s .= $sk->makeLink( $link, $text, "", $trail );
987 } else if ( $pre != $wgLanguageCode ) {
988 array_push( $this->mLanguageLinks, "$pre:$suf" );
994 if( $ns == $image ) {
995 $s .= $sk->makeImageLinkObj( $nt, $text ) . $trail;
996 $wgLinkCache->addImageLinkObj( $nt );
1000 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
1001 # $link = substr( $link, 2 );
1002 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
1004 if ( "" == $text ) { $text = $link; }
1006 $s .= $sk->makeLink( $link, $text, "", $trail );
1009 if( $ns == $media ) {
1010 $s .= $sk->makeMediaLinkObj( $nt, $text ) . $trail;
1011 $wgLinkCache->addImageLinkObj( $nt );
1013 } elseif( $ns == $special ) {
1014 $s .= $sk->makeKnownLinkObj( $nt, $text, "", $trail );
1017 $s .= $sk->makeLinkObj( $nt, $text, "", $trail );
1019 wfProfileOut( $fname );
1023 # Some functions here used by doBlockLevels()
1025 /* private */ function closeParagraph()
1028 if ( 0 != strcmp( "p", $this->mLastSection
) &&
1029 0 != strcmp( "", $this->mLastSection
) ) {
1030 $result = "</" . $this->mLastSection
. ">";
1032 $this->mLastSection
= "";
1035 # getCommon() returns the length of the longest common substring
1036 # of both arguments, starting at the beginning of both.
1038 /* private */ function getCommon( $st1, $st2 )
1040 $fl = strlen( $st1 );
1041 $shorter = strlen( $st2 );
1042 if ( $fl < $shorter ) { $shorter = $fl; }
1044 for ( $i = 0; $i < $shorter; ++
$i ) {
1045 if ( $st1{$i} != $st2{$i} ) { break; }
1049 # These next three functions open, continue, and close the list
1050 # element appropriate to the prefix character passed into them.
1052 /* private */ function openList( $char )
1054 $result = $this->closeParagraph();
1056 if ( "*" == $char ) { $result .= "<ul><li>"; }
1057 else if ( "#" == $char ) { $result .= "<ol><li>"; }
1058 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
1059 else if ( ";" == $char ) {
1060 $result .= "<dl><dt>";
1061 $this->mDTopen
= true;
1063 else { $result = "<!-- ERR 1 -->"; }
1068 /* private */ function nextItem( $char )
1070 if ( "*" == $char ||
"#" == $char ) { return "</li><li>"; }
1071 else if ( ":" == $char ||
";" == $char ) {
1073 if ( $this->mDTopen
) { $close = "</dt>"; }
1074 if ( ";" == $char ) {
1075 $this->mDTopen
= true;
1076 return $close . "<dt>";
1078 $this->mDTopen
= false;
1079 return $close . "<dd>";
1082 return "<!-- ERR 2 -->";
1085 /* private */function closeList( $char )
1087 if ( "*" == $char ) { return "</li></ul>"; }
1088 else if ( "#" == $char ) { return "</li></ol>"; }
1089 else if ( ":" == $char ) {
1090 if ( $this->mDTopen
) {
1091 $this->mDTopen
= false;
1092 return "</dt></dl>";
1094 return "</dd></dl>";
1097 return "<!-- ERR 3 -->";
1100 /* private */ function doBlockLevels( $text, $linestart )
1102 $fname = "OutputPage::doBlockLevels";
1103 wfProfileIn( $fname );
1104 # Parsing through the text line by line. The main thing
1105 # happening here is handling of block-level elements p, pre,
1106 # and making lists from lines starting with * # : etc.
1108 $a = explode( "\n", $text );
1109 $text = $lastPref = "";
1110 $this->mDTopen
= $inBlockElem = false;
1112 if ( ! $linestart ) { $text .= array_shift( $a ); }
1113 foreach ( $a as $t ) {
1114 if ( "" != $text ) { $text .= "\n"; }
1117 $opl = strlen( $lastPref );
1118 $npl = strspn( $t, "*#:;" );
1119 $pref = substr( $t, 0, $npl );
1120 $pref2 = str_replace( ";", ":", $pref );
1121 $t = substr( $t, $npl );
1123 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
1124 $text .= $this->nextItem( substr( $pref, -1 ) );
1126 if ( ";" == substr( $pref, -1 ) ) {
1127 $cpos = strpos( $t, ":" );
1128 if ( ! ( false === $cpos ) ) {
1129 $term = substr( $t, 0, $cpos );
1130 $text .= $term . $this->nextItem( ":" );
1131 $t = substr( $t, $cpos +
1 );
1134 } else if (0 != $npl ||
0 != $opl) {
1135 $cpl = $this->getCommon( $pref, $lastPref );
1137 while ( $cpl < $opl ) {
1138 $text .= $this->closeList( $lastPref{$opl-1} );
1141 if ( $npl <= $cpl && $cpl > 0 ) {
1142 $text .= $this->nextItem( $pref{$cpl-1} );
1144 while ( $npl > $cpl ) {
1145 $char = substr( $pref, $cpl, 1 );
1146 $text .= $this->openList( $char );
1148 if ( ";" == $char ) {
1149 $cpos = strpos( $t, ":" );
1150 if ( ! ( false === $cpos ) ) {
1151 $term = substr( $t, 0, $cpos );
1152 $text .= $term . $this->nextItem( ":" );
1153 $t = substr( $t, $cpos +
1 );
1160 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1162 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1163 $text .= $this->closeParagraph();
1164 $inBlockElem = true;
1166 if ( ! $inBlockElem ) {
1167 if ( " " == $t{0} ) {
1168 $newSection = "pre";
1169 # $t = wfEscapeHTML( $t );
1171 else { $newSection = "p"; }
1173 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1174 $text .= $this->closeParagraph();
1175 $text .= "<" . $newSection . ">";
1176 } else if ( 0 != strcmp( $this->mLastSection
,
1178 $text .= $this->closeParagraph();
1179 if ( 0 != strcmp( "p", $newSection ) ) {
1180 $text .= "<" . $newSection . ">";
1183 $this->mLastSection
= $newSection;
1185 if ( $inBlockElem &&
1186 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1187 $inBlockElem = false;
1193 $text .= $this->closeList( $pref2{$npl-1} );
1196 if ( "" != $this->mLastSection
) {
1197 if ( "p" != $this->mLastSection
) {
1198 $text .= "</" . $this->mLastSection
. ">";
1200 $this->mLastSection
= "";
1202 wfProfileOut( $fname );
1206 /* private */ function replaceVariables( $text )
1209 $fname = "OutputPage::replaceVariables";
1210 wfProfileIn( $fname );
1214 # See Language.php for the definition of each magic word
1216 # As with sigs, this uses the server's local time -- ensure
1217 # this is appropriate for your audience!
1219 $mw =& MagicWord
::get( MAG_CURRENTMONTH
);
1220 $text = $mw->replace( $v, $text );
1222 $v = $wgLang->getMonthName( date( "n" ) );
1223 $mw =& MagicWord
::get( MAG_CURRENTMONTHNAME
);
1224 $text = $mw->replace( $v, $text );
1226 $v = $wgLang->getMonthNameGen( date( "n" ) );
1227 $mw =& MagicWord
::get( MAG_CURRENTMONTHNAMEGEN
);
1228 $text = $mw->replace( $v, $text );
1231 $mw = MagicWord
::get( MAG_CURRENTDAY
);
1232 $text = $mw->replace( $v, $text );
1234 $v = $wgLang->getWeekdayName( date( "w" )+
1 );
1235 $mw =& MagicWord
::get( MAG_CURRENTDAYNAME
);
1236 $text = $mw->replace( $v, $text );
1239 $mw =& MagicWord
::get( MAG_CURRENTYEAR
);
1240 $text = $mw->replace( $v, $text );
1242 $v = $wgLang->time( wfTimestampNow(), false );
1243 $mw =& MagicWord
::get( MAG_CURRENTTIME
);
1244 $text = $mw->replace( $v, $text );
1246 $mw =& MagicWord
::get( MAG_NUMBEROFARTICLES
);
1247 if ( $mw->match( $text ) ) {
1248 $v = wfNumberOfArticles();
1249 $text = $mw->replace( $v, $text );
1252 # "Variables" with an additional parameter e.g. {{MSG:wikipedia}}
1253 # The callbacks are at the bottom of this file
1254 $mw =& MagicWord
::get( MAG_MSG
);
1255 $text = $mw->substituteCallback( $text, "wfReplaceMsgVar" );
1257 $mw =& MagicWord
::get( MAG_MSGNW
);
1258 $text = $mw->substituteCallback( $text, "wfReplaceMsgnwVar" );
1260 wfProfileOut( $fname );
1264 # Cleans up HTML, removes dangerous tags and attributes
1265 /* private */ function removeHTMLtags( $text )
1267 $fname = "OutputPage::removeHTMLtags";
1268 wfProfileIn( $fname );
1269 $htmlpairs = array( # Tags that must be closed
1270 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1271 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1272 "strike", "strong", "tt", "var", "div", "center",
1273 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1274 "ruby", "rt" , "rb" , "rp"
1276 $htmlsingle = array(
1277 "br", "p", "hr", "li", "dt", "dd"
1279 $htmlnest = array( # Tags that can be nested--??
1280 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1281 "dl", "font", "big", "small", "sub", "sup"
1283 $tabletags = array( # Can only appear inside table
1287 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1288 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1290 $htmlattrs = $this->getHTMLattrs () ;
1292 # Remove HTML comments
1293 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1295 $bits = explode( "<", $text );
1296 $text = array_shift( $bits );
1297 $tagstack = array(); $tablestack = array();
1299 foreach ( $bits as $x ) {
1300 $prev = error_reporting( E_ALL
& ~
( E_NOTICE | E_WARNING
) );
1301 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1303 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1304 error_reporting( $prev );
1307 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1311 if ( ! in_array( $t, $htmlsingle ) &&
1312 ( $ot = array_pop( $tagstack ) ) != $t ) {
1313 array_push( $tagstack, $ot );
1316 if ( $t == "table" ) {
1317 $tagstack = array_pop( $tablestack );
1322 # Keep track for later
1323 if ( in_array( $t, $tabletags ) &&
1324 ! in_array( "table", $tagstack ) ) {
1326 } else if ( in_array( $t, $tagstack ) &&
1327 ! in_array ( $t , $htmlnest ) ) {
1329 } else if ( ! in_array( $t, $htmlsingle ) ) {
1330 if ( $t == "table" ) {
1331 array_push( $tablestack, $tagstack );
1332 $tagstack = array();
1334 array_push( $tagstack, $t );
1336 # Strip non-approved attributes from the tag
1337 $newparams = preg_replace(
1338 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1339 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1343 $rest = str_replace( ">", ">", $rest );
1344 $text .= "<$slash$t$newparams$brace$rest";
1348 $text .= "<" . str_replace( ">", ">", $x);
1350 # Close off any remaining tags
1351 while ( $t = array_pop( $tagstack ) ) {
1353 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1355 wfProfileOut( $fname );
1362 * This function accomplishes several tasks:
1363 * 1) Auto-number headings if that option is enabled
1364 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1365 * 3) Add a Table of contents on the top for users who have enabled the option
1366 * 4) Auto-anchor headings
1368 * It loops through all headlines, collects the necessary data, then splits up the
1369 * string and re-inserts the newly formatted headlines.
1372 /* private */ function formatHeadings( $text )
1374 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1375 $nh=$wgUser->getOption( "numberheadings" );
1376 $st=$wgUser->getOption( "showtoc" );
1377 if(!$wgTitle->userCanEdit()) {
1381 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1382 $esr=$wgUser->getID() && $wgUser->getOption( "editsectiononrightclick" );
1385 # Inhibit editsection links if requested in the page
1387 $esw=& MagicWord
::get(MAG_NOEDITSECTION
);
1388 if ($esw->matchAndRemove( $text )) {
1392 # if the string __NOTOC__ (not case-sensitive) occurs in the HTML,
1394 $mw =& MagicWord
::get( MAG_NOTOC
);
1395 if ($mw->matchAndRemove( $text ))
1400 # never add the TOC to the Main Page. This is an entry page that should not
1401 # be more than 1-2 screens large anyway
1402 if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1404 # We need this to perform operations on the HTML
1405 $sk=$wgUser->getSkin();
1407 # Get all headlines for numbering them and adding funky stuff like [edit]
1409 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1414 # Ugh .. the TOC should have neat indentation levels which can be
1415 # passed to the skin functions. These are determined here
1416 foreach($matches[3] as $headline) {
1417 if($level) { $prevlevel=$level;}
1418 $level=$matches[1][$c];
1419 if(($nh||
$st) && $prevlevel && $level>$prevlevel) {
1421 $h[$level]=0; // reset when we enter a new level
1422 $toc.=$sk->tocIndent($level-$prevlevel);
1423 $toclevel+
=$level-$prevlevel;
1426 if(($nh||
$st) && $level<$prevlevel) {
1427 $h[$level+
1]=0; // reset when we step back a level
1428 $toc.=$sk->tocUnindent($prevlevel-$level);
1429 $toclevel-=$prevlevel-$level;
1432 $h[$level]++
; // count number of headlines for each level
1435 for($i=1;$i<=$level;$i++
) {
1437 if($dot) {$numbering.=".";}
1444 // The canonized header is a version of the header text safe to use for links
1446 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1447 $tocline = trim( $canonized_headline );
1448 $canonized_headline=str_replace('"',"",$canonized_headline);
1449 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1450 $refer[$c]=$canonized_headline;
1451 $refers[$canonized_headline]++
; // count how many in assoc. array so we can track dupes in anchors
1452 $refcount[$c]=$refers[$canonized_headline];
1454 // Prepend the number to the heading text
1457 $tocline=$numbering ." ". $tocline;
1459 // Don't number the heading if it is the only one (looks silly)
1460 if($nh && count($matches[3]) > 1) {
1461 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1465 // Create the anchor for linking from the TOC to the section
1467 $anchor=$canonized_headline;
1468 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1470 $toc.=$sk->tocLine($anchor,$tocline,$toclevel);
1472 if($es && !isset($wpPreview)) {
1473 $head[$c].=$sk->editSectionLink($c+
1);
1476 // Put it all together
1478 $head[$c].="<h".$level.$matches[2][$c]
1479 ."<a name=\"".$anchor."\">"
1484 // Add the edit section link
1486 if($esr && !isset($wpPreview)) {
1487 $head[$c]=$sk->editSectionScript($c+
1,$head[$c]);
1497 $toc.=$sk->tocUnindent($toclevel);
1498 $toc=$sk->tocTable($toc);
1501 // split up and insert constructed headlines
1503 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1506 foreach($blocks as $block) {
1507 if(($es) && !isset($wpPreview) && $c>0 && $i==0) {
1508 # This is the [edit] link that appears for the top block of text when
1509 # section editing is enabled
1510 $full.=$sk->editSectionLink(0);
1513 if($st && $toclines>3 && !$i) {
1514 # Let's add a top anchor just in case we want to link to the top of the page
1515 $full="<a name=\"top\"></a>".$full.$toc;
1525 /* private */ function magicISBN( $text )
1529 $a = split( "ISBN ", " $text" );
1530 if ( count ( $a ) < 2 ) return $text;
1531 $text = substr( array_shift( $a ), 1);
1532 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1534 foreach ( $a as $x ) {
1535 $isbn = $blank = "" ;
1536 while ( " " == $x{0} ) {
1538 $x = substr( $x, 1 );
1540 while ( strstr( $valid, $x{0} ) != false ) {
1542 $x = substr( $x, 1 );
1544 $num = str_replace( "-", "", $isbn );
1545 $num = str_replace( " ", "", $num );
1548 $text .= "ISBN $blank$x";
1550 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1551 "Booksources"), "isbn={$num}" ) . "\" class=\"internal\">ISBN $isbn</a>";
1558 /* private */ function magicRFC( $text )
1563 /* private */ function headElement()
1565 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding, $wgLang;
1567 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
1569 if ( "" == $this->mHTMLtitle
) {
1570 $this->mHTMLtitle
= $this->mPagetitle
;
1572 $rtl = $wgLang->isRTL() ?
" dir='RTL'" : "";
1573 $ret .= "<html lang=\"$wgLanguageCode\"$rtl><head><title>{$this->mHTMLtitle}</title>\n";
1574 array_push( $this->mMetatags
, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1575 foreach ( $this->mMetatags
as $tag ) {
1576 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1578 $tag[0] = substr( $tag[0], 5 );
1582 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1584 $p = $this->mRobotpolicy
;
1585 if ( "" == $p ) { $p = "index,follow"; }
1586 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1588 if ( count( $this->mKeywords
) > 0 ) {
1589 $ret .= "<meta name=\"keywords\" content=\"" .
1590 implode( ",", $this->mKeywords
) . "\">\n";
1592 foreach ( $this->mLinktags
as $tag ) {
1594 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1595 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1596 $ret .= "href=\"{$tag[2]}\">\n";
1598 $sk = $wgUser->getSkin();
1599 $ret .= $sk->getHeadScripts();
1600 $ret .= $sk->getUserStyles();
1602 $ret .= "</head>\n";
1607 # Regex callbacks, used in OutputPage::replaceVariables
1609 # Just get rid of the dangerous stuff
1610 # Necessary because replaceVariables is called after removeHTMLtags,
1611 # and message text can come from any user
1612 function wfReplaceMsgVar( $matches ) {
1614 $text = $wgOut->removeHTMLtags( wfMsg( $matches[1] ) );
1618 # Effective <nowiki></nowiki>
1619 # Not real <nowiki> because this is called after nowiki sections are processed
1620 function wfReplaceMsgnwVar( $matches ) {
1621 $text = wfEscapeWikiText( wfMsg( $matches[1] ) );