4 if($wgUseTeX) include_once( "Math.php" );
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;
13 var $mDTopen, $mLastSection; # Used for processing DL, PRE
14 var $mLanguageLinks, $mSupressQuickbar;
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;
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; }
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 ) ); }
39 function checkLastModified ( $timestamp )
41 global $wgLang, $wgCachePages, $wgUser;
42 if( !$wgCachePages ) {
43 wfDebug( "CACHE DISABLED\n", false );
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 );
51 if( $wgUser->getOption( "nocache" ) ) {
52 wfDebug( "USER DISABLED CACHE\n", false );
56 $lastmod = gmdate( "D, j M Y H:i:s", wfTimestamp2Unix(
57 max( $timestamp, $wgUser->mTouched
) ) ) . " GMT";
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 );
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 );
77 wfDebug( "READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp\n", false );
78 $this->mLastModified
= $lastmod;
81 wfDebug( "We're confused.\n", false );
82 $this->mLastModified
= $lastmod;
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
; }
97 function getLanguageLinks() {
98 global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode;
99 global $wgDBconnection, $wgDBname, $wgDBintlname;
101 if ( ! $wgUseNewInterlanguage )
102 return $this->mLanguageLinks
;
104 mysql_select_db( $wgDBintlname, $wgDBconnection ) or die(
105 htmlspecialchars(mysql_error()) );
108 $sql = "SELECT * FROM ilinks WHERE lang_from=\"" .
109 "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\"";
110 $res = mysql_query( $sql, $wgDBconnection );
112 while ( $q = mysql_fetch_object ( $res ) ) {
113 $list[] = $q->lang_to
. ":" . $q->title_to
;
115 mysql_free_result( $res );
116 mysql_select_db( $wgDBname, $wgDBconnection ) or die(
117 htmlspecialchars(mysql_error()) );
122 function supressQuickbar() { $this->mSupressQuickbar
= true; }
123 function isQuickbarSupressed() { return $this->mSupressQuickbar
; }
125 function addHTML( $text ) { $this->mBodytext
.= $text; }
126 function addHeadtext( $text ) { $this->mHeadtext
.= $text; }
127 function debug( $text ) { $this->mDebugtext
.= $text; }
129 # First pass--just handle <nowiki> sections, pass the rest off
130 # to doWikiPass2() which does all the real work.
133 function addWikiText( $text, $linestart = true )
136 wfProfileIn( "OutputPage::addWikiText" );
137 $unique = "3iyZiyA7iMwg5rhxP0Dcc9oTnj8qD1jm1Sfv4";
138 $unique2 = "4LIQ9nXtiYFPCSfitVwDw7EYwQlL4GeeQ7qSO";
139 $unique3 = "fPaA8gDfdLBqzj68Yjg9Hil3qEF8JGO0uszIp";
150 while ( "" != $text ) {
151 $p = preg_split( "/<\\s*nowiki\\s*>/i", $text, 2 );
153 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $text = ""; }
155 $q = preg_split( "/<\\/\\s*nowiki\\s*>/i", $p[1], 2 );
157 $nwlist[$nwsecs] = wfEscapeHTMLTagsOnly($q[0]);
158 $stripped .= $unique;
164 while ( "" != $stripped ) {
165 $p = preg_split( "/<\\s*math\\s*>/i", $stripped, 2 );
167 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $stripped = ""; }
169 $q = preg_split( "/<\\/\\s*math\\s*>/i", $p[1], 2 );
171 $mathlist[$mathsecs] = renderMath($q[0]);
172 $stripped2 .= $unique2;
177 $stripped2 = $stripped;
180 while ( "" != $stripped2 ) {
181 $p = preg_split( "/<\\s*pre\\s*>/i", $stripped2, 2 );
183 if ( ( count( $p ) < 2 ) ||
( "" == $p[1] ) ) { $stripped2 = ""; }
185 $q = preg_split( "/<\\/\\s*pre\\s*>/i", $p[1], 2 );
187 $prelist[$presecs] = "<pre>". wfEscapeHTMLTagsOnly($q[0]). "</pre>";
188 $stripped3 .= $unique3;
193 $text = $this->doWikiPass2( $stripped3, $linestart );
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 );
202 for ( $i = 1; $i <= $mathsecs; ++
$i ) {
203 $text = preg_replace( "/{$unique2}/", str_replace( $specialChars,
204 $escapedChars, $mathlist[$i] ), $text, 1 );
207 for ( $i = 1; $i <= $nwsecs; ++
$i ) {
208 $text = preg_replace( "/{$unique}/", str_replace( $specialChars,
209 $escapedChars, $nwlist[$i] ), $text, 1 );
211 $this->addHTML( $text );
215 function sendCacheControl() {
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}" );
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" );
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" );
232 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
235 # Finally, all the text has been munged and accumulated into
236 # the object, let's actually output it:
240 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
241 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
242 wfProfileIn( "OutputPage::output" );
243 $sk = $wgUser->getSkin();
245 wfProfileIn( "OutputPage::output-headers" );
246 $this->sendCacheControl();
248 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
249 header( "Content-language: {$wgLanguageCode}" );
251 if ( "" != $this->mRedirect
) {
252 header( "Location: {$this->mRedirect}" );
257 $exp = time() +
$wgCookieExpiration;
258 foreach( $this->mCookies
as $name => $val ) {
259 setcookie( $name, $val, $exp, "/" );
263 wfProfileIn( "OutputPage::output-middle" );
265 $this->out( $this->headElement() );
267 $this->out( "\n<body" );
268 $ops = $sk->getBodyOptions();
269 foreach ( $ops as $name => $val ) {
270 $this->out( " $name='$val'" );
273 if ( $wgDebugComments ) {
274 $this->out( "<!-- Wiki debugging output:\n" .
275 $this->mDebugtext
. "-->\n" );
277 $this->out( $sk->beforeContent() );
280 wfProfileIn( "OutputPage::output-bodytext" );
281 $this->out( $this->mBodytext
);
283 wfProfileIn( "OutputPage::output-after" );
284 $this->out( $sk->afterContent() );
287 wfProfileOut(); # A hack - we can't report after here
288 $this->out( $this->reportTime() );
290 $this->out( "\n</body></html>" );
296 global $wgInputEncoding, $wgOutputEncoding, $wgLang;
297 if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
300 $outs = $wgLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
301 if ( false === $outs ) { $outs = $ins; }
306 function setEncodings()
308 global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
309 global $wgUser, $wgLang;
311 $wgInputEncoding = strtolower( $wgInputEncoding );
312 $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
314 if( $wgUser->getOption( 'altencoding' ) ) {
315 $wgLang->setAltEncoding();
320 $wgOutputEncoding = strtolower( $wgOutputEncoding );
323 $a = explode( ",", $s );
327 foreach ( $a as $s ) {
328 if ( preg_match( "/(.*);q=(.*)/", $s, $m ) ) {
340 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
341 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
342 $wgOutputEncoding = strtolower( $bestset );
346 $wgOutputEncoding = $wgInputEncoding;
349 function reportTime()
351 global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
352 global $wgProfiling, $wgProfileStack, $wgUser;
354 list( $usec, $sec ) = explode( " ", microtime() );
355 $now = (float)$sec +
(float)$usec;
357 list( $usec, $sec ) = explode( " ", $wgRequestTime );
358 $start = (float)$sec +
(float)$usec;
359 $elapsed = $now - $start;
361 if ( "" != $wgDebugLogFile ) {
363 if( $wgProfiling and count( $wgProfileStack ) ) {
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;
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";
379 $prof .= "\t?broken? $ile\n";
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";
391 $forward = "\t(proxied via {$HTTP_SERVER_VARS['REMOTE_ADDR']}{$forward})";
392 if($wgUser->getId() == 0)
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 );
399 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
404 # Note: these arguments are keys into wfMsg(), not text!
406 function errorpage( $title, $msg )
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 );
417 $this->mBodytext
= "";
418 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
419 $this->returnToMain( false );
425 function sysopRequired()
429 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
430 $this->setPageTitle( wfMsg( "sysoptitle" ) );
431 $this->setRobotpolicy( "noindex,nofollow" );
432 $this->setArticleFlag( false );
433 $this->mBodytext
= "";
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();
442 function developerRequired()
446 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
447 $this->setPageTitle( wfMsg( "developertitle" ) );
448 $this->setRobotpolicy( "noindex,nofollow" );
449 $this->setArticleFlag( false );
450 $this->mBodytext
= "";
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();
459 function databaseError( $fname )
461 global $wgUser, $wgCommandLineMode;
463 $this->setPageTitle( wfMsg( "databaseerror" ) );
464 $this->setRobotpolicy( "noindex,nofollow" );
465 $this->setArticleFlag( false );
467 if ( $wgCommandLineMode ) {
468 $msg = wfMsg( "dberrortextcl" );
470 $msg = wfMsg( "dberrortextcl" );
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 );
477 if ( $wgCommandLineMode ) {
481 $sk = $wgUser->getSkin();
482 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
483 wfMsg( "searchingwikipedia" ) );
484 $msg = str_replace( "$5", $shlink, $msg );
486 $this->mBodytext
= $msg;
491 function readOnlyPage()
493 global $wgUser, $wgReadOnlyFile;
495 $this->setPageTitle( wfMsg( "readonly" ) );
496 $this->setRobotpolicy( "noindex,nofollow" );
497 $this->setArticleFlag( false );
499 $reason = implode( "", file( $wgReadOnlyFile ) );
500 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
501 $this->addHTML( $text );
502 $this->returnToMain( false );
505 function fatalError( $message )
507 $this->setPageTitle( wfMsg( "internalerror" ) );
508 $this->setRobotpolicy( "noindex,nofollow" );
509 $this->setArticleFlag( false );
511 $this->mBodytext
= $message;
516 function unexpectedValueError( $name, $val )
518 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
519 $msg = str_replace( "$2", $val, $msg );
520 $this->fatalError( $msg );
523 function fileCopyError( $old, $new )
525 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
526 $msg = str_replace( "$2", $new, $msg );
527 $this->fatalError( $msg );
530 function fileRenameError( $old, $new )
532 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
533 $msg = str_replace( "$2", $new, $msg );
534 $this->fatalError( $msg );
537 function fileDeleteError( $name )
539 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
540 $this->fatalError( $msg );
543 function fileNotFoundError( $name )
545 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
546 $this->fatalError( $msg );
549 function returnToMain( $auto = true )
551 global $wgUser, $wgOut, $returnto;
553 $sk = $wgUser->getSkin();
554 if ( "" == $returnto ) {
555 $returnto = wfMsg( "mainpage" );
557 $link = $sk->makeKnownLink( $returnto, "" );
559 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
561 $wgOut->addMeta( "http:Refresh", "10;url=" .
562 wfLocalUrlE( wfUrlencode( $returnto ) ) );
564 $wgOut->addHTML( "\n<p>$r\n" );
568 function categoryMagic ()
570 global $wgTitle , $wgUseCategoryMagic ;
571 if ( !isset ( $wgUseCategoryMagic ) ||
!$wgUseCategoryMagic ) return ;
572 $id = $wgTitle->getArticleID() ;
573 $cat = ucfirst ( wfMsg ( "category" ) ) ;
574 $ti = $wgTitle->getText() ;
575 $ti = explode ( ":" , $ti , 2 ) ;
576 if ( $cat != $ti[0] ) return "" ;
577 $r = "<br break=all>\n" ;
579 $articles = array() ;
580 $parents = array () ;
581 $children = array() ;
585 $sk = $wgUser->getSkin() ;
586 $sql = "SELECT l_from FROM links WHERE l_to={$id}" ;
587 $res = wfQuery ( $sql ) ;
588 while ( $x = wfFetchObject ( $res ) )
591 # $t->newFromDBkey ( $x->l_from ) ;
592 # $t = $t->getText() ;
594 $y = explode ( ":" , $t , 2 ) ;
595 if ( count ( $y ) == 2 && $y[0] == $cat )
597 array_push ( $children , $sk->makeLink ( $t , $y[1] ) ) ;
599 else array_push ( $articles , $sk->makeLink ( $t ) ) ;
601 wfFreeResult ( $res ) ;
604 if ( count ( $children ) > 0 )
606 asort ( $children ) ;
607 $r .= "<h2>".wfMsg("subcategories")."</h2>\n" ;
608 $r .= implode ( ", " , $children ) ;
612 if ( count ( $articles ) > 0 )
614 asort ( $articles ) ;
615 $h = str_replace ( "$1" , $ti[1] , wfMsg("category_header") ) ;
616 $r .= "<h2>{$h}</h2>\n" ;
617 $r .= implode ( ", " , $articles ) ;
625 # Well, OK, it's actually about 14 passes. But since all the
626 # hard lifting is done inside PHP's regex code, it probably
627 # wouldn't speed things up much to add a real parser.
629 function doWikiPass2( $text, $linestart )
631 global $wgUser, $wgLang, $wgUseDynamicDates;
632 wfProfileIn( "OutputPage::doWikiPass2" );
634 $text = $this->removeHTMLtags( $text );
635 $text = $this->replaceVariables( $text );
637 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
638 $text = str_replace ( "<HR>", "<hr>", $text );
640 $text = $this->doAllQuotes( $text );
641 $text = $this->doHeadings( $text );
642 $text = $this->doBlockLevels( $text, $linestart );
644 if($wgUseDynamicDates) {
645 $text = $wgLang->replaceDates( $text );
648 $text = $this->replaceExternalLinks( $text );
649 $text = $this->replaceInternalLinks ( $text );
651 $text = $this->magicISBN( $text );
652 $text = $this->magicRFC( $text );
653 $text = $this->formatHeadings( $text );
655 $sk = $wgUser->getSkin();
656 $text = $sk->transformContent( $text );
657 $text .= $this->categoryMagic () ;
663 /* private */ function doAllQuotes( $text )
666 $lines = explode( "\r\n", $text );
667 foreach ( $lines as $line ) {
668 $outtext .= $this->doQuotes ( "", $line, "" ) . "\r\n";
673 /* private */ function doQuotes( $pre, $text, $mode )
675 if ( preg_match( "/^(.*)''(.*)$/sU", $text, $m ) ) {
676 $m1_strong = ($m[1] == "") ?
"" : "<strong>{$m[1]}</strong>";
677 $m1_em = ($m[1] == "") ?
"" : "<em>{$m[1]}</em>";
678 if ( substr ($m[2], 0, 1) == "'" ) {
679 $m[2] = substr ($m[2], 1);
681 return $this->doQuotes ( $m[1], $m[2], ($m[1] == "") ?
"both" : "emstrong" );
682 } else if ($mode == "strong") {
683 return $m1_strong . $this->doQuotes ( "", $m[2], "" );
684 } else if (($mode == "emstrong") ||
($mode == "both")) {
685 return $this->doQuotes ( "", $pre.$m1_strong.$m[2], "em" );
686 } else if ($mode == "strongem") {
687 return "<strong>{$pre}{$m1_em}</strong>" . $this->doQuotes ( "", $m[2], "em" );
689 return $m[1] . $this->doQuotes ( "", $m[2], "strong" );
692 if ($mode == "strong") {
693 return $this->doQuotes ( $m[1], $m[2], ($m[1] == "") ?
"both" : "strongem" );
694 } else if ($mode == "em") {
695 return $m1_em . $this->doQuotes ( "", $m[2], "" );
696 } else if ($mode == "emstrong") {
697 return "<em>{$pre}{$m1_strong}</em>" . $this->doQuotes ( "", $m[2], "strong" );
698 } else if (($mode == "strongem") ||
($mode == "both")) {
699 return $this->doQuotes ( "", $pre.$m1_em.$m[2], "strong" );
701 return $m[1] . $this->doQuotes ( "", $m[2], "em" );
705 $text_strong = ($text == "") ?
"" : "<strong>{$text}</strong>";
706 $text_em = ($text == "") ?
"" : "<em>{$text}</em>";
709 } else if ($mode == "em") {
710 return $pre . $text_em;
711 } else if ($mode == "strong") {
712 return $pre . $text_strong;
713 } else if ($mode == "strongem") {
714 return (($pre == "") && ($text == "")) ?
"" : "<strong>{$pre}{$text_em}</strong>";
716 return (($pre == "") && ($text == "")) ?
"" : "<em>{$pre}{$text_strong}</em>";
721 /* private */ function doHeadings( $text )
723 for ( $i = 6; $i >= 1; --$i ) {
724 $h = substr( "======", 0, $i );
725 $text = preg_replace( "/^{$h}([^=]+){$h}(\\s|$)/m",
726 "<h{$i}>\\1</h{$i}>\\2", $text );
731 # Note: we have to do external links before the internal ones,
732 # and otherwise take great care in the order of things here, so
733 # that we don't end up interpreting some URLs twice.
735 /* private */ function replaceExternalLinks( $text )
737 wfProfileIn( "OutputPage::replaceExternalLinks" );
738 $text = $this->subReplaceExternalLinks( $text, "http", true );
739 $text = $this->subReplaceExternalLinks( $text, "https", true );
740 $text = $this->subReplaceExternalLinks( $text, "ftp", false );
741 $text = $this->subReplaceExternalLinks( $text, "gopher", false );
742 $text = $this->subReplaceExternalLinks( $text, "news", false );
743 $text = $this->subReplaceExternalLinks( $text, "mailto", false );
748 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
750 global $wgUser, $printable;
751 global $wgAllowExternalImages;
754 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
755 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
757 # this is the list of separators that should be ignored if they
758 # are the last character of an URL but that should be included
759 # if they occur within the URL, e.g. "go to www.foo.com, where .."
760 # in this case, the last comma should not become part of the URL,
761 # but in "www.foo.com/123,2342,32.htm" it should.
763 $fnc = "A-Za-z0-9_.,~%\\-+&;#*?!=()@\\x80-\\xFF";
764 $images = "gif|png|jpg|jpeg";
766 # PLEASE NOTE: The curly braces { } are not part of the regex,
767 # they are interpreted as part of the string (used to tell PHP
768 # that the content of the string should be inserted there).
769 $e1 = "/(^|[^\\[])({$protocol}:)([{$uc}{$sep}]+)\\/([{$fnc}]+)\\." .
770 "((?i){$images})([^{$uc}]|$)/";
772 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
773 $sk = $wgUser->getSkin();
775 if ( $autonumber and $wgAllowExternalImages) { # Use img tags only for HTTP urls
776 $s = preg_replace( $e1, "\\1" . $sk->makeImage( "{$unique}:\\3" .
777 "/\\4.\\5", "\\4.\\5" ) . "\\6", $s );
779 $s = preg_replace( $e2, "\\1" . "<a href=\"{$unique}:\\3\"" .
780 $sk->getExternalLinkAttributes( "{$unique}:\\3", wfEscapeHTML(
781 "{$unique}:\\3" ) ) . ">" . wfEscapeHTML( "{$unique}:\\3" ) .
783 $s = str_replace( $unique, $protocol, $s );
785 $a = explode( "[{$protocol}:", " " . $s );
786 $s = array_shift( $a );
787 $s = substr( $s, 1 );
789 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
790 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
792 foreach ( $a as $line ) {
793 if ( preg_match( $e1, $line, $m ) ) {
794 $link = "{$protocol}:{$m[1]}";
796 if ( $autonumber ) { $text = "[" . ++
$this->mAutonumber
. "]"; }
797 else { $text = wfEscapeHTML( $link ); }
798 } else if ( preg_match( $e2, $line, $m ) ) {
799 $link = "{$protocol}:{$m[1]}";
803 $s .= "[{$protocol}:" . $line;
806 if ( $printable == "yes") $paren = " (<i>" . htmlspecialchars ( $link ) . "</i>)";
808 $la = $sk->getExternalLinkAttributes( $link, $text );
809 $s .= "<a href='{$link}'{$la}>{$text}</a>{$paren}{$trail}";
815 /* private */ function replaceInternalLinks( $s )
817 global $wgTitle, $wgUser, $wgLang;
818 global $wgLinkCache, $wgInterwikiMagic, $wgUseCategoryMagic;
819 global $wgNamespacesWithSubpages, $wgLanguageCode;
820 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
822 wfProfileIn( "$fname-setup" );
823 $tc = Title
::legalChars() . "#";
824 $sk = $wgUser->getSkin();
826 $a = explode( "[[", " " . $s );
827 $s = array_shift( $a );
828 $s = substr( $s, 1 );
830 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
831 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
834 foreach ( $a as $line ) {
835 wfProfileIn( "$fname-loop" );
836 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
841 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
847 else { # Invalid form; output directly
852 if(substr($m[1],0,1)=="/") { # subpage
853 if(substr($m[1],-1,1)=="/") { # / at end means we don't want the slash to be shown
854 $m[1]=substr($m[1],1,strlen($m[1])-2);
858 $noslash=substr($m[1],1);
860 if($wgNamespacesWithSubpages[$wgTitle->getNamespace()]) { # subpages allowed here
861 $link = $wgTitle->getPrefixedText(). "/" . trim($noslash);
864 } # this might be changed for ugliness reasons
866 $link = $noslash; # no subpage allowed, use standard link
868 } else { # no subpage
872 if ( preg_match( "/^((?:i|x|[a-z]{2,3})(?:-[a-z0-9]+)?|[A-Za-z\\x80-\\xff]+):(.*)\$/", $link, $m ) ) {
873 $pre = strtolower( $m[1] );
875 if ( $wgLang->getNsIndex( $pre ) ==
876 Namespace::getImage() ) {
877 $nt = Title
::newFromText( $suf );
878 $name = $nt->getDBkey();
879 if ( "" == $text ) { $text = $nt->GetText(); }
881 $wgLinkCache->addImageLink( $name );
882 $s .= $sk->makeImageLink( $name,
883 wfImageUrl( $name ), $text );
885 } else if ( "media" == $pre ) {
886 $nt = Title
::newFromText( $suf );
887 $name = $nt->getDBkey();
888 if ( "" == $text ) { $text = $nt->GetText(); }
890 $wgLinkCache->addImageLink( $name );
891 $s .= $sk->makeMediaLink( $name,
892 wfImageUrl( $name ), $text );
894 } else if ( isset($wgUseCategoryMagic) && $wgUseCategoryMagic && $pre == wfMsg ( "category" ) ) {
895 $l = $sk->makeLink ( $pre.":".ucfirst($m[2]) , ucfirst ( $m[2] ) ) ;
896 array_push ( $this->mCategoryLinks
, $l ) ;
899 $l = $wgLang->getLanguageName( $pre );
900 if ( "" == $l or !$wgInterwikiMagic or
901 Namespace::isTalk( $wgTitle->getNamespace() ) ) {
902 if ( "" == $text ) { $text = $link; }
903 $s .= $sk->makeLink( $link, $text, "", $trail );
904 } else if ( $pre != $wgLanguageCode ) {
905 array_push( $this->mLanguageLinks
, "$pre:$suf" );
909 # } else if ( 0 == strcmp( "##", substr( $link, 0, 2 ) ) ) {
910 # $link = substr( $link, 2 );
911 # $s .= "<a name=\"{$link}\">{$text}</a>{$trail}";
913 if ( "" == $text ) { $text = $link; }
914 $s .= $sk->makeLink( $link, $text, "", $trail );
922 # Some functions here used by doBlockLevels()
924 /* private */ function closeParagraph()
927 if ( 0 != strcmp( "p", $this->mLastSection
) &&
928 0 != strcmp( "", $this->mLastSection
) ) {
929 $result = "</" . $this->mLastSection
. ">";
931 $this->mLastSection
= "";
934 # getCommon() returns the length of the longest common substring
935 # of both arguments, starting at the beginning of both.
937 /* private */ function getCommon( $st1, $st2 )
939 $fl = strlen( $st1 );
940 $shorter = strlen( $st2 );
941 if ( $fl < $shorter ) { $shorter = $fl; }
943 for ( $i = 0; $i < $shorter; ++
$i ) {
944 if ( $st1{$i} != $st2{$i} ) { break; }
948 # These next three functions open, continue, and close the list
949 # element appropriate to the prefix character passed into them.
951 /* private */ function openList( $char )
953 $result = $this->closeParagraph();
955 if ( "*" == $char ) { $result .= "<ul><li>"; }
956 else if ( "#" == $char ) { $result .= "<ol><li>"; }
957 else if ( ":" == $char ) { $result .= "<dl><dd>"; }
958 else if ( ";" == $char ) {
959 $result .= "<dl><dt>";
960 $this->mDTopen
= true;
962 else { $result = "<!-- ERR 1 -->"; }
967 /* private */ function nextItem( $char )
969 if ( "*" == $char ||
"#" == $char ) { return "</li><li>"; }
970 else if ( ":" == $char ||
";" == $char ) {
972 if ( $this->mDTopen
) { $close = "</dt>"; }
973 if ( ";" == $char ) {
974 $this->mDTopen
= true;
975 return $close . "<dt>";
977 $this->mDTopen
= false;
978 return $close . "<dd>";
981 return "<!-- ERR 2 -->";
984 /* private */function closeList( $char )
986 if ( "*" == $char ) { return "</li></ul>"; }
987 else if ( "#" == $char ) { return "</li></ol>"; }
988 else if ( ":" == $char ) {
989 if ( $this->mDTopen
) {
990 $this->mDTopen
= false;
996 return "<!-- ERR 3 -->";
999 /* private */ function doBlockLevels( $text, $linestart )
1001 wfProfileIn( "OutputPage::doBlockLevels" );
1002 # Parsing through the text line by line. The main thing
1003 # happening here is handling of block-level elements p, pre,
1004 # and making lists from lines starting with * # : etc.
1006 $a = explode( "\n", $text );
1007 $text = $lastPref = "";
1008 $this->mDTopen
= $inBlockElem = false;
1010 if ( ! $linestart ) { $text .= array_shift( $a ); }
1011 foreach ( $a as $t ) {
1012 if ( "" != $text ) { $text .= "\n"; }
1015 $opl = strlen( $lastPref );
1016 $npl = strspn( $t, "*#:;" );
1017 $pref = substr( $t, 0, $npl );
1018 $pref2 = str_replace( ";", ":", $pref );
1019 $t = substr( $t, $npl );
1021 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
1022 $text .= $this->nextItem( substr( $pref, -1 ) );
1024 if ( ";" == substr( $pref, -1 ) ) {
1025 $cpos = strpos( $t, ":" );
1026 if ( ! ( false === $cpos ) ) {
1027 $term = substr( $t, 0, $cpos );
1028 $text .= $term . $this->nextItem( ":" );
1029 $t = substr( $t, $cpos +
1 );
1032 } else if (0 != $npl ||
0 != $opl) {
1033 $cpl = $this->getCommon( $pref, $lastPref );
1035 while ( $cpl < $opl ) {
1036 $text .= $this->closeList( $lastPref{$opl-1} );
1039 if ( $npl <= $cpl && $cpl > 0 ) {
1040 $text .= $this->nextItem( $pref{$cpl-1} );
1042 while ( $npl > $cpl ) {
1043 $char = substr( $pref, $cpl, 1 );
1044 $text .= $this->openList( $char );
1046 if ( ";" == $char ) {
1047 $cpos = strpos( $t, ":" );
1048 if ( ! ( false === $cpos ) ) {
1049 $term = substr( $t, 0, $cpos );
1050 $text .= $term . $this->nextItem( ":" );
1051 $t = substr( $t, $cpos +
1 );
1058 if ( 0 == $npl ) { # No prefix--go to paragraph mode
1060 "/(<table|<blockquote|<h1|<h2|<h3|<h4|<h5|<h6)/i", $t ) ) {
1061 $text .= $this->closeParagraph();
1062 $inBlockElem = true;
1064 if ( ! $inBlockElem ) {
1065 if ( " " == $t{0} ) {
1066 $newSection = "pre";
1067 # $t = wfEscapeHTML( $t );
1069 else { $newSection = "p"; }
1071 if ( 0 == strcmp( "", trim( $oLine ) ) ) {
1072 $text .= $this->closeParagraph();
1073 $text .= "<" . $newSection . ">";
1074 } else if ( 0 != strcmp( $this->mLastSection
,
1076 $text .= $this->closeParagraph();
1077 if ( 0 != strcmp( "p", $newSection ) ) {
1078 $text .= "<" . $newSection . ">";
1081 $this->mLastSection
= $newSection;
1083 if ( $inBlockElem &&
1084 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1085 $inBlockElem = false;
1091 $text .= $this->closeList( $pref2{$npl-1} );
1094 if ( "" != $this->mLastSection
) {
1095 if ( "p" != $this->mLastSection
) {
1096 $text .= "</" . $this->mLastSection
. ">";
1098 $this->mLastSection
= "";
1104 /* private */ function replaceVariables( $text )
1107 wfProfileIn( "OutputPage:replaceVariables" );
1109 /* As with sigs, use server's local time --
1110 ensure this is appropriate for your audience! */
1112 $mw =& MagicWord
::get( MAG_CURRENTMONTH
);
1113 $text = $mw->replace( $v, $text );
1115 $v = $wgLang->getMonthName( date( "n" ) );
1116 $mw =& MagicWord
::get( MAG_CURRENTMONTHNAME
);
1117 $text = $mw->replace( $v, $text );
1119 $v = $wgLang->getMonthNameGen( date( "n" ) );
1120 $mw =& MagicWord
::get( MAG_CURRENTMONTHNAMEGEN
);
1121 $text = $mw->replace( $v, $text );
1124 $mw = MagicWord
::get( MAG_CURRENTDAY
);
1125 $text = $mw->replace( $v, $text );
1127 $v = $wgLang->getWeekdayName( date( "w" )+
1 );
1128 $mw =& MagicWord
::get( MAG_CURRENTDAYNAME
);
1129 $text = $mw->replace( $v, $text );
1132 $mw =& MagicWord
::get( MAG_CURRENTYEAR
);
1133 $text = $mw->replace( $v, $text );
1135 $v = $wgLang->time( wfTimestampNow(), false );
1136 $mw =& MagicWord
::get( MAG_CURRENTTIME
);
1137 $text = $mw->replace( $v, $text );
1139 $mw =& MagicWord
::get( MAG_NUMBEROFARTICLES
);
1140 if ( $mw->match( $text ) ) {
1141 $v = wfNumberOfArticles();
1142 $text = $mw->replace( $v, $text );
1148 /* private */ function removeHTMLtags( $text )
1150 wfProfileIn( "OutputPage::removeHTMLtags" );
1151 $htmlpairs = array( # Tags that must be closed
1152 "b", "i", "u", "font", "big", "small", "sub", "sup", "h1",
1153 "h2", "h3", "h4", "h5", "h6", "cite", "code", "em", "s",
1154 "strike", "strong", "tt", "var", "div", "center",
1155 "blockquote", "ol", "ul", "dl", "table", "caption", "pre",
1156 "ruby", "rt" , "rb" , "rp"
1158 $htmlsingle = array(
1159 "br", "p", "hr", "li", "dt", "dd"
1161 $htmlnest = array( # Tags that can be nested--??
1162 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1163 "dl", "font", "big", "small", "sub", "sup"
1165 $tabletags = array( # Can only appear inside table
1169 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1170 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
1172 $htmlattrs = array( # Allowed attributes--no scripting, etc.
1173 "title", "align", "lang", "dir", "width", "height",
1174 "bgcolor", "clear", /* BR */ "noshade", /* HR */
1175 "cite", /* BLOCKQUOTE, Q */ "size", "face", "color",
1176 /* FONT */ "type", "start", "value", "compact",
1177 /* For various lists, mostly deprecated but safe */
1178 "summary", "width", "border", "frame", "rules",
1179 "cellspacing", "cellpadding", "valign", "char",
1180 "charoff", "colgroup", "col", "span", "abbr", "axis",
1181 "headers", "scope", "rowspan", "colspan", /* Tables */
1182 "id", "class", "name", "style" /* For CSS */
1185 # Remove HTML comments
1186 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1188 $bits = explode( "<", $text );
1189 $text = array_shift( $bits );
1190 $tagstack = array(); $tablestack = array();
1192 foreach ( $bits as $x ) {
1193 $prev = error_reporting( E_ALL
& ~
( E_NOTICE | E_WARNING
) );
1194 preg_match( "/^(\\/?)(\\w+)([^>]*)(\\/{0,1}>)([^<]*)$/",
1196 list( $qbar, $slash, $t, $params, $brace, $rest ) = $regs;
1197 error_reporting( $prev );
1200 if ( in_array( $t = strtolower( $t ), $htmlelements ) ) {
1204 if ( ! in_array( $t, $htmlsingle ) &&
1205 ( $ot = array_pop( $tagstack ) ) != $t ) {
1206 array_push( $tagstack, $ot );
1209 if ( $t == "table" ) {
1210 $tagstack = array_pop( $tablestack );
1215 # Keep track for later
1216 if ( in_array( $t, $tabletags ) &&
1217 ! in_array( "table", $tagstack ) ) {
1219 } else if ( in_array( $t, $tagstack ) &&
1220 ! in_array ( $t , $htmlnest ) ) {
1222 } else if ( ! in_array( $t, $htmlsingle ) ) {
1223 if ( $t == "table" ) {
1224 array_push( $tablestack, $tagstack );
1225 $tagstack = array();
1227 array_push( $tagstack, $t );
1229 # Strip non-approved attributes from the tag
1230 $newparams = preg_replace(
1231 "/(\\w+)(\\s*=\\s*([^\\s\">]+|\"[^\">]*\"))?/e",
1232 "(in_array(strtolower(\"\$1\"),\$htmlattrs)?(\"\$1\".((\"x\$3\" != \"x\")?\"=\$3\":'')):'')",
1236 $rest = str_replace( ">", ">", $rest );
1237 $text .= "<$slash$t$newparams$brace$rest";
1241 $text .= "<" . str_replace( ">", ">", $x);
1243 # Close off any remaining tags
1244 while ( $t = array_pop( $tagstack ) ) {
1246 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1255 * This function accomplishes several tasks:
1256 * 1) Auto-number headings if that option is enabled
1257 * 2) Add an [edit] link to sections for logged in users who have enabled the option
1258 * 3) Add a Table of contents on the top for users who have enabled the option
1259 * 4) Auto-anchor headings
1261 * It loops through all headlines, collects the necessary data, then splits up the
1262 * string and re-inserts the newly formatted headlines.
1265 /* private */ function formatHeadings( $text )
1267 global $wgUser,$wgArticle,$wgTitle,$wpPreview;
1268 $nh=$wgUser->getOption( "numberheadings" );
1269 $st=$wgUser->getOption( "showtoc" );
1270 if(!$wgTitle->userCanEdit()) {
1274 $es=$wgUser->getID() && $wgUser->getOption( "editsection" );
1275 $esr=$wgUser->getID() && $wgUser->getOption( "editsectiononrightclick" );
1277 # if the string __NOTOC__ (not case-sensitive) occurs in the HTML, do not
1279 $mw =& MagicWord
::get( MAG_NOTOC
);
1280 $st = ! $mw->matchAndRemove( $text );
1282 # never add the TOC to the Main Page. This is an entry page that should not
1283 # be more than 1-2 screens large anyway
1284 if($wgTitle->getPrefixedText()==wfMsg("mainpage")) {$st=0;}
1286 # We need this to perform operations on the HTML
1287 $sk=$wgUser->getSkin();
1289 # Get all headlines for numbering them and adding funky stuff like [edit]
1291 preg_match_all("/<H([1-6])(.*?>)(.*?)<\/H[1-6]>/i",$text,$matches);
1296 # Ugh .. the TOC should have neat indentation levels which can be
1297 # passed to the skin functions. These are determined here
1298 foreach($matches[3] as $headline) {
1299 if($level) { $prevlevel=$level;}
1300 $level=$matches[1][$c];
1301 if(($nh||
$st) && $prevlevel && $level>$prevlevel) {
1303 $h[$level]=0; // reset when we enter a new level
1304 $toc.=$sk->tocIndent($level-$prevlevel);
1305 $toclevel+
=$level-$prevlevel;
1308 if(($nh||
$st) && $level<$prevlevel) {
1309 $h[$level+
1]=0; // reset when we step back a level
1310 $toc.=$sk->tocUnindent($prevlevel-$level);
1311 $toclevel-=$prevlevel-$level;
1314 $h[$level]++
; // count number of headlines for each level
1317 for($i=1;$i<=$level;$i++
) {
1319 if($dot) {$numbering.=".";}
1327 $canonized_headline=preg_replace("/<.*?>/","",$headline); // strip out HTML
1328 $tocline=$canonized_headline;
1329 $canonized_headline=str_replace('"',"",$canonized_headline);
1330 $canonized_headline=str_replace(" ","_",trim($canonized_headline));
1331 $refer[$c]=$canonized_headline;
1332 $refers[$canonized_headline]++
; // count how many in assoc. array so we can track dupes in anchors
1333 $refcount[$c]=$refers[$canonized_headline];
1335 $tocline=$numbering ." ". $tocline;
1337 $headline=$numbering . " " . $headline; // the two are different if the line contains a link
1340 $anchor=$canonized_headline;
1341 if($refcount[$c]>1) {$anchor.="_".$refcount[$c];}
1343 $toc.=$sk->tocLine($anchor,$tocline,$toclevel);
1345 if($es && !isset($wpPreview)) {
1346 $head[$c].=$sk->editSectionLink($c+
1);
1348 $head[$c].="<H".$level.$matches[2][$c]
1349 ."<a name=\"".$anchor."\">"
1353 if($esr && !isset($wpPreview)) {
1354 $head[$c]=$sk->editSectionScript($c+
1,$head[$c]);
1363 $toc.=$sk->tocUnindent($toclevel);
1364 $toc=$sk->tocTable($toc);
1367 // split up and insert constructed headlines
1369 $blocks=preg_split("/<H[1-6].*?>.*?<\/H[1-6]>/i",$text);
1373 foreach($blocks as $block) {
1374 if(($es) && !isset($wpPreview) && $c>0 && $i==0) {
1375 # This is the [edit] link that appears for the top block of text when
1376 # section editing is enabled
1377 $full.=$sk->editSectionLink(0);
1380 if($st && $toclines>3 && !$i) {
1381 # Let's add a top anchor just in case we want to link to the top of the page
1382 $full="<a name=\"top\"></a>".$full.$toc;
1391 /* private */ function magicISBN( $text )
1395 $a = split( "ISBN ", " $text" );
1396 if ( count ( $a ) < 2 ) return $text;
1397 $text = substr( array_shift( $a ), 1);
1398 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1400 foreach ( $a as $x ) {
1401 $isbn = $blank = "" ;
1402 while ( " " == $x{0} ) {
1404 $x = substr( $x, 1 );
1406 while ( strstr( $valid, $x{0} ) != false ) {
1408 $x = substr( $x, 1 );
1410 $num = str_replace( "-", "", $isbn );
1411 $num = str_replace( " ", "", $num );
1414 $text .= "ISBN $blank$x";
1416 $text .= "<a href=\"" . wfLocalUrlE( $wgLang->specialPage(
1417 "Booksources"), "isbn={$num}" ) . "\" CLASS=\"internal\">ISBN $isbn</a>";
1424 /* private */ function magicRFC( $text )
1429 /* private */ function headElement()
1431 global $wgDocType, $wgDTD, $wgUser, $wgLanguageCode, $wgOutputEncoding, $wgLang;
1433 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
1435 if ( "" == $this->mHTMLtitle
) {
1436 $this->mHTMLtitle
= $this->mPagetitle
;
1438 $rtl = $wgLang->isRTL() ?
" dir='RTL'" : "";
1439 $ret .= "<html lang=\"$wgLanguageCode\"$rtl><head><title>{$this->mHTMLtitle}</title>\n";
1440 array_push( $this->mMetatags
, array( "http:Content-type", "text/html; charset={$wgOutputEncoding}" ) );
1441 foreach ( $this->mMetatags
as $tag ) {
1442 if ( 0 == strcasecmp( "http:", substr( $tag[0], 0, 5 ) ) ) {
1444 $tag[0] = substr( $tag[0], 5 );
1448 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1450 $p = $this->mRobotpolicy
;
1451 if ( "" == $p ) { $p = "index,follow"; }
1452 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1454 if ( count( $this->mKeywords
) > 0 ) {
1455 $ret .= "<meta name=\"keywords\" content=\"" .
1456 implode( ",", $this->mKeywords
) . "\">\n";
1458 foreach ( $this->mLinktags
as $tag ) {
1460 if ( "" != $tag[0] ) { $ret .= "rel=\"{$tag[0]}\" "; }
1461 if ( "" != $tag[1] ) { $ret .= "rev=\"{$tag[1]}\" "; }
1462 $ret .= "href=\"{$tag[2]}\">\n";
1464 $sk = $wgUser->getSkin();
1465 $ret .= $sk->getHeadScripts();
1466 $ret .= $sk->getUserStyles();
1468 $ret .= "</head>\n";