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