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