4 function linkToMathImage ( $tex, $outputhash )
7 return "<img src=\"".$wgMathPath."/".$outputhash.".png\" alt=\"".wfEscapeHTML($tex)."\">";
10 function renderMath( $tex )
12 global $wgUser, $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding;
13 $mf = wfMsg( "math_failure" );
14 $munk = wfMsg( "math_unknown_error" );
16 $fname = "renderMath";
18 $math = $wgUser->getOption("math");
20 return ('$ '.wfEscapeHTML($tex).' $');
23 $md5_sql = mysql_escape_string(pack("H32", $md5));
25 $sql = "SELECT math_outputhash FROM math WHERE math_inputhash = '".$md5_sql."'";
27 $sql = "SELECT math_outputhash,math_html_conservativeness,math_html FROM math WHERE math_inputhash = '".$md5_sql."'";
29 $res = wfQuery( $sql, $fname );
30 if ( wfNumRows( $res ) == 0 )
32 $cmd = "./math/texvc ".escapeshellarg($wgTmpDirectory)." ".
33 escapeshellarg($wgMathDirectory)." ".escapeshellarg($tex)." ".escapeshellarg($wgInputEncoding);
36 if (strlen($contents) == 0)
37 return "<b>".$mf." (".$munk."): ".wfEscapeHTML($tex)."</b>";
38 $retval = substr ($contents, 0, 1);
39 if (($retval == "C") ||
($retval == "M") ||
($retval == "L")) {
41 $conservativeness = 2;
42 else if ($retval == "M")
43 $conservativeness = 1;
45 $conservativeness = 0;
46 $outdata = substr ($contents, 33);
48 $i = strpos($outdata, "\000");
50 $outhtml = substr($outdata, 0, $i);
51 $mathml = substr($outdata, $i+
1);
53 $sql_html = "'".mysql_escape_string($outhtml)."'";
54 $sql_mathml = "'".mysql_escape_string($mathml)."'";
55 } else if (($retval == "c") ||
($retval == "m") ||
($retval == "l")) {
56 $outhtml = substr ($contents, 33);
58 $conservativeness = 2;
59 else if ($retval == "m")
60 $conservativeness = 1;
62 $conservativeness = 0;
63 $sql_html = "'".mysql_escape_string($outhtml)."'";
66 } else if ($retval == "X") {
68 $mathml = substr ($contents, 33);
70 $sql_mathml = "'".mysql_escape_string($mathml)."'";
71 $conservativeness = 0;
72 } else if ($retval == "+") {
77 $conservativeness = 0;
80 $errmsg = wfMsg( "math_lexing_error" );
81 else if ($retval == "S")
82 $errmsg = wfMsg( "math_syntax_error" );
83 else if ($retval == "F")
84 $errmsg = wfMsg( "math_unknown_function" );
87 return "<h3>".$mf." (".$errmsg.substr($contents, 1)."): ".wfEscapeHTML($tex)."</h3>";
90 $outmd5 = substr ($contents, 1, 32);
91 if (!preg_match("/^[a-f0-9]{32}$/", $outmd5))
92 return "<b>".$mf." (".$munk."): ".wfEscapeHTML($tex)."</b>";
94 $outmd5_sql = mysql_escape_string(pack("H32", $outmd5));
96 $sql = "REPLACE INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$conservativeness.", ".$sql_html.", ".$sql_mathml.")";
98 $res = wfQuery( $sql, $fname );
99 # we don't really care if it fails
101 if (($math == 0) ||
($rpage->math_html
== '') ||
(($math == 1) && ($conservativeness != 2)) ||
(($math == 4) && ($conservativeness == 0)))
102 return linkToMathImage($tex, $outmd5);
106 $rpage = wfFetchObject ( $res );
107 $outputhash = unpack( "H32md5", $rpage->math_outputhash
. " " );
108 $outputhash = $outputhash ['md5'];
110 if (($math == 0) ||
($rpage->math_html
== '') ||
(($math == 1) && ($rpage->math_html_conservativeness
!= 2)) ||
(($math == 4) && ($rpage->math_html_conservativeness
== 0)))
111 return linkToMathImage ( $tex, $outputhash );
113 return $rpage->math_html
;
118 var $mHeaders, $mCookies, $mMetatags, $mKeywords;
119 var $mLinktags, $mPagetitle, $mBodytext, $mDebugtext;
120 var $mHTMLtitle, $mRobotpolicy, $mIsarticle, $mPrintable;
121 var $mSubtitle, $mRedirect, $mAutonumber, $mHeadtext;
122 var $mLastModified, $mCategoryLinks;
124 var $mDTopen, $mLastSection; # Used for processing DL, PRE
125 var $mLanguageLinks, $mSupressQuickbar;
127 function OutputPage()
129 $this->mHeaders
= $this->mCookies
= $this->mMetatags
=
130 $this->mKeywords
= $this->mLinktags
= array();
131 $this->mHTMLtitle
= $this->mPagetitle
= $this->mBodytext
=
132 $this->mLastSection
= $this->mRedirect
= $this->mLastModified
=
133 $this->mSubtitle
= $this->mDebugtext
= $this->mRobotpolicy
= "";
134 $this->mIsarticle
= $this->mPrintable
= true;
135 $this->mSupressQuickbar
= $this->mDTopen
= $this->mPrintable
= false;
136 $this->mLanguageLinks
= array();
137 $this->mCategoryLinks
= array() ;
138 $this->mAutonumber
= 0;
141 function addHeader( $name, $val ) { array_push( $this->mHeaders
, "$name: $val" ) ; }
142 function addCookie( $name, $val ) { array_push( $this->mCookies
, array( $name, $val ) ); }
143 function redirect( $url ) { $this->mRedirect
= $url; }
145 # To add an http-equiv meta tag, precede the name with "http:"
146 function addMeta( $name, $val ) { array_push( $this->mMetatags
, array( $name, $val ) ); }
147 function addKeyword( $text ) { array_push( $this->mKeywords
, $text ); }
148 function addLink( $rel, $rev, $target ) { array_push( $this->mLinktags
, array( $rel, $rev, $target ) ); }
150 function checkLastModified ( $timestamp )
152 global $wgLang, $wgCachePages, $wgUser;
153 if( !$wgCachePages ) {
154 wfDebug( "CACHE DISABLED\n", false );
157 if( preg_match( '/MSIE ([1-4]|5\.0)/', $_SERVER["HTTP_USER_AGENT"] ) ) {
158 # IE 5.0 has probs with our caching
159 wfDebug( "-- bad client, not caching\n", false );
162 if( $wgUser->getOption( "nocache" ) ) {
163 wfDebug( "USER DISABLED CACHE\n", false );
167 $lastmod = gmdate( "D, j M Y H:i:s", wfTimestamp2Unix(
168 max( $timestamp, $wgUser->mTouched
) ) ) . " GMT";
170 if( $_SERVER["HTTP_IF_MODIFIED_SINCE"] != "" ) {
171 # IE sends sizes after the date for compressed pages:
172 # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
173 # this breaks strtotime().
174 $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
175 $ismodsince = wfUnix2Timestamp( strtotime( $modsince ) );
176 wfDebug( "-- client send If-Modified-Since: " . $modsince . "\n", false );
177 wfDebug( "-- we might send Last-Modified : $lastmod\n", false );
179 if( ($ismodsince >= $timestamp ) and $wgUser->validateCache( $ismodsince ) ) {
180 # Make sure you're in a place you can leave when you call us!
181 header( "HTTP/1.0 304 Not Modified" );
182 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
183 header( "Cache-Control: private, must-revalidate, max-age=0" );
184 header( "Last-Modified: {$lastmod}" );
185 wfDebug( "CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
188 wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
189 $this->mLastModified
= $lastmod;
192 wfDebug( "We're confused.\n", false );
193 $this->mLastModified
= $lastmod;
197 function setRobotpolicy( $str ) { $this->mRobotpolicy
= $str; }
198 function setHTMLtitle( $name ) { $this->mHTMLtitle
= $name; }
199 function setPageTitle( $name ) { $this->mPagetitle
= $name; }
200 function getPageTitle() { return $this->mPagetitle
; }
201 function setSubtitle( $str ) { $this->mSubtitle
= $str; }
202 function getSubtitle() { return $this->mSubtitle
; }
203 function setArticleFlag( $v ) { $this->mIsarticle
= $v; }
204 function isArticle() { return $this->mIsarticle
; }
205 function setPrintable() { $this->mPrintable
= true; }
206 function isPrintable() { return $this->mPrintable
; }
208 function getLanguageLinks() {
209 global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode;
210 global $wgDBconnection, $wgDBname, $wgDBintlname;
212 if ( ! $wgUseNewInterlanguage )
213 return $this->mLanguageLinks
;
215 mysql_select_db( $wgDBintlname, $wgDBconnection ) or die(
216 htmlspecialchars(mysql_error()) );
219 $sql = "SELECT * FROM ilinks WHERE lang_from=\"" .
220 "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\"";
221 $res = mysql_query( $sql, $wgDBconnection );
223 while ( $q = mysql_fetch_object ( $res ) ) {
224 $list[] = $q->lang_to
. ":" . $q->title_to
;
226 mysql_free_result( $res );
227 mysql_select_db( $wgDBname, $wgDBconnection ) or die(
228 htmlspecialchars(mysql_error()) );
233 function supressQuickbar() { $this->mSupressQuickbar
= true; }
234 function isQuickbarSupressed() { return $this->mSupressQuickbar
; }
236 function addHTML( $text ) { $this->mBodytext
.= $text; }
237 function addHeadtext( $text ) { $this->mHeadtext
.= $text; }
238 function debug( $text ) { $this->mDebugtext
.= $text; }
240 # First pass--just handle <nowiki> sections, pass the rest off
241 # to doWikiPass2() which does all the real work.
244 function addWikiText( $text, $linestart = true )
247 wfProfileIn( "OutputPage::addWikiText" );
248 $unique = "3iyZiyA7iMwg5rhxP0Dcc9oTnj8qD1jm1Sfv4";
249 $unique2 = "4LIQ9nXtiYFPCSfitVwDw7EYwQlL4GeeQ7qSO";
250 $unique3 = "fPaA8gDfdLBqzj68Yjg9Hil3qEF8JGO0uszIp";
261 while ( "" != $text ) {
262 $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
264 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $text = ""; }
266 $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
268 $nwlist[$nwsecs] = wfEscapeHTMLTagsOnly($q[0]);
269 $stripped .= $unique;
275 while ( "" != $stripped ) {
276 $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 );
278 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $stripped = ""; }
280 $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 );
282 $mathlist[$mathsecs] = renderMath($q[0]);
283 $stripped2 .= $unique2;
288 $stripped2 = $stripped;
291 while ( "" != $stripped2 ) {
292 $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 );
294 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $stripped2 = ""; }
296 $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 );
298 $prelist[$presecs] = "<pre>". wfEscapeHTMLTagsOnly($q[0]). "</pre>";
299 $stripped3 .= $unique3;
304 $text = $this->doWikiPass2( $stripped3, $linestart );
306 $specialChars = array("\\", "$");
307 $escapedChars = array("\\\\", "\\$");
308 for ( $i = 1; $i <= $presecs; ++
$i ) {
309 $text = preg_replace( "/{$unique3}/", str_replace( $specialChars,
310 $escapedChars, $prelist[$i] ), $text, 1 );
313 for ( $i = 1; $i <= $mathsecs; ++
$i ) {
314 $text = preg_replace( "/{$unique2}/", str_replace( $specialChars,
315 $escapedChars, $mathlist[$i] ), $text, 1 );
318 for ( $i = 1; $i <= $nwsecs; ++
$i ) {
319 $text = preg_replace( "/{$unique}/", str_replace( $specialChars,
320 $escapedChars, $nwlist[$i] ), $text, 1 );
322 $this->addHTML( $text );
326 function sendCacheControl() {
328 if( $this->mLastModified
!= "" ) {
329 wfDebug( "** private caching; {$this->mLastModified} **\n", false );
330 header( "Cache-Control: private, must-revalidate, max-age=0" );
331 header( "Last-modified: {$this->mLastModified}" );
333 # We should put in Accept-Encoding, but IE chokes on anything but
334 # User-Agent in a Vary: header (at least through 6.0)
335 header( "Vary: User-Agent" );
338 wfDebug( "** no caching **\n", false );
339 header( "Cache-Control: no-cache" ); # Experimental - see below
340 header( "Pragma: no-cache" );
341 header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
343 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
346 # Finally, all the text has been munged and accumulated into
347 # the object, let's actually output it:
351 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
352 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
353 wfProfileIn( "OutputPage::output" );
354 $sk = $wgUser->getSkin();
356 wfProfileIn( "OutputPage::output-headers" );
357 $this->sendCacheControl();
359 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
360 header( "Content-language: {$wgLanguageCode}" );
362 if ( "" != $this->mRedirect
) {
363 header( "Location: {$this->mRedirect}" );
368 $exp = time() +
$wgCookieExpiration;
369 foreach( $this->mCookies
as $name => $val ) {
370 setcookie( $name, $val, $exp, "/" );
374 wfProfileIn( "OutputPage::output-middle" );
376 $this->out( $this->headElement() );
378 $this->out( "\n<body" );
379 $ops = $sk->getBodyOptions();
380 foreach ( $ops as $name => $val ) {
381 $this->out( " $name='$val'" );
384 if ( $wgDebugComments ) {
385 $this->out( "<!-- Wiki debugging output:\n" .
386 $this->mDebugtext
. "-->\n" );
388 $this->out( $sk->beforeContent() );
391 wfProfileIn( "OutputPage::output-bodytext" );
392 $this->out( $this->mBodytext
);
394 wfProfileIn( "OutputPage::output-after" );
395 $this->out( $sk->afterContent() );
398 wfProfileOut(); # A hack - we can't report after here
399 $this->out( $this->reportTime() );
401 $this->out( "\n</body></html>" );
407 global $wgInputEncoding, $wgOutputEncoding, $wgLang;
408 if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
411 $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
412 if ( false === $outs ) { $outs = $ins; }
417 function setEncodings()
419 global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
420 global $wgUser, $wgLang;
422 $wgInputEncoding = strtolower( $wgInputEncoding );
423 $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
425 if( $wgUser->getOption( 'altencoding' ) ) {
426 $wgLang->setAltEncoding();
431 $wgOutputEncoding = strtolower( $wgOutputEncoding );
434 $a = explode( ",", $s );
438 foreach ( $a as $s ) {
439 if ( preg_match( "/(.*);q=(.*)/", $s, $m ) ) {
451 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
452 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
453 $wgOutputEncoding = strtolower( $bestset );
457 $wgOutputEncoding = $wgInputEncoding;
460 function reportTime()
462 global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
463 global $wgProfiling, $wgProfileStack, $wgUser;
465 list( $usec, $sec ) = explode( " ", microtime() );
466 $now = (float)$sec +
(float)$usec;
468 list( $usec, $sec ) = explode( " ", $wgRequestTime );
469 $start = (float)$sec +
(float)$usec;
470 $elapsed = $now - $start;
472 if ( "" != $wgDebugLogFile ) {
474 if( $wgProfiling and count( $wgProfileStack ) ) {
476 foreach( $wgProfileStack as $ile ) {
477 # "foo::bar 99 0.12345 1 0.23456 2"
478 if( preg_match( '/^(\S+)\s+([0-9]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/', $ile, $m ) ) {
479 $thisstart = (float)$m[3] +
(float)$m[4] - $start;
480 $thisend = (float)$m[5] +
(float)$m[6] - $start;
481 $thiselapsed = $thisend - $thisstart;
482 $thispercent = $thiselapsed / $elapsed * 100.0;
484 $prof .= sprintf( "\tat %04.3f in %04.3f (%2.1f%%) - %s %s\n",
485 $thisstart, $thiselapsed, $thispercent,
486 str_repeat( "*", $m[2] ), $m[1] );
487 $lasttime = $thistime;
488 #$prof .= "\t(^ $ile)\n";
490 $prof .= "\t?broken? $ile\n";
495 if( $forward = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'] )
496 $forward = " forwarded for $forward";
497 if( $client = $HTTP_SERVER_VARS['HTTP_CLIENT_IP'] )
498 $forward .= " client IP $client";
499 if( $from = $HTTP_SERVER_VARS['HTTP_FROM'] )
500 $forward .= " from $from";
502 $forward = "\t(proxied via {$HTTP_SERVER_VARS['REMOTE_ADDR']}{$forward})";
503 if($wgUser->getId() == 0)
505 $log = sprintf( "%s\t%04.3f\t%s\n",
506 gmdate( "YmdHis" ), $elapsed,
507 urldecode( $HTTP_SERVER_VARS['REQUEST_URI'] . $forward ) );
508 error_log( $log . $prof, 3, $wgDebugLogFile );
510 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
515 # Note: these arguments are keys into wfMsg(), not text!
517 function errorpage( $title, $msg )
521 $this->mDebugtext
.= "Original title: " .
522 $wgTitle->getPrefixedText() . "\n";
523 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
524 $this->setPageTitle( wfMsg( $title ) );
525 $this->setRobotpolicy( "noindex,nofollow" );
526 $this->setArticleFlag( false );
528 $this->mBodytext
= "";
529 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
530 $this->returnToMain( false );
536 function sysopRequired()
540 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
541 $this->setPageTitle( wfMsg( "sysoptitle" ) );
542 $this->setRobotpolicy( "noindex,nofollow" );
543 $this->setArticleFlag( false );
544 $this->mBodytext
= "";
546 $sk = $wgUser->getSkin();
547 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
548 $text = str_replace( "$1", $ap, wfMsg( "sysoptext" ) );
549 $this->addHTML( $text );
550 $this->returnToMain();
553 function developerRequired()
557 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
558 $this->setPageTitle( wfMsg( "developertitle" ) );
559 $this->setRobotpolicy( "noindex,nofollow" );
560 $this->setArticleFlag( false );
561 $this->mBodytext
= "";
563 $sk = $wgUser->getSkin();
564 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
565 $text = str_replace( "$1", $ap, wfMsg( "developertext" ) );
566 $this->addHTML( $text );
567 $this->returnToMain();
570 function databaseError( $fname )
572 global $wgUser, $wgCommandLineMode;
574 $this->setPageTitle( wfMsg( "databaseerror" ) );
575 $this->setRobotpolicy( "noindex,nofollow" );
576 $this->setArticleFlag( false );
578 if ( $wgCommandLineMode ) {
579 $msg = wfMsg( "dberrortextcl" );
581 $msg = wfMsg( "dberrortextcl" );
583 $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), $msg );
584 $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg );
585 $msg = str_replace( "$3", wfLastErrno(), $msg );
586 $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg );
588 if ( $wgCommandLineMode ) {
592 $sk = $wgUser->getSkin();
593 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
594 wfMsg( "searchingwikipedia" ) );
595 $msg = str_replace( "$5", $shlink, $msg );
597 $this->mBodytext
= $msg;
602 function readOnlyPage()
604 global $wgUser, $wgReadOnlyFile;
606 $this->setPageTitle( wfMsg( "readonly" ) );
607 $this->setRobotpolicy( "noindex,nofollow" );
608 $this->setArticleFlag( false );
610 $reason = implode( "", file( $wgReadOnlyFile ) );
611 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
612 $this->addHTML( $text );
613 $this->returnToMain( false );
616 function fatalError( $message )
618 $this->setPageTitle( wfMsg( "internalerror" ) );
619 $this->setRobotpolicy( "noindex,nofollow" );
620 $this->setArticleFlag( false );
622 $this->mBodytext
= $message;
627 function unexpectedValueError( $name, $val )
629 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
630 $msg = str_replace( "$2", $val, $msg );
631 $this->fatalError( $msg );
634 function fileCopyError( $old, $new )
636 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
637 $msg = str_replace( "$2", $new, $msg );
638 $this->fatalError( $msg );
641 function fileRenameError( $old, $new )
643 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
644 $msg = str_replace( "$2", $new, $msg );
645 $this->fatalError( $msg );
648 function fileDeleteError( $name )
650 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
651 $this->fatalError( $msg );
654 function fileNotFoundError( $name )
656 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
657 $this->fatalError( $msg );
660 function returnToMain( $auto = true )
662 global $wgUser, $wgOut, $returnto;
664 $sk = $wgUser->getSkin();
665 if ( "" == $returnto ) {
666 $returnto = wfMsg( "mainpage" );
668 $link = $sk->makeKnownLink( $returnto, "" );
670 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
672 $wgOut->addMeta( "http:Refresh", "10;url=" .
673 wfLocalUrlE( wfUrlencode( $returnto ) ) );
675 $wgOut->addHTML( "\n<p>$r\n" );
679 function categoryMagic ()
681 global $wgTitle , $wgUseCategoryMagic ;
682 if ( !isset ( $wgUseCategoryMagic ) ||
!$wgUseCategoryMagic ) return ;
683 $id = $wgTitle->getArticleID() ;
684 $cat = ucfirst ( wfMsg ( "category" ) ) ;
685 $ti = $wgTitle->getText() ;
686 $ti = explode ( ":" , $ti , 2 ) ;
687 if ( $cat != $ti[0] ) return "" ;
688 $r = "<br break=all>\n" ;
690 $articles = array() ;
691 $parents = array () ;
692 $children = array() ;
696 $sk = $wgUser->getSkin() ;
697 $sql = "SELECT l_from FROM links WHERE l_to={$id}" ;
698 $res = wfQuery ( $sql ) ;
699 while ( $x = wfFetchObject ( $res ) )
702 # $t->newFromDBkey ( $x->l_from ) ;
703 # $t = $t->getText() ;
705 $y = explode ( ":" , $t , 2 ) ;
706 if ( count ( $y ) == 2 && $y[0] == $cat )
708 array_push ( $children , $sk->makeLink ( $t , $y[1] ) ) ;
710 else array_push ( $articles , $sk->makeLink ( $t ) ) ;
712 wfFreeResult ( $res ) ;
715 if ( count ( $children ) > 0 )
717 asort ( $children ) ;
718 $r .= "<h2>".wfMsg("subcategories")."</h2>\n" ;
719 $r .= implode ( ", " , $children ) ;
723 if ( count ( $articles ) > 0 )
725 asort ( $articles ) ;
726 $h = str_replace ( "$1" , $ti[1] , wfMsg("category_header") ) ;
727 $r .= "<h2>{$h}</h2>\n" ;
728 $r .= implode ( ", " , $articles ) ;
736 # Well, OK, it's actually about 14 passes. But since all the
737 # hard lifting is done inside PHP's regex code, it probably
738 # wouldn't speed things up much to add a real parser.
740 function doWikiPass2( $text, $linestart )
742 global $wgUser, $wgLang, $wgUseDynamicDates;
743 wfProfileIn( "OutputPage::doWikiPass2" );
745 $text = $this->removeHTMLtags( $text );
746 $text = $this->replaceVariables( $text );
748 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
749 $text = str_replace ( "<HR>", "<hr>", $text );
751 $text = $this->doAllQuotes( $text );
752 $text = $this->doHeadings( $text );
753 $text = $this->doBlockLevels( $text, $linestart );
755 if($wgUseDynamicDates) {
756 $text = $wgLang->replaceDates( $text );
759 $text = $this->replaceExternalLinks( $text );
760 $text = $this->replaceInternalLinks ( $text );
762 $text = $this->magicISBN( $text );
763 $text = $this->magicRFC( $text );
764 $text = $this->formatHeadings( $text );
766 $sk = $wgUser->getSkin();
767 $text = $sk->transformContent( $text );
768 $text .= $this->categoryMagic () ;
774 /* private */ function doAllQuotes( $text )
777 $lines = explode( "\r\n", $text );
778 foreach ( $lines as $line ) {
779 $outtext .= $this->doQuotes ( "", $line, "" ) . "\r\n";
784 /* private */ function doQuotes( $pre, $text, $mode )
786 if ( preg_match( "/^(.*)''(.*)$/sU", $text, $m ) ) {
787 $m1_strong = ($m[1] == "") ?
"" : "<strong>{$m[1]}</strong>";
788 $m1_em = ($m[1] == "") ?
"" : "<em>{$m[1]}</em>";
789 if ( substr ($m[2], 0, 1) == "'" ) {
790 $m[2] = substr ($m[2], 1);
792 return $this->doQuotes ( $m[1], $m[2], ($m[1] == "") ?
"both" : "emstrong" );
793 } else if ($mode == "strong") {
794 return $m1_strong . $this->doQuotes ( "", $m[2], "" );
795 } else if (($mode == "emstrong") ||
($mode == "both")) {
796 return $this->doQuotes ( "", $pre.$m1_strong.$m[2], "em" );
797 } else if ($mode == "strongem") {
798 return "<strong>{$pre}{$m1_em}</strong>" . $this->doQuotes ( "", $m[2], "em" );
800 return $m[1] . $this->doQuotes ( "", $m[2], "strong" );
803 if ($mode == "strong") {
804 return $this->doQuotes ( $m[1], $m[2], ($m[1] == "") ?
"both" : "strongem" );
805 } else if ($mode == "em") {
806 return $m1_em . $this->doQuotes ( "", $m[2], "" );
807 } else if ($mode == "emstrong") {
808 return "<em>{$pre}{$m1_strong}</em>" . $this->doQuotes ( "", $m[2], "strong" );
809 } else if (($mode == "strongem") ||
($mode == "both")) {
810 return $this->doQuotes ( "", $pre.$m1_em.$m[2], "strong" );
812 return $m[1] . $this->doQuotes ( "", $m[2], "em" );
816 $text_strong = ($text == "") ?
"" : "<strong>{$text}</strong>";
817 $text_em = ($text == "") ?
"" : "<em>{$text}</em>";
820 } else if ($mode == "em") {
821 return $pre . $text_em;
822 } else if ($mode == "strong") {
823 return $pre . $text_strong;
824 } else if ($mode == "strongem") {
825 return (($pre == "") && ($text == "")) ?
"" : "<strong>{$pre}{$text_em}</strong>";
827 return (($pre == "") && ($text == "")) ?
"" : "<em>{$pre}{$text_strong}</em>";
832 /* private */ function doHeadings( $text )
834 for ( $i = 6; $i >= 1; --$i ) {
835 $h = substr( "======", 0, $i );
836 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
837 "<h{$i}>\\1</h{$i}>\\2", $text );
842 # Note: we have to do external links before the internal ones,
843 # and otherwise take great care in the order of things here, so
844 # that we don't end up interpreting some URLs twice.
846 /* private */ function replaceExternalLinks( $text )
848 wfProfileIn( "OutputPage::replaceExternalLinks" );
849 $text = $this->subReplaceExternalLinks( $text, "http", true );
850 $text = $this->subReplaceExternalLinks( $text, "https", true );
851 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
852 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
853 $text = $this->subReplaceExternalLinks( $text, "news", false );
854 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
859 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
861 global $wgUser, $printable;
862 global $wgAllowExternalImages;
865 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
866 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
868 # this is the list of separators that should be ignored if they
869 # are the last character of an URL but that should be included
870 # if they occur within the URL, e.g. "go to www.foo.com, where .."
871 # in this case, the last comma should not become part of the URL,
872 # but in "www.foo.com/123,2342,32.htm" it should.
874 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
875 $images = "gif|png|jpg|jpeg";
877 # PLEASE NOTE: The curly braces { } are not part of the regex,
878 # they are interpreted as part of the string (used to tell PHP
879 # that the content of the string should be inserted there).
880 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
881 "((?i){$images})([^{$uc}]|$)/";
883 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
884 $sk = $wgUser->getSkin();
886 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
887 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
888 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
890 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
891 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
892 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
894 $s = str_replace( $unique, $protocol, $s );
896 $a = explode( "[{$protocol}:", " " . $s );
897 $s = array_shift( $a );
898 $s = substr( $s, 1 );
900 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
901 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
903 foreach ( $a as $line ) {
904 if ( preg_match( $e1, $line, $m ) ) {
905 $link = "{$protocol}:{$m[1]}";
907 if ( $autonumber ) { $text = "[" . ++
$this->mAutonumber
. "]"; }
908 else { $text = wfEscapeHTML( $link ); }
909 } else if ( preg_match( $e2, $line, $m ) ) {
910 $link = "{$protocol}:{$m[1]}";
914 $s .= "[{$protocol}:" . $line;
917 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
919 $la = $sk->getExternalLinkAttributes( $link, $text );
920 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
926 /* private */ function replaceInternalLinks( $s )
928 global $wgTitle, $wgUser, $wgLang;
929 global $wgLinkCache, $wgInterwikiMagic, $wgUseCategoryMagic;
930 global $wgNamespacesWithSubpages, $wgLanguageCode;
931 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
933 wfProfileIn( "$fname-setup" );
934 $tc = Title
::legalChars() . "#";
935 $sk = $wgUser->getSkin();
937 $a = explode( "[[", " " . $s );
938 $s = array_shift( $a );
939 $s = substr( $s, 1 );
941 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
942 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
945 wfProfileIn( "$fname-loop" );
946 foreach ( $a as $line ) {
947 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
952 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
958 else { # Invalid form; output directly
962 if(substr($m[1],0,1)=="/") { # subpage
963 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
964 $m[1]=substr($m[1],1,strlen($m[1])-2);
968 $noslash=substr($m[1],1);
970 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
971 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
974 } # this might be changed for ugliness reasons
976 $link = $noslash; # no subpage allowed, use standard link
978 } else { # no subpage
982 if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
983 $pre = strtolower( $m[1] );
985 if ( $wgLang->getNsIndex( $pre ) ==
986 Namespace::getImage() ) {
987 $nt = Title
::newFromText( $suf );
988 $name = $nt->getDBkey();
989 if ( "" == $text ) { $text = $nt->GetText(); }
991 $wgLinkCache->addImageLink( $name );
992 $s .= $sk->makeImageLink( $name,
993 wfImageUrl( $name ), $text );
995 } else if ( "media" == $pre ) {
996 $nt = Title
::newFromText( $suf );
997 $name = $nt->getDBkey();
998 if ( "" == $text ) { $text = $nt->GetText(); }
1000 $wgLinkCache->addImageLink( $name );
1001 $s .= $sk->makeMediaLink( $name,
1002 wfImageUrl( $name ), $text );
1004 } else if ( isset($wgUseCategoryMagic) && $wgUseCategoryMagic && $pre == wfMsg ( "category" ) ) {
1005 $l = $sk->makeLink ( $pre.":".ucfirst($m[2]) , ucfirst ( $m[2] ) ) ;
1006 array_push ( $this->mCategoryLinks
, $l ) ;
1009 $l = $wgLang->getLanguageName( $pre );
1010 if ( "" == $l or !$wgInterwikiMagic or
1011 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
1012 if ( "" == $text ) { $text = $link; }
1013 $s .= $sk->makeLink( $link, $text, "", $trail );
1014 } else if ( $pre != $wgLanguageCode ) {
1015 array_push( $this->mLanguageLinks
, "$pre:$suf" );
1019 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
1020 # $link = substr( $link, 2 );
1021 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
1023 if ( "" == $text ) { $text = $link; }
1024 $s .= $sk->makeLink( $link, $text, "", $trail );
1032 # Some functions here used by doBlockLevels()
1034 /* private */ function closeParagraph()
1037 if ( 0 != strcmp( "p", $this->mLastSection
) &&
1038 0 != strcmp( "", $this->mLastSection
) ) {
1039 $result = "</" . $this->mLastSection
. ">";
1041 $this->mLastSection
= "";
1044 # getCommon() returns the length of the longest common substring
1045 # of both arguments, starting at the beginning of both.
1047 /* private */ function getCommon( $st1, $st2 )
1049 $fl = strlen( $st1 );
1050 $shorter = strlen( $st2 );
1051 if ( $fl < $shorter ) { $shorter = $fl; }
1053 for ( $i = 0; $i < $shorter; ++
$i ) {
1054 if ( $st1{$i} != $st2{$i} ) { break; }
1058 # These next three functions open, continue, and close the list
1059 # element appropriate to the prefix character passed into them.
1061 /* private */ function openList( $char )
1063 $result = $this->closeParagraph();
1065 if ( "*" == $char ) { $result .= "<ul><li>"; }
1066 else if ( "#" == $char ) { $result .= "<ol><li>"; }
1067 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
1068 else if ( ";" == $char ) {
1069 $result .= "<dl><dt>";
1070 $this->mDTopen
= true;
1072 else { $result = "<!-- ERR 1 -->"; }
1077 /* private */ function nextItem( $char )
1079 if ( "*" == $char ||
"#" == $char ) { return "</li><li>"; }
1080 else if ( ":" == $char ||
";" == $char ) {
1082 if ( $this->mDTopen
) { $close = "</dt>"; }
1083 if ( ";" == $char ) {
1084 $this->mDTopen
= true;
1085 return $close . "<dt>";
1087 $this->mDTopen
= false;
1088 return $close . "<dd>";
1091 return "<!-- ERR 2 -->";
1094 /* private */function closeList( $char )
1096 if ( "*" == $char ) { return "</li></ul>"; }
1097 else if ( "#" == $char ) { return "</li></ol>"; }
1098 else if ( ":" == $char ) {
1099 if ( $this->mDTopen
) {
1100 $this->mDTopen
= false;
1101 return "</dt></dl>";
1103 return "</dd></dl>";
1106 return "<!-- ERR 3 -->";
1109 /* private */ function doBlockLevels( $text, $linestart )
1111 wfProfileIn( "OutputPage::doBlockLevels" );
1112 # Parsing through the text line by line. The main thing
1113 # happening here is handling of block-level elements p, pre,
1114 # and making lists from lines starting with * # : etc.
1116 $a = explode( "\n", $text );
1117 $text = $lastPref = "";
1118 $this->mDTopen
= $inBlockElem = false;
1120 if ( ! $linestart ) { $text .= array_shift( $a ); }
1121 foreach ( $a as $t ) {
1122 if ( "" != $text ) { $text .= "\n"; }
1125 $opl = strlen( $lastPref );
1126 $npl = strspn( $t, "*#:;" );
1127 $pref = substr( $t, 0, $npl );
1128 $pref2 = str_replace( ";", ":", $pref );
1129 $t = substr( $t, $npl );
1131 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
1132 $text .= $this->nextItem( substr( $pref, -1 ) );
1134 if ( ";" == substr( $pref, -1 ) ) {
1135 $cpos = strpos( $t, ":" );
1136 if ( ! ( false === $cpos ) ) {
1137 $term = substr( $t, 0, $cpos );
1138 $text .= $term . $this->nextItem( ":" );
1139 $t = substr( $t, $cpos +
1 );
1142 } else if (0 != $npl ||
0 != $opl) {
1143 $cpl = $this->getCommon( $pref, $lastPref );
1145 while ( $cpl < $opl ) {
1146 $text .= $this->closeList( $lastPref{$opl-1} );
1149 if ( $npl <= $cpl && $cpl > 0 ) {
1150 $text .= $this->nextItem( $pref{$cpl-1} );
1152 while ( $npl > $cpl ) {
1153 $char = substr( $pref, $cpl, 1 );
1154 $text .= $this->openList( $char );
1156 if ( ";" == $char ) {
1157 $cpos = strpos( $t, ":" );
1158 if ( ! ( false === $cpos ) ) {
1159 $term = substr( $t, 0, $cpos );
1160 $text .= $term . $this->nextItem( ":" );
1161 $t = substr( $t, $cpos +
1 );
1168 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1170 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1171 $text .= $this->closeParagraph();
1172 $inBlockElem = true;
1174 if ( ! $inBlockElem ) {
1175 if ( " " == $t{0} ) {
1176 $newSection = "pre";
1177 # $t = wfEscapeHTML( $t );
1179 else { $newSection = "p"; }
1181 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1182 $text .= $this->closeParagraph();
1183 $text .= "<" . $newSection . ">";
1184 } else if ( 0 != strcmp( $this->mLastSection
,
1186 $text .= $this->closeParagraph();
1187 if ( 0 != strcmp( "p", $newSection ) ) {
1188 $text .= "<" . $newSection . ">";
1191 $this->mLastSection
= $newSection;
1193 if ( $inBlockElem &&
1194 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1195 $inBlockElem = false;
1201 $text .= $this->closeList( $pref2{$npl-1} );
1204 if ( "" != $this->mLastSection
) {
1205 if ( "p" != $this->mLastSection
) {
1206 $text .= "</" . $this->mLastSection
. ">";
1208 $this->mLastSection
= "";
1214 /* private */ function replaceVariables( $text )
1217 wfProfileIn( "OutputPage:replaceVariables" );
1219 /* As with sigs, use server's local time --
1220 ensure this is appropriate for your audience! */
1222 $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
1223 $v = $wgLang->getMonthName( date( "n" ) );
1224 $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
1225 $v = $wgLang->getMonthNameGen( date( "n" ) );
1226 $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
1228 $text = str_replace( "{{CURRENTDAY}}", $v, $text );
1229 $v = $wgLang->getWeekdayName( date( "w" )+
1 );
1230 $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
1232 $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
1233 $v = $wgLang->time( wfTimestampNow(), false );
1234 $text = str_replace( "{{CURRENTTIME}}", $v, $text );
1236 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1237 $v = wfNumberOfArticles();
1238 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1244 /* private */ function removeHTMLtags( $text )
1246 wfProfileIn( "OutputPage::removeHTMLtags" );
1247 $htmlpairs = array( # Tags that must be closed
1248 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1249 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1250 "strike", "strong", "tt", "var", "div", "center",
1251 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1252 "ruby", "rt" , "rb" , "rp"
1254 $htmlsingle = array(
1255 "br", "p", "hr", "li", "dt", "dd"
1257 $htmlnest = array( # Tags that can be nested--??
1258 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1259 "dl", "font", "big", "small", "sub", "sup"
1261 $tabletags = array( # Can only appear inside table
1265 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1266 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1268 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1269 "title", "align", "lang", "dir", "width", "height",
1270 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1271 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1272 /* FONT */ "type", "start", "value", "compact",
1273 /* For various lists, mostly deprecated but safe */
1274 "summary", "width", "border", "frame", "rules",
1275 "cellspacing", "cellpadding", "valign", "char",
1276 "charoff", "colgroup", "col", "span", "abbr", "axis",
1277 "headers", "scope", "rowspan", "colspan", /* Tables */
1278 "id", "class", "name", "style" /* For CSS */
1281 # Remove HTML comments
1282 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1284 $bits = explode( "<", $text );
1285 $text = array_shift( $bits );
1286 $tagstack = array(); $tablestack = array();
1288 foreach ( $bits as $x ) {
1289 $prev = error_reporting( E_ALL
& ~
( E_NOTICE | E_WARNING
) );
1290 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1292 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1293 error_reporting( $prev );
1296 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1300 if ( ! in_array( $t, $htmlsingle ) &&
1301 ( $ot = array_pop( $tagstack ) ) != $t ) {
1302 array_push( $tagstack, $ot );
1305 if ( $t == "table" ) {
1306 $tagstack = array_pop( $tablestack );
1311 # Keep track for later
1312 if ( in_array( $t, $tabletags ) &&
1313 ! in_array( "table", $tagstack ) ) {
1315 } else if ( in_array( $t, $tagstack ) &&
1316 ! in_array ( $t , $htmlnest ) ) {
1318 } else if ( ! in_array( $t, $htmlsingle ) ) {
1319 if ( $t == "table" ) {
1320 array_push( $tablestack, $tagstack );
1321 $tagstack = array();
1323 array_push( $tagstack, $t );
1325 # Strip non-approved attributes from the tag
1326 $newparams = preg_replace(
1327 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1328 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1332 $rest = str_replace( ">", ">", $rest );
1333 $text .= "<$slash$t$newparams$brace$rest";
1337 $text .= "<" . str_replace( ">", ">", $x);
1339 # Close off any remaining tags
1340 while ( $t = array_pop( $tagstack ) ) {
1342 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1351 * This function accomplishes several tasks:
1352 * 1) Auto-number headings if that option is enabled
1353 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1354 * 3) Add a Table of contents on the top for users who have enabled the option
1355 * 4) Auto-anchor headings
1357 * It loops through all headlines, collects the necessary data, then splits up the
1358 * string and re-inserts the newly formatted headlines.
1361 /* private */ function formatHeadings( $text )
1363 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1364 $nh=$wgUser->getOption( "numberheadings" );
1365 $st=$wgUser->getOption( "showtoc" );
1366 if(!$wgTitle->userCanEdit()) {
1370 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1371 $esr=$wgUser->getID() && $wgUser->getOption( "editsectiononrightclick" );
1373 # if the string __NOTOC__ (not case-sensitive) occurs in the HTML, do not
1375 if(preg_match("/__NOTOC__/i",$text)) {
1376 $text=preg_replace("/__NOTOC__/i","",$text);
1380 # never add the TOC to the Main Page. This is an entry page that should not
1381 # be more than 1-2 screens large anyway
1382 if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1384 # We need this to perform operations on the HTML
1385 $sk=$wgUser->getSkin();
1387 # Get all headlines for numbering them and adding funky stuff like [edit]
1389 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1394 # Ugh .. the TOC should have neat indentation levels which can be
1395 # passed to the skin functions. These are determined here
1396 foreach($matches[3] as $headline) {
1397 if($level) { $prevlevel=$level;}
1398 $level=$matches[1][$c];
1399 if(($nh||
$st) && $prevlevel && $level>$prevlevel) {
1401 $h[$level]=0; // reset when we enter a new level
1402 $toc.=$sk->tocIndent($level-$prevlevel);
1403 $toclevel+
=$level-$prevlevel;
1406 if(($nh||
$st) && $level<$prevlevel) {
1407 $h[$level+
1]=0; // reset when we step back a level
1408 $toc.=$sk->tocUnindent($prevlevel-$level);
1409 $toclevel-=$prevlevel-$level;
1412 $h[$level]++
; // count number of headlines for each level
1415 for($i=1;$i<=$level;$i++
) {
1417 if($dot) {$numbering.=".";}
1425 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1426 $tocline=$canonized_headline;
1427 $canonized_headline=str_replace('"',"",$canonized_headline);
1428 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1429 $refer[$c]=$canonized_headline;
1430 $refers[$canonized_headline]++
; // count how many in assoc. array so we can track dupes in anchors
1431 $refcount[$c]=$refers[$canonized_headline];
1433 $tocline=$numbering ." ". $tocline;
1435 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1438 $anchor=$canonized_headline;
1439 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1441 $toc.=$sk->tocLine($anchor,$tocline,$toclevel);
1443 if($es && !isset($wpPreview)) {
1444 $head[$c].=$sk->editSectionLink($c+
1);
1446 $head[$c].="<H".$level.$matches[2][$c]
1447 ."<a name=\"".$anchor."\">"
1451 if($esr && !isset($wpPreview)) {
1452 $head[$c]=$sk->editSectionScript($c+
1,$head[$c]);
1461 $toc.=$sk->tocUnindent($toclevel);
1462 $toc=$sk->tocTable($toc);
1465 // split up and insert constructed headlines
1467 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1471 foreach($blocks as $block) {
1472 if(($es) && !isset($wpPreview) && $c>0 && $i==0) {
1473 # This is the [edit] link that appears for the top block of text when
1474 # section editing is enabled
1475 $full.=$sk->editSectionLink(0);
1478 if($st && $toclines>3 && !$i) {
1479 # Let's add a top anchor just in case we want to link to the top of the page
1480 $full="<a name=\"top\"></a>".$full.$toc;
1489 /* private */ function magicISBN( $text )
1493 $a = split( "ISBN ", " $text" );
1494 if ( count ( $a ) < 2 ) return $text;
1495 $text = substr( array_shift( $a ), 1);
1496 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1498 foreach ( $a as $x ) {
1499 $isbn = $blank = "" ;
1500 while ( " " == $x{0} ) {
1502 $x = substr( $x, 1 );
1504 while ( strstr( $valid, $x{0} ) != false ) {
1506 $x = substr( $x, 1 );
1508 $num = str_replace( "-", "", $isbn );
1509 $num = str_replace( " ", "", $num );
1512 $text .= "ISBN $blank$x";
1514 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1515 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1522 /* private */ function magicRFC( $text )
1527 /* private */ function headElement()
1529 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding, $wgLang;
1531 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
1533 if ( "" == $this->mHTMLtitle
) {
1534 $this->mHTMLtitle
= $this->mPagetitle
;
1536 $rtl = $wgLang->isRTL() ?
" dir='RTL'" : "";
1537 $ret .= "<html lang=\"$wgLanguageCode\"$rtl><head><title>{$this->mHTMLtitle}</title>\n";
1538 array_push( $this->mMetatags
, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1539 foreach ( $this->mMetatags
as $tag ) {
1540 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1542 $tag[0] = substr( $tag[0], 5 );
1546 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1548 $p = $this->mRobotpolicy
;
1549 if ( "" == $p ) { $p = "index,follow"; }
1550 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1552 if ( count( $this->mKeywords
) > 0 ) {
1553 $ret .= "<meta name=\"keywords\" content=\"" .
1554 implode( ",", $this->mKeywords
) . "\">\n";
1556 foreach ( $this->mLinktags
as $tag ) {
1558 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1559 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1560 $ret .= "href=\"{$tag[2]}\">\n";
1562 $sk = $wgUser->getSkin();
1563 $ret .= $sk->getHeadScripts();
1564 $ret .= $sk->getUserStyles();
1566 $ret .= "</head>\n";