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