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