Dynamic date bug fix
[mediawiki.git] / includes / OutputPage.php
blob0c623f3971ca3da7ae45a9f982b6c33ee8e7fbaf
1 <?
2 # See design.doc
4 function linkToMathImage ( $tex, $outputhash )
6 global $wgMathPath;
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");
19 if ($math == 3)
20 return ('$ '.wfEscapeHTML($tex).' $');
22 $md5 = md5($tex);
23 $md5_sql = mysql_escape_string(pack("H32", $md5));
24 if ($math == 0)
25 $sql = "SELECT math_outputhash FROM math WHERE math_inputhash = '".$md5_sql."'";
26 else
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);
34 $contents = `$cmd`;
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")) {
40 if ($retval == "C")
41 $conservativeness = 2;
42 else if ($retval == "M")
43 $conservativeness = 1;
44 else
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);
57 if ($retval == "c")
58 $conservativeness = 2;
59 else if ($retval == "m")
60 $conservativeness = 1;
61 else
62 $conservativeness = 0;
63 $sql_html = "'".mysql_escape_string($outhtml)."'";
64 $mathml = '';
65 $sql_mathml = 'NULL';
66 } else if ($retval == "X") {
67 $outhtml = '';
68 $mathml = substr ($contents, 33);
69 $sql_html = 'NULL';
70 $sql_mathml = "'".mysql_escape_string($mathml)."'";
71 $conservativeness = 0;
72 } else if ($retval == "+") {
73 $outhtml = '';
74 $mathml = '';
75 $sql_html = 'NULL';
76 $sql_mathml = 'NULL';
77 $conservativeness = 0;
78 } else {
79 if ($retval == "E")
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" );
85 else
86 $errmsg = $munk;
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);
103 else
104 return $outhtml;
105 } else {
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 );
112 else
113 return $rpage->math_html;
117 class OutputPage {
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;
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
155 #wfDebug( "-- bad client, not caching\n", false );
156 return;
158 if( $wgUser->getOption( "nocache" ) ) return;
160 if( $_SERVER["HTTP_IF_MODIFIED_SINCE"] != "" ) {
161 $ismodsince = wfUnix2Timestamp( strtotime( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) );
162 #wfDebug( "-- client send If-Modified-Since: " . $_SERVER["HTTP_IF_MODIFIED_SINCE"] . "\n", false );
163 $lastmod = gmdate( "D, j M Y H:i:s", wfTimestamp2Unix(
164 max( $timestamp, $wgUser->mTouched ) ) ) . " GMT";
165 #wfDebug( "-- we might send Last-Modified : $lastmod\n", false );
167 if( ($ismodsince >= $timestamp ) and $wgUser->validateCache( $ismodsince ) ) {
168 # Make sure you're in a place you can leave when you call us!
169 header( "HTTP/1.0 304 Not Modified" );
170 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
171 header( "Cache-Control: private, must-revalidate, max-age=0" );
172 header( "Last-Modified: {$lastmod}" );
173 #wfDebug( "CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
174 exit;
175 } else {
176 #wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
177 $this->mLastModified = $lastmod;
182 function setRobotpolicy( $str ) { $this->mRobotpolicy = $str; }
183 function setHTMLtitle( $name ) { $this->mHTMLtitle = $name; }
184 function setPageTitle( $name ) { $this->mPagetitle = $name; }
185 function getPageTitle() { return $this->mPagetitle; }
186 function setSubtitle( $str ) { $this->mSubtitle = $str; }
187 function getSubtitle() { return $this->mSubtitle; }
188 function setArticleFlag( $v ) { $this->mIsarticle = $v; }
189 function isArticle() { return $this->mIsarticle; }
190 function setPrintable() { $this->mPrintable = true; }
191 function isPrintable() { return $this->mPrintable; }
193 function getLanguageLinks() {
194 global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode;
195 global $wgDBconnection, $wgDBname, $wgDBintlname;
197 if ( ! $wgUseNewInterlanguage )
198 return $this->mLanguageLinks;
200 mysql_select_db( $wgDBintlname, $wgDBconnection ) or die(
201 htmlspecialchars(mysql_error()) );
203 $list = array();
204 $sql = "SELECT * FROM ilinks WHERE lang_from=\"" .
205 "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\"";
206 $res = mysql_query( $sql, $wgDBconnection );
208 while ( $q = mysql_fetch_object ( $res ) ) {
209 $list[] = $q->lang_to . ":" . $q->title_to;
211 mysql_free_result( $res );
212 mysql_select_db( $wgDBname, $wgDBconnection ) or die(
213 htmlspecialchars(mysql_error()) );
215 return $list;
218 function supressQuickbar() { $this->mSupressQuickbar = true; }
219 function isQuickbarSupressed() { return $this->mSupressQuickbar; }
221 function addHTML( $text ) { $this->mBodytext .= $text; }
222 function addHeadtext( $text ) { $this->mHeadtext .= $text; }
223 function debug( $text ) { $this->mDebugtext .= $text; }
225 # First pass--just handle <nowiki> sections, pass the rest off
226 # to doWikiPass2() which does all the real work.
229 function addWikiText( $text, $linestart = true )
231 global $wgUseTeX;
232 wfProfileIn( "OutputPage::addWikiText" );
233 $unique = "3iyZiyA7iMwg5rhxP0Dcc9oTnj8qD1jm1Sfv4";
234 $unique2 = "4LIQ9nXtiYFPCSfitVwDw7EYwQlL4GeeQ7qSO";
235 $unique3 = "fPaA8gDfdLBqzj68Yjg9Hil3qEF8JGO0uszIp";
236 $nwlist = array();
237 $nwsecs = 0;
238 $mathlist = array();
239 $mathsecs = 0;
240 $prelist = array ();
241 $presecs = 0;
242 $stripped = "";
243 $stripped2 = "";
244 $stripped3 = "";
246 while ( "" != $text ) {
247 $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
248 $stripped .= $p[0];
249 if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $text = ""; }
250 else {
251 $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
252 ++$nwsecs;
253 $nwlist[$nwsecs] = wfEscapeHTMLTagsOnly($q[0]);
254 $stripped .= $unique;
255 $text = $q[1];
259 if( $wgUseTeX ) {
260 while ( "" != $stripped ) {
261 $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 );
262 $stripped2 .= $p[0];
263 if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $stripped = ""; }
264 else {
265 $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 );
266 ++$mathsecs;
267 $mathlist[$mathsecs] = renderMath($q[0]);
268 $stripped2 .= $unique2;
269 $stripped = $q[1];
272 } else {
273 $stripped2 = $stripped;
276 while ( "" != $stripped2 ) {
277 $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 );
278 $stripped3 .= $p[0];
279 if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $stripped2 = ""; }
280 else {
281 $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 );
282 ++$presecs;
283 $prelist[$presecs] = "<pre>". wfEscapeHTMLTagsOnly($q[0]). "</pre>";
284 $stripped3 .= $unique3;
285 $stripped2 = $q[1];
289 $text = $this->doWikiPass2( $stripped3, $linestart );
291 $specialChars = array("\\", "$");
292 $escapedChars = array("\\\\", "\\$");
293 for ( $i = 1; $i <= $presecs; ++$i ) {
294 $text = preg_replace( "/{$unique3}/", str_replace( $specialChars,
295 $escapedChars, $prelist[$i] ), $text, 1 );
298 for ( $i = 1; $i <= $mathsecs; ++$i ) {
299 $text = preg_replace( "/{$unique2}/", str_replace( $specialChars,
300 $escapedChars, $mathlist[$i] ), $text, 1 );
303 for ( $i = 1; $i <= $nwsecs; ++$i ) {
304 $text = preg_replace( "/{$unique}/", str_replace( $specialChars,
305 $escapedChars, $nwlist[$i] ), $text, 1 );
307 $this->addHTML( $text );
308 wfProfileOut();
311 function sendCacheControl() {
312 if( $this->mLastModified != "" ) {
313 header( "Cache-Control: private, must-revalidate, max-age=0" );
314 header( "Last-modified: {$this->mLastModified}" );
315 } else {
316 header( "Cache-Control: no-cache" ); # Experimental - see below
317 header( "Pragma: no-cache" );
318 header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
320 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
323 # Finally, all the text has been munged and accumulated into
324 # the object, let's actually output it:
326 function output()
328 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
329 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
330 wfProfileIn( "OutputPage::output" );
331 $sk = $wgUser->getSkin();
333 wfProfileIn( "OutputPage::output-headers" );
334 $this->sendCacheControl();
336 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
337 header( "Content-language: {$wgLanguageCode}" );
339 if ( "" != $this->mRedirect ) {
340 header( "Location: {$this->mRedirect}" );
341 wfProfileOut();
342 return;
345 $exp = time() + $wgCookieExpiration;
346 foreach( $this->mCookies as $name => $val ) {
347 setcookie( $name, $val, $exp, "/" );
349 wfProfileOut();
351 wfProfileIn( "OutputPage::output-middle" );
352 $sk->initPage();
353 $this->out( $this->headElement() );
355 $this->out( "\n<body" );
356 $ops = $sk->getBodyOptions();
357 foreach ( $ops as $name => $val ) {
358 $this->out( " $name='$val'" );
360 $this->out( ">\n" );
361 if ( $wgDebugComments ) {
362 $this->out( "<!-- Wiki debugging output:\n" .
363 $this->mDebugtext . "-->\n" );
365 $this->out( $sk->beforeContent() );
366 wfProfileOut();
368 wfProfileIn( "OutputPage::output-bodytext" );
369 $this->out( $this->mBodytext );
370 wfProfileOut();
371 wfProfileIn( "OutputPage::output-after" );
372 $this->out( $sk->afterContent() );
373 wfProfileOut();
375 wfProfileOut(); # A hack - we can't report after here
376 $this->out( $this->reportTime() );
378 $this->out( "\n</body></html>" );
379 flush();
382 function out( $ins )
384 global $wgInputEncoding, $wgOutputEncoding, $wgLang;
385 if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
386 $outs = $ins;
387 } else {
388 $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
389 if ( false === $outs ) { $outs = $ins; }
391 print $outs;
394 function setEncodings()
396 global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
397 global $wgUser, $wgLang;
399 $wgInputEncoding = strtolower( $wgInputEncoding );
400 $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
402 if( $wgUser->getOption( 'altencoding' ) ) {
403 $wgLang->setAltEncoding();
404 return;
407 if ( "" == $s ) {
408 $wgOutputEncoding = strtolower( $wgOutputEncoding );
409 return;
411 $a = explode( ",", $s );
412 $best = 0.0;
413 $bestset = "*";
415 foreach ( $a as $s ) {
416 if ( preg_match( "/(.*);q=(.*)/", $s, $m ) ) {
417 $set = $m[1];
418 $q = (float)($m[2]);
419 } else {
420 $set = $s;
421 $q = 1.0;
423 if ( $q > $best ) {
424 $bestset = $set;
425 $best = $q;
428 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
429 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
430 $wgOutputEncoding = strtolower( $bestset );
432 # Disable for now
434 $wgOutputEncoding = $wgInputEncoding;
437 function reportTime()
439 global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
440 global $wgProfiling, $wgProfileStack, $wgUser;
442 list( $usec, $sec ) = explode( " ", microtime() );
443 $now = (float)$sec + (float)$usec;
445 list( $usec, $sec ) = explode( " ", $wgRequestTime );
446 $start = (float)$sec + (float)$usec;
447 $elapsed = $now - $start;
449 if ( "" != $wgDebugLogFile ) {
450 $prof = "";
451 if( $wgProfiling and count( $wgProfileStack ) ) {
452 $lasttime = $start;
453 foreach( $wgProfileStack as $ile ) {
454 # "foo::bar 99 0.12345 1 0.23456 2"
455 if( preg_match( '/^(\S+)\s+([0-9]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/', $ile, $m ) ) {
456 $thisstart = (float)$m[3] + (float)$m[4] - $start;
457 $thisend = (float)$m[5] + (float)$m[6] - $start;
458 $thiselapsed = $thisend - $thisstart;
459 $thispercent = $thiselapsed / $elapsed * 100.0;
461 $prof .= sprintf( "\tat %04.3f in %04.3f (%2.1f%%) - %s %s\n",
462 $thisstart, $thiselapsed, $thispercent,
463 str_repeat( "*", $m[2] ), $m[1] );
464 $lasttime = $thistime;
465 #$prof .= "\t(^ $ile)\n";
466 } else {
467 $prof .= "\t?broken? $ile\n";
472 if( $forward = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'] )
473 $forward = " forwarded for $forward";
474 if( $client = $HTTP_SERVER_VARS['HTTP_CLIENT_IP'] )
475 $forward .= " client IP $client";
476 if( $from = $HTTP_SERVER_VARS['HTTP_FROM'] )
477 $forward .= " from $from";
478 if( $forward )
479 $forward = "\t(proxied via {$HTTP_SERVER_VARS['REMOTE_ADDR']}{$forward})";
480 if($wgUser->getId() == 0)
481 $forward .= " anon";
482 $log = sprintf( "%s\t%04.3f\t%s\n",
483 gmdate( "YmdHis" ), $elapsed,
484 urldecode( $HTTP_SERVER_VARS['REQUEST_URI'] . $forward ) );
485 error_log( $log . $prof, 3, $wgDebugLogFile );
487 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
488 $elapsed );
489 return $com;
492 # Note: these arguments are keys into wfMsg(), not text!
494 function errorpage( $title, $msg )
496 global $wgTitle;
498 $this->mDebugtext .= "Original title: " .
499 $wgTitle->getPrefixedText() . "\n";
500 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
501 $this->setPageTitle( wfMsg( $title ) );
502 $this->setRobotpolicy( "noindex,nofollow" );
503 $this->setArticleFlag( false );
505 $this->mBodytext = "";
506 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
507 $this->returnToMain( false );
509 $this->output();
510 exit;
513 function sysopRequired()
515 global $wgUser;
517 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
518 $this->setPageTitle( wfMsg( "sysoptitle" ) );
519 $this->setRobotpolicy( "noindex,nofollow" );
520 $this->setArticleFlag( false );
521 $this->mBodytext = "";
523 $sk = $wgUser->getSkin();
524 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
525 $text = str_replace( "$1", $ap, wfMsg( "sysoptext" ) );
526 $this->addHTML( $text );
527 $this->returnToMain();
530 function developerRequired()
532 global $wgUser;
534 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
535 $this->setPageTitle( wfMsg( "developertitle" ) );
536 $this->setRobotpolicy( "noindex,nofollow" );
537 $this->setArticleFlag( false );
538 $this->mBodytext = "";
540 $sk = $wgUser->getSkin();
541 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
542 $text = str_replace( "$1", $ap, wfMsg( "developertext" ) );
543 $this->addHTML( $text );
544 $this->returnToMain();
547 function databaseError( $fname )
549 global $wgUser, $wgCommandLineMode;
551 $this->setPageTitle( wfMsg( "databaseerror" ) );
552 $this->setRobotpolicy( "noindex,nofollow" );
553 $this->setArticleFlag( false );
555 if ( $wgCommandLineMode ) {
556 $msg = wfMsg( "dberrortextcl" );
557 } else {
558 $msg = wfMsg( "dberrortextcl" );
560 $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), $msg );
561 $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg );
562 $msg = str_replace( "$3", wfLastErrno(), $msg );
563 $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg );
565 if ( $wgCommandLineMode ) {
566 print $msg;
567 exit();
569 $sk = $wgUser->getSkin();
570 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
571 wfMsg( "searchingwikipedia" ) );
572 $msg = str_replace( "$5", $shlink, $msg );
574 $this->mBodytext = $msg;
575 $this->output();
576 exit();
579 function readOnlyPage()
581 global $wgUser, $wgReadOnlyFile;
583 $this->setPageTitle( wfMsg( "readonly" ) );
584 $this->setRobotpolicy( "noindex,nofollow" );
585 $this->setArticleFlag( false );
587 $reason = implode( "", file( $wgReadOnlyFile ) );
588 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
589 $this->addHTML( $text );
590 $this->returnToMain( false );
593 function fatalError( $message )
595 $this->setPageTitle( wfMsg( "internalerror" ) );
596 $this->setRobotpolicy( "noindex,nofollow" );
597 $this->setArticleFlag( false );
599 $this->mBodytext = $message;
600 $this->output();
601 exit;
604 function unexpectedValueError( $name, $val )
606 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
607 $msg = str_replace( "$2", $val, $msg );
608 $this->fatalError( $msg );
611 function fileCopyError( $old, $new )
613 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
614 $msg = str_replace( "$2", $new, $msg );
615 $this->fatalError( $msg );
618 function fileRenameError( $old, $new )
620 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
621 $msg = str_replace( "$2", $new, $msg );
622 $this->fatalError( $msg );
625 function fileDeleteError( $name )
627 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
628 $this->fatalError( $msg );
631 function fileNotFoundError( $name )
633 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
634 $this->fatalError( $msg );
637 function returnToMain( $auto = true )
639 global $wgUser, $wgOut, $returnto;
641 $sk = $wgUser->getSkin();
642 if ( "" == $returnto ) {
643 $returnto = wfMsg( "mainpage" );
645 $link = $sk->makeKnownLink( $returnto, "" );
647 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
648 if ( $auto ) {
649 $wgOut->addMeta( "http:Refresh", "10;url=" .
650 wfLocalUrlE( wfUrlencode( $returnto ) ) );
652 $wgOut->addHTML( "\n<p>$r\n" );
655 # Well, OK, it's actually about 14 passes. But since all the
656 # hard lifting is done inside PHP's regex code, it probably
657 # wouldn't speed things up much to add a real parser.
659 function doWikiPass2( $text, $linestart )
661 global $wgUser, $wgLang, $wgUseDynamicDates;
662 wfProfileIn( "OutputPage::doWikiPass2" );
664 $text = $this->removeHTMLtags( $text );
665 $text = $this->replaceVariables( $text );
667 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
668 $text = str_replace ( "<HR>", "<hr>", $text );
670 $text = $this->doQuotes( $text );
671 $text = $this->doHeadings( $text );
672 $text = $this->doBlockLevels( $text, $linestart );
674 if($wgUseDynamicDates) {
675 $text = $wgLang->replaceDates( $text );
678 $text = $this->replaceExternalLinks( $text );
679 $text = $this->replaceInternalLinks ( $text );
681 $text = $this->magicISBN( $text );
682 $text = $this->magicRFC( $text );
683 $text = $this->formatHeadings( $text );
685 $sk = $wgUser->getSkin();
686 $text = $sk->transformContent( $text );
688 wfProfileOut();
689 return $text;
692 /* private */ function doQuotes( $text )
694 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
695 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
696 return $text;
699 /* private */ function doHeadings( $text )
701 for ( $i = 6; $i >= 1; --$i ) {
702 $h = substr( "======", 0, $i );
703 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
704 "<h{$i}>\\1</h{$i}>\\2", $text );
706 return $text;
709 # Note: we have to do external links before the internal ones,
710 # and otherwise take great care in the order of things here, so
711 # that we don't end up interpreting some URLs twice.
713 /* private */ function replaceExternalLinks( $text )
715 wfProfileIn( "OutputPage::replaceExternalLinks" );
716 $text = $this->subReplaceExternalLinks( $text, "http", true );
717 $text = $this->subReplaceExternalLinks( $text, "https", true );
718 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
719 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
720 $text = $this->subReplaceExternalLinks( $text, "news", false );
721 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
722 wfProfileOut();
723 return $text;
726 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
728 global $wgUser, $printable;
729 global $wgAllowExternalImages;
732 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
733 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
735 # this is the list of separators that should be ignored if they
736 # are the last character of an URL but that should be included
737 # if they occur within the URL, e.g. "go to www.foo.com, where .."
738 # in this case, the last comma should not become part of the URL,
739 # but in "www.foo.com/123,2342,32.htm" it should.
740 $sep = ",;\.:";
741 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
742 $images = "gif|png|jpg|jpeg";
744 # PLEASE NOTE: The curly braces { } are not part of the regex,
745 # they are interpreted as part of the string (used to tell PHP
746 # that the content of the string should be inserted there).
747 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
748 "((?i){$images})([^{$uc}]|$)/";
750 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
751 $sk = $wgUser->getSkin();
753 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
754 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
755 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
757 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
758 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
759 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
760 "</a>\\5", $s );
761 $s = str_replace( $unique, $protocol, $s );
763 $a = explode( "[{$protocol}:", " " . $s );
764 $s = array_shift( $a );
765 $s = substr( $s, 1 );
767 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
768 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
770 foreach ( $a as $line ) {
771 if ( preg_match( $e1, $line, $m ) ) {
772 $link = "{$protocol}:{$m[1]}";
773 $trail = $m[2];
774 if ( $autonumber ) { $text = "[" . ++$this->mAutonumber . "]"; }
775 else { $text = wfEscapeHTML( $link ); }
776 } else if ( preg_match( $e2, $line, $m ) ) {
777 $link = "{$protocol}:{$m[1]}";
778 $text = $m[2];
779 $trail = $m[3];
780 } else {
781 $s .= "[{$protocol}:" . $line;
782 continue;
784 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
785 else $paren = "";
786 $la = $sk->getExternalLinkAttributes( $link, $text );
787 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
790 return $s;
793 /* private */ function replaceInternalLinks( $s )
795 global $wgTitle, $wgUser, $wgLang;
796 global $wgLinkCache, $wgInterwikiMagic;
797 global $wgNamespacesWithSubpages, $wgLanguageCode;
798 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
800 wfProfileIn( "$fname-setup" );
801 $tc = Title::legalChars() . "#";
802 $sk = $wgUser->getSkin();
804 $a = explode( "[[", " " . $s );
805 $s = array_shift( $a );
806 $s = substr( $s, 1 );
808 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
809 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
810 wfProfileOut();
812 wfProfileIn( "$fname-loop" );
813 foreach ( $a as $line ) {
814 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
816 $text = $m[2];
817 $trail = $m[3];
819 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
821 $text = "";
822 $trail = $m[2];
825 else { # Invalid form; output directly
826 $s .= "[[" . $line ;
827 continue;
829 if(substr($m[1],0,1)=="/") { # subpage
830 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
831 $m[1]=substr($m[1],1,strlen($m[1])-2);
832 $noslash=$m[1];
834 } else {
835 $noslash=substr($m[1],1);
837 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
838 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
839 if(!$text) {
840 $text= $m[1];
841 } # this might be changed for ugliness reasons
842 } else {
843 $link = $noslash; # no subpage allowed, use standard link
845 } else { # no subpage
846 $link = $m[1];
849 if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
850 $pre = strtolower( $m[1] );
851 $suf = $m[2];
852 if ( $wgLang->getNsIndex( $pre ) ==
853 Namespace::getImage() ) {
854 $nt = Title::newFromText( $suf );
855 $name = $nt->getDBkey();
856 if ( "" == $text ) { $text = $nt->GetText(); }
858 $wgLinkCache->addImageLink( $name );
859 $s .= $sk->makeImageLink( $name,
860 wfImageUrl( $name ), $text );
861 $s .= $trail;
862 } else if ( "media" == $pre ) {
863 $nt = Title::newFromText( $suf );
864 $name = $nt->getDBkey();
865 if ( "" == $text ) { $text = $nt->GetText(); }
867 $wgLinkCache->addImageLink( $name );
868 $s .= $sk->makeMediaLink( $name,
869 wfImageUrl( $name ), $text );
870 $s .= $trail;
871 } else {
872 $l = $wgLang->getLanguageName( $pre );
873 if ( "" == $l or !$wgInterwikiMagic or
874 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
875 if ( "" == $text ) { $text = $link; }
876 $s .= $sk->makeLink( $link, $text, "", $trail );
877 } else if ( $pre != $wgLanguageCode ) {
878 array_push( $this->mLanguageLinks, "$pre:$suf" );
879 $s .= $trail;
882 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
883 # $link = substr( $link, 2 );
884 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
885 } else {
886 if ( "" == $text ) { $text = $link; }
887 $s .= $sk->makeLink( $link, $text, "", $trail );
890 wfProfileOut();
891 wfProfileOut();
892 return $s;
895 # Some functions here used by doBlockLevels()
897 /* private */ function closeParagraph()
899 $result = "";
900 if ( 0 != strcmp( "p", $this->mLastSection ) &&
901 0 != strcmp( "", $this->mLastSection ) ) {
902 $result = "</" . $this->mLastSection . ">";
904 $this->mLastSection = "";
905 return $result;
907 # getCommon() returns the length of the longest common substring
908 # of both arguments, starting at the beginning of both.
910 /* private */ function getCommon( $st1, $st2 )
912 $fl = strlen( $st1 );
913 $shorter = strlen( $st2 );
914 if ( $fl < $shorter ) { $shorter = $fl; }
916 for ( $i = 0; $i < $shorter; ++$i ) {
917 if ( $st1{$i} != $st2{$i} ) { break; }
919 return $i;
921 # These next three functions open, continue, and close the list
922 # element appropriate to the prefix character passed into them.
924 /* private */ function openList( $char )
926 $result = $this->closeParagraph();
928 if ( "*" == $char ) { $result .= "<ul><li>"; }
929 else if ( "#" == $char ) { $result .= "<ol><li>"; }
930 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
931 else if ( ";" == $char ) {
932 $result .= "<dl><dt>";
933 $this->mDTopen = true;
935 else { $result = "<!-- ERR 1 -->"; }
937 return $result;
940 /* private */ function nextItem( $char )
942 if ( "*" == $char || "#" == $char ) { return "</li><li>"; }
943 else if ( ":" == $char || ";" == $char ) {
944 $close = "</dd>";
945 if ( $this->mDTopen ) { $close = "</dt>"; }
946 if ( ";" == $char ) {
947 $this->mDTopen = true;
948 return $close . "<dt>";
949 } else {
950 $this->mDTopen = false;
951 return $close . "<dd>";
954 return "<!-- ERR 2 -->";
957 /* private */function closeList( $char )
959 if ( "*" == $char ) { return "</li></ul>"; }
960 else if ( "#" == $char ) { return "</li></ol>"; }
961 else if ( ":" == $char ) {
962 if ( $this->mDTopen ) {
963 $this->mDTopen = false;
964 return "</dt></dl>";
965 } else {
966 return "</dd></dl>";
969 return "<!-- ERR 3 -->";
972 /* private */ function doBlockLevels( $text, $linestart )
974 wfProfileIn( "OutputPage::doBlockLevels" );
975 # Parsing through the text line by line. The main thing
976 # happening here is handling of block-level elements p, pre,
977 # and making lists from lines starting with * # : etc.
979 $a = explode( "\n", $text );
980 $text = $lastPref = "";
981 $this->mDTopen = $inBlockElem = false;
983 if ( ! $linestart ) { $text .= array_shift( $a ); }
984 foreach ( $a as $t ) {
985 if ( "" != $text ) { $text .= "\n"; }
987 $oLine = $t;
988 $opl = strlen( $lastPref );
989 $npl = strspn( $t, "*#:;" );
990 $pref = substr( $t, 0, $npl );
991 $pref2 = str_replace( ";", ":", $pref );
992 $t = substr( $t, $npl );
994 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
995 $text .= $this->nextItem( substr( $pref, -1 ) );
997 if ( ";" == substr( $pref, -1 ) ) {
998 $cpos = strpos( $t, ":" );
999 if ( ! ( false === $cpos ) ) {
1000 $term = substr( $t, 0, $cpos );
1001 $text .= $term . $this->nextItem( ":" );
1002 $t = substr( $t, $cpos + 1 );
1005 } else if (0 != $npl || 0 != $opl) {
1006 $cpl = $this->getCommon( $pref, $lastPref );
1008 while ( $cpl < $opl ) {
1009 $text .= $this->closeList( $lastPref{$opl-1} );
1010 --$opl;
1012 if ( $npl <= $cpl && $cpl > 0 ) {
1013 $text .= $this->nextItem( $pref{$cpl-1} );
1015 while ( $npl > $cpl ) {
1016 $char = substr( $pref, $cpl, 1 );
1017 $text .= $this->openList( $char );
1019 if ( ";" == $char ) {
1020 $cpos = strpos( $t, ":" );
1021 if ( ! ( false === $cpos ) ) {
1022 $term = substr( $t, 0, $cpos );
1023 $text .= $term . $this->nextItem( ":" );
1024 $t = substr( $t, $cpos + 1 );
1027 ++$cpl;
1029 $lastPref = $pref2;
1031 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1032 if ( preg_match(
1033 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1034 $text .= $this->closeParagraph();
1035 $inBlockElem = true;
1037 if ( ! $inBlockElem ) {
1038 if ( " " == $t{0} ) {
1039 $newSection = "pre";
1040 # $t = wfEscapeHTML( $t );
1042 else { $newSection = "p"; }
1044 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1045 $text .= $this->closeParagraph();
1046 $text .= "<" . $newSection . ">";
1047 } else if ( 0 != strcmp( $this->mLastSection,
1048 $newSection ) ) {
1049 $text .= $this->closeParagraph();
1050 if ( 0 != strcmp( "p", $newSection ) ) {
1051 $text .= "<" . $newSection . ">";
1054 $this->mLastSection = $newSection;
1056 if ( $inBlockElem &&
1057 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1058 $inBlockElem = false;
1061 $text .= $t;
1063 while ( $npl ) {
1064 $text .= $this->closeList( $pref2{$npl-1} );
1065 --$npl;
1067 if ( "" != $this->mLastSection ) {
1068 if ( "p" != $this->mLastSection ) {
1069 $text .= "</" . $this->mLastSection . ">";
1071 $this->mLastSection = "";
1073 wfProfileOut();
1074 return $text;
1077 /* private */ function replaceVariables( $text )
1079 global $wgLang;
1080 wfProfileIn( "OutputPage:replaceVariables" );
1082 /* As with sigs, use server's local time --
1083 ensure this is appropriate for your audience! */
1084 $v = date( "m" );
1085 $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
1086 $v = $wgLang->getMonthName( date( "n" ) );
1087 $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
1088 $v = $wgLang->getMonthNameGen( date( "n" ) );
1089 $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
1090 $v = date( "j" );
1091 $text = str_replace( "{{CURRENTDAY}}", $v, $text );
1092 $v = $wgLang->getWeekdayName( date( "w" )+1 );
1093 $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
1094 $v = date( "Y" );
1095 $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
1096 $v = $wgLang->time( wfTimestampNow(), false );
1097 $text = str_replace( "{{CURRENTTIME}}", $v, $text );
1099 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1100 $v = wfNumberOfArticles();
1101 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1103 wfProfileOut();
1104 return $text;
1107 /* private */ function removeHTMLtags( $text )
1109 wfProfileIn( "OutputPage::removeHTMLtags" );
1110 $htmlpairs = array( # Tags that must be closed
1111 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1112 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1113 "strike", "strong", "tt", "var", "div", "center",
1114 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1115 "ruby", "rt" , "rb" , "rp"
1117 $htmlsingle = array(
1118 "br", "p", "hr", "li", "dt", "dd"
1120 $htmlnest = array( # Tags that can be nested--??
1121 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1122 "dl", "font", "big", "small", "sub", "sup"
1124 $tabletags = array( # Can only appear inside table
1125 "td", "th", "tr"
1128 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1129 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1131 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1132 "title", "align", "lang", "dir", "width", "height",
1133 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1134 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1135 /* FONT */ "type", "start", "value", "compact",
1136 /* For various lists, mostly deprecated but safe */
1137 "summary", "width", "border", "frame", "rules",
1138 "cellspacing", "cellpadding", "valign", "char",
1139 "charoff", "colgroup", "col", "span", "abbr", "axis",
1140 "headers", "scope", "rowspan", "colspan", /* Tables */
1141 "id", "class", "name", "style" /* For CSS */
1144 # Remove HTML comments
1145 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1147 $bits = explode( "<", $text );
1148 $text = array_shift( $bits );
1149 $tagstack = array(); $tablestack = array();
1151 foreach ( $bits as $x ) {
1152 $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) );
1153 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1154 $x, $regs );
1155 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1156 error_reporting( $prev );
1158 $badtag = 0 ;
1159 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1160 # Check our stack
1161 if ( $slash ) {
1162 # Closing a tag...
1163 if ( ! in_array( $t, $htmlsingle ) &&
1164 ( $ot = array_pop( $tagstack ) ) != $t ) {
1165 array_push( $tagstack, $ot );
1166 $badtag = 1;
1167 } else {
1168 if ( $t == "table" ) {
1169 $tagstack = array_pop( $tablestack );
1171 $newparams = "";
1173 } else {
1174 # Keep track for later
1175 if ( in_array( $t, $tabletags ) &&
1176 ! in_array( "table", $tagstack ) ) {
1177 $badtag = 1;
1178 } else if ( in_array( $t, $tagstack ) &&
1179 ! in_array ( $t , $htmlnest ) ) {
1180 $badtag = 1 ;
1181 } else if ( ! in_array( $t, $htmlsingle ) ) {
1182 if ( $t == "table" ) {
1183 array_push( $tablestack, $tagstack );
1184 $tagstack = array();
1186 array_push( $tagstack, $t );
1188 # Strip non-approved attributes from the tag
1189 $newparams = preg_replace(
1190 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1191 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1192 $params);
1194 if ( ! $badtag ) {
1195 $rest = str_replace( ">", "&gt;", $rest );
1196 $text .= "<$slash$t$newparams$brace$rest";
1197 continue;
1200 $text .= "&lt;" . str_replace( ">", "&gt;", $x);
1202 # Close off any remaining tags
1203 while ( $t = array_pop( $tagstack ) ) {
1204 $text .= "</$t>\n";
1205 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1207 wfProfileOut();
1208 return $text;
1214 * This function accomplishes several tasks:
1215 * 1) Auto-number headings if that option is enabled
1216 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1217 * 3) Add a Table of contents on the top for users who have enabled the option
1218 * 4) Auto-anchor headings
1220 * It loops through all headlines, collects the necessary data, then splits up the
1221 * string and re-inserts the newly formatted headlines.
1223 * */
1224 /* private */ function formatHeadings( $text )
1226 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1227 $nh=$wgUser->getOption( "numberheadings" );
1228 $st=$wgUser->getOption( "showtoc" );
1229 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1230 if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1232 $sk=$wgUser->getSkin();
1233 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1235 $c=0;
1237 foreach($matches[3] as $headline) {
1238 if($level) { $prevlevel=$level;}
1239 $level=$matches[1][$c];
1240 if(($nh||$st) && $prevlevel && $level>$prevlevel) {
1242 $h[$level]=0; // reset when we enter a new level
1243 $toc.=$sk->tocIndent($level-$prevlevel);
1244 $toclevel+=$level-$prevlevel;
1247 if(($nh||$st) && $level<$prevlevel) {
1248 $h[$level+1]=0; // reset when we step back a level
1249 $toc.=$sk->tocUnindent($prevlevel-$level);
1250 $toclevel-=$prevlevel-$level;
1253 $h[$level]++; // count number of headlines for each level
1255 if($nh||$st) {
1256 for($i=1;$i<=$level;$i++) {
1257 if($h[$i]) {
1258 if($dot) {$numbering.=".";}
1259 $numbering.=$h[$i];
1260 $dot=1;
1266 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1267 $tocline=$canonized_headline;
1268 $canonized_headline=str_replace('"',"",$canonized_headline);
1269 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1270 $refer[$c]=$canonized_headline;
1271 $refers[$canonized_headline]++; // count how many in assoc. array so we can track dupes in anchors
1272 $refcount[$c]=$refers[$canonized_headline];
1273 if($nh||$st) {
1274 $tocline=$numbering ." ". $tocline;
1275 if($nh) {
1276 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1279 $anchor=$canonized_headline;
1280 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1281 if($st) {
1282 $toc.=$sk->tocLine($anchor,$tocline,$toclevel);
1284 if($es && !isset($wpPreview)) {
1285 $head[$c].=$sk->editSectionLink($c+1);
1287 $head[$c].="<H".$level.$matches[2][$c]
1288 ."<a name=\"".$anchor."\">"
1289 .$headline
1290 ."</a>"
1291 ."</H".$level.">";
1292 $numbering="";
1293 $c++;
1294 $dot=0;
1297 if($st) {
1298 $toclines=$c;
1299 $toc.=$sk->tocUnindent($toclevel);
1300 $toc=$sk->tocTable($toc);
1303 // split up and insert constructed headlines
1305 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1306 $i=0;
1309 foreach($blocks as $block) {
1310 if($es && !isset($wpPreview) && $c>0 && $i==0) {
1311 $full.=$sk->editSectionLink(0);
1313 $full.=$block;
1314 if($st && $toclines>3 && !$i) {
1315 $full="<a name=\"top\"></a>".$full.$toc;
1318 $full.=$head[$i];
1319 $i++;
1321 return $full;
1324 /* private */ function magicISBN( $text )
1326 global $wgLang;
1328 $a = split( "ISBN ", " $text" );
1329 if ( count ( $a ) < 2 ) return $text;
1330 $text = substr( array_shift( $a ), 1);
1331 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1333 foreach ( $a as $x ) {
1334 $isbn = $blank = "" ;
1335 while ( " " == $x{0} ) {
1336 $blank .= " ";
1337 $x = substr( $x, 1 );
1339 while ( strstr( $valid, $x{0} ) != false ) {
1340 $isbn .= $x{0};
1341 $x = substr( $x, 1 );
1343 $num = str_replace( "-", "", $isbn );
1344 $num = str_replace( " ", "", $num );
1346 if ( "" == $num ) {
1347 $text .= "ISBN $blank$x";
1348 } else {
1349 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1350 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1351 $text .= $x;
1354 return $text;
1357 /* private */ function magicRFC( $text )
1359 return $text;
1362 /* private */ function headElement()
1364 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding, $wgLang;
1366 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
1368 if ( "" == $this->mHTMLtitle ) {
1369 $this->mHTMLtitle = $this->mPagetitle;
1371 $rtl = $wgLang->isRTL() ? " dir='RTL'" : "";
1372 $ret .= "<html lang=\"$wgLanguageCode\"$rtl><head><title>{$this->mHTMLtitle}</title>\n";
1373 array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1374 foreach ( $this->mMetatags as $tag ) {
1375 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1376 $a = "http-equiv";
1377 $tag[0] = substr( $tag[0], 5 );
1378 } else {
1379 $a = "name";
1381 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1383 $p = $this->mRobotpolicy;
1384 if ( "" == $p ) { $p = "index,follow"; }
1385 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1387 if ( count( $this->mKeywords ) > 0 ) {
1388 $ret .= "<meta name=\"keywords\" content=\"" .
1389 implode( ",", $this->mKeywords ) . "\">\n";
1391 foreach ( $this->mLinktags as $tag ) {
1392 $ret .= "<link ";
1393 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1394 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1395 $ret .= "href=\"{$tag[2]}\">\n";
1397 $sk = $wgUser->getSkin();
1398 $ret .= $sk->getHeadScripts();
1399 $ret .= $sk->getUserStyles();
1401 $ret .= "</head>\n";
1402 return $ret;