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 )
534 global $wgUser, $wgCommandLineMode;
536 $this->setPageTitle( wfMsg( "databaseerror" ) );
537 $this->setRobotpolicy( "noindex,nofollow" );
538 $this->setArticleFlag( false );
540 if ( $wgCommandLineMode ) {
541 $msg = wfMsg( "dberrortextcl" );
543 $msg = wfMsg( "dberrortextcl" );
545 $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), $msg );
546 $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg );
547 $msg = str_replace( "$3", wfLastErrno(), $msg );
548 $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg );
550 if ( $wgCommandLineMode ) {
554 $sk = $wgUser->getSkin();
555 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
556 wfMsg( "searchingwikipedia" ) );
557 $msg = str_replace( "$5", $shlink, $msg );
559 $this->mBodytext
= $msg;
564 function readOnlyPage()
566 global $wgUser, $wgReadOnlyFile;
568 $this->setPageTitle( wfMsg( "readonly" ) );
569 $this->setRobotpolicy( "noindex,nofollow" );
570 $this->setArticleFlag( false );
572 $reason = implode( "", file( $wgReadOnlyFile ) );
573 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
574 $this->addHTML( $text );
575 $this->returnToMain( false );
578 function fatalError( $message )
580 $this->setPageTitle( wfMsg( "internalerror" ) );
581 $this->setRobotpolicy( "noindex,nofollow" );
582 $this->setArticleFlag( false );
584 $this->mBodytext
= $message;
589 function unexpectedValueError( $name, $val )
591 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
592 $msg = str_replace( "$2", $val, $msg );
593 $this->fatalError( $msg );
596 function fileCopyError( $old, $new )
598 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
599 $msg = str_replace( "$2", $new, $msg );
600 $this->fatalError( $msg );
603 function fileRenameError( $old, $new )
605 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
606 $msg = str_replace( "$2", $new, $msg );
607 $this->fatalError( $msg );
610 function fileDeleteError( $name )
612 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
613 $this->fatalError( $msg );
616 function fileNotFoundError( $name )
618 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
619 $this->fatalError( $msg );
622 function returnToMain( $auto = true )
624 global $wgUser, $wgOut, $returnto;
626 $sk = $wgUser->getSkin();
627 if ( "" == $returnto ) {
628 $returnto = wfMsg( "mainpage" );
630 $link = $sk->makeKnownLink( $returnto, "" );
632 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
634 $wgOut->addMeta( "http:Refresh", "10;url=" .
635 wfLocalUrlE( wfUrlencode( $returnto ) ) );
637 $wgOut->addHTML( "\n<p>$r\n" );
640 # Well, OK, it's actually about 14 passes. But since all the
641 # hard lifting is done inside PHP's regex code, it probably
642 # wouldn't speed things up much to add a real parser.
644 function doWikiPass2( $text, $linestart )
647 wfProfileIn( "OutputPage::doWikiPass2" );
649 $text = $this->removeHTMLtags( $text );
650 $text = $this->replaceVariables( $text );
652 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
653 $text = str_replace ( "<HR>", "<hr>", $text );
655 $text = $this->doQuotes( $text );
656 $text = $this->doHeadings( $text );
657 $text = $this->doBlockLevels( $text, $linestart );
659 $text = $this->replaceExternalLinks( $text );
660 $text = $this->replaceInternalLinks ( $text );
662 $text = $this->magicISBN( $text );
663 $text = $this->magicRFC( $text );
664 $text = $this->autoNumberHeadings( $text );
666 $sk = $wgUser->getSkin();
667 $text = $sk->transformContent( $text );
673 /* private */ function doQuotes( $text )
675 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
676 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
680 /* private */ function doHeadings( $text )
682 for ( $i = 6; $i >= 1; --$i ) {
683 $h = substr( "======", 0, $i );
684 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
685 "<h{$i}>\\1</h{$i}>\\2", $text );
690 # Note: we have to do external links before the internal ones,
691 # and otherwise take great care in the order of things here, so
692 # that we don't end up interpreting some URLs twice.
694 /* private */ function replaceExternalLinks( $text )
696 wfProfileIn( "OutputPage::replaceExternalLinks" );
697 $text = $this->subReplaceExternalLinks( $text, "http", true );
698 $text = $this->subReplaceExternalLinks( $text, "https", true );
699 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
700 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
701 $text = $this->subReplaceExternalLinks( $text, "news", false );
702 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
707 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
709 global $wgUser, $printable;
710 global $wgAllowExternalImages;
713 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
714 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
716 # this is the list of separators that should be ignored if they
717 # are the last character of an URL but that should be included
718 # if they occur within the URL, e.g. "go to www.foo.com, where .."
719 # in this case, the last comma should not become part of the URL,
720 # but in "www.foo.com/123,2342,32.htm" it should.
722 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
723 $images = "gif|png|jpg|jpeg";
725 # PLEASE NOTE: The curly braces { } are not part of the regex,
726 # they are interpreted as part of the string (used to tell PHP
727 # that the content of the string should be inserted there).
728 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
729 "((?i){$images})([^{$uc}]|$)/";
731 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
732 $sk = $wgUser->getSkin();
734 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
735 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
736 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
738 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
739 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
740 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
742 $s = str_replace( $unique, $protocol, $s );
744 $a = explode( "[{$protocol}:", " " . $s );
745 $s = array_shift( $a );
746 $s = substr( $s, 1 );
748 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
749 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
751 foreach ( $a as $line ) {
752 if ( preg_match( $e1, $line, $m ) ) {
753 $link = "{$protocol}:{$m[1]}";
755 if ( $autonumber ) { $text = "[" . ++
$this->mAutonumber
. "]"; }
756 else { $text = wfEscapeHTML( $link ); }
757 } else if ( preg_match( $e2, $line, $m ) ) {
758 $link = "{$protocol}:{$m[1]}";
762 $s .= "[{$protocol}:" . $line;
765 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
767 $la = $sk->getExternalLinkAttributes( $link, $text );
768 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
774 /* private */ function replaceInternalLinks( $s )
776 global $wgTitle, $wgUser, $wgLang;
777 global $wgLinkCache, $wgInterwikiMagic;
778 global $wgNamespacesWithSubpages;
779 wfProfileIn( "OutputPage::replaceInternalLinks" );
781 $tc = Title
::legalChars() . "#";
782 $sk = $wgUser->getSkin();
784 $a = explode( "[[", " " . $s );
785 $s = array_shift( $a );
786 $s = substr( $s, 1 );
788 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
789 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
791 foreach ( $a as $line ) {
792 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
797 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
803 else { # Invalid form; output directly
807 if(substr($m[1],0,1)=="/") { # subpage
808 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
809 $m[1]=substr($m[1],1,strlen($m[1])-2);
813 $noslash=substr($m[1],1);
815 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
816 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
819 } # this might be changed for ugliness reasons
821 $link = $noslash; # no subpage allowed, use standard link
823 } else { # no subpage
827 if ( preg_match( "/^([A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
828 $pre = strtolower( $m[1] );
830 if ( $wgLang->getNsIndex( $pre ) ==
831 Namespace::getImage() ) {
832 $nt = Title
::newFromText( $suf );
833 $name = $nt->getDBkey();
834 if ( "" == $text ) { $text = $nt->GetText(); }
836 $wgLinkCache->addImageLink( $name );
837 $s .= $sk->makeImageLink( $name,
838 wfImageUrl( $name ), $text );
840 } else if ( "media" == $pre ) {
841 $nt = Title
::newFromText( $suf );
842 $name = $nt->getDBkey();
843 if ( "" == $text ) { $text = $nt->GetText(); }
845 $wgLinkCache->addImageLink( $name );
846 $s .= $sk->makeMediaLink( $name,
847 wfImageUrl( $name ), $text );
850 $l = $wgLang->getLanguageName( $pre );
851 if ( "" == $l or !$wgInterwikiMagic or
852 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
853 if ( "" == $text ) { $text = $link; }
854 $s .= $sk->makeLink( $link, $text, "", $trail );
856 array_push( $this->mLanguageLinks
, "$pre:$suf" );
860 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
861 # $link = substr( $link, 2 );
862 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
864 if ( "" == $text ) { $text = $link; }
865 $s .= $sk->makeLink( $link, $text, "", $trail );
872 # Some functions here used by doBlockLevels()
874 /* private */ function closeParagraph()
877 if ( 0 != strcmp( "p", $this->mLastSection
) &&
878 0 != strcmp( "", $this->mLastSection
) ) {
879 $result = "</" . $this->mLastSection
. ">";
881 $this->mLastSection
= "";
884 # getCommon() returns the length of the longest common substring
885 # of both arguments, starting at the beginning of both.
887 /* private */ function getCommon( $st1, $st2 )
889 $fl = strlen( $st1 );
890 $shorter = strlen( $st2 );
891 if ( $fl < $shorter ) { $shorter = $fl; }
893 for ( $i = 0; $i < $shorter; ++
$i ) {
894 if ( $st1{$i} != $st2{$i} ) { break; }
898 # These next three functions open, continue, and close the list
899 # element appropriate to the prefix character passed into them.
901 /* private */ function openList( $char )
903 $result = $this->closeParagraph();
905 if ( "*" == $char ) { $result .= "<ul><li>"; }
906 else if ( "#" == $char ) { $result .= "<ol><li>"; }
907 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
908 else if ( ";" == $char ) {
909 $result .= "<dl><dt>";
910 $this->mDTopen
= true;
912 else { $result = "<!-- ERR 1 -->"; }
917 /* private */ function nextItem( $char )
919 if ( "*" == $char ||
"#" == $char ) { return "</li><li>"; }
920 else if ( ":" == $char ||
";" == $char ) {
922 if ( $this->mDTopen
) { $close = "</dt>"; }
923 if ( ";" == $char ) {
924 $this->mDTopen
= true;
925 return $close . "<dt>";
927 $this->mDTopen
= false;
928 return $close . "<dd>";
931 return "<!-- ERR 2 -->";
934 /* private */function closeList( $char )
936 if ( "*" == $char ) { return "</li></ul>"; }
937 else if ( "#" == $char ) { return "</li></ol>"; }
938 else if ( ":" == $char ) {
939 if ( $this->mDTopen
) {
940 $this->mDTopen
= false;
946 return "<!-- ERR 3 -->";
949 /* private */ function doBlockLevels( $text, $linestart )
951 wfProfileIn( "OutputPage::doBlockLevels" );
952 # Parsing through the text line by line. The main thing
953 # happening here is handling of block-level elements p, pre,
954 # and making lists from lines starting with * # : etc.
956 $a = explode( "\n", $text );
957 $text = $lastPref = "";
958 $this->mDTopen
= $inBlockElem = false;
960 if ( ! $linestart ) { $text .= array_shift( $a ); }
961 foreach ( $a as $t ) {
962 if ( "" != $text ) { $text .= "\n"; }
965 $opl = strlen( $lastPref );
966 $npl = strspn( $t, "*#:;" );
967 $pref = substr( $t, 0, $npl );
968 $pref2 = str_replace( ";", ":", $pref );
969 $t = substr( $t, $npl );
971 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
972 $text .= $this->nextItem( substr( $pref, -1 ) );
974 if ( ";" == substr( $pref, -1 ) ) {
975 $cpos = strpos( $t, ":" );
976 if ( ! ( false === $cpos ) ) {
977 $term = substr( $t, 0, $cpos );
978 $text .= $term . $this->nextItem( ":" );
979 $t = substr( $t, $cpos +
1 );
982 } else if (0 != $npl ||
0 != $opl) {
983 $cpl = $this->getCommon( $pref, $lastPref );
985 while ( $cpl < $opl ) {
986 $text .= $this->closeList( $lastPref{$opl-1} );
989 if ( $npl <= $cpl && $cpl > 0 ) {
990 $text .= $this->nextItem( $pref{$cpl-1} );
992 while ( $npl > $cpl ) {
993 $char = substr( $pref, $cpl, 1 );
994 $text .= $this->openList( $char );
996 if ( ";" == $char ) {
997 $cpos = strpos( $t, ":" );
998 if ( ! ( false === $cpos ) ) {
999 $term = substr( $t, 0, $cpos );
1000 $text .= $term . $this->nextItem( ":" );
1001 $t = substr( $t, $cpos +
1 );
1008 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1010 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1011 $text .= $this->closeParagraph();
1012 $inBlockElem = true;
1014 if ( ! $inBlockElem ) {
1015 if ( " " == $t{0} ) {
1016 $newSection = "pre";
1017 # $t = wfEscapeHTML( $t );
1019 else { $newSection = "p"; }
1021 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1022 $text .= $this->closeParagraph();
1023 $text .= "<" . $newSection . ">";
1024 } else if ( 0 != strcmp( $this->mLastSection
,
1026 $text .= $this->closeParagraph();
1027 if ( 0 != strcmp( "p", $newSection ) ) {
1028 $text .= "<" . $newSection . ">";
1031 $this->mLastSection
= $newSection;
1033 if ( $inBlockElem &&
1034 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1035 $inBlockElem = false;
1041 $text .= $this->closeList( $pref2{$npl-1} );
1044 if ( "" != $this->mLastSection
) {
1045 if ( "p" != $this->mLastSection
) {
1046 $text .= "</" . $this->mLastSection
. ">";
1048 $this->mLastSection
= "";
1054 /* private */ function replaceVariables( $text )
1057 wfProfileIn( "OutputPage:replaceVariables" );
1060 $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
1061 $v = $wgLang->getMonthName( date( "n" ) );
1062 $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
1063 $v = $wgLang->getMonthNameGen( date( "n" ) );
1064 $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
1066 $text = str_replace( "{{CURRENTDAY}}", $v, $text );
1067 $v = $wgLang->getWeekdayName( date( "w" )+
1 );
1068 $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
1070 $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
1071 $v = $wgLang->time( date( "YmdHis" ), false );
1072 $text = str_replace( "{{CURRENTTIME}}", $v, $text );
1074 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1075 $v = wfNumberOfArticles();
1076 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1082 /* private */ function removeHTMLtags( $text )
1084 wfProfileIn( "OutputPage::removeHTMLtags" );
1085 $htmlpairs = array( # Tags that must be closed
1086 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1087 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1088 "strike", "strong", "tt", "var", "div", "center",
1089 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1090 "ruby", "rt" , "rb" , "rp"
1092 $htmlsingle = array(
1093 "br", "p", "hr", "li", "dt", "dd"
1095 $htmlnest = array( # Tags that can be nested--??
1096 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1097 "dl", "font", "big", "small", "sub", "sup"
1099 $tabletags = array( # Can only appear inside table
1103 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1104 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1106 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1107 "title", "align", "lang", "dir", "width", "height",
1108 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1109 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1110 /* FONT */ "type", "start", "value", "compact",
1111 /* For various lists, mostly deprecated but safe */
1112 "summary", "width", "border", "frame", "rules",
1113 "cellspacing", "cellpadding", "valign", "char",
1114 "charoff", "colgroup", "col", "span", "abbr", "axis",
1115 "headers", "scope", "rowspan", "colspan", /* Tables */
1116 "id", "class", "name", "style" /* For CSS */
1119 # Remove HTML comments
1120 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1122 $bits = explode( "<", $text );
1123 $text = array_shift( $bits );
1124 $tagstack = array(); $tablestack = array();
1126 foreach ( $bits as $x ) {
1127 $prev = error_reporting( E_ALL
& ~
( E_NOTICE | E_WARNING
) );
1128 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1130 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1131 error_reporting( $prev );
1134 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1138 if ( ! in_array( $t, $htmlsingle ) &&
1139 ( $ot = array_pop( $tagstack ) ) != $t ) {
1140 array_push( $tagstack, $ot );
1143 if ( $t == "table" ) {
1144 $tagstack = array_pop( $tablestack );
1149 # Keep track for later
1150 if ( in_array( $t, $tabletags ) &&
1151 ! in_array( "table", $tagstack ) ) {
1153 } else if ( in_array( $t, $tagstack ) &&
1154 ! in_array ( $t , $htmlnest ) ) {
1156 } else if ( ! in_array( $t, $htmlsingle ) ) {
1157 if ( $t == "table" ) {
1158 array_push( $tablestack, $tagstack );
1159 $tagstack = array();
1161 array_push( $tagstack, $t );
1163 # Strip non-approved attributes from the tag
1164 $newparams = preg_replace(
1165 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1166 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1170 $rest = str_replace( ">", ">", $rest );
1171 $text .= "<$slash$t$newparams$brace$rest";
1175 $text .= "<" . str_replace( ">", ">", $x);
1177 # Close off any remaining tags
1178 while ( $t = array_pop( $tagstack ) ) {
1180 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1186 /* private */ function autoNumberHeadings( $text )
1189 if ( 1 != $wgUser->getOption( "numberheadings" ) ) {
1194 for ( $i = 0; $i < 9; ++
$i ) {
1195 if ( stristr( $text, "<h$i>" ) != false ) {
1197 if ( $n == -1 ) $n = $i;
1200 if ( $j < 2 ) return $text;
1202 $v = array( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
1204 while ( count( spliti( "<h", $text, 2 ) ) == 2 ) {
1205 $a = spliti( "<h", $text, 2 );
1206 $j = substr( $a[1], 0, 1 );
1207 if ( strtolower( $j ) != "r" ) {
1208 $t .= $a[0] . "<h" . $j . ">";
1211 for ( $k = $i; $k <= $j; $k++
) array_push( $b, $v[$k] );
1212 for ( $k = $j+
1; $k < 9; $k++
) $v[$k] = 0;
1213 $t .= implode( ".", $b ) . " ";
1214 $text = substr( $a[1] , 2 ) ;
1215 } else { # <HR> tag, not a heading!
1216 $t .= $a[0] . "<hr>";
1217 $text = substr( $a[1], 2 );
1223 /* private */ function magicISBN( $text )
1227 $a = split( "ISBN ", " $text" );
1228 if ( count ( $a ) < 2 ) return $text;
1229 $text = substr( array_shift( $a ), 1);
1230 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1232 foreach ( $a as $x ) {
1233 $isbn = $blank = "" ;
1234 while ( " " == $x{0} ) {
1236 $x = substr( $x, 1 );
1238 while ( strstr( $valid, $x{0} ) != false ) {
1240 $x = substr( $x, 1 );
1242 $num = str_replace( "-", "", $isbn );
1243 $num = str_replace( " ", "", $num );
1246 $text .= "ISBN $blank$x";
1248 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1249 "Booksources"), "isbn={$num}" ) . "\">ISBN $isbn</a>";
1256 /* private */ function magicRFC( $text )
1261 /* private */ function headElement()
1263 global $wgDocType, $wgUser, $wgLanguageCode, $wgOutputEncoding;
1265 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\">\n";
1267 if ( "" == $this->mHTMLtitle
) {
1268 $this->mHTMLtitle
= $this->mPagetitle
;
1270 $ret .= "<html lang=\"$wgLanguageCode\"><head><title>{$this->mHTMLtitle}</title>\n";
1271 array_push( $this->mMetatags
, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1272 foreach ( $this->mMetatags
as $tag ) {
1273 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1275 $tag[0] = substr( $tag[0], 5 );
1279 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1281 $p = $this->mRobotpolicy
;
1282 if ( "" == $p ) { $p = "index,follow"; }
1283 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1285 if ( count( $this->mKeywords
) > 0 ) {
1286 $ret .= "<meta name=\"keywords\" content=\"" .
1287 implode( ",", $this->mKeywords
) . "\">\n";
1289 foreach ( $this->mLinktags
as $tag ) {
1291 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1292 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1293 $ret .= "href=\"{$tag[2]}\">\n";
1295 $sk = $wgUser->getSkin();
1296 $ret .= $sk->getHeadScripts();
1297 $ret .= $sk->getUserStyles();
1299 $ret .= "</head>\n";