Consolidate a bit of the link update code from movepage into linksupdate
[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, $wgMungeDates;
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($wgMungeDates)
675 $text = $wgLang->replaceDates( $text );
676 $text = $this->replaceExternalLinks( $text );
677 $text = $this->replaceInternalLinks ( $text );
678
679 $text = $this->magicISBN( $text );
680 $text = $this->magicRFC( $text );
681 $text = $this->formatHeadings( $text );
682
683 $sk = $wgUser->getSkin();
684 $text = $sk->transformContent( $text );
685
686 wfProfileOut();
687 return $text;
688 }
689
690 /* private */ function doQuotes( $text )
691 {
692 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
693 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
694 return $text;
695 }
696
697 /* private */ function doHeadings( $text )
698 {
699 for ( $i = 6; $i >= 1; --$i ) {
700 $h = substr( "======", 0, $i );
701 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
702 "<h{$i}>\\1</h{$i}>\\2", $text );
703 }
704 return $text;
705 }
706
707 # Note: we have to do external links before the internal ones,
708 # and otherwise take great care in the order of things here, so
709 # that we don't end up interpreting some URLs twice.
710
711 /* private */ function replaceExternalLinks( $text )
712 {
713 wfProfileIn( "OutputPage::replaceExternalLinks" );
714 $text = $this->subReplaceExternalLinks( $text, "http", true );
715 $text = $this->subReplaceExternalLinks( $text, "https", true );
716 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
717 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
718 $text = $this->subReplaceExternalLinks( $text, "news", false );
719 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
720 wfProfileOut();
721 return $text;
722 }
723
724 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
725 {
726 global $wgUser, $printable;
727 global $wgAllowExternalImages;
728
729
730 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
731 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
732
733 # this is the list of separators that should be ignored if they
734 # are the last character of an URL but that should be included
735 # if they occur within the URL, e.g. "go to www.foo.com, where .."
736 # in this case, the last comma should not become part of the URL,
737 # but in "www.foo.com/123,2342,32.htm" it should.
738 $sep = ",;\.:";
739 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
740 $images = "gif|png|jpg|jpeg";
741
742 # PLEASE NOTE: The curly braces { } are not part of the regex,
743 # they are interpreted as part of the string (used to tell PHP
744 # that the content of the string should be inserted there).
745 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
746 "((?i){$images})([^{$uc}]|$)/";
747
748 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
749 $sk = $wgUser->getSkin();
750
751 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
752 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
753 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
754 }
755 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
756 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
757 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
758 "</a>\\5", $s );
759 $s = str_replace( $unique, $protocol, $s );
760
761 $a = explode( "[{$protocol}:", " " . $s );
762 $s = array_shift( $a );
763 $s = substr( $s, 1 );
764
765 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
766 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
767
768 foreach ( $a as $line ) {
769 if ( preg_match( $e1, $line, $m ) ) {
770 $link = "{$protocol}:{$m[1]}";
771 $trail = $m[2];
772 if ( $autonumber ) { $text = "[" . ++$this->mAutonumber . "]"; }
773 else { $text = wfEscapeHTML( $link ); }
774 } else if ( preg_match( $e2, $line, $m ) ) {
775 $link = "{$protocol}:{$m[1]}";
776 $text = $m[2];
777 $trail = $m[3];
778 } else {
779 $s .= "[{$protocol}:" . $line;
780 continue;
781 }
782 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
783 else $paren = "";
784 $la = $sk->getExternalLinkAttributes( $link, $text );
785 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
786
787 }
788 return $s;
789 }
790
791 /* private */ function replaceInternalLinks( $s )
792 {
793 global $wgTitle, $wgUser, $wgLang;
794 global $wgLinkCache, $wgInterwikiMagic;
795 global $wgNamespacesWithSubpages, $wgLanguageCode;
796 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
797
798 wfProfileIn( "$fname-setup" );
799 $tc = Title::legalChars() . "#";
800 $sk = $wgUser->getSkin();
801
802 $a = explode( "[[", " " . $s );
803 $s = array_shift( $a );
804 $s = substr( $s, 1 );
805
806 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
807 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
808 wfProfileOut();
809
810 wfProfileIn( "$fname-loop" );
811 foreach ( $a as $line ) {
812 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
813
814 $text = $m[2];
815 $trail = $m[3];
816
817 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
818
819 $text = "";
820 $trail = $m[2];
821 }
822
823 else { # Invalid form; output directly
824 $s .= "[[" . $line ;
825 continue;
826 }
827 if(substr($m[1],0,1)=="/") { # subpage
828 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
829 $m[1]=substr($m[1],1,strlen($m[1])-2);
830 $noslash=$m[1];
831
832 } else {
833 $noslash=substr($m[1],1);
834 }
835 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
836 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
837 if(!$text) {
838 $text= $m[1];
839 } # this might be changed for ugliness reasons
840 } else {
841 $link = $noslash; # no subpage allowed, use standard link
842 }
843 } else { # no subpage
844 $link = $m[1];
845 }
846
847 if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
848 $pre = strtolower( $m[1] );
849 $suf = $m[2];
850 if ( $wgLang->getNsIndex( $pre ) ==
851 Namespace::getImage() ) {
852 $nt = Title::newFromText( $suf );
853 $name = $nt->getDBkey();
854 if ( "" == $text ) { $text = $nt->GetText(); }
855
856 $wgLinkCache->addImageLink( $name );
857 $s .= $sk->makeImageLink( $name,
858 wfImageUrl( $name ), $text );
859 $s .= $trail;
860 } else if ( "media" == $pre ) {
861 $nt = Title::newFromText( $suf );
862 $name = $nt->getDBkey();
863 if ( "" == $text ) { $text = $nt->GetText(); }
864
865 $wgLinkCache->addImageLink( $name );
866 $s .= $sk->makeMediaLink( $name,
867 wfImageUrl( $name ), $text );
868 $s .= $trail;
869 } else {
870 $l = $wgLang->getLanguageName( $pre );
871 if ( "" == $l or !$wgInterwikiMagic or
872 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
873 if ( "" == $text ) { $text = $link; }
874 $s .= $sk->makeLink( $link, $text, "", $trail );
875 } else if ( $pre != $wgLanguageCode ) {
876 array_push( $this->mLanguageLinks, "$pre:$suf" );
877 $s .= $trail;
878 }
879 }
880 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
881 # $link = substr( $link, 2 );
882 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
883 } else {
884 if ( "" == $text ) { $text = $link; }
885 $s .= $sk->makeLink( $link, $text, "", $trail );
886 }
887 }
888 wfProfileOut();
889 wfProfileOut();
890 return $s;
891 }
892
893 # Some functions here used by doBlockLevels()
894 #
895 /* private */ function closeParagraph()
896 {
897 $result = "";
898 if ( 0 != strcmp( "p", $this->mLastSection ) &&
899 0 != strcmp( "", $this->mLastSection ) ) {
900 $result = "</" . $this->mLastSection . ">";
901 }
902 $this->mLastSection = "";
903 return $result;
904 }
905 # getCommon() returns the length of the longest common substring
906 # of both arguments, starting at the beginning of both.
907 #
908 /* private */ function getCommon( $st1, $st2 )
909 {
910 $fl = strlen( $st1 );
911 $shorter = strlen( $st2 );
912 if ( $fl < $shorter ) { $shorter = $fl; }
913
914 for ( $i = 0; $i < $shorter; ++$i ) {
915 if ( $st1{$i} != $st2{$i} ) { break; }
916 }
917 return $i;
918 }
919 # These next three functions open, continue, and close the list
920 # element appropriate to the prefix character passed into them.
921 #
922 /* private */ function openList( $char )
923 {
924 $result = $this->closeParagraph();
925
926 if ( "*" == $char ) { $result .= "<ul><li>"; }
927 else if ( "#" == $char ) { $result .= "<ol><li>"; }
928 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
929 else if ( ";" == $char ) {
930 $result .= "<dl><dt>";
931 $this->mDTopen = true;
932 }
933 else { $result = "<!-- ERR 1 -->"; }
934
935 return $result;
936 }
937
938 /* private */ function nextItem( $char )
939 {
940 if ( "*" == $char || "#" == $char ) { return "</li><li>"; }
941 else if ( ":" == $char || ";" == $char ) {
942 $close = "</dd>";
943 if ( $this->mDTopen ) { $close = "</dt>"; }
944 if ( ";" == $char ) {
945 $this->mDTopen = true;
946 return $close . "<dt>";
947 } else {
948 $this->mDTopen = false;
949 return $close . "<dd>";
950 }
951 }
952 return "<!-- ERR 2 -->";
953 }
954
955 /* private */function closeList( $char )
956 {
957 if ( "*" == $char ) { return "</li></ul>"; }
958 else if ( "#" == $char ) { return "</li></ol>"; }
959 else if ( ":" == $char ) {
960 if ( $this->mDTopen ) {
961 $this->mDTopen = false;
962 return "</dt></dl>";
963 } else {
964 return "</dd></dl>";
965 }
966 }
967 return "<!-- ERR 3 -->";
968 }
969
970 /* private */ function doBlockLevels( $text, $linestart )
971 {
972 wfProfileIn( "OutputPage::doBlockLevels" );
973 # Parsing through the text line by line. The main thing
974 # happening here is handling of block-level elements p, pre,
975 # and making lists from lines starting with * # : etc.
976 #
977 $a = explode( "\n", $text );
978 $text = $lastPref = "";
979 $this->mDTopen = $inBlockElem = false;
980
981 if ( ! $linestart ) { $text .= array_shift( $a ); }
982 foreach ( $a as $t ) {
983 if ( "" != $text ) { $text .= "\n"; }
984
985 $oLine = $t;
986 $opl = strlen( $lastPref );
987 $npl = strspn( $t, "*#:;" );
988 $pref = substr( $t, 0, $npl );
989 $pref2 = str_replace( ";", ":", $pref );
990 $t = substr( $t, $npl );
991
992 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
993 $text .= $this->nextItem( substr( $pref, -1 ) );
994
995 if ( ";" == substr( $pref, -1 ) ) {
996 $cpos = strpos( $t, ":" );
997 if ( ! ( false === $cpos ) ) {
998 $term = substr( $t, 0, $cpos );
999 $text .= $term . $this->nextItem( ":" );
1000 $t = substr( $t, $cpos + 1 );
1001 }
1002 }
1003 } else if (0 != $npl || 0 != $opl) {
1004 $cpl = $this->getCommon( $pref, $lastPref );
1005
1006 while ( $cpl < $opl ) {
1007 $text .= $this->closeList( $lastPref{$opl-1} );
1008 --$opl;
1009 }
1010 if ( $npl <= $cpl && $cpl > 0 ) {
1011 $text .= $this->nextItem( $pref{$cpl-1} );
1012 }
1013 while ( $npl > $cpl ) {
1014 $char = substr( $pref, $cpl, 1 );
1015 $text .= $this->openList( $char );
1016
1017 if ( ";" == $char ) {
1018 $cpos = strpos( $t, ":" );
1019 if ( ! ( false === $cpos ) ) {
1020 $term = substr( $t, 0, $cpos );
1021 $text .= $term . $this->nextItem( ":" );
1022 $t = substr( $t, $cpos + 1 );
1023 }
1024 }
1025 ++$cpl;
1026 }
1027 $lastPref = $pref2;
1028 }
1029 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1030 if ( preg_match(
1031 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1032 $text .= $this->closeParagraph();
1033 $inBlockElem = true;
1034 }
1035 if ( ! $inBlockElem ) {
1036 if ( " " == $t{0} ) {
1037 $newSection = "pre";
1038 # $t = wfEscapeHTML( $t );
1039 }
1040 else { $newSection = "p"; }
1041
1042 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1043 $text .= $this->closeParagraph();
1044 $text .= "<" . $newSection . ">";
1045 } else if ( 0 != strcmp( $this->mLastSection,
1046 $newSection ) ) {
1047 $text .= $this->closeParagraph();
1048 if ( 0 != strcmp( "p", $newSection ) ) {
1049 $text .= "<" . $newSection . ">";
1050 }
1051 }
1052 $this->mLastSection = $newSection;
1053 }
1054 if ( $inBlockElem &&
1055 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1056 $inBlockElem = false;
1057 }
1058 }
1059 $text .= $t;
1060 }
1061 while ( $npl ) {
1062 $text .= $this->closeList( $pref2{$npl-1} );
1063 --$npl;
1064 }
1065 if ( "" != $this->mLastSection ) {
1066 if ( "p" != $this->mLastSection ) {
1067 $text .= "</" . $this->mLastSection . ">";
1068 }
1069 $this->mLastSection = "";
1070 }
1071 wfProfileOut();
1072 return $text;
1073 }
1074
1075 /* private */ function replaceVariables( $text )
1076 {
1077 global $wgLang;
1078 wfProfileIn( "OutputPage:replaceVariables" );
1079
1080 /* As with sigs, use server's local time --
1081 ensure this is appropriate for your audience! */
1082 $v = date( "m" );
1083 $text = str_replace( "{{CURRENTMONTH}}", $v, $text );
1084 $v = $wgLang->getMonthName( date( "n" ) );
1085 $text = str_replace( "{{CURRENTMONTHNAME}}", $v, $text );
1086 $v = $wgLang->getMonthNameGen( date( "n" ) );
1087 $text = str_replace( "{{CURRENTMONTHNAMEGEN}}", $v, $text );
1088 $v = date( "j" );
1089 $text = str_replace( "{{CURRENTDAY}}", $v, $text );
1090 $v = $wgLang->getWeekdayName( date( "w" )+1 );
1091 $text = str_replace( "{{CURRENTDAYNAME}}", $v, $text );
1092 $v = date( "Y" );
1093 $text = str_replace( "{{CURRENTYEAR}}", $v, $text );
1094 $v = $wgLang->time( wfTimestampNow(), false );
1095 $text = str_replace( "{{CURRENTTIME}}", $v, $text );
1096
1097 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1098 $v = wfNumberOfArticles();
1099 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1100 }
1101 wfProfileOut();
1102 return $text;
1103 }
1104
1105 /* private */ function removeHTMLtags( $text )
1106 {
1107 wfProfileIn( "OutputPage::removeHTMLtags" );
1108 $htmlpairs = array( # Tags that must be closed
1109 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1110 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1111 "strike", "strong", "tt", "var", "div", "center",
1112 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1113 "ruby", "rt" , "rb" , "rp"
1114 );
1115 $htmlsingle = array(
1116 "br", "p", "hr", "li", "dt", "dd"
1117 );
1118 $htmlnest = array( # Tags that can be nested--??
1119 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1120 "dl", "font", "big", "small", "sub", "sup"
1121 );
1122 $tabletags = array( # Can only appear inside table
1123 "td", "th", "tr"
1124 );
1125
1126 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1127 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1128
1129 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1130 "title", "align", "lang", "dir", "width", "height",
1131 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1132 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1133 /* FONT */ "type", "start", "value", "compact",
1134 /* For various lists, mostly deprecated but safe */
1135 "summary", "width", "border", "frame", "rules",
1136 "cellspacing", "cellpadding", "valign", "char",
1137 "charoff", "colgroup", "col", "span", "abbr", "axis",
1138 "headers", "scope", "rowspan", "colspan", /* Tables */
1139 "id", "class", "name", "style" /* For CSS */
1140 );
1141
1142 # Remove HTML comments
1143 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1144
1145 $bits = explode( "<", $text );
1146 $text = array_shift( $bits );
1147 $tagstack = array(); $tablestack = array();
1148
1149 foreach ( $bits as $x ) {
1150 $prev = error_reporting( E_ALL & ~( E_NOTICE | E_WARNING ) );
1151 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1152 $x, $regs );
1153 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1154 error_reporting( $prev );
1155
1156 $badtag = 0 ;
1157 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1158 # Check our stack
1159 if ( $slash ) {
1160 # Closing a tag...
1161 if ( ! in_array( $t, $htmlsingle ) &&
1162 ( $ot = array_pop( $tagstack ) ) != $t ) {
1163 array_push( $tagstack, $ot );
1164 $badtag = 1;
1165 } else {
1166 if ( $t == "table" ) {
1167 $tagstack = array_pop( $tablestack );
1168 }
1169 $newparams = "";
1170 }
1171 } else {
1172 # Keep track for later
1173 if ( in_array( $t, $tabletags ) &&
1174 ! in_array( "table", $tagstack ) ) {
1175 $badtag = 1;
1176 } else if ( in_array( $t, $tagstack ) &&
1177 ! in_array ( $t , $htmlnest ) ) {
1178 $badtag = 1 ;
1179 } else if ( ! in_array( $t, $htmlsingle ) ) {
1180 if ( $t == "table" ) {
1181 array_push( $tablestack, $tagstack );
1182 $tagstack = array();
1183 }
1184 array_push( $tagstack, $t );
1185 }
1186 # Strip non-approved attributes from the tag
1187 $newparams = preg_replace(
1188 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1189 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1190 $params);
1191 }
1192 if ( ! $badtag ) {
1193 $rest = str_replace( ">", "&gt;", $rest );
1194 $text .= "<$slash$t$newparams$brace$rest";
1195 continue;
1196 }
1197 }
1198 $text .= "&lt;" . str_replace( ">", "&gt;", $x);
1199 }
1200 # Close off any remaining tags
1201 while ( $t = array_pop( $tagstack ) ) {
1202 $text .= "</$t>\n";
1203 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1204 }
1205 wfProfileOut();
1206 return $text;
1207 }
1208
1209
1210 /*
1211 *
1212 * This function accomplishes several tasks:
1213 * 1) Auto-number headings if that option is enabled
1214 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1215 * 3) Add a Table of contents on the top for users who have enabled the option
1216 * 4) Auto-anchor headings
1217 *
1218 * It loops through all headlines, collects the necessary data, then splits up the
1219 * string and re-inserts the newly formatted headlines.
1220 *
1221 * */
1222 /* private */ function formatHeadings( $text )
1223 {
1224 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1225 $nh=$wgUser->getOption( "numberheadings" );
1226 $st=$wgUser->getOption( "showtoc" );
1227 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1228 if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1229
1230 $sk=$wgUser->getSkin();
1231 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1232
1233 $c=0;
1234
1235 foreach($matches[3] as $headline) {
1236 if($level) { $prevlevel=$level;}
1237 $level=$matches[1][$c];
1238 if(($nh||$st) && $prevlevel && $level>$prevlevel) {
1239
1240 $h[$level]=0; // reset when we enter a new level
1241 $toc.=$sk->tocIndent($level-$prevlevel);
1242 $toclevel+=$level-$prevlevel;
1243
1244 }
1245 if(($nh||$st) && $level<$prevlevel) {
1246 $h[$level+1]=0; // reset when we step back a level
1247 $toc.=$sk->tocUnindent($prevlevel-$level);
1248 $toclevel-=$prevlevel-$level;
1249
1250 }
1251 $h[$level]++; // count number of headlines for each level
1252
1253 if($nh||$st) {
1254 for($i=1;$i<=$level;$i++) {
1255 if($h[$i]) {
1256 if($dot) {$numbering.=".";}
1257 $numbering.=$h[$i];
1258 $dot=1;
1259 }
1260 }
1261 }
1262
1263
1264 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1265 $tocline=$canonized_headline;
1266 $canonized_headline=str_replace('"',"",$canonized_headline);
1267 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1268 $refer[$c]=$canonized_headline;
1269 $refers[$canonized_headline]++; // count how many in assoc. array so we can track dupes in anchors
1270 $refcount[$c]=$refers[$canonized_headline];
1271 if($nh||$st) {
1272 $tocline=$numbering ." ". $tocline;
1273 if($nh) {
1274 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1275 }
1276 }
1277 $anchor=$canonized_headline;
1278 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1279 if($st) {
1280 $toc.=$sk->tocLine($anchor,$tocline,$toclevel);
1281 }
1282 if($es && !isset($wpPreview)) {
1283 $head[$c].=$sk->editSectionLink($c+1);
1284 }
1285 $head[$c].="<H".$level.$matches[2][$c]
1286 ."<a name=\"".$anchor."\">"
1287 .$headline
1288 ."</a>"
1289 ."</H".$level.">";
1290 $numbering="";
1291 $c++;
1292 $dot=0;
1293 }
1294
1295 if($st) {
1296 $toclines=$c;
1297 $toc.=$sk->tocUnindent($toclevel);
1298 $toc=$sk->tocTable($toc);
1299 }
1300
1301 // split up and insert constructed headlines
1302
1303 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1304 $i=0;
1305
1306
1307 foreach($blocks as $block) {
1308 if($es && !isset($wpPreview) && $c>0 && $i==0) {
1309 $full.=$sk->editSectionLink(0);
1310 }
1311 $full.=$block;
1312 if($st && $toclines>3 && !$i) {
1313 $full="<a name=\"top\"></a>".$full.$toc;
1314 }
1315
1316 $full.=$head[$i];
1317 $i++;
1318 }
1319 return $full;
1320 }
1321
1322 /* private */ function magicISBN( $text )
1323 {
1324 global $wgLang;
1325
1326 $a = split( "ISBN ", " $text" );
1327 if ( count ( $a ) < 2 ) return $text;
1328 $text = substr( array_shift( $a ), 1);
1329 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1330
1331 foreach ( $a as $x ) {
1332 $isbn = $blank = "" ;
1333 while ( " " == $x{0} ) {
1334 $blank .= " ";
1335 $x = substr( $x, 1 );
1336 }
1337 while ( strstr( $valid, $x{0} ) != false ) {
1338 $isbn .= $x{0};
1339 $x = substr( $x, 1 );
1340 }
1341 $num = str_replace( "-", "", $isbn );
1342 $num = str_replace( " ", "", $num );
1343
1344 if ( "" == $num ) {
1345 $text .= "ISBN $blank$x";
1346 } else {
1347 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1348 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1349 $text .= $x;
1350 }
1351 }
1352 return $text;
1353 }
1354
1355 /* private */ function magicRFC( $text )
1356 {
1357 return $text;
1358 }
1359
1360 /* private */ function headElement()
1361 {
1362 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding;
1363
1364 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
1365
1366 if ( "" == $this->mHTMLtitle ) {
1367 $this->mHTMLtitle = $this->mPagetitle;
1368 }
1369 $ret .= "<html lang=\"$wgLanguageCode\"><head><title>{$this->mHTMLtitle}</title>\n";
1370 array_push( $this->mMetatags, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1371 foreach ( $this->mMetatags as $tag ) {
1372 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1373 $a = "http-equiv";
1374 $tag[0] = substr( $tag[0], 5 );
1375 } else {
1376 $a = "name";
1377 }
1378 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1379 }
1380 $p = $this->mRobotpolicy;
1381 if ( "" == $p ) { $p = "index,follow"; }
1382 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1383
1384 if ( count( $this->mKeywords ) > 0 ) {
1385 $ret .= "<meta name=\"keywords\" content=\"" .
1386 implode( ",", $this->mKeywords ) . "\">\n";
1387 }
1388 foreach ( $this->mLinktags as $tag ) {
1389 $ret .= "<link ";
1390 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1391 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1392 $ret .= "href=\"{$tag[2]}\">\n";
1393 }
1394 $sk = $wgUser->getSkin();
1395 $ret .= $sk->getHeadScripts();
1396 $ret .= $sk->getUserStyles();
1397
1398 $ret .= "</head>\n";
1399 return $ret;
1400 }
1401 }
1402
1403 ?>