Don't show "__NOTOC__" text when user has toc disabled
[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, $mCategoryLinks;
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->mCategoryLinks = array() ;
138 $this->mAutonumber = 0;
139 }
140
141 function addHeader( $name, $val ) { array_push( $this->mHeaders, "$name: $val" ) ; }
142 function addCookie( $name, $val ) { array_push( $this->mCookies, array( $name, $val ) ); }
143 function redirect( $url ) { $this->mRedirect = $url; }
144
145 # To add an http-equiv meta tag, precede the name with "http:"
146 function addMeta( $name, $val ) { array_push( $this->mMetatags, array( $name, $val ) ); }
147 function addKeyword( $text ) { array_push( $this->mKeywords, $text ); }
148 function addLink( $rel, $rev, $target ) { array_push( $this->mLinktags, array( $rel, $rev, $target ) ); }
149
150 function checkLastModified ( $timestamp )
151 {
152 global $wgLang, $wgCachePages, $wgUser;
153 if( !$wgCachePages ) {
154 wfDebug( "CACHE DISABLED\n", false );
155 return;
156 }
157 if( preg_match( '/MSIE ([1-4]|5\.0)/', $_SERVER["HTTP_USER_AGENT"] ) ) {
158 # IE 5.0 has probs with our caching
159 wfDebug( "-- bad client, not caching\n", false );
160 return;
161 }
162 if( $wgUser->getOption( "nocache" ) ) {
163 wfDebug( "USER DISABLED CACHE\n", false );
164 return;
165 }
166
167 $lastmod = gmdate( "D, j M Y H:i:s", wfTimestamp2Unix(
168 max( $timestamp, $wgUser->mTouched ) ) ) . " GMT";
169
170 if( $_SERVER["HTTP_IF_MODIFIED_SINCE"] != "" ) {
171 $ismodsince = wfUnix2Timestamp( strtotime( $_SERVER["HTTP_IF_MODIFIED_SINCE"] ) );
172 wfDebug( "-- client send If-Modified-Since: " . $_SERVER["HTTP_IF_MODIFIED_SINCE"] . "\n", false );
173 wfDebug( "-- we might send Last-Modified : $lastmod\n", false );
174
175 if( ($ismodsince >= $timestamp ) and $wgUser->validateCache( $ismodsince ) ) {
176 # Make sure you're in a place you can leave when you call us!
177 header( "HTTP/1.0 304 Not Modified" );
178 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
179 header( "Cache-Control: private, must-revalidate, max-age=0" );
180 header( "Last-Modified: {$lastmod}" );
181 wfDebug( "CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
182 exit;
183 } else {
184 wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
185 $this->mLastModified = $lastmod;
186 }
187 } else {
188 wfDebug( "We're confused.\n", false );
189 $this->mLastModified = $lastmod;
190 }
191 }
192
193 function setRobotpolicy( $str ) { $this->mRobotpolicy = $str; }
194 function setHTMLtitle( $name ) { $this->mHTMLtitle = $name; }
195 function setPageTitle( $name ) { $this->mPagetitle = $name; }
196 function getPageTitle() { return $this->mPagetitle; }
197 function setSubtitle( $str ) { $this->mSubtitle = $str; }
198 function getSubtitle() { return $this->mSubtitle; }
199 function setArticleFlag( $v ) { $this->mIsarticle = $v; }
200 function isArticle() { return $this->mIsarticle; }
201 function setPrintable() { $this->mPrintable = true; }
202 function isPrintable() { return $this->mPrintable; }
203
204 function getLanguageLinks() {
205 global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode;
206 global $wgDBconnection, $wgDBname, $wgDBintlname;
207
208 if ( ! $wgUseNewInterlanguage )
209 return $this->mLanguageLinks;
210
211 mysql_select_db( $wgDBintlname, $wgDBconnection ) or die(
212 htmlspecialchars(mysql_error()) );
213
214 $list = array();
215 $sql = "SELECT * FROM ilinks WHERE lang_from=\"" .
216 "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\"";
217 $res = mysql_query( $sql, $wgDBconnection );
218
219 while ( $q = mysql_fetch_object ( $res ) ) {
220 $list[] = $q->lang_to . ":" . $q->title_to;
221 }
222 mysql_free_result( $res );
223 mysql_select_db( $wgDBname, $wgDBconnection ) or die(
224 htmlspecialchars(mysql_error()) );
225
226 return $list;
227 }
228
229 function supressQuickbar() { $this->mSupressQuickbar = true; }
230 function isQuickbarSupressed() { return $this->mSupressQuickbar; }
231
232 function addHTML( $text ) { $this->mBodytext .= $text; }
233 function addHeadtext( $text ) { $this->mHeadtext .= $text; }
234 function debug( $text ) { $this->mDebugtext .= $text; }
235
236 # First pass--just handle <nowiki> sections, pass the rest off
237 # to doWikiPass2() which does all the real work.
238 #
239
240 function addWikiText( $text, $linestart = true )
241 {
242 global $wgUseTeX;
243 wfProfileIn( "OutputPage::addWikiText" );
244 $unique = "3iyZiyA7iMwg5rhxP0Dcc9oTnj8qD1jm1Sfv4";
245 $unique2 = "4LIQ9nXtiYFPCSfitVwDw7EYwQlL4GeeQ7qSO";
246 $unique3 = "fPaA8gDfdLBqzj68Yjg9Hil3qEF8JGO0uszIp";
247 $nwlist = array();
248 $nwsecs = 0;
249 $mathlist = array();
250 $mathsecs = 0;
251 $prelist = array ();
252 $presecs = 0;
253 $stripped = "";
254 $stripped2 = "";
255 $stripped3 = "";
256
257 while ( "" != $text ) {
258 $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
259 $stripped .= $p[0];
260 if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $text = ""; }
261 else {
262 $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
263 ++$nwsecs;
264 $nwlist[$nwsecs] = wfEscapeHTMLTagsOnly($q[0]);
265 $stripped .= $unique;
266 $text = $q[1];
267 }
268 }
269
270 if( $wgUseTeX ) {
271 while ( "" != $stripped ) {
272 $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 );
273 $stripped2 .= $p[0];
274 if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $stripped = ""; }
275 else {
276 $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 );
277 ++$mathsecs;
278 $mathlist[$mathsecs] = renderMath($q[0]);
279 $stripped2 .= $unique2;
280 $stripped = $q[1];
281 }
282 }
283 } else {
284 $stripped2 = $stripped;
285 }
286
287 while ( "" != $stripped2 ) {
288 $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 );
289 $stripped3 .= $p[0];
290 if ( ( count( $p ) < 2 ) || ( "" == $p[1] ) ) { $stripped2 = ""; }
291 else {
292 $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 );
293 ++$presecs;
294 $prelist[$presecs] = "<pre>". wfEscapeHTMLTagsOnly($q[0]). "</pre>";
295 $stripped3 .= $unique3;
296 $stripped2 = $q[1];
297 }
298 }
299
300 $text = $this->doWikiPass2( $stripped3, $linestart );
301
302 $specialChars = array("\\", "$");
303 $escapedChars = array("\\\\", "\\$");
304 for ( $i = 1; $i <= $presecs; ++$i ) {
305 $text = preg_replace( "/{$unique3}/", str_replace( $specialChars,
306 $escapedChars, $prelist[$i] ), $text, 1 );
307 }
308
309 for ( $i = 1; $i <= $mathsecs; ++$i ) {
310 $text = preg_replace( "/{$unique2}/", str_replace( $specialChars,
311 $escapedChars, $mathlist[$i] ), $text, 1 );
312 }
313
314 for ( $i = 1; $i <= $nwsecs; ++$i ) {
315 $text = preg_replace( "/{$unique}/", str_replace( $specialChars,
316 $escapedChars, $nwlist[$i] ), $text, 1 );
317 }
318 $this->addHTML( $text );
319 wfProfileOut();
320 }
321
322 function sendCacheControl() {
323 if( $this->mLastModified != "" ) {
324 wfDebug( "** private caching; {$this->mLastModified} **\n", false );
325 header( "Cache-Control: private, must-revalidate, max-age=0" );
326 header( "Last-modified: {$this->mLastModified}" );
327 } else {
328 wfDebug( "** no caching **\n", false );
329 header( "Cache-Control: no-cache" ); # Experimental - see below
330 header( "Pragma: no-cache" );
331 header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
332 }
333 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
334 }
335
336 # Finally, all the text has been munged and accumulated into
337 # the object, let's actually output it:
338 #
339 function output()
340 {
341 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
342 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
343 wfProfileIn( "OutputPage::output" );
344 $sk = $wgUser->getSkin();
345
346 wfProfileIn( "OutputPage::output-headers" );
347 $this->sendCacheControl();
348
349 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
350 header( "Content-language: {$wgLanguageCode}" );
351
352 if ( "" != $this->mRedirect ) {
353 header( "Location: {$this->mRedirect}" );
354 wfProfileOut();
355 return;
356 }
357
358 $exp = time() + $wgCookieExpiration;
359 foreach( $this->mCookies as $name => $val ) {
360 setcookie( $name, $val, $exp, "/" );
361 }
362 wfProfileOut();
363
364 wfProfileIn( "OutputPage::output-middle" );
365 $sk->initPage();
366 $this->out( $this->headElement() );
367
368 $this->out( "\n<body" );
369 $ops = $sk->getBodyOptions();
370 foreach ( $ops as $name => $val ) {
371 $this->out( " $name='$val'" );
372 }
373 $this->out( ">\n" );
374 if ( $wgDebugComments ) {
375 $this->out( "<!-- Wiki debugging output:\n" .
376 $this->mDebugtext . "-->\n" );
377 }
378 $this->out( $sk->beforeContent() );
379 wfProfileOut();
380
381 wfProfileIn( "OutputPage::output-bodytext" );
382 $this->out( $this->mBodytext );
383 wfProfileOut();
384 wfProfileIn( "OutputPage::output-after" );
385 $this->out( $sk->afterContent() );
386 wfProfileOut();
387
388 wfProfileOut(); # A hack - we can't report after here
389 $this->out( $this->reportTime() );
390
391 $this->out( "\n</body></html>" );
392 flush();
393 }
394
395 function out( $ins )
396 {
397 global $wgInputEncoding, $wgOutputEncoding, $wgLang;
398 if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
399 $outs = $ins;
400 } else {
401 $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
402 if ( false === $outs ) { $outs = $ins; }
403 }
404 print $outs;
405 }
406
407 function setEncodings()
408 {
409 global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
410 global $wgUser, $wgLang;
411
412 $wgInputEncoding = strtolower( $wgInputEncoding );
413 $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
414
415 if( $wgUser->getOption( 'altencoding' ) ) {
416 $wgLang->setAltEncoding();
417 return;
418 }
419
420 if ( "" == $s ) {
421 $wgOutputEncoding = strtolower( $wgOutputEncoding );
422 return;
423 }
424 $a = explode( ",", $s );
425 $best = 0.0;
426 $bestset = "*";
427
428 foreach ( $a as $s ) {
429 if ( preg_match( "/(.*);q=(.*)/", $s, $m ) ) {
430 $set = $m[1];
431 $q = (float)($m[2]);
432 } else {
433 $set = $s;
434 $q = 1.0;
435 }
436 if ( $q > $best ) {
437 $bestset = $set;
438 $best = $q;
439 }
440 }
441 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
442 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
443 $wgOutputEncoding = strtolower( $bestset );
444
445 # Disable for now
446 #
447 $wgOutputEncoding = $wgInputEncoding;
448 }
449
450 function reportTime()
451 {
452 global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
453 global $wgProfiling, $wgProfileStack, $wgUser;
454
455 list( $usec, $sec ) = explode( " ", microtime() );
456 $now = (float)$sec + (float)$usec;
457
458 list( $usec, $sec ) = explode( " ", $wgRequestTime );
459 $start = (float)$sec + (float)$usec;
460 $elapsed = $now - $start;
461
462 if ( "" != $wgDebugLogFile ) {
463 $prof = "";
464 if( $wgProfiling and count( $wgProfileStack ) ) {
465 $lasttime = $start;
466 foreach( $wgProfileStack as $ile ) {
467 # "foo::bar 99 0.12345 1 0.23456 2"
468 if( preg_match( '/^(\S+)\s+([0-9]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)/', $ile, $m ) ) {
469 $thisstart = (float)$m[3] + (float)$m[4] - $start;
470 $thisend = (float)$m[5] + (float)$m[6] - $start;
471 $thiselapsed = $thisend - $thisstart;
472 $thispercent = $thiselapsed / $elapsed * 100.0;
473
474 $prof .= sprintf( "\tat %04.3f in %04.3f (%2.1f%%) - %s %s\n",
475 $thisstart, $thiselapsed, $thispercent,
476 str_repeat( "*", $m[2] ), $m[1] );
477 $lasttime = $thistime;
478 #$prof .= "\t(^ $ile)\n";
479 } else {
480 $prof .= "\t?broken? $ile\n";
481 }
482 }
483 }
484
485 if( $forward = $HTTP_SERVER_VARS['HTTP_X_FORWARDED_FOR'] )
486 $forward = " forwarded for $forward";
487 if( $client = $HTTP_SERVER_VARS['HTTP_CLIENT_IP'] )
488 $forward .= " client IP $client";
489 if( $from = $HTTP_SERVER_VARS['HTTP_FROM'] )
490 $forward .= " from $from";
491 if( $forward )
492 $forward = "\t(proxied via {$HTTP_SERVER_VARS['REMOTE_ADDR']}{$forward})";
493 if($wgUser->getId() == 0)
494 $forward .= " anon";
495 $log = sprintf( "%s\t%04.3f\t%s\n",
496 gmdate( "YmdHis" ), $elapsed,
497 urldecode( $HTTP_SERVER_VARS['REQUEST_URI'] . $forward ) );
498 error_log( $log . $prof, 3, $wgDebugLogFile );
499 }
500 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
501 $elapsed );
502 return $com;
503 }
504
505 # Note: these arguments are keys into wfMsg(), not text!
506 #
507 function errorpage( $title, $msg )
508 {
509 global $wgTitle;
510
511 $this->mDebugtext .= "Original title: " .
512 $wgTitle->getPrefixedText() . "\n";
513 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
514 $this->setPageTitle( wfMsg( $title ) );
515 $this->setRobotpolicy( "noindex,nofollow" );
516 $this->setArticleFlag( false );
517
518 $this->mBodytext = "";
519 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
520 $this->returnToMain( false );
521
522 $this->output();
523 exit;
524 }
525
526 function sysopRequired()
527 {
528 global $wgUser;
529
530 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
531 $this->setPageTitle( wfMsg( "sysoptitle" ) );
532 $this->setRobotpolicy( "noindex,nofollow" );
533 $this->setArticleFlag( false );
534 $this->mBodytext = "";
535
536 $sk = $wgUser->getSkin();
537 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
538 $text = str_replace( "$1", $ap, wfMsg( "sysoptext" ) );
539 $this->addHTML( $text );
540 $this->returnToMain();
541 }
542
543 function developerRequired()
544 {
545 global $wgUser;
546
547 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
548 $this->setPageTitle( wfMsg( "developertitle" ) );
549 $this->setRobotpolicy( "noindex,nofollow" );
550 $this->setArticleFlag( false );
551 $this->mBodytext = "";
552
553 $sk = $wgUser->getSkin();
554 $ap = $sk->makeKnownLink( wfMsg( "administrators" ), "" );
555 $text = str_replace( "$1", $ap, wfMsg( "developertext" ) );
556 $this->addHTML( $text );
557 $this->returnToMain();
558 }
559
560 function databaseError( $fname )
561 {
562 global $wgUser, $wgCommandLineMode;
563
564 $this->setPageTitle( wfMsg( "databaseerror" ) );
565 $this->setRobotpolicy( "noindex,nofollow" );
566 $this->setArticleFlag( false );
567
568 if ( $wgCommandLineMode ) {
569 $msg = wfMsg( "dberrortextcl" );
570 } else {
571 $msg = wfMsg( "dberrortextcl" );
572 }
573 $msg = str_replace( "$1", htmlspecialchars( wfLastDBquery() ), $msg );
574 $msg = str_replace( "$2", htmlspecialchars( $fname ), $msg );
575 $msg = str_replace( "$3", wfLastErrno(), $msg );
576 $msg = str_replace( "$4", htmlspecialchars( wfLastError() ), $msg );
577
578 if ( $wgCommandLineMode ) {
579 print $msg;
580 exit();
581 }
582 $sk = $wgUser->getSkin();
583 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
584 wfMsg( "searchingwikipedia" ) );
585 $msg = str_replace( "$5", $shlink, $msg );
586
587 $this->mBodytext = $msg;
588 $this->output();
589 exit();
590 }
591
592 function readOnlyPage()
593 {
594 global $wgUser, $wgReadOnlyFile;
595
596 $this->setPageTitle( wfMsg( "readonly" ) );
597 $this->setRobotpolicy( "noindex,nofollow" );
598 $this->setArticleFlag( false );
599
600 $reason = implode( "", file( $wgReadOnlyFile ) );
601 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
602 $this->addHTML( $text );
603 $this->returnToMain( false );
604 }
605
606 function fatalError( $message )
607 {
608 $this->setPageTitle( wfMsg( "internalerror" ) );
609 $this->setRobotpolicy( "noindex,nofollow" );
610 $this->setArticleFlag( false );
611
612 $this->mBodytext = $message;
613 $this->output();
614 exit;
615 }
616
617 function unexpectedValueError( $name, $val )
618 {
619 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
620 $msg = str_replace( "$2", $val, $msg );
621 $this->fatalError( $msg );
622 }
623
624 function fileCopyError( $old, $new )
625 {
626 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
627 $msg = str_replace( "$2", $new, $msg );
628 $this->fatalError( $msg );
629 }
630
631 function fileRenameError( $old, $new )
632 {
633 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
634 $msg = str_replace( "$2", $new, $msg );
635 $this->fatalError( $msg );
636 }
637
638 function fileDeleteError( $name )
639 {
640 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
641 $this->fatalError( $msg );
642 }
643
644 function fileNotFoundError( $name )
645 {
646 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
647 $this->fatalError( $msg );
648 }
649
650 function returnToMain( $auto = true )
651 {
652 global $wgUser, $wgOut, $returnto;
653
654 $sk = $wgUser->getSkin();
655 if ( "" == $returnto ) {
656 $returnto = wfMsg( "mainpage" );
657 }
658 $link = $sk->makeKnownLink( $returnto, "" );
659
660 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
661 if ( $auto ) {
662 $wgOut->addMeta( "http:Refresh", "10;url=" .
663 wfLocalUrlE( wfUrlencode( $returnto ) ) );
664 }
665 $wgOut->addHTML( "\n<p>$r\n" );
666 }
667
668
669 function categoryMagic ()
670 {
671 global $wgTitle , $wgUseCategoryMagic ;
672 if ( !isset ( $wgUseCategoryMagic ) || !$wgUseCategoryMagic ) return ;
673 $id = $wgTitle->getArticleID() ;
674 $cat = ucfirst ( wfMsg ( "category" ) ) ;
675 $ti = $wgTitle->getText() ;
676 $ti = explode ( ":" , $ti , 2 ) ;
677 if ( $cat != $ti[0] ) return "" ;
678 $r = "<br break=all>\n" ;
679
680 $articles = array() ;
681 $parents = array () ;
682 $children = array() ;
683
684
685 global $wgUser ;
686 $sk = $wgUser->getSkin() ;
687 $sql = "SELECT l_from FROM links WHERE l_to={$id}" ;
688 $res = wfQuery ( $sql ) ;
689 while ( $x = wfFetchObject ( $res ) )
690 {
691 # $t = new Title ;
692 # $t->newFromDBkey ( $x->l_from ) ;
693 # $t = $t->getText() ;
694 $t = $x->l_from ;
695 $y = explode ( ":" , $t , 2 ) ;
696 if ( count ( $y ) == 2 && $y[0] == $cat )
697 {
698 array_push ( $children , $sk->makeLink ( $t , $y[1] ) ) ;
699 }
700 else array_push ( $articles , $sk->makeLink ( $t ) ) ;
701 }
702 wfFreeResult ( $res ) ;
703
704 # Children
705 if ( count ( $children ) > 0 )
706 {
707 asort ( $children ) ;
708 $r .= "<h2>".wfMsg("subcategories")."</h2>\n" ;
709 $r .= implode ( ", " , $children ) ;
710 }
711
712 # Articles
713 if ( count ( $articles ) > 0 )
714 {
715 asort ( $articles ) ;
716 $h = str_replace ( "$1" , $ti[1] , wfMsg("category_header") ) ;
717 $r .= "<h2>{$h}</h2>\n" ;
718 $r .= implode ( ", " , $articles ) ;
719 }
720
721
722 return $r ;
723 }
724
725
726 # Well, OK, it's actually about 14 passes. But since all the
727 # hard lifting is done inside PHP's regex code, it probably
728 # wouldn't speed things up much to add a real parser.
729 #
730 function doWikiPass2( $text, $linestart )
731 {
732 global $wgUser, $wgLang, $wgUseDynamicDates;
733 wfProfileIn( "OutputPage::doWikiPass2" );
734
735 $text = $this->removeHTMLtags( $text );
736 $text = $this->replaceVariables( $text );
737
738 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
739 $text = str_replace ( "<HR>", "<hr>", $text );
740
741 $text = $this->doAllQuotes( $text );
742 $text = $this->doHeadings( $text );
743 $text = $this->doBlockLevels( $text, $linestart );
744
745 if($wgUseDynamicDates) {
746 $text = $wgLang->replaceDates( $text );
747 }
748
749 $text = $this->replaceExternalLinks( $text );
750 $text = $this->replaceInternalLinks ( $text );
751
752 $text = $this->magicISBN( $text );
753 $text = $this->magicRFC( $text );
754 $text = $this->formatHeadings( $text );
755
756 $sk = $wgUser->getSkin();
757 $text = $sk->transformContent( $text );
758 $text .= $this->categoryMagic () ;
759
760 wfProfileOut();
761 return $text;
762 }
763
764 /* private */ function doAllQuotes( $text )
765 {
766 $outtext = "";
767 $lines = explode( "\r\n", $text );
768 foreach ( $lines as $line ) {
769 $outtext .= $this->doQuotes ( "", $line, "" ) . "\r\n";
770 }
771 return $outtext;
772 }
773
774 /* private */ function doQuotes( $pre, $text, $mode )
775 {
776 if ( preg_match( "/^(.*)''(.*)$/sU", $text, $m ) ) {
777 $m1_strong = ($m[1] == "") ? "" : "<strong>{$m[1]}</strong>";
778 $m1_em = ($m[1] == "") ? "" : "<em>{$m[1]}</em>";
779 if ( substr ($m[2], 0, 1) == "'" ) {
780 $m[2] = substr ($m[2], 1);
781 if ($mode == "em") {
782 return $this->doQuotes ( $m[1], $m[2], ($m[1] == "") ? "both" : "emstrong" );
783 } else if ($mode == "strong") {
784 return $m1_strong . $this->doQuotes ( "", $m[2], "" );
785 } else if (($mode == "emstrong") || ($mode == "both")) {
786 return $this->doQuotes ( "", $pre.$m1_strong.$m[2], "em" );
787 } else if ($mode == "strongem") {
788 return "<strong>{$pre}{$m1_em}</strong>" . $this->doQuotes ( "", $m[2], "em" );
789 } else {
790 return $m[1] . $this->doQuotes ( "", $m[2], "strong" );
791 }
792 } else {
793 if ($mode == "strong") {
794 return $this->doQuotes ( $m[1], $m[2], ($m[1] == "") ? "both" : "strongem" );
795 } else if ($mode == "em") {
796 return $m1_em . $this->doQuotes ( "", $m[2], "" );
797 } else if ($mode == "emstrong") {
798 return "<em>{$pre}{$m1_strong}</em>" . $this->doQuotes ( "", $m[2], "strong" );
799 } else if (($mode == "strongem") || ($mode == "both")) {
800 return $this->doQuotes ( "", $pre.$m1_em.$m[2], "strong" );
801 } else {
802 return $m[1] . $this->doQuotes ( "", $m[2], "em" );
803 }
804 }
805 } else {
806 $text_strong = ($text == "") ? "" : "<strong>{$text}</strong>";
807 $text_em = ($text == "") ? "" : "<em>{$text}</em>";
808 if ($mode == "") {
809 return $pre . $text;
810 } else if ($mode == "em") {
811 return $pre . $text_em;
812 } else if ($mode == "strong") {
813 return $pre . $text_strong;
814 } else if ($mode == "strongem") {
815 return (($pre == "") && ($text == "")) ? "" : "<strong>{$pre}{$text_em}</strong>";
816 } else {
817 return (($pre == "") && ($text == "")) ? "" : "<em>{$pre}{$text_strong}</em>";
818 }
819 }
820 }
821
822 /* private */ function doHeadings( $text )
823 {
824 for ( $i = 6; $i >= 1; --$i ) {
825 $h = substr( "======", 0, $i );
826 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
827 "<h{$i}>\\1</h{$i}>\\2", $text );
828 }
829 return $text;
830 }
831
832 # Note: we have to do external links before the internal ones,
833 # and otherwise take great care in the order of things here, so
834 # that we don't end up interpreting some URLs twice.
835
836 /* private */ function replaceExternalLinks( $text )
837 {
838 wfProfileIn( "OutputPage::replaceExternalLinks" );
839 $text = $this->subReplaceExternalLinks( $text, "http", true );
840 $text = $this->subReplaceExternalLinks( $text, "https", true );
841 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
842 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
843 $text = $this->subReplaceExternalLinks( $text, "news", false );
844 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
845 wfProfileOut();
846 return $text;
847 }
848
849 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
850 {
851 global $wgUser, $printable;
852 global $wgAllowExternalImages;
853
854
855 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
856 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
857
858 # this is the list of separators that should be ignored if they
859 # are the last character of an URL but that should be included
860 # if they occur within the URL, e.g. "go to www.foo.com, where .."
861 # in this case, the last comma should not become part of the URL,
862 # but in "www.foo.com/123,2342,32.htm" it should.
863 $sep = ",;\.:";
864 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
865 $images = "gif|png|jpg|jpeg";
866
867 # PLEASE NOTE: The curly braces { } are not part of the regex,
868 # they are interpreted as part of the string (used to tell PHP
869 # that the content of the string should be inserted there).
870 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
871 "((?i){$images})([^{$uc}]|$)/";
872
873 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
874 $sk = $wgUser->getSkin();
875
876 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
877 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
878 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
879 }
880 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
881 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
882 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
883 "</a>\\5", $s );
884 $s = str_replace( $unique, $protocol, $s );
885
886 $a = explode( "[{$protocol}:", " " . $s );
887 $s = array_shift( $a );
888 $s = substr( $s, 1 );
889
890 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
891 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
892
893 foreach ( $a as $line ) {
894 if ( preg_match( $e1, $line, $m ) ) {
895 $link = "{$protocol}:{$m[1]}";
896 $trail = $m[2];
897 if ( $autonumber ) { $text = "[" . ++$this->mAutonumber . "]"; }
898 else { $text = wfEscapeHTML( $link ); }
899 } else if ( preg_match( $e2, $line, $m ) ) {
900 $link = "{$protocol}:{$m[1]}";
901 $text = $m[2];
902 $trail = $m[3];
903 } else {
904 $s .= "[{$protocol}:" . $line;
905 continue;
906 }
907 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
908 else $paren = "";
909 $la = $sk->getExternalLinkAttributes( $link, $text );
910 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
911
912 }
913 return $s;
914 }
915
916 /* private */ function replaceInternalLinks( $s )
917 {
918 global $wgTitle, $wgUser, $wgLang;
919 global $wgLinkCache, $wgInterwikiMagic;
920 global $wgNamespacesWithSubpages, $wgLanguageCode;
921 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
922
923 wfProfileIn( "$fname-setup" );
924 $tc = Title::legalChars() . "#";
925 $sk = $wgUser->getSkin();
926
927 $a = explode( "[[", " " . $s );
928 $s = array_shift( $a );
929 $s = substr( $s, 1 );
930
931 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
932 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
933 wfProfileOut();
934
935 wfProfileIn( "$fname-loop" );
936 foreach ( $a as $line ) {
937 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
938
939 $text = $m[2];
940 $trail = $m[3];
941
942 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
943
944 $text = "";
945 $trail = $m[2];
946 }
947
948 else { # Invalid form; output directly
949 $s .= "[[" . $line ;
950 continue;
951 }
952 if(substr($m[1],0,1)=="/") { # subpage
953 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
954 $m[1]=substr($m[1],1,strlen($m[1])-2);
955 $noslash=$m[1];
956
957 } else {
958 $noslash=substr($m[1],1);
959 }
960 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
961 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
962 if(!$text) {
963 $text= $m[1];
964 } # this might be changed for ugliness reasons
965 } else {
966 $link = $noslash; # no subpage allowed, use standard link
967 }
968 } else { # no subpage
969 $link = $m[1];
970 }
971
972 if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
973 $pre = strtolower( $m[1] );
974 $suf = $m[2];
975 if ( $wgLang->getNsIndex( $pre ) ==
976 Namespace::getImage() ) {
977 $nt = Title::newFromText( $suf );
978 $name = $nt->getDBkey();
979 if ( "" == $text ) { $text = $nt->GetText(); }
980
981 $wgLinkCache->addImageLink( $name );
982 $s .= $sk->makeImageLink( $name,
983 wfImageUrl( $name ), $text );
984 $s .= $trail;
985 } else if ( "media" == $pre ) {
986 $nt = Title::newFromText( $suf );
987 $name = $nt->getDBkey();
988 if ( "" == $text ) { $text = $nt->GetText(); }
989
990 $wgLinkCache->addImageLink( $name );
991 $s .= $sk->makeMediaLink( $name,
992 wfImageUrl( $name ), $text );
993 $s .= $trail;
994 } else {
995 $l = $wgLang->getLanguageName( $pre );
996 if ( "" == $l or !$wgInterwikiMagic or
997 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
998 if ( "" == $text ) { $text = $link; }
999 $s .= $sk->makeLink( $link, $text, "", $trail );
1000 } else if ( $pre != $wgLanguageCode ) {
1001 array_push( $this->mLanguageLinks, "$pre:$suf" );
1002 $s .= $trail;
1003 }
1004 }
1005 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
1006 # $link = substr( $link, 2 );
1007 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
1008 } else {
1009 if ( "" == $text ) { $text = $link; }
1010 $s .= $sk->makeLink( $link, $text, "", $trail );
1011 }
1012 }
1013 wfProfileOut();
1014 wfProfileOut();
1015 return $s;
1016 }
1017
1018 # Some functions here used by doBlockLevels()
1019 #
1020 /* private */ function closeParagraph()
1021 {
1022 $result = "";
1023 if ( 0 != strcmp( "p", $this->mLastSection ) &&
1024 0 != strcmp( "", $this->mLastSection ) ) {
1025 $result = "</" . $this->mLastSection . ">";
1026 }
1027 $this->mLastSection = "";
1028 return $result;
1029 }
1030 # getCommon() returns the length of the longest common substring
1031 # of both arguments, starting at the beginning of both.
1032 #
1033 /* private */ function getCommon( $st1, $st2 )
1034 {
1035 $fl = strlen( $st1 );
1036 $shorter = strlen( $st2 );
1037 if ( $fl < $shorter ) { $shorter = $fl; }
1038
1039 for ( $i = 0; $i < $shorter; ++$i ) {
1040 if ( $st1{$i} != $st2{$i} ) { break; }
1041 }
1042 return $i;
1043 }
1044 # These next three functions open, continue, and close the list
1045 # element appropriate to the prefix character passed into them.
1046 #
1047 /* private */ function openList( $char )
1048 {
1049 $result = $this->closeParagraph();
1050
1051 if ( "*" == $char ) { $result .= "<ul><li>"; }
1052 else if ( "#" == $char ) { $result .= "<ol><li>"; }
1053 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
1054 else if ( ";" == $char ) {
1055 $result .= "<dl><dt>";
1056 $this->mDTopen = true;
1057 }
1058 else { $result = "<!-- ERR 1 -->"; }
1059
1060 return $result;
1061 }
1062
1063 /* private */ function nextItem( $char )
1064 {
1065 if ( "*" == $char || "#" == $char ) { return "</li><li>"; }
1066 else if ( ":" == $char || ";" == $char ) {
1067 $close = "</dd>";
1068 if ( $this->mDTopen ) { $close = "</dt>"; }
1069 if ( ";" == $char ) {
1070 $this->mDTopen = true;
1071 return $close . "<dt>";
1072 } else {
1073 $this->mDTopen = false;
1074 return $close . "<dd>";
1075 }
1076 }
1077 return "<!-- ERR 2 -->";
1078 }
1079
1080 /* private */function closeList( $char )
1081 {
1082 if ( "*" == $char ) { return "</li></ul>"; }
1083 else if ( "#" == $char ) { return "</li></ol>"; }
1084 else if ( ":" == $char ) {
1085 if ( $this->mDTopen ) {
1086 $this->mDTopen = false;
1087 return "</dt></dl>";
1088 } else {
1089 return "</dd></dl>";
1090 }
1091 }
1092 return "<!-- ERR 3 -->";
1093 }
1094
1095 /* private */ function doBlockLevels( $text, $linestart )
1096 {
1097 wfProfileIn( "OutputPage::doBlockLevels" );
1098 # Parsing through the text line by line. The main thing
1099 # happening here is handling of block-level elements p, pre,
1100 # and making lists from lines starting with * # : etc.
1101 #
1102 $a = explode( "\n", $text );
1103 $text = $lastPref = "";
1104 $this->mDTopen = $inBlockElem = false;
1105
1106 if ( ! $linestart ) { $text .= array_shift( $a ); }
1107 foreach ( $a as $t ) {
1108 if ( "" != $text ) { $text .= "\n"; }
1109
1110 $oLine = $t;
1111 $opl = strlen( $lastPref );
1112 $npl = strspn( $t, "*#:;" );
1113 $pref = substr( $t, 0, $npl );
1114 $pref2 = str_replace( ";", ":", $pref );
1115 $t = substr( $t, $npl );
1116
1117 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
1118 $text .= $this->nextItem( substr( $pref, -1 ) );
1119
1120 if ( ";" == substr( $pref, -1 ) ) {
1121 $cpos = strpos( $t, ":" );
1122 if ( ! ( false === $cpos ) ) {
1123 $term = substr( $t, 0, $cpos );
1124 $text .= $term . $this->nextItem( ":" );
1125 $t = substr( $t, $cpos + 1 );
1126 }
1127 }
1128 } else if (0 != $npl || 0 != $opl) {
1129 $cpl = $this->getCommon( $pref, $lastPref );
1130
1131 while ( $cpl < $opl ) {
1132 $text .= $this->closeList( $lastPref{$opl-1} );
1133 --$opl;
1134 }
1135 if ( $npl <= $cpl && $cpl > 0 ) {
1136 $text .= $this->nextItem( $pref{$cpl-1} );
1137 }
1138 while ( $npl > $cpl ) {
1139 $char = substr( $pref, $cpl, 1 );
1140 $text .= $this->openList( $char );
1141
1142 if ( ";" == $char ) {
1143 $cpos = strpos( $t, ":" );
1144 if ( ! ( false === $cpos ) ) {
1145 $term = substr( $t, 0, $cpos );
1146 $text .= $term . $this->nextItem( ":" );
1147 $t = substr( $t, $cpos + 1 );
1148 }
1149 }
1150 ++$cpl;
1151 }
1152 $lastPref = $pref2;
1153 }
1154 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1155 if ( preg_match(
1156 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1157 $text .= $this->closeParagraph();
1158 $inBlockElem = true;
1159 }
1160 if ( ! $inBlockElem ) {
1161 if ( " " == $t{0} ) {
1162 $newSection = "pre";
1163 # $t = wfEscapeHTML( $t );
1164 }
1165 else { $newSection = "p"; }
1166
1167 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1168 $text .= $this->closeParagraph();
1169 $text .= "<" . $newSection . ">";
1170 } else if ( 0 != strcmp( $this->mLastSection,
1171 $newSection ) ) {
1172 $text .= $this->closeParagraph();
1173 if ( 0 != strcmp( "p", $newSection ) ) {
1174 $text .= "<" . $newSection . ">";
1175 }
1176 }
1177 $this->mLastSection = $newSection;
1178 }
1179 if ( $inBlockElem &&
1180 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1181 $inBlockElem = false;
1182 }
1183 }
1184 $text .= $t;
1185 }
1186 while ( $npl ) {
1187 $text .= $this->closeList( $pref2{$npl-1} );
1188 --$npl;
1189 }
1190 if ( "" != $this->mLastSection ) {
1191 if ( "p" != $this->mLastSection ) {
1192 $text .= "</" . $this->mLastSection . ">";
1193 }
1194 $this->mLastSection = "";
1195 }
1196 wfProfileOut();
1197 return $text;
1198 }
1199
1200 /* private */ function replaceVariables( $text )
1201 {
1202 global $wgLang;
1203 wfProfileIn( "OutputPage:replaceVariables" );
1204
1205 /* As with sigs, use server's local time --
1206 ensure this is appropriate for your audience! */
1207 $v = date( "m" );
1208 $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
1209 $v = $wgLang->getMonthName( date( "n" ) );
1210 $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
1211 $v = $wgLang->getMonthNameGen( date( "n" ) );
1212 $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
1213 $v = date( "j" );
1214 $text = str_replace( "{{CURRENTDAY}}", $v, $text );
1215 $v = $wgLang->getWeekdayName( date( "w" )+1 );
1216 $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
1217 $v = date( "Y" );
1218 $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
1219 $v = $wgLang->time( wfTimestampNow(), false );
1220 $text = str_replace( "{{CURRENTTIME}}", $v, $text );
1221
1222 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1223 $v = wfNumberOfArticles();
1224 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1225 }
1226 wfProfileOut();
1227 return $text;
1228 }
1229
1230 /* private */ function removeHTMLtags( $text )
1231 {
1232 wfProfileIn( "OutputPage::removeHTMLtags" );
1233 $htmlpairs = array( # Tags that must be closed
1234 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1235 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1236 "strike", "strong", "tt", "var", "div", "center",
1237 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1238 "ruby", "rt" , "rb" , "rp"
1239 );
1240 $htmlsingle = array(
1241 "br", "p", "hr", "li", "dt", "dd"
1242 );
1243 $htmlnest = array( # Tags that can be nested--??
1244 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1245 "dl", "font", "big", "small", "sub", "sup"
1246 );
1247 $tabletags = array( # Can only appear inside table
1248 "td", "th", "tr"
1249 );
1250
1251 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1252 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1253
1254 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1255 "title", "align", "lang", "dir", "width", "height",
1256 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1257 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1258 /* FONT */ "type", "start", "value", "compact",
1259 /* For various lists, mostly deprecated but safe */
1260 "summary", "width", "border", "frame", "rules",
1261 "cellspacing", "cellpadding", "valign", "char",
1262 "charoff", "colgroup", "col", "span", "abbr", "axis",
1263 "headers", "scope", "rowspan", "colspan", /* Tables */
1264 "id", "class", "name", "style" /* For CSS */
1265 );
1266
1267 # Remove HTML comments
1268 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1269
1270 $bits = explode( "<", $text );
1271 $text = array_shift( $bits );
1272 $tagstack = array(); $tablestack = array();
1273
1274 foreach ( $bits as $x ) {
1275 $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) );
1276 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1277 $x, $regs );
1278 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1279 error_reporting( $prev );
1280
1281 $badtag = 0 ;
1282 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1283 # Check our stack
1284 if ( $slash ) {
1285 # Closing a tag...
1286 if ( ! in_array( $t, $htmlsingle ) &&
1287 ( $ot = array_pop( $tagstack ) ) != $t ) {
1288 array_push( $tagstack, $ot );
1289 $badtag = 1;
1290 } else {
1291 if ( $t == "table" ) {
1292 $tagstack = array_pop( $tablestack );
1293 }
1294 $newparams = "";
1295 }
1296 } else {
1297 # Keep track for later
1298 if ( in_array( $t, $tabletags ) &&
1299 ! in_array( "table", $tagstack ) ) {
1300 $badtag = 1;
1301 } else if ( in_array( $t, $tagstack ) &&
1302 ! in_array ( $t , $htmlnest ) ) {
1303 $badtag = 1 ;
1304 } else if ( ! in_array( $t, $htmlsingle ) ) {
1305 if ( $t == "table" ) {
1306 array_push( $tablestack, $tagstack );
1307 $tagstack = array();
1308 }
1309 array_push( $tagstack, $t );
1310 }
1311 # Strip non-approved attributes from the tag
1312 $newparams = preg_replace(
1313 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1314 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1315 $params);
1316 }
1317 if ( ! $badtag ) {
1318 $rest = str_replace( ">", "&gt;", $rest );
1319 $text .= "<$slash$t$newparams$brace$rest";
1320 continue;
1321 }
1322 }
1323 $text .= "&lt;" . str_replace( ">", "&gt;", $x);
1324 }
1325 # Close off any remaining tags
1326 while ( $t = array_pop( $tagstack ) ) {
1327 $text .= "</$t>\n";
1328 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1329 }
1330 wfProfileOut();
1331 return $text;
1332 }
1333
1334
1335 /*
1336 *
1337 * This function accomplishes several tasks:
1338 * 1) Auto-number headings if that option is enabled
1339 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1340 * 3) Add a Table of contents on the top for users who have enabled the option
1341 * 4) Auto-anchor headings
1342 *
1343 * It loops through all headlines, collects the necessary data, then splits up the
1344 * string and re-inserts the newly formatted headlines.
1345 *
1346 * */
1347 /* private */ function formatHeadings( $text )
1348 {
1349 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1350 $nh=$wgUser->getOption( "numberheadings" );
1351 $st=$wgUser->getOption( "showtoc" );
1352 if(!$wgTitle->userCanEdit()) {
1353 $es=0;
1354 $esr=0;
1355 } else {
1356 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1357 $esr=$wgUser->getID() && $wgUser->getOption( "editsectiononrightclick" );
1358 }
1359 # if the string __NOTOC__ (not case-sensitive) occurs in the HTML, do not
1360 # add TOC
1361 if(preg_match("/__NOTOC__/i",$text)) {
1362 $text=preg_replace("/__NOTOC__/i","",$text);
1363 $st=0;
1364 }
1365
1366 # never add the TOC to the Main Page. This is an entry page that should not
1367 # be more than 1-2 screens large anyway
1368 if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1369
1370 # We need this to perform operations on the HTML
1371 $sk=$wgUser->getSkin();
1372
1373 # Get all headlines for numbering them and adding funky stuff like [edit]
1374 # links
1375 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1376
1377 # headline counter
1378 $c=0;
1379
1380 # Ugh .. the TOC should have neat indentation levels which can be
1381 # passed to the skin functions. These are determined here
1382 foreach($matches[3] as $headline) {
1383 if($level) { $prevlevel=$level;}
1384 $level=$matches[1][$c];
1385 if(($nh||$st) && $prevlevel && $level>$prevlevel) {
1386
1387 $h[$level]=0; // reset when we enter a new level
1388 $toc.=$sk->tocIndent($level-$prevlevel);
1389 $toclevel+=$level-$prevlevel;
1390
1391 }
1392 if(($nh||$st) && $level<$prevlevel) {
1393 $h[$level+1]=0; // reset when we step back a level
1394 $toc.=$sk->tocUnindent($prevlevel-$level);
1395 $toclevel-=$prevlevel-$level;
1396
1397 }
1398 $h[$level]++; // count number of headlines for each level
1399
1400 if($nh||$st) {
1401 for($i=1;$i<=$level;$i++) {
1402 if($h[$i]) {
1403 if($dot) {$numbering.=".";}
1404 $numbering.=$h[$i];
1405 $dot=1;
1406 }
1407 }
1408 }
1409
1410
1411 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1412 $tocline=$canonized_headline;
1413 $canonized_headline=str_replace('"',"",$canonized_headline);
1414 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1415 $refer[$c]=$canonized_headline;
1416 $refers[$canonized_headline]++; // count how many in assoc. array so we can track dupes in anchors
1417 $refcount[$c]=$refers[$canonized_headline];
1418 if($nh||$st) {
1419 $tocline=$numbering ." ". $tocline;
1420 if($nh) {
1421 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1422 }
1423 }
1424 $anchor=$canonized_headline;
1425 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1426 if($st) {
1427 $toc.=$sk->tocLine($anchor,$tocline,$toclevel);
1428 }
1429 if($es && !isset($wpPreview)) {
1430 $head[$c].=$sk->editSectionLink($c+1);
1431 }
1432 $head[$c].="<H".$level.$matches[2][$c]
1433 ."<a name=\"".$anchor."\">"
1434 .$headline
1435 ."</a>"
1436 ."</H".$level.">";
1437 if($esr && !isset($wpPreview)) {
1438 $head[$c]=$sk->editSectionScript($c+1,$head[$c]);
1439 }
1440 $numbering="";
1441 $c++;
1442 $dot=0;
1443 }
1444
1445 if($st) {
1446 $toclines=$c;
1447 $toc.=$sk->tocUnindent($toclevel);
1448 $toc=$sk->tocTable($toc);
1449 }
1450
1451 // split up and insert constructed headlines
1452
1453 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1454 $i=0;
1455
1456
1457 foreach($blocks as $block) {
1458 if(($es) && !isset($wpPreview) && $c>0 && $i==0) {
1459 # This is the [edit] link that appears for the top block of text when
1460 # section editing is enabled
1461 $full.=$sk->editSectionLink(0);
1462 }
1463 $full.=$block;
1464 if($st && $toclines>3 && !$i) {
1465 # Let's add a top anchor just in case we want to link to the top of the page
1466 $full="<a name=\"top\"></a>".$full.$toc;
1467 }
1468
1469 $full.=$head[$i];
1470 $i++;
1471 }
1472 return $full;
1473 }
1474
1475 /* private */ function magicISBN( $text )
1476 {
1477 global $wgLang;
1478
1479 $a = split( "ISBN ", " $text" );
1480 if ( count ( $a ) < 2 ) return $text;
1481 $text = substr( array_shift( $a ), 1);
1482 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1483
1484 foreach ( $a as $x ) {
1485 $isbn = $blank = "" ;
1486 while ( " " == $x{0} ) {
1487 $blank .= " ";
1488 $x = substr( $x, 1 );
1489 }
1490 while ( strstr( $valid, $x{0} ) != false ) {
1491 $isbn .= $x{0};
1492 $x = substr( $x, 1 );
1493 }
1494 $num = str_replace( "-", "", $isbn );
1495 $num = str_replace( " ", "", $num );
1496
1497 if ( "" == $num ) {
1498 $text .= "ISBN $blank$x";
1499 } else {
1500 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1501 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1502 $text .= $x;
1503 }
1504 }
1505 return $text;
1506 }
1507
1508 /* private */ function magicRFC( $text )
1509 {
1510 return $text;
1511 }
1512
1513 /* private */ function headElement()
1514 {
1515 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding, $wgLang;
1516
1517 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
1518
1519 if ( "" == $this->mHTMLtitle ) {
1520 $this->mHTMLtitle = $this->mPagetitle;
1521 }
1522 $rtl = $wgLang->isRTL() ? " dir='RTL'" : "";
1523 $ret .= "<html lang=\"$wgLanguageCode\"$rtl><head><title>{$this->mHTMLtitle}</title>\n";
1524 array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1525 foreach ( $this->mMetatags as $tag ) {
1526 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1527 $a = "http-equiv";
1528 $tag[0] = substr( $tag[0], 5 );
1529 } else {
1530 $a = "name";
1531 }
1532 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1533 }
1534 $p = $this->mRobotpolicy;
1535 if ( "" == $p ) { $p = "index,follow"; }
1536 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1537
1538 if ( count( $this->mKeywords ) > 0 ) {
1539 $ret .= "<meta name=\"keywords\" content=\"" .
1540 implode( ",", $this->mKeywords ) . "\">\n";
1541 }
1542 foreach ( $this->mLinktags as $tag ) {
1543 $ret .= "<link ";
1544 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1545 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1546 $ret .= "href=\"{$tag[2]}\">\n";
1547 }
1548 $sk = $wgUser->getSkin();
1549 $ret .= $sk->getHeadScripts();
1550 $ret .= $sk->getUserStyles();
1551
1552 $ret .= "</head>\n";
1553 return $ret;
1554 }
1555 }
1556
1557 ?>