Some fixes to table generation, show table after intro
[mediawiki.git] / includes / OutputPage.php
blob2ce3041645659e0342931ef3007436b5f6061983
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 for ( $i = 1; $i <= $presecs; ++$i ) {
292 $text = preg_replace( "/{$unique3}/", str_replace( '$', '\$', $prelist[$i] ), $text, 1 );
295 for ( $i = 1; $i <= $mathsecs; ++$i ) {
296 $text = preg_replace( "/{$unique2}/", str_replace( '$', '\$', $mathlist[$i] ), $text, 1 );
299 for ( $i = 1; $i <= $nwsecs; ++$i ) {
300 $text = preg_replace( "/{$unique}/", str_replace( '$', '\$', $nwlist[$i] ), $text, 1 );
302 $this->addHTML( $text );
303 wfProfileOut();
306 # Finally, all the text has been munged and accumulated into
307 # the object, let's actually output it:
309 function output()
311 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
312 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
313 wfProfileIn( "OutputPage::output" );
314 $sk = $wgUser->getSkin();
316 wfProfileIn( "OutputPage::output-headers" );
317 if( $this->mLastModified != "" ) {
318 header( "Cache-Control: private, must-revalidate, max-age=0" );
319 header( "Last-modified: {$this->mLastModified}" );
320 } else {
321 header( "Cache-Control: no-cache" ); # Experimental - see below
322 header( "Pragma: no-cache" );
323 header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
325 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
327 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
328 header( "Content-language: {$wgLanguageCode}" );
330 if ( "" != $this->mRedirect ) {
331 header( "Location: {$this->mRedirect}" );
332 wfProfileOut();
333 return;
336 $exp = time() + $wgCookieExpiration;
337 foreach( $this->mCookies as $name => $val ) {
338 setcookie( $name, $val, $exp, "/" );
340 wfProfileOut();
342 wfProfileIn( "OutputPage::output-middle" );
343 $sk->initPage();
344 $this->out( $this->headElement() );
346 $this->out( "\n<body" );
347 $ops = $sk->getBodyOptions();
348 foreach ( $ops as $name => $val ) {
349 $this->out( " $name='$val'" );
351 $this->out( ">\n" );
352 if ( $wgDebugComments ) {
353 $this->out( "<!-- Wiki debugging output:\n" .
354 $this->mDebugtext . "-->\n" );
356 $this->out( $sk->beforeContent() );
357 wfProfileOut();
359 wfProfileIn( "OutputPage::output-bodytext" );
360 $this->out( $this->mBodytext );
361 wfProfileOut();
362 wfProfileIn( "OutputPage::output-after" );
363 $this->out( $sk->afterContent() );
364 wfProfileOut();
366 wfProfileOut(); # A hack - we can't report after here
367 $this->out( $this->reportTime() );
369 $this->out( "\n</body></html>" );
370 flush();
373 function out( $ins )
375 global $wgInputEncoding, $wgOutputEncoding, $wgLang;
376 if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
377 $outs = $ins;
378 } else {
379 $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
380 if ( false === $outs ) { $outs = $ins; }
382 print $outs;
385 function setEncodings()
387 global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
388 global $wgUser, $wgLang;
390 $wgInputEncoding = strtolower( $wgInputEncoding );
391 $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
393 if( $wgUser->getOption( 'altencoding' ) ) {
394 $wgLang->setAltEncoding();
395 return;
398 if ( "" == $s ) {
399 $wgOutputEncoding = strtolower( $wgOutputEncoding );
400 return;
402 $a = explode( ",", $s );
403 $best = 0.0;
404 $bestset = "*";
406 foreach ( $a as $s ) {
407 if ( preg_match( "/(.*);q=(.*)/", $s, $m ) ) {
408 $set = $m[1];
409 $q = (float)($m[2]);
410 } else {
411 $set = $s;
412 $q = 1.0;
414 if ( $q > $best ) {
415 $bestset = $set;
416 $best = $q;
419 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
420 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
421 $wgOutputEncoding = strtolower( $bestset );
423 # Disable for now
425 $wgOutputEncoding = $wgInputEncoding;
428 function reportTime()
430 global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
431 global $wgProfiling, $wgProfileStack, $wgUser;
433 list( $usec, $sec ) = explode( " ", microtime() );
434 $now = (float)$sec + (float)$usec;
436 list( $usec, $sec ) = explode( " ", $wgRequestTime );
437 $start = (float)$sec + (float)$usec;
438 $elapsed = $now - $start;
440 if ( "" != $wgDebugLogFile ) {
441 $prof = "";
442 if( $wgProfiling and count( $wgProfileStack ) ) {
443 $lasttime = $start;
444 foreach( $wgProfileStack as $ile ) {
445 # "foo::bar 99 0.12345 1 0.23456 2"
446 if( preg_match( '/^(\S+)\s+([0-9]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/', $ile, $m ) ) {
447 $thisstart = (float)$m[3] + (float)$m[4] - $start;
448 $thisend = (float)$m[5] + (float)$m[6] - $start;
449 $thiselapsed = $thisend - $thisstart;
450 $thispercent = $thiselapsed / $elapsed * 100.0;
452 $prof .= sprintf( "\tat %04.3f in %04.3f (%2.1f%%) - %s %s\n",
453 $thisstart, $thiselapsed, $thispercent,
454 str_repeat( "*", $m[2] ), $m[1] );
455 $lasttime = $thistime;
456 #$prof .= "\t(^ $ile)\n";
457 } else {
458 $prof .= "\t?broken? $ile\n";
463 if( $forward = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'] )
464 $forward = " forwarded for $forward";
465 if( $client = $HTTP_SERVER_VARS['HTTP_CLIENT_IP'] )
466 $forward .= " client IP $client";
467 if( $from = $HTTP_SERVER_VARS['HTTP_FROM'] )
468 $forward .= " from $from";
469 if( $forward )
470 $forward = "\t(proxied via {$HTTP_SERVER_VARS['REMOTE_ADDR']}{$forward})";
471 if($wgUser->getId() == 0)
472 $forward .= " anon";
473 $log = sprintf( "%s\t%04.3f\t%s\n",
474 gmdate( "YmdHis" ), $elapsed,
475 urldecode( $HTTP_SERVER_VARS['REQUEST_URI'] . $forward ) );
476 error_log( $log . $prof, 3, $wgDebugLogFile );
478 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
479 $elapsed );
480 return $com;
483 # Note: these arguments are keys into wfMsg(), not text!
485 function errorpage( $title, $msg )
487 global $wgTitle;
489 $this->mDebugtext .= "Original title: " .
490 $wgTitle->getPrefixedText() . "\n";
491 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
492 $this->setPageTitle( wfMsg( $title ) );
493 $this->setRobotpolicy( "noindex,nofollow" );
494 $this->setArticleFlag( false );
496 $this->mBodytext = "";
497 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
498 $this->returnToMain( false );
500 $this->output();
501 exit;
504 function sysopRequired()
506 global $wgUser;
508 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
509 $this->setPageTitle( wfMsg( "sysoptitle" ) );
510 $this->setRobotpolicy( "noindex,nofollow" );
511 $this->setArticleFlag( false );
512 $this->mBodytext = "";
514 $sk = $wgUser->getSkin();
515 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
516 $text = str_replace( "$1", $ap, wfMsg( "sysoptext" ) );
517 $this->addHTML( $text );
518 $this->returnToMain();
521 function developerRequired()
523 global $wgUser;
525 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
526 $this->setPageTitle( wfMsg( "developertitle" ) );
527 $this->setRobotpolicy( "noindex,nofollow" );
528 $this->setArticleFlag( false );
529 $this->mBodytext = "";
531 $sk = $wgUser->getSkin();
532 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
533 $text = str_replace( "$1", $ap, wfMsg( "developertext" ) );
534 $this->addHTML( $text );
535 $this->returnToMain();
538 function databaseError( $fname )
540 global $wgUser, $wgCommandLineMode;
542 $this->setPageTitle( wfMsg( "databaseerror" ) );
543 $this->setRobotpolicy( "noindex,nofollow" );
544 $this->setArticleFlag( false );
546 if ( $wgCommandLineMode ) {
547 $msg = wfMsg( "dberrortextcl" );
548 } else {
549 $msg = wfMsg( "dberrortextcl" );
551 $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), $msg );
552 $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg );
553 $msg = str_replace( "$3", wfLastErrno(), $msg );
554 $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg );
556 if ( $wgCommandLineMode ) {
557 print $msg;
558 exit();
560 $sk = $wgUser->getSkin();
561 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
562 wfMsg( "searchingwikipedia" ) );
563 $msg = str_replace( "$5", $shlink, $msg );
565 $this->mBodytext = $msg;
566 $this->output();
567 exit();
570 function readOnlyPage()
572 global $wgUser, $wgReadOnlyFile;
574 $this->setPageTitle( wfMsg( "readonly" ) );
575 $this->setRobotpolicy( "noindex,nofollow" );
576 $this->setArticleFlag( false );
578 $reason = implode( "", file( $wgReadOnlyFile ) );
579 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
580 $this->addHTML( $text );
581 $this->returnToMain( false );
584 function fatalError( $message )
586 $this->setPageTitle( wfMsg( "internalerror" ) );
587 $this->setRobotpolicy( "noindex,nofollow" );
588 $this->setArticleFlag( false );
590 $this->mBodytext = $message;
591 $this->output();
592 exit;
595 function unexpectedValueError( $name, $val )
597 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
598 $msg = str_replace( "$2", $val, $msg );
599 $this->fatalError( $msg );
602 function fileCopyError( $old, $new )
604 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
605 $msg = str_replace( "$2", $new, $msg );
606 $this->fatalError( $msg );
609 function fileRenameError( $old, $new )
611 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
612 $msg = str_replace( "$2", $new, $msg );
613 $this->fatalError( $msg );
616 function fileDeleteError( $name )
618 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
619 $this->fatalError( $msg );
622 function fileNotFoundError( $name )
624 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
625 $this->fatalError( $msg );
628 function returnToMain( $auto = true )
630 global $wgUser, $wgOut, $returnto;
632 $sk = $wgUser->getSkin();
633 if ( "" == $returnto ) {
634 $returnto = wfMsg( "mainpage" );
636 $link = $sk->makeKnownLink( $returnto, "" );
638 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
639 if ( $auto ) {
640 $wgOut->addMeta( "http:Refresh", "10;url=" .
641 wfLocalUrlE( wfUrlencode( $returnto ) ) );
643 $wgOut->addHTML( "\n<p>$r\n" );
646 # Well, OK, it's actually about 14 passes. But since all the
647 # hard lifting is done inside PHP's regex code, it probably
648 # wouldn't speed things up much to add a real parser.
650 function doWikiPass2( $text, $linestart )
652 global $wgUser, $wgLang;
653 wfProfileIn( "OutputPage::doWikiPass2" );
655 $text = $this->removeHTMLtags( $text );
656 $text = $this->replaceVariables( $text );
658 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
659 $text = str_replace ( "<HR>", "<hr>", $text );
661 $text = $this->doQuotes( $text );
662 $text = $this->doHeadings( $text );
663 $text = $this->doBlockLevels( $text, $linestart );
665 $text = $wgLang->replaceDates( $text );
666 $text = $this->replaceExternalLinks( $text );
667 $text = $this->replaceInternalLinks ( $text );
669 $text = $this->magicISBN( $text );
670 $text = $this->magicRFC( $text );
671 $text = $this->formatHeadings( $text );
673 $sk = $wgUser->getSkin();
674 $text = $sk->transformContent( $text );
676 wfProfileOut();
677 return $text;
680 /* private */ function doQuotes( $text )
682 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
683 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
684 return $text;
687 /* private */ function doHeadings( $text )
689 for ( $i = 6; $i >= 1; --$i ) {
690 $h = substr( "======", 0, $i );
691 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
692 "<h{$i}>\\1</h{$i}>\\2", $text );
694 return $text;
697 # Note: we have to do external links before the internal ones,
698 # and otherwise take great care in the order of things here, so
699 # that we don't end up interpreting some URLs twice.
701 /* private */ function replaceExternalLinks( $text )
703 wfProfileIn( "OutputPage::replaceExternalLinks" );
704 $text = $this->subReplaceExternalLinks( $text, "http", true );
705 $text = $this->subReplaceExternalLinks( $text, "https", true );
706 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
707 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
708 $text = $this->subReplaceExternalLinks( $text, "news", false );
709 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
710 wfProfileOut();
711 return $text;
714 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
716 global $wgUser, $printable;
717 global $wgAllowExternalImages;
720 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
721 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
723 # this is the list of separators that should be ignored if they
724 # are the last character of an URL but that should be included
725 # if they occur within the URL, e.g. "go to www.foo.com, where .."
726 # in this case, the last comma should not become part of the URL,
727 # but in "www.foo.com/123,2342,32.htm" it should.
728 $sep = ",;\.:";
729 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
730 $images = "gif|png|jpg|jpeg";
732 # PLEASE NOTE: The curly braces { } are not part of the regex,
733 # they are interpreted as part of the string (used to tell PHP
734 # that the content of the string should be inserted there).
735 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
736 "((?i){$images})([^{$uc}]|$)/";
738 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
739 $sk = $wgUser->getSkin();
741 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
742 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
743 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
745 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
746 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
747 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
748 "</a>\\5", $s );
749 $s = str_replace( $unique, $protocol, $s );
751 $a = explode( "[{$protocol}:", " " . $s );
752 $s = array_shift( $a );
753 $s = substr( $s, 1 );
755 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
756 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
758 foreach ( $a as $line ) {
759 if ( preg_match( $e1, $line, $m ) ) {
760 $link = "{$protocol}:{$m[1]}";
761 $trail = $m[2];
762 if ( $autonumber ) { $text = "[" . ++$this->mAutonumber . "]"; }
763 else { $text = wfEscapeHTML( $link ); }
764 } else if ( preg_match( $e2, $line, $m ) ) {
765 $link = "{$protocol}:{$m[1]}";
766 $text = $m[2];
767 $trail = $m[3];
768 } else {
769 $s .= "[{$protocol}:" . $line;
770 continue;
772 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
773 else $paren = "";
774 $la = $sk->getExternalLinkAttributes( $link, $text );
775 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
778 return $s;
781 /* private */ function replaceInternalLinks( $s )
783 global $wgTitle, $wgUser, $wgLang;
784 global $wgLinkCache, $wgInterwikiMagic;
785 global $wgNamespacesWithSubpages;
786 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
788 wfProfileIn( "$fname-setup" );
789 $tc = Title::legalChars() . "#";
790 $sk = $wgUser->getSkin();
792 $a = explode( "[[", " " . $s );
793 $s = array_shift( $a );
794 $s = substr( $s, 1 );
796 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
797 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
798 wfProfileOut();
800 wfProfileIn( "$fname-loop" );
801 foreach ( $a as $line ) {
802 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
804 $text = $m[2];
805 $trail = $m[3];
807 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
809 $text = "";
810 $trail = $m[2];
813 else { # Invalid form; output directly
814 $s .= "[[" . $line ;
815 continue;
817 if(substr($m[1],0,1)=="/") { # subpage
818 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
819 $m[1]=substr($m[1],1,strlen($m[1])-2);
820 $noslash=$m[1];
822 } else {
823 $noslash=substr($m[1],1);
825 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
826 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
827 if(!$text) {
828 $text= $m[1];
829 } # this might be changed for ugliness reasons
830 } else {
831 $link = $noslash; # no subpage allowed, use standard link
833 } else { # no subpage
834 $link = $m[1];
837 if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
838 $pre = strtolower( $m[1] );
839 $suf = $m[2];
840 if ( $wgLang->getNsIndex( $pre ) ==
841 Namespace::getImage() ) {
842 $nt = Title::newFromText( $suf );
843 $name = $nt->getDBkey();
844 if ( "" == $text ) { $text = $nt->GetText(); }
846 $wgLinkCache->addImageLink( $name );
847 $s .= $sk->makeImageLink( $name,
848 wfImageUrl( $name ), $text );
849 $s .= $trail;
850 } else if ( "media" == $pre ) {
851 $nt = Title::newFromText( $suf );
852 $name = $nt->getDBkey();
853 if ( "" == $text ) { $text = $nt->GetText(); }
855 $wgLinkCache->addImageLink( $name );
856 $s .= $sk->makeMediaLink( $name,
857 wfImageUrl( $name ), $text );
858 $s .= $trail;
859 } else {
860 $l = $wgLang->getLanguageName( $pre );
861 if ( "" == $l or !$wgInterwikiMagic or
862 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
863 if ( "" == $text ) { $text = $link; }
864 $s .= $sk->makeLink( $link, $text, "", $trail );
865 } else {
866 array_push( $this->mLanguageLinks, "$pre:$suf" );
867 $s .= $trail;
870 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
871 # $link = substr( $link, 2 );
872 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
873 } else {
874 if ( "" == $text ) { $text = $link; }
875 $s .= $sk->makeLink( $link, $text, "", $trail );
878 wfProfileOut();
879 wfProfileOut();
880 return $s;
883 # Some functions here used by doBlockLevels()
885 /* private */ function closeParagraph()
887 $result = "";
888 if ( 0 != strcmp( "p", $this->mLastSection ) &&
889 0 != strcmp( "", $this->mLastSection ) ) {
890 $result = "</" . $this->mLastSection . ">";
892 $this->mLastSection = "";
893 return $result;
895 # getCommon() returns the length of the longest common substring
896 # of both arguments, starting at the beginning of both.
898 /* private */ function getCommon( $st1, $st2 )
900 $fl = strlen( $st1 );
901 $shorter = strlen( $st2 );
902 if ( $fl < $shorter ) { $shorter = $fl; }
904 for ( $i = 0; $i < $shorter; ++$i ) {
905 if ( $st1{$i} != $st2{$i} ) { break; }
907 return $i;
909 # These next three functions open, continue, and close the list
910 # element appropriate to the prefix character passed into them.
912 /* private */ function openList( $char )
914 $result = $this->closeParagraph();
916 if ( "*" == $char ) { $result .= "<ul><li>"; }
917 else if ( "#" == $char ) { $result .= "<ol><li>"; }
918 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
919 else if ( ";" == $char ) {
920 $result .= "<dl><dt>";
921 $this->mDTopen = true;
923 else { $result = "<!-- ERR 1 -->"; }
925 return $result;
928 /* private */ function nextItem( $char )
930 if ( "*" == $char || "#" == $char ) { return "</li><li>"; }
931 else if ( ":" == $char || ";" == $char ) {
932 $close = "</dd>";
933 if ( $this->mDTopen ) { $close = "</dt>"; }
934 if ( ";" == $char ) {
935 $this->mDTopen = true;
936 return $close . "<dt>";
937 } else {
938 $this->mDTopen = false;
939 return $close . "<dd>";
942 return "<!-- ERR 2 -->";
945 /* private */function closeList( $char )
947 if ( "*" == $char ) { return "</li></ul>"; }
948 else if ( "#" == $char ) { return "</li></ol>"; }
949 else if ( ":" == $char ) {
950 if ( $this->mDTopen ) {
951 $this->mDTopen = false;
952 return "</dt></dl>";
953 } else {
954 return "</dd></dl>";
957 return "<!-- ERR 3 -->";
960 /* private */ function doBlockLevels( $text, $linestart )
962 wfProfileIn( "OutputPage::doBlockLevels" );
963 # Parsing through the text line by line. The main thing
964 # happening here is handling of block-level elements p, pre,
965 # and making lists from lines starting with * # : etc.
967 $a = explode( "\n", $text );
968 $text = $lastPref = "";
969 $this->mDTopen = $inBlockElem = false;
971 if ( ! $linestart ) { $text .= array_shift( $a ); }
972 foreach ( $a as $t ) {
973 if ( "" != $text ) { $text .= "\n"; }
975 $oLine = $t;
976 $opl = strlen( $lastPref );
977 $npl = strspn( $t, "*#:;" );
978 $pref = substr( $t, 0, $npl );
979 $pref2 = str_replace( ";", ":", $pref );
980 $t = substr( $t, $npl );
982 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
983 $text .= $this->nextItem( substr( $pref, -1 ) );
985 if ( ";" == substr( $pref, -1 ) ) {
986 $cpos = strpos( $t, ":" );
987 if ( ! ( false === $cpos ) ) {
988 $term = substr( $t, 0, $cpos );
989 $text .= $term . $this->nextItem( ":" );
990 $t = substr( $t, $cpos + 1 );
993 } else if (0 != $npl || 0 != $opl) {
994 $cpl = $this->getCommon( $pref, $lastPref );
996 while ( $cpl < $opl ) {
997 $text .= $this->closeList( $lastPref{$opl-1} );
998 --$opl;
1000 if ( $npl <= $cpl && $cpl > 0 ) {
1001 $text .= $this->nextItem( $pref{$cpl-1} );
1003 while ( $npl > $cpl ) {
1004 $char = substr( $pref, $cpl, 1 );
1005 $text .= $this->openList( $char );
1007 if ( ";" == $char ) {
1008 $cpos = strpos( $t, ":" );
1009 if ( ! ( false === $cpos ) ) {
1010 $term = substr( $t, 0, $cpos );
1011 $text .= $term . $this->nextItem( ":" );
1012 $t = substr( $t, $cpos + 1 );
1015 ++$cpl;
1017 $lastPref = $pref2;
1019 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1020 if ( preg_match(
1021 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1022 $text .= $this->closeParagraph();
1023 $inBlockElem = true;
1025 if ( ! $inBlockElem ) {
1026 if ( " " == $t{0} ) {
1027 $newSection = "pre";
1028 # $t = wfEscapeHTML( $t );
1030 else { $newSection = "p"; }
1032 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1033 $text .= $this->closeParagraph();
1034 $text .= "<" . $newSection . ">";
1035 } else if ( 0 != strcmp( $this->mLastSection,
1036 $newSection ) ) {
1037 $text .= $this->closeParagraph();
1038 if ( 0 != strcmp( "p", $newSection ) ) {
1039 $text .= "<" . $newSection . ">";
1042 $this->mLastSection = $newSection;
1044 if ( $inBlockElem &&
1045 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1046 $inBlockElem = false;
1049 $text .= $t;
1051 while ( $npl ) {
1052 $text .= $this->closeList( $pref2{$npl-1} );
1053 --$npl;
1055 if ( "" != $this->mLastSection ) {
1056 if ( "p" != $this->mLastSection ) {
1057 $text .= "</" . $this->mLastSection . ">";
1059 $this->mLastSection = "";
1061 wfProfileOut();
1062 return $text;
1065 /* private */ function replaceVariables( $text )
1067 global $wgLang;
1068 wfProfileIn( "OutputPage:replaceVariables" );
1070 /* As with sigs, use server's local time --
1071 ensure this is appropriate for your audience! */
1072 $v = date( "m" );
1073 $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
1074 $v = $wgLang->getMonthName( date( "n" ) );
1075 $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
1076 $v = $wgLang->getMonthNameGen( date( "n" ) );
1077 $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
1078 $v = date( "j" );
1079 $text = str_replace( "{{CURRENTDAY}}", $v, $text );
1080 $v = $wgLang->getWeekdayName( date( "w" )+1 );
1081 $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
1082 $v = date( "Y" );
1083 $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
1084 $v = $wgLang->time( wfTimestampNow(), false );
1085 $text = str_replace( "{{CURRENTTIME}}", $v, $text );
1087 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1088 $v = wfNumberOfArticles();
1089 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1091 wfProfileOut();
1092 return $text;
1095 /* private */ function removeHTMLtags( $text )
1097 wfProfileIn( "OutputPage::removeHTMLtags" );
1098 $htmlpairs = array( # Tags that must be closed
1099 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1100 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1101 "strike", "strong", "tt", "var", "div", "center",
1102 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1103 "ruby", "rt" , "rb" , "rp"
1105 $htmlsingle = array(
1106 "br", "p", "hr", "li", "dt", "dd"
1108 $htmlnest = array( # Tags that can be nested--??
1109 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1110 "dl", "font", "big", "small", "sub", "sup"
1112 $tabletags = array( # Can only appear inside table
1113 "td", "th", "tr"
1116 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1117 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1119 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1120 "title", "align", "lang", "dir", "width", "height",
1121 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1122 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1123 /* FONT */ "type", "start", "value", "compact",
1124 /* For various lists, mostly deprecated but safe */
1125 "summary", "width", "border", "frame", "rules",
1126 "cellspacing", "cellpadding", "valign", "char",
1127 "charoff", "colgroup", "col", "span", "abbr", "axis",
1128 "headers", "scope", "rowspan", "colspan", /* Tables */
1129 "id", "class", "name", "style" /* For CSS */
1132 # Remove HTML comments
1133 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1135 $bits = explode( "<", $text );
1136 $text = array_shift( $bits );
1137 $tagstack = array(); $tablestack = array();
1139 foreach ( $bits as $x ) {
1140 $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) );
1141 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1142 $x, $regs );
1143 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1144 error_reporting( $prev );
1146 $badtag = 0 ;
1147 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1148 # Check our stack
1149 if ( $slash ) {
1150 # Closing a tag...
1151 if ( ! in_array( $t, $htmlsingle ) &&
1152 ( $ot = array_pop( $tagstack ) ) != $t ) {
1153 array_push( $tagstack, $ot );
1154 $badtag = 1;
1155 } else {
1156 if ( $t == "table" ) {
1157 $tagstack = array_pop( $tablestack );
1159 $newparams = "";
1161 } else {
1162 # Keep track for later
1163 if ( in_array( $t, $tabletags ) &&
1164 ! in_array( "table", $tagstack ) ) {
1165 $badtag = 1;
1166 } else if ( in_array( $t, $tagstack ) &&
1167 ! in_array ( $t , $htmlnest ) ) {
1168 $badtag = 1 ;
1169 } else if ( ! in_array( $t, $htmlsingle ) ) {
1170 if ( $t == "table" ) {
1171 array_push( $tablestack, $tagstack );
1172 $tagstack = array();
1174 array_push( $tagstack, $t );
1176 # Strip non-approved attributes from the tag
1177 $newparams = preg_replace(
1178 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1179 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1180 $params);
1182 if ( ! $badtag ) {
1183 $rest = str_replace( ">", "&gt;", $rest );
1184 $text .= "<$slash$t$newparams$brace$rest";
1185 continue;
1188 $text .= "&lt;" . str_replace( ">", "&gt;", $x);
1190 # Close off any remaining tags
1191 while ( $t = array_pop( $tagstack ) ) {
1192 $text .= "</$t>\n";
1193 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1195 wfProfileOut();
1196 return $text;
1202 * This function accomplishes several tasks:
1203 * 1) Auto-number headings if that option is enabled
1204 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1205 * 3) Add a Table of contents on the top for users who have enabled the option
1206 * 4) Auto-anchor headings
1208 * It loops through all headlines, collects the necessary data, then splits up the
1209 * string and re-inserts the newly formatted headlines.
1211 * */
1212 /* private */ function formatHeadings( $text )
1214 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1215 $nh=$wgUser->getOption( "numberheadings" );
1216 $st=$wgUser->getOption( "showtoc" );
1217 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1218 if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1220 $sk=$wgUser->getSkin();
1221 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1223 $c=0;
1225 foreach($matches[3] as $headline) {
1226 if($level) { $prevlevel=$level;}
1227 $level=$matches[1][$c];
1228 if(($nh||$st) && $prevlevel && $level>$prevlevel) {
1230 $h[$level]=0; // reset when we enter a new level
1231 $toc.=$sk->tocIndent($level-$prevlevel);
1232 $toclevel+=$level-$prevlevel;
1235 if(($nh||$st) && $level<$prevlevel) {
1236 $h[$level+1]=0; // reset when we step back a level
1237 $toc.=$sk->tocUnindent($prevlevel-$level);
1238 $toclevel-=$prevlevel-$level;
1241 $h[$level]++; // count number of headlines for each level
1243 if($nh||$st) {
1244 for($i=1;$i<=$level;$i++) {
1245 if($h[$i]) {
1246 if($dot) {$numbering.=".";}
1247 $numbering.=$h[$i];
1248 $dot=1;
1254 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1255 $tocline=$canonized_headline;
1256 $canonized_headline=str_replace('"',"",$canonized_headline);
1257 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1258 $refer[$c]=$canonized_headline;
1259 $refers[$canonized_headline]++; // count how many in assoc. array so we can track dupes in anchors
1260 $refcount[$c]=$refers[$canonized_headline];
1261 if($nh||$st) {
1262 $tocline=$numbering ." ". $tocline;
1263 if($nh) {
1264 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1267 $anchor=$canonized_headline;
1268 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1269 if($st) {
1270 $toc.=$sk->tocLine($anchor,$tocline,$toclevel);
1272 if($es && !isset($wpPreview)) {
1273 $head[$c].=$sk->editSectionLink($c+1);
1275 $head[$c].="<H".$level.$matches[2][$c]
1276 ."<a name=\"".$anchor."\">"
1277 .$headline
1278 ."</a>"
1279 ."</H".$level.">";
1280 $numbering="";
1281 $c++;
1282 $dot=0;
1285 if($st) {
1286 $toclines=$c;
1287 $toc.=$sk->tocUnindent($toclevel);
1288 $toc=$sk->tocTable($toc);
1291 // split up and insert constructed headlines
1293 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1294 $i=0;
1297 foreach($blocks as $block) {
1298 if($es && !isset($wpPreview) && $c>0 && $i==0) {
1299 $full.=$sk->editSectionLink(0);
1301 $full.=$block;
1302 if($st && $toclines>3 && !$i) {
1303 $full="<a name=\"top\"></a>".$full.$toc;
1306 $full.=$head[$i];
1307 $i++;
1309 return $full;
1312 /* private */ function magicISBN( $text )
1314 global $wgLang;
1316 $a = split( "ISBN ", " $text" );
1317 if ( count ( $a ) < 2 ) return $text;
1318 $text = substr( array_shift( $a ), 1);
1319 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1321 foreach ( $a as $x ) {
1322 $isbn = $blank = "" ;
1323 while ( " " == $x{0} ) {
1324 $blank .= " ";
1325 $x = substr( $x, 1 );
1327 while ( strstr( $valid, $x{0} ) != false ) {
1328 $isbn .= $x{0};
1329 $x = substr( $x, 1 );
1331 $num = str_replace( "-", "", $isbn );
1332 $num = str_replace( " ", "", $num );
1334 if ( "" == $num ) {
1335 $text .= "ISBN $blank$x";
1336 } else {
1337 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1338 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1339 $text .= $x;
1342 return $text;
1345 /* private */ function magicRFC( $text )
1347 return $text;
1350 /* private */ function headElement()
1352 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding;
1354 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
1356 if ( "" == $this->mHTMLtitle ) {
1357 $this->mHTMLtitle = $this->mPagetitle;
1359 $ret .= "<html lang=\"$wgLanguageCode\"><head><title>{$this->mHTMLtitle}</title>\n";
1360 array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1361 foreach ( $this->mMetatags as $tag ) {
1362 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1363 $a = "http-equiv";
1364 $tag[0] = substr( $tag[0], 5 );
1365 } else {
1366 $a = "name";
1368 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1370 $p = $this->mRobotpolicy;
1371 if ( "" == $p ) { $p = "index,follow"; }
1372 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1374 if ( count( $this->mKeywords ) > 0 ) {
1375 $ret .= "<meta name=\"keywords\" content=\"" .
1376 implode( ",", $this->mKeywords ) . "\">\n";
1378 foreach ( $this->mLinktags as $tag ) {
1379 $ret .= "<link ";
1380 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1381 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1382 $ret .= "href=\"{$tag[2]}\">\n";
1384 $sk = $wgUser->getSkin();
1385 $ret .= $sk->getHeadScripts();
1386 $ret .= $sk->getUserStyles();
1388 $ret .= "</head>\n";
1389 return $ret;