Implemented the "default namespaces for search" feature. This includes
[mediawiki.git] / includes / OutputPage.php
blob598bdcde283c7b10154e9c9e9e7306a22ed9c800
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;
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 = $this->replaceExternalLinks( $text );
666 $text = $this->replaceInternalLinks ( $text );
668 $text = $this->magicISBN( $text );
669 $text = $this->magicRFC( $text );
670 $text = $this->formatHeadings( $text );
672 $sk = $wgUser->getSkin();
673 $text = $sk->transformContent( $text );
675 wfProfileOut();
676 return $text;
679 /* private */ function doQuotes( $text )
681 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
682 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
683 return $text;
686 /* private */ function doHeadings( $text )
688 for ( $i = 6; $i >= 1; --$i ) {
689 $h = substr( "======", 0, $i );
690 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
691 "<h{$i}>\\1</h{$i}>\\2", $text );
693 return $text;
696 # Note: we have to do external links before the internal ones,
697 # and otherwise take great care in the order of things here, so
698 # that we don't end up interpreting some URLs twice.
700 /* private */ function replaceExternalLinks( $text )
702 wfProfileIn( "OutputPage::replaceExternalLinks" );
703 $text = $this->subReplaceExternalLinks( $text, "http", true );
704 $text = $this->subReplaceExternalLinks( $text, "https", true );
705 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
706 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
707 $text = $this->subReplaceExternalLinks( $text, "news", false );
708 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
709 wfProfileOut();
710 return $text;
713 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
715 global $wgUser, $printable;
716 global $wgAllowExternalImages;
719 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
720 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
722 # this is the list of separators that should be ignored if they
723 # are the last character of an URL but that should be included
724 # if they occur within the URL, e.g. "go to www.foo.com, where .."
725 # in this case, the last comma should not become part of the URL,
726 # but in "www.foo.com/123,2342,32.htm" it should.
727 $sep = ",;\.:";
728 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
729 $images = "gif|png|jpg|jpeg";
731 # PLEASE NOTE: The curly braces { } are not part of the regex,
732 # they are interpreted as part of the string (used to tell PHP
733 # that the content of the string should be inserted there).
734 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
735 "((?i){$images})([^{$uc}]|$)/";
737 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
738 $sk = $wgUser->getSkin();
740 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
741 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
742 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
744 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
745 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
746 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
747 "</a>\\5", $s );
748 $s = str_replace( $unique, $protocol, $s );
750 $a = explode( "[{$protocol}:", " " . $s );
751 $s = array_shift( $a );
752 $s = substr( $s, 1 );
754 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
755 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
757 foreach ( $a as $line ) {
758 if ( preg_match( $e1, $line, $m ) ) {
759 $link = "{$protocol}:{$m[1]}";
760 $trail = $m[2];
761 if ( $autonumber ) { $text = "[" . ++$this->mAutonumber . "]"; }
762 else { $text = wfEscapeHTML( $link ); }
763 } else if ( preg_match( $e2, $line, $m ) ) {
764 $link = "{$protocol}:{$m[1]}";
765 $text = $m[2];
766 $trail = $m[3];
767 } else {
768 $s .= "[{$protocol}:" . $line;
769 continue;
771 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
772 else $paren = "";
773 $la = $sk->getExternalLinkAttributes( $link, $text );
774 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
777 return $s;
780 /* private */ function replaceInternalLinks( $s )
782 global $wgTitle, $wgUser, $wgLang;
783 global $wgLinkCache, $wgInterwikiMagic;
784 global $wgNamespacesWithSubpages;
785 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
787 wfProfileIn( "$fname-setup" );
788 $tc = Title::legalChars() . "#";
789 $sk = $wgUser->getSkin();
791 $a = explode( "[[", " " . $s );
792 $s = array_shift( $a );
793 $s = substr( $s, 1 );
795 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
796 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
797 wfProfileOut();
799 wfProfileIn( "$fname-loop" );
800 foreach ( $a as $line ) {
801 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
803 $text = $m[2];
804 $trail = $m[3];
806 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
808 $text = "";
809 $trail = $m[2];
812 else { # Invalid form; output directly
813 $s .= "[[" . $line ;
814 continue;
816 if(substr($m[1],0,1)=="/") { # subpage
817 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
818 $m[1]=substr($m[1],1,strlen($m[1])-2);
819 $noslash=$m[1];
821 } else {
822 $noslash=substr($m[1],1);
824 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
825 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
826 if(!$text) {
827 $text= $m[1];
828 } # this might be changed for ugliness reasons
829 } else {
830 $link = $noslash; # no subpage allowed, use standard link
832 } else { # no subpage
833 $link = $m[1];
836 if ( preg_match( "/^([A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
837 $pre = strtolower( $m[1] );
838 $suf = $m[2];
839 if ( $wgLang->getNsIndex( $pre ) ==
840 Namespace::getImage() ) {
841 $nt = Title::newFromText( $suf );
842 $name = $nt->getDBkey();
843 if ( "" == $text ) { $text = $nt->GetText(); }
845 $wgLinkCache->addImageLink( $name );
846 $s .= $sk->makeImageLink( $name,
847 wfImageUrl( $name ), $text );
848 $s .= $trail;
849 } else if ( "media" == $pre ) {
850 $nt = Title::newFromText( $suf );
851 $name = $nt->getDBkey();
852 if ( "" == $text ) { $text = $nt->GetText(); }
854 $wgLinkCache->addImageLink( $name );
855 $s .= $sk->makeMediaLink( $name,
856 wfImageUrl( $name ), $text );
857 $s .= $trail;
858 } else {
859 $l = $wgLang->getLanguageName( $pre );
860 if ( "" == $l or !$wgInterwikiMagic or
861 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
862 if ( "" == $text ) { $text = $link; }
863 $s .= $sk->makeLink( $link, $text, "", $trail );
864 } else {
865 array_push( $this->mLanguageLinks, "$pre:$suf" );
866 $s .= $trail;
869 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
870 # $link = substr( $link, 2 );
871 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
872 } else {
873 if ( "" == $text ) { $text = $link; }
874 $s .= $sk->makeLink( $link, $text, "", $trail );
877 wfProfileOut();
878 wfProfileOut();
879 return $s;
882 # Some functions here used by doBlockLevels()
884 /* private */ function closeParagraph()
886 $result = "";
887 if ( 0 != strcmp( "p", $this->mLastSection ) &&
888 0 != strcmp( "", $this->mLastSection ) ) {
889 $result = "</" . $this->mLastSection . ">";
891 $this->mLastSection = "";
892 return $result;
894 # getCommon() returns the length of the longest common substring
895 # of both arguments, starting at the beginning of both.
897 /* private */ function getCommon( $st1, $st2 )
899 $fl = strlen( $st1 );
900 $shorter = strlen( $st2 );
901 if ( $fl < $shorter ) { $shorter = $fl; }
903 for ( $i = 0; $i < $shorter; ++$i ) {
904 if ( $st1{$i} != $st2{$i} ) { break; }
906 return $i;
908 # These next three functions open, continue, and close the list
909 # element appropriate to the prefix character passed into them.
911 /* private */ function openList( $char )
913 $result = $this->closeParagraph();
915 if ( "*" == $char ) { $result .= "<ul><li>"; }
916 else if ( "#" == $char ) { $result .= "<ol><li>"; }
917 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
918 else if ( ";" == $char ) {
919 $result .= "<dl><dt>";
920 $this->mDTopen = true;
922 else { $result = "<!-- ERR 1 -->"; }
924 return $result;
927 /* private */ function nextItem( $char )
929 if ( "*" == $char || "#" == $char ) { return "</li><li>"; }
930 else if ( ":" == $char || ";" == $char ) {
931 $close = "</dd>";
932 if ( $this->mDTopen ) { $close = "</dt>"; }
933 if ( ";" == $char ) {
934 $this->mDTopen = true;
935 return $close . "<dt>";
936 } else {
937 $this->mDTopen = false;
938 return $close . "<dd>";
941 return "<!-- ERR 2 -->";
944 /* private */function closeList( $char )
946 if ( "*" == $char ) { return "</li></ul>"; }
947 else if ( "#" == $char ) { return "</li></ol>"; }
948 else if ( ":" == $char ) {
949 if ( $this->mDTopen ) {
950 $this->mDTopen = false;
951 return "</dt></dl>";
952 } else {
953 return "</dd></dl>";
956 return "<!-- ERR 3 -->";
959 /* private */ function doBlockLevels( $text, $linestart )
961 wfProfileIn( "OutputPage::doBlockLevels" );
962 # Parsing through the text line by line. The main thing
963 # happening here is handling of block-level elements p, pre,
964 # and making lists from lines starting with * # : etc.
966 $a = explode( "\n", $text );
967 $text = $lastPref = "";
968 $this->mDTopen = $inBlockElem = false;
970 if ( ! $linestart ) { $text .= array_shift( $a ); }
971 foreach ( $a as $t ) {
972 if ( "" != $text ) { $text .= "\n"; }
974 $oLine = $t;
975 $opl = strlen( $lastPref );
976 $npl = strspn( $t, "*#:;" );
977 $pref = substr( $t, 0, $npl );
978 $pref2 = str_replace( ";", ":", $pref );
979 $t = substr( $t, $npl );
981 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
982 $text .= $this->nextItem( substr( $pref, -1 ) );
984 if ( ";" == substr( $pref, -1 ) ) {
985 $cpos = strpos( $t, ":" );
986 if ( ! ( false === $cpos ) ) {
987 $term = substr( $t, 0, $cpos );
988 $text .= $term . $this->nextItem( ":" );
989 $t = substr( $t, $cpos + 1 );
992 } else if (0 != $npl || 0 != $opl) {
993 $cpl = $this->getCommon( $pref, $lastPref );
995 while ( $cpl < $opl ) {
996 $text .= $this->closeList( $lastPref{$opl-1} );
997 --$opl;
999 if ( $npl <= $cpl && $cpl > 0 ) {
1000 $text .= $this->nextItem( $pref{$cpl-1} );
1002 while ( $npl > $cpl ) {
1003 $char = substr( $pref, $cpl, 1 );
1004 $text .= $this->openList( $char );
1006 if ( ";" == $char ) {
1007 $cpos = strpos( $t, ":" );
1008 if ( ! ( false === $cpos ) ) {
1009 $term = substr( $t, 0, $cpos );
1010 $text .= $term . $this->nextItem( ":" );
1011 $t = substr( $t, $cpos + 1 );
1014 ++$cpl;
1016 $lastPref = $pref2;
1018 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1019 if ( preg_match(
1020 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1021 $text .= $this->closeParagraph();
1022 $inBlockElem = true;
1024 if ( ! $inBlockElem ) {
1025 if ( " " == $t{0} ) {
1026 $newSection = "pre";
1027 # $t = wfEscapeHTML( $t );
1029 else { $newSection = "p"; }
1031 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1032 $text .= $this->closeParagraph();
1033 $text .= "<" . $newSection . ">";
1034 } else if ( 0 != strcmp( $this->mLastSection,
1035 $newSection ) ) {
1036 $text .= $this->closeParagraph();
1037 if ( 0 != strcmp( "p", $newSection ) ) {
1038 $text .= "<" . $newSection . ">";
1041 $this->mLastSection = $newSection;
1043 if ( $inBlockElem &&
1044 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1045 $inBlockElem = false;
1048 $text .= $t;
1050 while ( $npl ) {
1051 $text .= $this->closeList( $pref2{$npl-1} );
1052 --$npl;
1054 if ( "" != $this->mLastSection ) {
1055 if ( "p" != $this->mLastSection ) {
1056 $text .= "</" . $this->mLastSection . ">";
1058 $this->mLastSection = "";
1060 wfProfileOut();
1061 return $text;
1064 /* private */ function replaceVariables( $text )
1066 global $wgLang;
1067 wfProfileIn( "OutputPage:replaceVariables" );
1069 /* As with sigs, use server's local time --
1070 ensure this is appropriate for your audience! */
1071 $v = date( "m" );
1072 $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
1073 $v = $wgLang->getMonthName( date( "n" ) );
1074 $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
1075 $v = $wgLang->getMonthNameGen( date( "n" ) );
1076 $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
1077 $v = date( "j" );
1078 $text = str_replace( "{{CURRENTDAY}}", $v, $text );
1079 $v = $wgLang->getWeekdayName( date( "w" )+1 );
1080 $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
1081 $v = date( "Y" );
1082 $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
1083 $v = $wgLang->time( wfTimestampNow(), false );
1084 $text = str_replace( "{{CURRENTTIME}}", $v, $text );
1086 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1087 $v = wfNumberOfArticles();
1088 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1090 wfProfileOut();
1091 return $text;
1094 /* private */ function removeHTMLtags( $text )
1096 wfProfileIn( "OutputPage::removeHTMLtags" );
1097 $htmlpairs = array( # Tags that must be closed
1098 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1099 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1100 "strike", "strong", "tt", "var", "div", "center",
1101 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1102 "ruby", "rt" , "rb" , "rp"
1104 $htmlsingle = array(
1105 "br", "p", "hr", "li", "dt", "dd"
1107 $htmlnest = array( # Tags that can be nested--??
1108 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1109 "dl", "font", "big", "small", "sub", "sup"
1111 $tabletags = array( # Can only appear inside table
1112 "td", "th", "tr"
1115 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1116 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1118 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1119 "title", "align", "lang", "dir", "width", "height",
1120 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1121 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1122 /* FONT */ "type", "start", "value", "compact",
1123 /* For various lists, mostly deprecated but safe */
1124 "summary", "width", "border", "frame", "rules",
1125 "cellspacing", "cellpadding", "valign", "char",
1126 "charoff", "colgroup", "col", "span", "abbr", "axis",
1127 "headers", "scope", "rowspan", "colspan", /* Tables */
1128 "id", "class", "name", "style" /* For CSS */
1131 # Remove HTML comments
1132 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1134 $bits = explode( "<", $text );
1135 $text = array_shift( $bits );
1136 $tagstack = array(); $tablestack = array();
1138 foreach ( $bits as $x ) {
1139 $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) );
1140 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1141 $x, $regs );
1142 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1143 error_reporting( $prev );
1145 $badtag = 0 ;
1146 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1147 # Check our stack
1148 if ( $slash ) {
1149 # Closing a tag...
1150 if ( ! in_array( $t, $htmlsingle ) &&
1151 ( $ot = array_pop( $tagstack ) ) != $t ) {
1152 array_push( $tagstack, $ot );
1153 $badtag = 1;
1154 } else {
1155 if ( $t == "table" ) {
1156 $tagstack = array_pop( $tablestack );
1158 $newparams = "";
1160 } else {
1161 # Keep track for later
1162 if ( in_array( $t, $tabletags ) &&
1163 ! in_array( "table", $tagstack ) ) {
1164 $badtag = 1;
1165 } else if ( in_array( $t, $tagstack ) &&
1166 ! in_array ( $t , $htmlnest ) ) {
1167 $badtag = 1 ;
1168 } else if ( ! in_array( $t, $htmlsingle ) ) {
1169 if ( $t == "table" ) {
1170 array_push( $tablestack, $tagstack );
1171 $tagstack = array();
1173 array_push( $tagstack, $t );
1175 # Strip non-approved attributes from the tag
1176 $newparams = preg_replace(
1177 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1178 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1179 $params);
1181 if ( ! $badtag ) {
1182 $rest = str_replace( ">", "&gt;", $rest );
1183 $text .= "<$slash$t$newparams$brace$rest";
1184 continue;
1187 $text .= "&lt;" . str_replace( ">", "&gt;", $x);
1189 # Close off any remaining tags
1190 while ( $t = array_pop( $tagstack ) ) {
1191 $text .= "</$t>\n";
1192 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1194 wfProfileOut();
1195 return $text;
1201 * This function accomplishes several tasks:
1202 * 1) Auto-number headings if that option is enabled
1203 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1204 * 3) Add a Table of contents on the top for users who have enabled the option
1205 * 4) Auto-anchor headings
1207 * It loops through all headlines, collects the necessary data, then splits up the
1208 * string and re-inserts the newly formatted headlines.
1210 * */
1211 /* private */ function formatHeadings( $text )
1213 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1214 $nh=$wgUser->getOption( "numberheadings" );
1215 $st=$wgUser->getOption( "showtoc" );
1216 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1217 if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1219 $sk=$wgUser->getSkin();
1220 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1222 $c=0;
1224 foreach($matches[3] as $headline) {
1225 if($level) { $prevlevel=$level;}
1226 $level=$matches[1][$c];
1227 if(($nh||$st) && $level>$prevlevel) {
1229 $h[$level]=0; // reset when we enter a new level
1230 if($toclevel) {
1231 $toc.=$sk->tocIndent($level-$prevlevel);
1233 $toclevel++;
1236 if(($nh||$st) && $level<$prevlevel) {
1237 $h[$level+1]=0; // reset when we step back a level
1238 if($toclevel) {
1239 $toc.=$sk->tocUnindent($prevlevel-$level);
1241 $toclevel--;
1244 $h[$level]++; // count number of headlines for each level
1246 if($nh||$st) {
1247 for($i=1;$i<=$level;$i++) {
1248 if($h[$i]) {
1249 if($dot) {$numbering.=".";}
1250 $numbering.=$h[$i];
1251 $dot=1;
1257 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1258 $tocline=$canonized_headline;
1259 $canonized_headline=str_replace('"',"",$canonized_headline);
1260 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1261 $refer[$c]=$canonized_headline;
1262 $refers[$canonized_headline]++; // count how many in assoc. array so we can track dupes in anchors
1263 $refcount[$c]=$refers[$canonized_headline];
1264 if($nh||$st) {
1265 $tocline=$numbering ." ". $tocline;
1266 if($nh) {
1267 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1270 $anchor=$canonized_headline;
1271 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1272 if($st) {
1273 $toc.=$sk->tocLine($anchor,$tocline);
1275 if($es && !isset($wpPreview)) {
1276 $head[$c].=$sk->editSectionLink($c+1);
1278 $head[$c].="<H".$level.$matches[2][$c]
1279 ."<a name=\"".$anchor."\">"
1280 .$headline
1281 ."</a>"
1282 ."</H".$level.">";
1283 $numbering="";
1284 $c++;
1285 $dot=0;
1288 if($st) {
1289 $toclines=$c;
1290 while($toclevel>0) {
1291 $toc.="</ul>";
1292 $toclevel--;
1295 $toc=$sk->tocTable($toc);
1299 // split up and insert constructed headlines
1301 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1302 $i=0;
1304 foreach($blocks as $block) {
1305 if($es && !isset($wpPreview) && $c>0 && $i==0) {
1306 $full.=$sk->editSectionLink(0);
1309 $full.=$block;
1310 $full.=$head[$i];
1311 $i++;
1313 if($st && $toclines>3) {
1314 $full=$toc."<a name=\"top\"></a>".$full;
1316 return $full;
1319 /* private */ function magicISBN( $text )
1321 global $wgLang;
1323 $a = split( "ISBN ", " $text" );
1324 if ( count ( $a ) < 2 ) return $text;
1325 $text = substr( array_shift( $a ), 1);
1326 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1328 foreach ( $a as $x ) {
1329 $isbn = $blank = "" ;
1330 while ( " " == $x{0} ) {
1331 $blank .= " ";
1332 $x = substr( $x, 1 );
1334 while ( strstr( $valid, $x{0} ) != false ) {
1335 $isbn .= $x{0};
1336 $x = substr( $x, 1 );
1338 $num = str_replace( "-", "", $isbn );
1339 $num = str_replace( " ", "", $num );
1341 if ( "" == $num ) {
1342 $text .= "ISBN $blank$x";
1343 } else {
1344 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1345 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1346 $text .= $x;
1349 return $text;
1352 /* private */ function magicRFC( $text )
1354 return $text;
1357 /* private */ function headElement()
1359 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding;
1361 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\" \"$wgDTD\">\n";
1363 if ( "" == $this->mHTMLtitle ) {
1364 $this->mHTMLtitle = $this->mPagetitle;
1366 $ret .= "<html lang=\"$wgLanguageCode\"><head><title>{$this->mHTMLtitle}</title>\n";
1367 array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1368 foreach ( $this->mMetatags as $tag ) {
1369 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1370 $a = "http-equiv";
1371 $tag[0] = substr( $tag[0], 5 );
1372 } else {
1373 $a = "name";
1375 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1377 $p = $this->mRobotpolicy;
1378 if ( "" == $p ) { $p = "index,follow"; }
1379 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1381 if ( count( $this->mKeywords ) > 0 ) {
1382 $ret .= "<meta name=\"keywords\" content=\"" .
1383 implode( ",", $this->mKeywords ) . "\">\n";
1385 foreach ( $this->mLinktags as $tag ) {
1386 $ret .= "<link ";
1387 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1388 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1389 $ret .= "href=\"{$tag[2]}\">\n";
1391 $sk = $wgUser->getSkin();
1392 $ret .= $sk->getHeadScripts();
1393 $ret .= $sk->getUserStyles();
1395 $ret .= "</head>\n";
1396 return $ret;