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