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 = "INSERT 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;
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->mAutonumber
= 0;
140 function addHeader( $name, $val ) { array_push( $this->mHeaders
, "$name: $val" ) ; }
141 function addCookie( $name, $val ) { array_push( $this->mCookies
, array( $name, $val ) ); }
142 function redirect( $url ) { $this->mRedirect
= $url; }
144 # To add an http-equiv meta tag, precede the name with "http:"
145 function addMeta( $name, $val ) { array_push( $this->mMetatags
, array( $name, $val ) ); }
146 function addKeyword( $text ) { array_push( $this->mKeywords
, $text ); }
147 function addLink( $rel, $rev, $target ) { array_push( $this->mLinktags
, array( $rel, $rev, $target ) ); }
149 function checkLastModified ( $timestamp )
151 global $wgLang, $wgCachePages, $wgUser;
152 if( !$wgCachePages ) return;
153 if( preg_match( '/MSIE ([1-4]|5\.0)/', $_SERVER["HTTP_USER_AGENT"] ) ) {
154 # IE 5.0 has probs with our caching
158 if( $_SERVER["HTTP_IF_MODIFIED_SINCE"] != "" ) {
159 $ismodsince = wfUnix2Timestamp( strtotime( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) );
160 $lastmod = gmdate( "D, j M Y H:i:s", wfTimestamp2Unix(
161 max( $timestamp, $wgUser->mTouched
) ) ) . " GMT";
163 if( ($ismodsince >= $timestamp ) and $wgUser->validateCache( $ismodsince ) ) {
164 # Make sure you're in a place you can leave when you call us!
165 header( "HTTP/1.0 304 Not Modified" );
166 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
167 header( "Cache-Control: private, must-revalidate, max-age=0" );
168 header( "Last-Modified: {$lastmod}" );
169 #wfDebug( "CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
172 #wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
173 $this->mLastModified
= $lastmod;
178 function setRobotpolicy( $str ) { $this->mRobotpolicy
= $str; }
179 function setHTMLtitle( $name ) { $this->mHTMLtitle
= $name; }
180 function setPageTitle( $name ) { $this->mPagetitle
= $name; }
181 function getPageTitle() { return $this->mPagetitle
; }
182 function setSubtitle( $str ) { $this->mSubtitle
= $str; }
183 function getSubtitle() { return $this->mSubtitle
; }
184 function setArticleFlag( $v ) { $this->mIsarticle
= $v; }
185 function isArticle() { return $this->mIsarticle
; }
186 function setPrintable() { $this->mPrintable
= true; }
187 function isPrintable() { return $this->mPrintable
; }
189 function getLanguageLinks() {
190 global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode;
191 global $wgDBconnection, $wgDBname, $wgDBintlname;
193 if ( ! $wgUseNewInterlanguage )
194 return $this->mLanguageLinks
;
196 mysql_select_db( $wgDBintlname, $wgDBconnection ) or die(
197 htmlspecialchars(mysql_error()) );
200 $sql = "SELECT * FROM ilinks WHERE lang_from=\"" .
201 "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\"";
202 $res = mysql_query( $sql, $wgDBconnection );
204 while ( $q = mysql_fetch_object ( $res ) ) {
205 $list[] = $q->lang_to
. ":" . $q->title_to
;
207 mysql_free_result( $res );
208 mysql_select_db( $wgDBname, $wgDBconnection ) or die(
209 htmlspecialchars(mysql_error()) );
214 function supressQuickbar() { $this->mSupressQuickbar
= true; }
215 function isQuickbarSupressed() { return $this->mSupressQuickbar
; }
217 function addHTML( $text ) { $this->mBodytext
.= $text; }
218 function addHeadtext( $text ) { $this->mHeadtext
.= $text; }
219 function debug( $text ) { $this->mDebugtext
.= $text; }
221 # First pass--just handle <nowiki> sections, pass the rest off
222 # to doWikiPass2() which does all the real work.
225 function addWikiText( $text, $linestart = true )
228 wfProfileIn( "OutputPage::addWikiText" );
229 $unique = "3iyZiyA7iMwg5rhxP0Dcc9oTnj8qD1jm1Sfv4";
230 $unique2 = "4LIQ9nXtiYFPCSfitVwDw7EYwQlL4GeeQ7qSO";
231 $unique3 = "fPaA8gDfdLBqzj68Yjg9Hil3qEF8JGO0uszIp";
242 while ( "" != $text ) {
243 $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
245 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $text = ""; }
247 $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
249 $nwlist[$nwsecs] = wfEscapeHTMLTagsOnly($q[0]);
250 $stripped .= $unique;
256 while ( "" != $stripped ) {
257 $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 );
259 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $stripped = ""; }
261 $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 );
263 $mathlist[$mathsecs] = renderMath($q[0]);
264 $stripped2 .= $unique2;
269 $stripped2 = $stripped;
272 while ( "" != $stripped2 ) {
273 $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 );
275 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $stripped2 = ""; }
277 $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 );
279 $prelist[$presecs] = "<pre>". wfEscapeHTMLTagsOnly($q[0]). "</pre>";
280 $stripped3 .= $unique3;
285 $text = $this->doWikiPass2( $stripped3, $linestart );
287 for ( $i = 1; $i <= $presecs; ++
$i ) {
288 $text = preg_replace( "/{$unique3}/", str_replace( '$', '\$', $prelist[$i] ), $text, 1 );
291 for ( $i = 1; $i <= $mathsecs; ++
$i ) {
292 $text = preg_replace( "/{$unique2}/", str_replace( '$', '\$', $mathlist[$i] ), $text, 1 );
295 for ( $i = 1; $i <= $nwsecs; ++
$i ) {
296 $text = preg_replace( "/{$unique}/", str_replace( '$', '\$', $nwlist[$i] ), $text, 1 );
298 $this->addHTML( $text );
302 # Finally, all the text has been munged and accumulated into
303 # the object, let's actually output it:
307 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
308 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
309 wfProfileIn( "OutputPage::output" );
310 $sk = $wgUser->getSkin();
312 wfProfileIn( "OutputPage::output-headers" );
313 if( $this->mLastModified
!= "" ) {
314 header( "Cache-Control: private, must-revalidate, max-age=0" );
315 header( "Last-modified: {$this->mLastModified}" );
317 header( "Cache-Control: no-cache" ); # Experimental - see below
318 header( "Pragma: no-cache" );
319 header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
321 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
323 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
324 header( "Content-language: {$wgLanguageCode}" );
326 if ( "" != $this->mRedirect
) {
327 header( "Location: {$this->mRedirect}" );
332 $exp = time() +
$wgCookieExpiration;
333 foreach( $this->mCookies
as $name => $val ) {
334 setcookie( $name, $val, $exp, "/" );
338 wfProfileIn( "OutputPage::output-middle" );
340 $this->out( $this->headElement() );
342 $this->out( "\n<body" );
343 $ops = $sk->getBodyOptions();
344 foreach ( $ops as $name => $val ) {
345 $this->out( " $name='$val'" );
348 if ( $wgDebugComments ) {
349 $this->out( "<!-- Wiki debugging output:\n" .
350 $this->mDebugtext
. "-->\n" );
352 $this->out( $sk->beforeContent() );
355 wfProfileIn( "OutputPage::output-bodytext" );
356 $this->out( $this->mBodytext
);
358 wfProfileIn( "OutputPage::output-after" );
359 $this->out( $sk->afterContent() );
362 wfProfileOut(); # A hack - we can't report after here
363 $this->out( $this->reportTime() );
365 $this->out( "\n</body></html>" );
371 global $wgInputEncoding, $wgOutputEncoding, $wgLang;
372 if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
375 $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
376 if ( false === $outs ) { $outs = $ins; }
381 function setEncodings()
383 global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
384 global $wgUser, $wgLang;
386 $wgInputEncoding = strtolower( $wgInputEncoding );
387 $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
389 if( $wgUser->getOption( 'altencoding' ) ) {
390 $wgLang->setAltEncoding();
395 $wgOutputEncoding = strtolower( $wgOutputEncoding );
398 $a = explode( ",", $s );
402 foreach ( $a as $s ) {
403 if ( preg_match( "/(.*);q=(.*)/", $s, $m ) ) {
415 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
416 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
417 $wgOutputEncoding = strtolower( $bestset );
421 $wgOutputEncoding = $wgInputEncoding;
424 function reportTime()
426 global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
427 global $wgProfiling, $wgProfileStack;
429 list( $usec, $sec ) = explode( " ", microtime() );
430 $now = (float)$sec +
(float)$usec;
432 list( $usec, $sec ) = explode( " ", $wgRequestTime );
433 $start = (float)$sec +
(float)$usec;
434 $elapsed = $now - $start;
436 if ( "" != $wgDebugLogFile ) {
438 if( $wgProfiling and count( $wgProfileStack ) ) {
440 foreach( $wgProfileStack as $ile ) {
441 # "foo::bar 99 0.12345 1 0.23456 2"
442 if( preg_match( '/^(\S+)\s+([0-9]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/', $ile, $m ) ) {
443 $thisstart = (float)$m[3] +
(float)$m[4] - $start;
444 $thisend = (float)$m[5] +
(float)$m[6] - $start;
445 $thiselapsed = $thisend - $thisstart;
446 $thispercent = $thiselapsed / $elapsed * 100.0;
448 $prof .= sprintf( "\tat %04.3f in %04.3f (%2.1f%%) - %s %s\n",
449 $thisstart, $thiselapsed, $thispercent,
450 str_repeat( "*", $m[2] ), $m[1] );
451 $lasttime = $thistime;
452 #$prof .= "\t(^ $ile)\n";
454 $prof .= "\t?broken? $ile\n";
459 if( $forward = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'] )
460 $forward = " forwarded for $forward";
461 if( $client = $HTTP_SERVER_VARS['HTTP_CLIENT_IP'] )
462 $forward .= " client IP $client";
463 if( $from = $HTTP_SERVER_VARS['HTTP_FROM'] )
464 $forward .= " from $from";
466 $forward = "\t(proxied via {$HTTP_SERVER_VARS['REMOTE_ADDR']}{$forward})";
467 $log = sprintf( "%s\t%04.3f\t%s\n",
468 date( "YmdHis" ), $elapsed,
469 urldecode( $HTTP_SERVER_VARS['REQUEST_URI'] . $forward ) );
470 error_log( $log . $prof, 3, $wgDebugLogFile );
472 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
477 # Note: these arguments are keys into wfMsg(), not text!
479 function errorpage( $title, $msg )
483 $this->mDebugtext
.= "Original title: " .
484 $wgTitle->getPrefixedText() . "\n";
485 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
486 $this->setPageTitle( wfMsg( $title ) );
487 $this->setRobotpolicy( "noindex,nofollow" );
488 $this->setArticleFlag( false );
490 $this->mBodytext
= "";
491 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
492 $this->returnToMain( false );
498 function sysopRequired()
502 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
503 $this->setPageTitle( wfMsg( "sysoptitle" ) );
504 $this->setRobotpolicy( "noindex,nofollow" );
505 $this->setArticleFlag( false );
506 $this->mBodytext
= "";
508 $sk = $wgUser->getSkin();
509 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
510 $text = str_replace( "$1", $ap, wfMsg( "sysoptext" ) );
511 $this->addHTML( $text );
512 $this->returnToMain();
515 function developerRequired()
519 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
520 $this->setPageTitle( wfMsg( "developertitle" ) );
521 $this->setRobotpolicy( "noindex,nofollow" );
522 $this->setArticleFlag( false );
523 $this->mBodytext
= "";
525 $sk = $wgUser->getSkin();
526 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
527 $text = str_replace( "$1", $ap, wfMsg( "developertext" ) );
528 $this->addHTML( $text );
529 $this->returnToMain();
532 function databaseError( $fname )
536 $this->setPageTitle( wfMsg( "databaseerror" ) );
537 $this->setRobotpolicy( "noindex,nofollow" );
538 $this->setArticleFlag( false );
540 $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), wfMsg( "dberrortext" ) );
541 $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg );
542 $msg = str_replace( "$3", wfLastErrno(), $msg );
543 $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg );
545 $sk = $wgUser->getSkin();
546 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
547 wfMsg( "searchingwikipedia" ) );
548 $msg = str_replace( "$5", $shlink, $msg );
550 $this->mBodytext
= $msg;
555 function readOnlyPage()
557 global $wgUser, $wgReadOnlyFile;
559 $this->setPageTitle( wfMsg( "readonly" ) );
560 $this->setRobotpolicy( "noindex,nofollow" );
561 $this->setArticleFlag( false );
563 $reason = implode( "", file( $wgReadOnlyFile ) );
564 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
565 $this->addHTML( $text );
566 $this->returnToMain( false );
569 function fatalError( $message )
571 $this->setPageTitle( wfMsg( "internalerror" ) );
572 $this->setRobotpolicy( "noindex,nofollow" );
573 $this->setArticleFlag( false );
575 $this->mBodytext
= $message;
580 function unexpectedValueError( $name, $val )
582 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
583 $msg = str_replace( "$2", $val, $msg );
584 $this->fatalError( $msg );
587 function fileCopyError( $old, $new )
589 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
590 $msg = str_replace( "$2", $new, $msg );
591 $this->fatalError( $msg );
594 function fileRenameError( $old, $new )
596 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
597 $msg = str_replace( "$2", $new, $msg );
598 $this->fatalError( $msg );
601 function fileDeleteError( $name )
603 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
604 $this->fatalError( $msg );
607 function fileNotFoundError( $name )
609 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
610 $this->fatalError( $msg );
613 function returnToMain( $auto = true )
615 global $wgUser, $wgOut, $returnto;
617 $sk = $wgUser->getSkin();
618 if ( "" == $returnto ) {
619 $returnto = wfMsg( "mainpage" );
621 $link = $sk->makeKnownLink( $returnto, "" );
623 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
625 $wgOut->addMeta( "http:Refresh", "10;url=" .
626 wfLocalUrlE( wfUrlencode( $returnto ) ) );
628 $wgOut->addHTML( "\n<p>$r\n" );
631 # Well, OK, it's actually about 14 passes. But since all the
632 # hard lifting is done inside PHP's regex code, it probably
633 # wouldn't speed things up much to add a real parser.
635 function doWikiPass2( $text, $linestart )
638 wfProfileIn( "OutputPage::doWikiPass2" );
640 $text = $this->removeHTMLtags( $text );
641 $text = $this->replaceVariables( $text );
643 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
644 $text = str_replace ( "<HR>", "<hr>", $text );
646 $text = $this->doQuotes( $text );
647 $text = $this->doHeadings( $text );
648 $text = $this->doBlockLevels( $text, $linestart );
650 $text = $this->replaceExternalLinks( $text );
651 $text = $this->replaceInternalLinks ( $text );
653 $text = $this->magicISBN( $text );
654 $text = $this->magicRFC( $text );
655 $text = $this->autoNumberHeadings( $text );
657 $sk = $wgUser->getSkin();
658 $text = $sk->transformContent( $text );
664 /* private */ function doQuotes( $text )
666 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
667 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
671 /* private */ function doHeadings( $text )
673 for ( $i = 6; $i >= 1; --$i ) {
674 $h = substr( "======", 0, $i );
675 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
676 "<h{$i}>\\1</h{$i}>\\2", $text );
681 # Note: we have to do external links before the internal ones,
682 # and otherwise take great care in the order of things here, so
683 # that we don't end up interpreting some URLs twice.
685 /* private */ function replaceExternalLinks( $text )
687 wfProfileIn( "OutputPage::replaceExternalLinks" );
688 $text = $this->subReplaceExternalLinks( $text, "http", true );
689 $text = $this->subReplaceExternalLinks( $text, "https", true );
690 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
691 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
692 $text = $this->subReplaceExternalLinks( $text, "news", false );
693 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
698 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
700 global $wgUser, $printable;
701 global $wgAllowExternalImages;
704 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
705 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
707 # this is the list of separators that should be ignored if they
708 # are the last character of an URL but that should be included
709 # if they occur within the URL, e.g. "go to www.foo.com, where .."
710 # in this case, the last comma should not become part of the URL,
711 # but in "www.foo.com/123,2342,32.htm" it should.
713 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
714 $images = "gif|png|jpg|jpeg";
716 # PLEASE NOTE: The curly braces { } are not part of the regex,
717 # they are interpreted as part of the string (used to tell PHP
718 # that the content of the string should be inserted there).
719 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
720 "((?i){$images})([^{$uc}]|$)/";
722 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
723 $sk = $wgUser->getSkin();
725 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
726 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
727 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
729 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
730 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
731 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
733 $s = str_replace( $unique, $protocol, $s );
735 $a = explode( "[{$protocol}:", " " . $s );
736 $s = array_shift( $a );
737 $s = substr( $s, 1 );
739 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
740 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
742 foreach ( $a as $line ) {
743 if ( preg_match( $e1, $line, $m ) ) {
744 $link = "{$protocol}:{$m[1]}";
746 if ( $autonumber ) { $text = "[" . ++
$this->mAutonumber
. "]"; }
747 else { $text = wfEscapeHTML( $link ); }
748 } else if ( preg_match( $e2, $line, $m ) ) {
749 $link = "{$protocol}:{$m[1]}";
753 $s .= "[{$protocol}:" . $line;
756 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
758 $la = $sk->getExternalLinkAttributes( $link, $text );
759 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
765 /* private */ function replaceInternalLinks( $s )
767 global $wgTitle, $wgUser, $wgLang;
768 global $wgLinkCache, $wgInterwikiMagic;
769 global $wgNamespacesWithSubpages;
770 wfProfileIn( "OutputPage::replaceInternalLinks" );
772 $tc = Title
::legalChars() . "#";
773 $sk = $wgUser->getSkin();
775 $a = explode( "[[", " " . $s );
776 $s = array_shift( $a );
777 $s = substr( $s, 1 );
779 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
780 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
782 foreach ( $a as $line ) {
783 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
788 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
794 else { # Invalid form; output directly
798 if(substr($m[1],0,1)=="/") { # subpage
799 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
800 $m[1]=substr($m[1],1,strlen($m[1])-2);
804 $noslash=substr($m[1],1);
806 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
807 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
810 } # this might be changed for ugliness reasons
812 $link = $noslash; # no subpage allowed, use standard link
814 } else { # no subpage
818 if ( preg_match( "/^([A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
819 $pre = strtolower( $m[1] );
821 if ( $wgLang->getNsIndex( $pre ) ==
822 Namespace::getImage() ) {
823 $nt = Title
::newFromText( $suf );
824 $name = $nt->getDBkey();
825 if ( "" == $text ) { $text = $nt->GetText(); }
827 $wgLinkCache->addImageLink( $name );
828 $s .= $sk->makeImageLink( $name,
829 wfImageUrl( $name ), $text );
831 } else if ( "media" == $pre ) {
832 $nt = Title
::newFromText( $suf );
833 $name = $nt->getDBkey();
834 if ( "" == $text ) { $text = $nt->GetText(); }
836 $wgLinkCache->addImageLink( $name );
837 $s .= $sk->makeMediaLink( $name,
838 wfImageUrl( $name ), $text );
841 $l = $wgLang->getLanguageName( $pre );
842 if ( "" == $l or !$wgInterwikiMagic or
843 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
844 if ( "" == $text ) { $text = $link; }
845 $s .= $sk->makeLink( $link, $text, "", $trail );
847 array_push( $this->mLanguageLinks
, "$pre:$suf" );
851 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
852 # $link = substr( $link, 2 );
853 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
855 if ( "" == $text ) { $text = $link; }
856 $s .= $sk->makeLink( $link, $text, "", $trail );
863 # Some functions here used by doBlockLevels()
865 /* private */ function closeParagraph()
868 if ( 0 != strcmp( "p", $this->mLastSection
) &&
869 0 != strcmp( "", $this->mLastSection
) ) {
870 $result = "</" . $this->mLastSection
. ">";
872 $this->mLastSection
= "";
875 # getCommon() returns the length of the longest common substring
876 # of both arguments, starting at the beginning of both.
878 /* private */ function getCommon( $st1, $st2 )
880 $fl = strlen( $st1 );
881 $shorter = strlen( $st2 );
882 if ( $fl < $shorter ) { $shorter = $fl; }
884 for ( $i = 0; $i < $shorter; ++
$i ) {
885 if ( $st1{$i} != $st2{$i} ) { break; }
889 # These next three functions open, continue, and close the list
890 # element appropriate to the prefix character passed into them.
892 /* private */ function openList( $char )
894 $result = $this->closeParagraph();
896 if ( "*" == $char ) { $result .= "<ul><li>"; }
897 else if ( "#" == $char ) { $result .= "<ol><li>"; }
898 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
899 else if ( ";" == $char ) {
900 $result .= "<dl><dt>";
901 $this->mDTopen
= true;
903 else { $result = "<!-- ERR 1 -->"; }
908 /* private */ function nextItem( $char )
910 if ( "*" == $char ||
"#" == $char ) { return "</li><li>"; }
911 else if ( ":" == $char ||
";" == $char ) {
913 if ( $this->mDTopen
) { $close = "</dt>"; }
914 if ( ";" == $char ) {
915 $this->mDTopen
= true;
916 return $close . "<dt>";
918 $this->mDTopen
= false;
919 return $close . "<dd>";
922 return "<!-- ERR 2 -->";
925 /* private */function closeList( $char )
927 if ( "*" == $char ) { return "</li></ul>"; }
928 else if ( "#" == $char ) { return "</li></ol>"; }
929 else if ( ":" == $char ) {
930 if ( $this->mDTopen
) {
931 $this->mDTopen
= false;
937 return "<!-- ERR 3 -->";
940 /* private */ function doBlockLevels( $text, $linestart )
942 wfProfileIn( "OutputPage::doBlockLevels" );
943 # Parsing through the text line by line. The main thing
944 # happening here is handling of block-level elements p, pre,
945 # and making lists from lines starting with * # : etc.
947 $a = explode( "\n", $text );
948 $text = $lastPref = "";
949 $this->mDTopen
= $inBlockElem = false;
951 if ( ! $linestart ) { $text .= array_shift( $a ); }
952 foreach ( $a as $t ) {
953 if ( "" != $text ) { $text .= "\n"; }
956 $opl = strlen( $lastPref );
957 $npl = strspn( $t, "*#:;" );
958 $pref = substr( $t, 0, $npl );
959 $pref2 = str_replace( ";", ":", $pref );
960 $t = substr( $t, $npl );
962 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
963 $text .= $this->nextItem( substr( $pref, -1 ) );
965 if ( ";" == substr( $pref, -1 ) ) {
966 $cpos = strpos( $t, ":" );
967 if ( ! ( false === $cpos ) ) {
968 $term = substr( $t, 0, $cpos );
969 $text .= $term . $this->nextItem( ":" );
970 $t = substr( $t, $cpos +
1 );
973 } else if (0 != $npl ||
0 != $opl) {
974 $cpl = $this->getCommon( $pref, $lastPref );
976 while ( $cpl < $opl ) {
977 $text .= $this->closeList( $lastPref{$opl-1} );
980 if ( $npl <= $cpl && $cpl > 0 ) {
981 $text .= $this->nextItem( $pref{$cpl-1} );
983 while ( $npl > $cpl ) {
984 $char = substr( $pref, $cpl, 1 );
985 $text .= $this->openList( $char );
987 if ( ";" == $char ) {
988 $cpos = strpos( $t, ":" );
989 if ( ! ( false === $cpos ) ) {
990 $term = substr( $t, 0, $cpos );
991 $text .= $term . $this->nextItem( ":" );
992 $t = substr( $t, $cpos +
1 );
999 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1001 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1002 $text .= $this->closeParagraph();
1003 $inBlockElem = true;
1005 if ( ! $inBlockElem ) {
1006 if ( " " == $t{0} ) {
1007 $newSection = "pre";
1008 # $t = wfEscapeHTML( $t );
1010 else { $newSection = "p"; }
1012 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1013 $text .= $this->closeParagraph();
1014 $text .= "<" . $newSection . ">";
1015 } else if ( 0 != strcmp( $this->mLastSection
,
1017 $text .= $this->closeParagraph();
1018 if ( 0 != strcmp( "p", $newSection ) ) {
1019 $text .= "<" . $newSection . ">";
1022 $this->mLastSection
= $newSection;
1024 if ( $inBlockElem &&
1025 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1026 $inBlockElem = false;
1032 $text .= $this->closeList( $pref2{$npl-1} );
1035 if ( "" != $this->mLastSection
) {
1036 if ( "p" != $this->mLastSection
) {
1037 $text .= "</" . $this->mLastSection
. ">";
1039 $this->mLastSection
= "";
1045 /* private */ function replaceVariables( $text )
1048 wfProfileIn( "OutputPage:replaceVariables" );
1051 $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
1052 $v = $wgLang->getMonthName( date( "n" ) );
1053 $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
1054 $v = $wgLang->getMonthNameGen( date( "n" ) );
1055 $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
1057 $text = str_replace( "{{CURRENTDAY}}", $v, $text );
1058 $v = $wgLang->getWeekdayName( date( "w" )+
1 );
1059 $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
1061 $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
1062 $v = $wgLang->time( date( "YmdHis" ), false );
1063 $text = str_replace( "{{CURRENTTIME}}", $v, $text );
1065 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1066 $v = wfNumberOfArticles();
1067 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1073 /* private */ function removeHTMLtags( $text )
1075 wfProfileIn( "OutputPage::removeHTMLtags" );
1076 $htmlpairs = array( # Tags that must be closed
1077 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1078 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1079 "strike", "strong", "tt", "var", "div", "center",
1080 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1081 "ruby", "rt" , "rb" , "rp"
1083 $htmlsingle = array(
1084 "br", "p", "hr", "li", "dt", "dd"
1086 $htmlnest = array( # Tags that can be nested--??
1087 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1088 "dl", "font", "big", "small", "sub", "sup"
1090 $tabletags = array( # Can only appear inside table
1094 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1095 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1097 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1098 "title", "align", "lang", "dir", "width", "height",
1099 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1100 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1101 /* FONT */ "type", "start", "value", "compact",
1102 /* For various lists, mostly deprecated but safe */
1103 "summary", "width", "border", "frame", "rules",
1104 "cellspacing", "cellpadding", "valign", "char",
1105 "charoff", "colgroup", "col", "span", "abbr", "axis",
1106 "headers", "scope", "rowspan", "colspan", /* Tables */
1107 "id", "class", "name", "style" /* For CSS */
1110 # Remove HTML comments
1111 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1113 $bits = explode( "<", $text );
1114 $text = array_shift( $bits );
1115 $tagstack = array(); $tablestack = array();
1117 foreach ( $bits as $x ) {
1118 $prev = error_reporting( E_ALL
& ~
( E_NOTICE | E_WARNING
) );
1119 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1121 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1122 error_reporting( $prev );
1125 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1129 if ( ! in_array( $t, $htmlsingle ) &&
1130 ( $ot = array_pop( $tagstack ) ) != $t ) {
1131 array_push( $tagstack, $ot );
1134 if ( $t == "table" ) {
1135 $tagstack = array_pop( $tablestack );
1140 # Keep track for later
1141 if ( in_array( $t, $tabletags ) &&
1142 ! in_array( "table", $tagstack ) ) {
1144 } else if ( in_array( $t, $tagstack ) &&
1145 ! in_array ( $t , $htmlnest ) ) {
1147 } else if ( ! in_array( $t, $htmlsingle ) ) {
1148 if ( $t == "table" ) {
1149 array_push( $tablestack, $tagstack );
1150 $tagstack = array();
1152 array_push( $tagstack, $t );
1154 # Strip non-approved attributes from the tag
1155 $newparams = preg_replace(
1156 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1157 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1161 $rest = str_replace( ">", ">", $rest );
1162 $text .= "<$slash$t$newparams$brace$rest";
1166 $text .= "<" . str_replace( ">", ">", $x);
1168 # Close off any remaining tags
1169 while ( $t = array_pop( $tagstack ) ) {
1171 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1177 /* private */ function autoNumberHeadings( $text )
1180 if ( 1 != $wgUser->getOption( "numberheadings" ) ) {
1185 for ( $i = 0; $i < 9; ++
$i ) {
1186 if ( stristr( $text, "<h$i>" ) != false ) {
1188 if ( $n == -1 ) $n = $i;
1191 if ( $j < 2 ) return $text;
1193 $v = array( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
1195 while ( count( spliti( "<h", $text, 2 ) ) == 2 ) {
1196 $a = spliti( "<h", $text, 2 );
1197 $j = substr( $a[1], 0, 1 );
1198 if ( strtolower( $j ) != "r" ) {
1199 $t .= $a[0] . "<h" . $j . ">";
1202 for ( $k = $i; $k <= $j; $k++
) array_push( $b, $v[$k] );
1203 for ( $k = $j+
1; $k < 9; $k++
) $v[$k] = 0;
1204 $t .= implode( ".", $b ) . " ";
1205 $text = substr( $a[1] , 2 ) ;
1206 } else { # <HR> tag, not a heading!
1207 $t .= $a[0] . "<hr>";
1208 $text = substr( $a[1], 2 );
1214 /* private */ function magicISBN( $text )
1218 $a = split( "ISBN ", " $text" );
1219 if ( count ( $a ) < 2 ) return $text;
1220 $text = substr( array_shift( $a ), 1);
1221 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1223 foreach ( $a as $x ) {
1224 $isbn = $blank = "" ;
1225 while ( " " == $x{0} ) {
1227 $x = substr( $x, 1 );
1229 while ( strstr( $valid, $x{0} ) != false ) {
1231 $x = substr( $x, 1 );
1233 $num = str_replace( "-", "", $isbn );
1234 $num = str_replace( " ", "", $num );
1237 $text .= "ISBN $blank$x";
1239 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1240 "Booksources"), "isbn={$num}" ) . "\">ISBN $isbn</a>";
1247 /* private */ function magicRFC( $text )
1252 /* private */ function headElement()
1254 global $wgDocType, $wgUser, $wgLanguageCode, $wgOutputEncoding;
1256 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\">\n";
1258 if ( "" == $this->mHTMLtitle
) {
1259 $this->mHTMLtitle
= $this->mPagetitle
;
1261 $ret .= "<html lang=\"$wgLanguageCode\"><head><title>{$this->mHTMLtitle}</title>\n";
1262 array_push( $this->mMetatags
, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1263 foreach ( $this->mMetatags
as $tag ) {
1264 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1266 $tag[0] = substr( $tag[0], 5 );
1270 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1272 $p = $this->mRobotpolicy
;
1273 if ( "" == $p ) { $p = "index,follow"; }
1274 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1276 if ( count( $this->mKeywords
) > 0 ) {
1277 $ret .= "<meta name=\"keywords\" content=\"" .
1278 implode( ",", $this->mKeywords
) . "\">\n";
1280 foreach ( $this->mLinktags
as $tag ) {
1282 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1283 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1284 $ret .= "href=\"{$tag[2]}\">\n";
1286 $sk = $wgUser->getSkin();
1287 $ret .= $sk->getHeadScripts();
1288 $ret .= $sk->getUserStyles();
1290 $ret .= "</head>\n";