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