Enhances special page links; allow some parameters to be passed via wikilinks to...
[lhc/web/wiklou.git] / includes / OutputPage.php
1 <?
2 # See design.doc
3
4 function linkToMathImage ( $tex, $outputhash )
5 {
6 global $wgMathPath;
7 return "<img src=\"".$wgMathPath."/".$outputhash.".png\" alt=\"".wfEscapeHTML($tex)."\">";
8 }
9
10 function renderMath( $tex )
11 {
12 global $wgUser, $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding;
13 $mf = wfMsg( "math_failure" );
14 $munk = wfMsg( "math_unknown_error" );
15
16 $fname = "renderMath";
17
18 $math = $wgUser->getOption("math");
19 if ($math == 3)
20 return ('$ '.wfEscapeHTML($tex).' $');
21
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."'";
28
29 $res = wfQuery( $sql, $fname );
30 if ( wfNumRows( $res ) == 0 )
31 {
32 $cmd = "./math/texvc ".escapeshellarg($wgTmpDirectory)." ".
33 escapeshellarg($wgMathDirectory)." ".escapeshellarg($tex)." ".escapeshellarg($wgInputEncoding);
34 $contents = `$cmd`;
35
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);
47
48 $i = strpos($outdata, "\000");
49
50 $outhtml = substr($outdata, 0, $i);
51 $mathml = substr($outdata, $i+1);
52
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>";
88 }
89
90 $outmd5 = substr ($contents, 1, 32);
91 if (!preg_match("/^[a-f0-9]{32}$/", $outmd5))
92 return "<b>".$mf." (".$munk."): ".wfEscapeHTML($tex)."</b>";
93
94 $outmd5_sql = mysql_escape_string(pack("H32", $outmd5));
95
96 $sql = "REPLACE INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$conservativeness.", ".$sql_html.", ".$sql_mathml.")";
97
98 $res = wfQuery( $sql, $fname );
99 # we don't really care if it fails
100
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'];
109
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;
114 }
115 }
116
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;
123
124 var $mDTopen, $mLastSection; # Used for processing DL, PRE
125 var $mLanguageLinks, $mSupressQuickbar;
126
127 function OutputPage()
128 {
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;
138 }
139
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; }
143
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 ) ); }
148
149 function checkLastModified ( $timestamp )
150 {
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;
157 }
158 if( $wgUser->getOption( "nocache" ) ) return;
159
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 );
166
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;
178 }
179 }
180 }
181
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; }
192
193 function getLanguageLinks() {
194 global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode;
195 global $wgDBconnection, $wgDBname, $wgDBintlname;
196
197 if ( ! $wgUseNewInterlanguage )
198 return $this->mLanguageLinks;
199
200 mysql_select_db( $wgDBintlname, $wgDBconnection ) or die(
201 htmlspecialchars(mysql_error()) );
202
203 $list = array();
204 $sql = "SELECT * FROM ilinks WHERE lang_from=\"" .
205 "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\"";
206 $res = mysql_query( $sql, $wgDBconnection );
207
208 while ( $q = mysql_fetch_object ( $res ) ) {
209 $list[] = $q->lang_to . ":" . $q->title_to;
210 }
211 mysql_free_result( $res );
212 mysql_select_db( $wgDBname, $wgDBconnection ) or die(
213 htmlspecialchars(mysql_error()) );
214
215 return $list;
216 }
217
218 function supressQuickbar() { $this->mSupressQuickbar = true; }
219 function isQuickbarSupressed() { return $this->mSupressQuickbar; }
220
221 function addHTML( $text ) { $this->mBodytext .= $text; }
222 function addHeadtext( $text ) { $this->mHeadtext .= $text; }
223 function debug( $text ) { $this->mDebugtext .= $text; }
224
225 # First pass--just handle <nowiki> sections, pass the rest off
226 # to doWikiPass2() which does all the real work.
227 #
228
229 function addWikiText( $text, $linestart = true )
230 {
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 = "";
245
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];
256 }
257 }
258
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];
270 }
271 }
272 } else {
273 $stripped2 = $stripped;
274 }
275
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];
286 }
287 }
288
289 $text = $this->doWikiPass2( $stripped3, $linestart );
290
291 for ( $i = 1; $i <= $presecs; ++$i ) {
292 $text = preg_replace( "/{$unique3}/", str_replace( '$', '\$', $prelist[$i] ), $text, 1 );
293 }
294
295 for ( $i = 1; $i <= $mathsecs; ++$i ) {
296 $text = preg_replace( "/{$unique2}/", str_replace( '$', '\$', $mathlist[$i] ), $text, 1 );
297 }
298
299 for ( $i = 1; $i <= $nwsecs; ++$i ) {
300 $text = preg_replace( "/{$unique}/", str_replace( '$', '\$', $nwlist[$i] ), $text, 1 );
301 }
302 $this->addHTML( $text );
303 wfProfileOut();
304 }
305
306 # Finally, all the text has been munged and accumulated into
307 # the object, let's actually output it:
308 #
309 function output()
310 {
311 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
312 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
313 wfProfileIn( "OutputPage::output" );
314 $sk = $wgUser->getSkin();
315
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" );
324 }
325 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
326
327 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
328 header( "Content-language: {$wgLanguageCode}" );
329
330 if ( "" != $this->mRedirect ) {
331 header( "Location: {$this->mRedirect}" );
332 wfProfileOut();
333 return;
334 }
335
336 $exp = time() + $wgCookieExpiration;
337 foreach( $this->mCookies as $name => $val ) {
338 setcookie( $name, $val, $exp, "/" );
339 }
340 wfProfileOut();
341
342 wfProfileIn( "OutputPage::output-middle" );
343 $sk->initPage();
344 $this->out( $this->headElement() );
345
346 $this->out( "\n<body" );
347 $ops = $sk->getBodyOptions();
348 foreach ( $ops as $name => $val ) {
349 $this->out( " $name='$val'" );
350 }
351 $this->out( ">\n" );
352 if ( $wgDebugComments ) {
353 $this->out( "<!-- Wiki debugging output:\n" .
354 $this->mDebugtext . "-->\n" );
355 }
356 $this->out( $sk->beforeContent() );
357 wfProfileOut();
358
359 wfProfileIn( "OutputPage::output-bodytext" );
360 $this->out( $this->mBodytext );
361 wfProfileOut();
362 wfProfileIn( "OutputPage::output-after" );
363 $this->out( $sk->afterContent() );
364 wfProfileOut();
365
366 wfProfileOut(); # A hack - we can't report after here
367 $this->out( $this->reportTime() );
368
369 $this->out( "\n</body></html>" );
370 flush();
371 }
372
373 function out( $ins )
374 {
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; }
381 }
382 print $outs;
383 }
384
385 function setEncodings()
386 {
387 global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
388 global $wgUser, $wgLang;
389
390 $wgInputEncoding = strtolower( $wgInputEncoding );
391 $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
392
393 if( $wgUser->getOption( 'altencoding' ) ) {
394 $wgLang->setAltEncoding();
395 return;
396 }
397
398 if ( "" == $s ) {
399 $wgOutputEncoding = strtolower( $wgOutputEncoding );
400 return;
401 }
402 $a = explode( ",", $s );
403 $best = 0.0;
404 $bestset = "*";
405
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;
413 }
414 if ( $q > $best ) {
415 $bestset = $set;
416 $best = $q;
417 }
418 }
419 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
420 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
421 $wgOutputEncoding = strtolower( $bestset );
422
423 # Disable for now
424 #
425 $wgOutputEncoding = $wgInputEncoding;
426 }
427
428 function reportTime()
429 {
430 global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
431 global $wgProfiling, $wgProfileStack, $wgUser;
432
433 list( $usec, $sec ) = explode( " ", microtime() );
434 $now = (float)$sec + (float)$usec;
435
436 list( $usec, $sec ) = explode( " ", $wgRequestTime );
437 $start = (float)$sec + (float)$usec;
438 $elapsed = $now - $start;
439
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;
451
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";
459 }
460 }
461 }
462
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 );
477 }
478 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
479 $elapsed );
480 return $com;
481 }
482
483 # Note: these arguments are keys into wfMsg(), not text!
484 #
485 function errorpage( $title, $msg )
486 {
487 global $wgTitle;
488
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 );
495
496 $this->mBodytext = "";
497 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
498 $this->returnToMain( false );
499
500 $this->output();
501 exit;
502 }
503
504 function sysopRequired()
505 {
506 global $wgUser;
507
508 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
509 $this->setPageTitle( wfMsg( "sysoptitle" ) );
510 $this->setRobotpolicy( "noindex,nofollow" );
511 $this->setArticleFlag( false );
512 $this->mBodytext = "";
513
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();
519 }
520
521 function developerRequired()
522 {
523 global $wgUser;
524
525 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
526 $this->setPageTitle( wfMsg( "developertitle" ) );
527 $this->setRobotpolicy( "noindex,nofollow" );
528 $this->setArticleFlag( false );
529 $this->mBodytext = "";
530
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();
536 }
537
538 function databaseError( $fname )
539 {
540 global $wgUser, $wgCommandLineMode;
541
542 $this->setPageTitle( wfMsg( "databaseerror" ) );
543 $this->setRobotpolicy( "noindex,nofollow" );
544 $this->setArticleFlag( false );
545
546 if ( $wgCommandLineMode ) {
547 $msg = wfMsg( "dberrortextcl" );
548 } else {
549 $msg = wfMsg( "dberrortextcl" );
550 }
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 );
555
556 if ( $wgCommandLineMode ) {
557 print $msg;
558 exit();
559 }
560 $sk = $wgUser->getSkin();
561 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
562 wfMsg( "searchingwikipedia" ) );
563 $msg = str_replace( "$5", $shlink, $msg );
564
565 $this->mBodytext = $msg;
566 $this->output();
567 exit();
568 }
569
570 function readOnlyPage()
571 {
572 global $wgUser, $wgReadOnlyFile;
573
574 $this->setPageTitle( wfMsg( "readonly" ) );
575 $this->setRobotpolicy( "noindex,nofollow" );
576 $this->setArticleFlag( false );
577
578 $reason = implode( "", file( $wgReadOnlyFile ) );
579 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
580 $this->addHTML( $text );
581 $this->returnToMain( false );
582 }
583
584 function fatalError( $message )
585 {
586 $this->setPageTitle( wfMsg( "internalerror" ) );
587 $this->setRobotpolicy( "noindex,nofollow" );
588 $this->setArticleFlag( false );
589
590 $this->mBodytext = $message;
591 $this->output();
592 exit;
593 }
594
595 function unexpectedValueError( $name, $val )
596 {
597 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
598 $msg = str_replace( "$2", $val, $msg );
599 $this->fatalError( $msg );
600 }
601
602 function fileCopyError( $old, $new )
603 {
604 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
605 $msg = str_replace( "$2", $new, $msg );
606 $this->fatalError( $msg );
607 }
608
609 function fileRenameError( $old, $new )
610 {
611 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
612 $msg = str_replace( "$2", $new, $msg );
613 $this->fatalError( $msg );
614 }
615
616 function fileDeleteError( $name )
617 {
618 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
619 $this->fatalError( $msg );
620 }
621
622 function fileNotFoundError( $name )
623 {
624 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
625 $this->fatalError( $msg );
626 }
627
628 function returnToMain( $auto = true )
629 {
630 global $wgUser, $wgOut, $returnto;
631
632 $sk = $wgUser->getSkin();
633 if ( "" == $returnto ) {
634 $returnto = wfMsg( "mainpage" );
635 }
636 $link = $sk->makeKnownLink( $returnto, "" );
637
638 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
639 if ( $auto ) {
640 $wgOut->addMeta( "http:Refresh", "10;url=" .
641 wfLocalUrlE( wfUrlencode( $returnto ) ) );
642 }
643 $wgOut->addHTML( "\n<p>$r\n" );
644 }
645
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.
649 #
650 function doWikiPass2( $text, $linestart )
651 {
652 global $wgUser, $wgLang;
653 wfProfileIn( "OutputPage::doWikiPass2" );
654
655 $text = $this->removeHTMLtags( $text );
656 $text = $this->replaceVariables( $text );
657
658 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
659 $text = str_replace ( "<HR>", "<hr>", $text );
660
661 $text = $this->doQuotes( $text );
662 $text = $this->doHeadings( $text );
663 $text = $this->doBlockLevels( $text, $linestart );
664
665 $text = $wgLang->replaceDates( $text );
666 $text = $this->replaceExternalLinks( $text );
667 $text = $this->replaceInternalLinks ( $text );
668
669 $text = $this->magicISBN( $text );
670 $text = $this->magicRFC( $text );
671 $text = $this->formatHeadings( $text );
672
673 $sk = $wgUser->getSkin();
674 $text = $sk->transformContent( $text );
675
676 wfProfileOut();
677 return $text;
678 }
679
680 /* private */ function doQuotes( $text )
681 {
682 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
683 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
684 return $text;
685 }
686
687 /* private */ function doHeadings( $text )
688 {
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 );
693 }
694 return $text;
695 }
696
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.
700
701 /* private */ function replaceExternalLinks( $text )
702 {
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;
712 }
713
714 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
715 {
716 global $wgUser, $printable;
717 global $wgAllowExternalImages;
718
719
720 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
721 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
722
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";
731
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}]|$)/";
737
738 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
739 $sk = $wgUser->getSkin();
740
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 );
744 }
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 );
750
751 $a = explode( "[{$protocol}:", " " . $s );
752 $s = array_shift( $a );
753 $s = substr( $s, 1 );
754
755 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
756 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
757
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;
771 }
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}";
776
777 }
778 return $s;
779 }
780
781 /* private */ function replaceInternalLinks( $s )
782 {
783 global $wgTitle, $wgUser, $wgLang;
784 global $wgLinkCache, $wgInterwikiMagic;
785 global $wgNamespacesWithSubpages;
786 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
787
788 wfProfileIn( "$fname-setup" );
789 $tc = Title::legalChars() . "#";
790 $sk = $wgUser->getSkin();
791
792 $a = explode( "[[", " " . $s );
793 $s = array_shift( $a );
794 $s = substr( $s, 1 );
795
796 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
797 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
798 wfProfileOut();
799
800 wfProfileIn( "$fname-loop" );
801 foreach ( $a as $line ) {
802 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
803
804 $text = $m[2];
805 $trail = $m[3];
806
807 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
808
809 $text = "";
810 $trail = $m[2];
811 }
812
813 else { # Invalid form; output directly
814 $s .= "[[" . $line ;
815 continue;
816 }
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];
821
822 } else {
823 $noslash=substr($m[1],1);
824 }
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
832 }
833 } else { # no subpage
834 $link = $m[1];
835 }
836
837 if ( preg_match( "/^([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(); }
845
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(); }
854
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;
868 }
869 }
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 );
876 }
877 }
878 wfProfileOut();
879 wfProfileOut();
880 return $s;
881 }
882
883 # Some functions here used by doBlockLevels()
884 #
885 /* private */ function closeParagraph()
886 {
887 $result = "";
888 if ( 0 != strcmp( "p", $this->mLastSection ) &&
889 0 != strcmp( "", $this->mLastSection ) ) {
890 $result = "</" . $this->mLastSection . ">";
891 }
892 $this->mLastSection = "";
893 return $result;
894 }
895 # getCommon() returns the length of the longest common substring
896 # of both arguments, starting at the beginning of both.
897 #
898 /* private */ function getCommon( $st1, $st2 )
899 {
900 $fl = strlen( $st1 );
901 $shorter = strlen( $st2 );
902 if ( $fl < $shorter ) { $shorter = $fl; }
903
904 for ( $i = 0; $i < $shorter; ++$i ) {
905 if ( $st1{$i} != $st2{$i} ) { break; }
906 }
907 return $i;
908 }
909 # These next three functions open, continue, and close the list
910 # element appropriate to the prefix character passed into them.
911 #
912 /* private */ function openList( $char )
913 {
914 $result = $this->closeParagraph();
915
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;
922 }
923 else { $result = "<!-- ERR 1 -->"; }
924
925 return $result;
926 }
927
928 /* private */ function nextItem( $char )
929 {
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>";
940 }
941 }
942 return "<!-- ERR 2 -->";
943 }
944
945 /* private */function closeList( $char )
946 {
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>";
955 }
956 }
957 return "<!-- ERR 3 -->";
958 }
959
960 /* private */ function doBlockLevels( $text, $linestart )
961 {
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.
966 #
967 $a = explode( "\n", $text );
968 $text = $lastPref = "";
969 $this->mDTopen = $inBlockElem = false;
970
971 if ( ! $linestart ) { $text .= array_shift( $a ); }
972 foreach ( $a as $t ) {
973 if ( "" != $text ) { $text .= "\n"; }
974
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 );
981
982 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
983 $text .= $this->nextItem( substr( $pref, -1 ) );
984
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 );
991 }
992 }
993 } else if (0 != $npl || 0 != $opl) {
994 $cpl = $this->getCommon( $pref, $lastPref );
995
996 while ( $cpl < $opl ) {
997 $text .= $this->closeList( $lastPref{$opl-1} );
998 --$opl;
999 }
1000 if ( $npl <= $cpl && $cpl > 0 ) {
1001 $text .= $this->nextItem( $pref{$cpl-1} );
1002 }
1003 while ( $npl > $cpl ) {
1004 $char = substr( $pref, $cpl, 1 );
1005 $text .= $this->openList( $char );
1006
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 );
1013 }
1014 }
1015 ++$cpl;
1016 }
1017 $lastPref = $pref2;
1018 }
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;
1024 }
1025 if ( ! $inBlockElem ) {
1026 if ( " " == $t{0} ) {
1027 $newSection = "pre";
1028 # $t = wfEscapeHTML( $t );
1029 }
1030 else { $newSection = "p"; }
1031
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 . ">";
1040 }
1041 }
1042 $this->mLastSection = $newSection;
1043 }
1044 if ( $inBlockElem &&
1045 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1046 $inBlockElem = false;
1047 }
1048 }
1049 $text .= $t;
1050 }
1051 while ( $npl ) {
1052 $text .= $this->closeList( $pref2{$npl-1} );
1053 --$npl;
1054 }
1055 if ( "" != $this->mLastSection ) {
1056 if ( "p" != $this->mLastSection ) {
1057 $text .= "</" . $this->mLastSection . ">";
1058 }
1059 $this->mLastSection = "";
1060 }
1061 wfProfileOut();
1062 return $text;
1063 }
1064
1065 /* private */ function replaceVariables( $text )
1066 {
1067 global $wgLang;
1068 wfProfileIn( "OutputPage:replaceVariables" );
1069
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 );
1086
1087 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1088 $v = wfNumberOfArticles();
1089 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1090 }
1091 wfProfileOut();
1092 return $text;
1093 }
1094
1095 /* private */ function removeHTMLtags( $text )
1096 {
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"
1104 );
1105 $htmlsingle = array(
1106 "br", "p", "hr", "li", "dt", "dd"
1107 );
1108 $htmlnest = array( # Tags that can be nested--??
1109 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1110 "dl", "font", "big", "small", "sub", "sup"
1111 );
1112 $tabletags = array( # Can only appear inside table
1113 "td", "th", "tr"
1114 );
1115
1116 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1117 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1118
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 */
1130 );
1131
1132 # Remove HTML comments
1133 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1134
1135 $bits = explode( "<", $text );
1136 $text = array_shift( $bits );
1137 $tagstack = array(); $tablestack = array();
1138
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 );
1145
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 );
1158 }
1159 $newparams = "";
1160 }
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();
1173 }
1174 array_push( $tagstack, $t );
1175 }
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);
1181 }
1182 if ( ! $badtag ) {
1183 $rest = str_replace( ">", "&gt;", $rest );
1184 $text .= "<$slash$t$newparams$brace$rest";
1185 continue;
1186 }
1187 }
1188 $text .= "&lt;" . str_replace( ">", "&gt;", $x);
1189 }
1190 # Close off any remaining tags
1191 while ( $t = array_pop( $tagstack ) ) {
1192 $text .= "</$t>\n";
1193 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1194 }
1195 wfProfileOut();
1196 return $text;
1197 }
1198
1199
1200 /*
1201 *
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
1207 *
1208 * It loops through all headlines, collects the necessary data, then splits up the
1209 * string and re-inserts the newly formatted headlines.
1210 *
1211 * */
1212 /* private */ function formatHeadings( $text )
1213 {
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;}
1219
1220 $sk=$wgUser->getSkin();
1221 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1222
1223 $c=0;
1224
1225 foreach($matches[3] as $headline) {
1226 if($level) { $prevlevel=$level;}
1227 $level=$matches[1][$c];
1228 if(($nh||$st) && $level>$prevlevel) {
1229
1230 $h[$level]=0; // reset when we enter a new level
1231 if($toclevel) {
1232 $toc.=$sk->tocIndent($level-$prevlevel);
1233 }
1234 $toclevel++;
1235
1236 }
1237 if(($nh||$st) && $level<$prevlevel) {
1238 $h[$level+1]=0; // reset when we step back a level
1239 if($toclevel) {
1240 $toc.=$sk->tocUnindent($prevlevel-$level);
1241 }
1242 $toclevel--;
1243
1244 }
1245 $h[$level]++; // count number of headlines for each level
1246
1247 if($nh||$st) {
1248 for($i=1;$i<=$level;$i++) {
1249 if($h[$i]) {
1250 if($dot) {$numbering.=".";}
1251 $numbering.=$h[$i];
1252 $dot=1;
1253 }
1254 }
1255 }
1256
1257
1258 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1259 $tocline=$canonized_headline;
1260 $canonized_headline=str_replace('"',"",$canonized_headline);
1261 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1262 $refer[$c]=$canonized_headline;
1263 $refers[$canonized_headline]++; // count how many in assoc. array so we can track dupes in anchors
1264 $refcount[$c]=$refers[$canonized_headline];
1265 if($nh||$st) {
1266 $tocline=$numbering ." ". $tocline;
1267 if($nh) {
1268 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1269 }
1270 }
1271 $anchor=$canonized_headline;
1272 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1273 if($st) {
1274 $toc.=$sk->tocLine($anchor,$tocline);
1275 }
1276 if($es && !isset($wpPreview)) {
1277 $head[$c].=$sk->editSectionLink($c+1);
1278 }
1279 $head[$c].="<H".$level.$matches[2][$c]
1280 ."<a name=\"".$anchor."\">"
1281 .$headline
1282 ."</a>"
1283 ."</H".$level.">";
1284 $numbering="";
1285 $c++;
1286 $dot=0;
1287 }
1288
1289 if($st) {
1290 $toclines=$c;
1291 while($toclevel>0) {
1292 $toc.="</ul>";
1293 $toclevel--;
1294 }
1295
1296 $toc=$sk->tocTable($toc);
1297 }
1298
1299
1300 // split up and insert constructed headlines
1301
1302 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1303 $i=0;
1304
1305 foreach($blocks as $block) {
1306 if($es && !isset($wpPreview) && $c>0 && $i==0) {
1307 $full.=$sk->editSectionLink(0);
1308 }
1309
1310 $full.=$block;
1311 $full.=$head[$i];
1312 $i++;
1313 }
1314 if($st && $toclines>3) {
1315 $full=$toc."<a name=\"top\"></a>".$full;
1316 }
1317 return $full;
1318 }
1319
1320 /* private */ function magicISBN( $text )
1321 {
1322 global $wgLang;
1323
1324 $a = split( "ISBN ", " $text" );
1325 if ( count ( $a ) < 2 ) return $text;
1326 $text = substr( array_shift( $a ), 1);
1327 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1328
1329 foreach ( $a as $x ) {
1330 $isbn = $blank = "" ;
1331 while ( " " == $x{0} ) {
1332 $blank .= " ";
1333 $x = substr( $x, 1 );
1334 }
1335 while ( strstr( $valid, $x{0} ) != false ) {
1336 $isbn .= $x{0};
1337 $x = substr( $x, 1 );
1338 }
1339 $num = str_replace( "-", "", $isbn );
1340 $num = str_replace( " ", "", $num );
1341
1342 if ( "" == $num ) {
1343 $text .= "ISBN $blank$x";
1344 } else {
1345 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1346 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1347 $text .= $x;
1348 }
1349 }
1350 return $text;
1351 }
1352
1353 /* private */ function magicRFC( $text )
1354 {
1355 return $text;
1356 }
1357
1358 /* private */ function headElement()
1359 {
1360 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding;
1361
1362 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
1363
1364 if ( "" == $this->mHTMLtitle ) {
1365 $this->mHTMLtitle = $this->mPagetitle;
1366 }
1367 $ret .= "<html lang=\"$wgLanguageCode\"><head><title>{$this->mHTMLtitle}</title>\n";
1368 array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1369 foreach ( $this->mMetatags as $tag ) {
1370 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1371 $a = "http-equiv";
1372 $tag[0] = substr( $tag[0], 5 );
1373 } else {
1374 $a = "name";
1375 }
1376 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1377 }
1378 $p = $this->mRobotpolicy;
1379 if ( "" == $p ) { $p = "index,follow"; }
1380 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1381
1382 if ( count( $this->mKeywords ) > 0 ) {
1383 $ret .= "<meta name=\"keywords\" content=\"" .
1384 implode( ",", $this->mKeywords ) . "\">\n";
1385 }
1386 foreach ( $this->mLinktags as $tag ) {
1387 $ret .= "<link ";
1388 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1389 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1390 $ret .= "href=\"{$tag[2]}\">\n";
1391 }
1392 $sk = $wgUser->getSkin();
1393 $ret .= $sk->getHeadScripts();
1394 $ret .= $sk->getUserStyles();
1395
1396 $ret .= "</head>\n";
1397 return $ret;
1398 }
1399 }
1400
1401 ?>