Date/time fixes: try to ensure that timestamps are always kept in GMT, with conversio...
[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;
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 = $this->replaceExternalLinks( $text );
666 $text = $this->replaceInternalLinks ( $text );
667
668 $text = $this->magicISBN( $text );
669 $text = $this->magicRFC( $text );
670 $text = $this->formatHeadings( $text );
671
672 $sk = $wgUser->getSkin();
673 $text = $sk->transformContent( $text );
674
675 wfProfileOut();
676 return $text;
677 }
678
679 /* private */ function doQuotes( $text )
680 {
681 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
682 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
683 return $text;
684 }
685
686 /* private */ function doHeadings( $text )
687 {
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 );
692 }
693 return $text;
694 }
695
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.
699
700 /* private */ function replaceExternalLinks( $text )
701 {
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;
711 }
712
713 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
714 {
715 global $wgUser, $printable;
716 global $wgAllowExternalImages;
717
718
719 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
720 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
721
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";
730
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}]|$)/";
736
737 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
738 $sk = $wgUser->getSkin();
739
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 );
743 }
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 );
749
750 $a = explode( "[{$protocol}:", " " . $s );
751 $s = array_shift( $a );
752 $s = substr( $s, 1 );
753
754 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
755 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
756
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;
770 }
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}";
775
776 }
777 return $s;
778 }
779
780 /* private */ function replaceInternalLinks( $s )
781 {
782 global $wgTitle, $wgUser, $wgLang;
783 global $wgLinkCache, $wgInterwikiMagic;
784 global $wgNamespacesWithSubpages;
785 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
786
787 wfProfileIn( "$fname-setup" );
788 $tc = Title::legalChars() . "#";
789 $sk = $wgUser->getSkin();
790
791 $a = explode( "[[", " " . $s );
792 $s = array_shift( $a );
793 $s = substr( $s, 1 );
794
795 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
796 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
797 wfProfileOut();
798
799 wfProfileIn( "$fname-loop" );
800 foreach ( $a as $line ) {
801 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
802
803 $text = $m[2];
804 $trail = $m[3];
805
806 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
807
808 $text = "";
809 $trail = $m[2];
810 }
811
812 else { # Invalid form; output directly
813 $s .= "[[" . $line ;
814 continue;
815 }
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];
820
821 } else {
822 $noslash=substr($m[1],1);
823 }
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
831 }
832 } else { # no subpage
833 $link = $m[1];
834 }
835
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(); }
844
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(); }
853
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;
867 }
868 }
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 );
875 }
876 }
877 wfProfileOut();
878 wfProfileOut();
879 return $s;
880 }
881
882 # Some functions here used by doBlockLevels()
883 #
884 /* private */ function closeParagraph()
885 {
886 $result = "";
887 if ( 0 != strcmp( "p", $this->mLastSection ) &&
888 0 != strcmp( "", $this->mLastSection ) ) {
889 $result = "</" . $this->mLastSection . ">";
890 }
891 $this->mLastSection = "";
892 return $result;
893 }
894 # getCommon() returns the length of the longest common substring
895 # of both arguments, starting at the beginning of both.
896 #
897 /* private */ function getCommon( $st1, $st2 )
898 {
899 $fl = strlen( $st1 );
900 $shorter = strlen( $st2 );
901 if ( $fl < $shorter ) { $shorter = $fl; }
902
903 for ( $i = 0; $i < $shorter; ++$i ) {
904 if ( $st1{$i} != $st2{$i} ) { break; }
905 }
906 return $i;
907 }
908 # These next three functions open, continue, and close the list
909 # element appropriate to the prefix character passed into them.
910 #
911 /* private */ function openList( $char )
912 {
913 $result = $this->closeParagraph();
914
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;
921 }
922 else { $result = "<!-- ERR 1 -->"; }
923
924 return $result;
925 }
926
927 /* private */ function nextItem( $char )
928 {
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>";
939 }
940 }
941 return "<!-- ERR 2 -->";
942 }
943
944 /* private */function closeList( $char )
945 {
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>";
954 }
955 }
956 return "<!-- ERR 3 -->";
957 }
958
959 /* private */ function doBlockLevels( $text, $linestart )
960 {
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.
965 #
966 $a = explode( "\n", $text );
967 $text = $lastPref = "";
968 $this->mDTopen = $inBlockElem = false;
969
970 if ( ! $linestart ) { $text .= array_shift( $a ); }
971 foreach ( $a as $t ) {
972 if ( "" != $text ) { $text .= "\n"; }
973
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 );
980
981 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
982 $text .= $this->nextItem( substr( $pref, -1 ) );
983
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 );
990 }
991 }
992 } else if (0 != $npl || 0 != $opl) {
993 $cpl = $this->getCommon( $pref, $lastPref );
994
995 while ( $cpl < $opl ) {
996 $text .= $this->closeList( $lastPref{$opl-1} );
997 --$opl;
998 }
999 if ( $npl <= $cpl && $cpl > 0 ) {
1000 $text .= $this->nextItem( $pref{$cpl-1} );
1001 }
1002 while ( $npl > $cpl ) {
1003 $char = substr( $pref, $cpl, 1 );
1004 $text .= $this->openList( $char );
1005
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 );
1012 }
1013 }
1014 ++$cpl;
1015 }
1016 $lastPref = $pref2;
1017 }
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;
1023 }
1024 if ( ! $inBlockElem ) {
1025 if ( " " == $t{0} ) {
1026 $newSection = "pre";
1027 # $t = wfEscapeHTML( $t );
1028 }
1029 else { $newSection = "p"; }
1030
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 . ">";
1039 }
1040 }
1041 $this->mLastSection = $newSection;
1042 }
1043 if ( $inBlockElem &&
1044 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1045 $inBlockElem = false;
1046 }
1047 }
1048 $text .= $t;
1049 }
1050 while ( $npl ) {
1051 $text .= $this->closeList( $pref2{$npl-1} );
1052 --$npl;
1053 }
1054 if ( "" != $this->mLastSection ) {
1055 if ( "p" != $this->mLastSection ) {
1056 $text .= "</" . $this->mLastSection . ">";
1057 }
1058 $this->mLastSection = "";
1059 }
1060 wfProfileOut();
1061 return $text;
1062 }
1063
1064 /* private */ function replaceVariables( $text )
1065 {
1066 global $wgLang;
1067 wfProfileIn( "OutputPage:replaceVariables" );
1068
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 );
1085
1086 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1087 $v = wfNumberOfArticles();
1088 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1089 }
1090 wfProfileOut();
1091 return $text;
1092 }
1093
1094 /* private */ function removeHTMLtags( $text )
1095 {
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"
1103 );
1104 $htmlsingle = array(
1105 "br", "p", "hr", "li", "dt", "dd"
1106 );
1107 $htmlnest = array( # Tags that can be nested--??
1108 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1109 "dl", "font", "big", "small", "sub", "sup"
1110 );
1111 $tabletags = array( # Can only appear inside table
1112 "td", "th", "tr"
1113 );
1114
1115 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1116 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1117
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 */
1129 );
1130
1131 # Remove HTML comments
1132 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1133
1134 $bits = explode( "<", $text );
1135 $text = array_shift( $bits );
1136 $tagstack = array(); $tablestack = array();
1137
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 );
1144
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 );
1157 }
1158 $newparams = "";
1159 }
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();
1172 }
1173 array_push( $tagstack, $t );
1174 }
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);
1180 }
1181 if ( ! $badtag ) {
1182 $rest = str_replace( ">", "&gt;", $rest );
1183 $text .= "<$slash$t$newparams$brace$rest";
1184 continue;
1185 }
1186 }
1187 $text .= "&lt;" . str_replace( ">", "&gt;", $x);
1188 }
1189 # Close off any remaining tags
1190 while ( $t = array_pop( $tagstack ) ) {
1191 $text .= "</$t>\n";
1192 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1193 }
1194 wfProfileOut();
1195 return $text;
1196 }
1197
1198
1199 /*
1200 *
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
1206 *
1207 * It loops through all headlines, collects the necessary data, then splits up the
1208 * string and re-inserts the newly formatted headlines.
1209 *
1210 * */
1211 /* private */ function formatHeadings( $text )
1212 {
1213 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1214 $nh=$wgUser->getOption( "numberheadings" );
1215 $st=$wgUser->getOption( "showtoc" );
1216 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1217
1218 $sk=$wgUser->getSkin();
1219 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1220
1221 $c=0;
1222
1223 foreach($matches[3] as $headline) {
1224 if($level) { $prevlevel=$level;}
1225 $level=$matches[1][$c];
1226 if(($nh||$st) && $level>$prevlevel) {
1227
1228 $h[$level]=0; // reset when we enter a new level
1229 if($toclevel) {
1230 $toc.=$sk->tocIndent($level-$prevlevel);
1231 }
1232 $toclevel++;
1233
1234 }
1235 if(($nh||$st) && $level<$prevlevel) {
1236 $h[$level+1]=0; // reset when we step back a level
1237 if($toclevel) {
1238 $toc.=$sk->tocUnindent($prevlevel-$level);
1239 }
1240 $toclevel--;
1241
1242 }
1243 $h[$level]++; // count number of headlines for each level
1244
1245 if($nh||$st) {
1246 for($i=1;$i<=$level;$i++) {
1247 if($h[$i]) {
1248 if($dot) {$numbering.=".";}
1249 $numbering.=$h[$i];
1250 $dot=1;
1251 }
1252 }
1253 }
1254
1255
1256 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1257 $tocline=$canonized_headline;
1258 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1259 $refer[$c]=$canonized_headline;
1260 $refers[$canonized_headline]++; // count how many in assoc. array so we can track dupes in anchors
1261 $refcount[$c]=$refers[$canonized_headline];
1262 if($nh||$st) {
1263 $tocline=$numbering ." ". $tocline;
1264 if($nh) {
1265 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1266 }
1267 }
1268 $anchor=$canonized_headline;
1269 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1270 if($st) {
1271 $toc.=$sk->tocLine($anchor,$tocline);
1272 }
1273 $head[$c].="<H".$level.$matches[2][$c]
1274 ."<a name=\"".$anchor."\">"
1275 .$headline
1276 ."</a>"
1277 ."</H".$level.">";
1278 $numbering="";
1279 $c++;
1280 $dot=0;
1281 }
1282
1283 if($st) {
1284 $toclines=$c;
1285 while($toclevel>0) {
1286 $toc.="</ul>";
1287 $toclevel--;
1288 }
1289
1290 $toc=$sk->tocTable($toc);
1291 }
1292
1293
1294 // split up and insert constructed headlines
1295
1296 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1297 $i=0;
1298 foreach($blocks as $block) {
1299 $full.=$block;
1300 if($es && $c>0 && !isset($wpPreview)) {
1301 $full.=$sk->editSectionLink($i);
1302 }
1303
1304 $full.=$head[$i];
1305 $i++;
1306 }
1307 if($st && $toclines>3) {
1308 $full=$toc."<a name=\"top\"></a>".$full;
1309 }
1310 return $full;
1311 }
1312
1313 /* private */ function magicISBN( $text )
1314 {
1315 global $wgLang;
1316
1317 $a = split( "ISBN ", " $text" );
1318 if ( count ( $a ) < 2 ) return $text;
1319 $text = substr( array_shift( $a ), 1);
1320 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1321
1322 foreach ( $a as $x ) {
1323 $isbn = $blank = "" ;
1324 while ( " " == $x{0} ) {
1325 $blank .= " ";
1326 $x = substr( $x, 1 );
1327 }
1328 while ( strstr( $valid, $x{0} ) != false ) {
1329 $isbn .= $x{0};
1330 $x = substr( $x, 1 );
1331 }
1332 $num = str_replace( "-", "", $isbn );
1333 $num = str_replace( " ", "", $num );
1334
1335 if ( "" == $num ) {
1336 $text .= "ISBN $blank$x";
1337 } else {
1338 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1339 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1340 $text .= $x;
1341 }
1342 }
1343 return $text;
1344 }
1345
1346 /* private */ function magicRFC( $text )
1347 {
1348 return $text;
1349 }
1350
1351 /* private */ function headElement()
1352 {
1353 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding;
1354
1355 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\" \"$wgDTD\">\n";
1356
1357 if ( "" == $this->mHTMLtitle ) {
1358 $this->mHTMLtitle = $this->mPagetitle;
1359 }
1360 $ret .= "<html lang=\"$wgLanguageCode\"><head><title>{$this->mHTMLtitle}</title>\n";
1361 array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1362 foreach ( $this->mMetatags as $tag ) {
1363 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1364 $a = "http-equiv";
1365 $tag[0] = substr( $tag[0], 5 );
1366 } else {
1367 $a = "name";
1368 }
1369 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1370 }
1371 $p = $this->mRobotpolicy;
1372 if ( "" == $p ) { $p = "index,follow"; }
1373 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1374
1375 if ( count( $this->mKeywords ) > 0 ) {
1376 $ret .= "<meta name=\"keywords\" content=\"" .
1377 implode( ",", $this->mKeywords ) . "\">\n";
1378 }
1379 foreach ( $this->mLinktags as $tag ) {
1380 $ret .= "<link ";
1381 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1382 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1383 $ret .= "href=\"{$tag[2]}\">\n";
1384 }
1385 $sk = $wgUser->getSkin();
1386 $ret .= $sk->getHeadScripts();
1387 $ret .= $sk->getUserStyles();
1388
1389 $ret .= "</head>\n";
1390 return $ret;
1391 }
1392 }
1393
1394 ?>