Support for [[Wikipedia:Book sources]] (or localized text), content of
[mediawiki.git] / includes / OutputPage.php
blob2c8255861b490e91526e42594d2b609b4de15c7e
1 <?
2 # See design.doc
4 function linkToMathImage ( $tex, $outputhash )
6 global $wgMathPath;
7 return "<img src=\"".$wgMathPath."/".$outputhash.".png\" alt=\"".wfEscapeHTML($tex)."\">";
10 function renderMath( $tex )
12 global $wgUser, $wgMathDirectory, $wgTmpDirectory, $wgInputEncoding;
13 $mf = wfMsg( "math_failure" );
14 $munk = wfMsg( "math_unknown_error" );
16 $fname = "renderMath";
18 $math = $wgUser->getOption("math");
19 if ($math == 3)
20 return ('$ '.wfEscapeHTML($tex).' $');
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."'";
29 $res = wfQuery( $sql, $fname );
30 if ( wfNumRows( $res ) == 0 )
32 $cmd = "./math/texvc ".escapeshellarg($wgTmpDirectory)." ".
33 escapeshellarg($wgMathDirectory)." ".escapeshellarg($tex)." ".escapeshellarg($wgInputEncoding);
34 $contents = `$cmd`;
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);
48 $i = strpos($outdata, "\000");
50 $outhtml = substr($outdata, 0, $i);
51 $mathml = substr($outdata, $i+1);
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>";
90 $outmd5 = substr ($contents, 1, 32);
91 if (!preg_match("/^[a-f0-9]{32}$/", $outmd5))
92 return "<b>".$mf." (".$munk."): ".wfEscapeHTML($tex)."</b>";
94 $outmd5_sql = mysql_escape_string(pack("H32", $outmd5));
96 $sql = "INSERT INTO math VALUES ('".$md5_sql."', '".$outmd5_sql."', ".$conservativeness.", ".$sql_html.", ".$sql_mathml.")";
98 $res = wfQuery( $sql, $fname );
99 # we don't really care if it fails
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'];
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;
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;
124 var $mDTopen, $mLastSection; # Used for processing DL, PRE
125 var $mLanguageLinks, $mSupressQuickbar;
127 function OutputPage()
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;
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; }
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 ) ); }
149 function checkLastModified ( $timestamp )
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;
157 if( $wgUser->getOption( "nocache" ) ) return;
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";
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;
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; }
190 function getLanguageLinks() {
191 global $wgUseNewInterlanguage, $wgTitle, $wgLanguageCode;
192 global $wgDBconnection, $wgDBname, $wgDBintlname;
194 if ( ! $wgUseNewInterlanguage )
195 return $this->mLanguageLinks;
197 mysql_select_db( $wgDBintlname, $wgDBconnection ) or die(
198 htmlspecialchars(mysql_error()) );
200 $list = array();
201 $sql = "SELECT * FROM ilinks WHERE lang_from=\"" .
202 "{$wgLanguageCode}\" AND title_from=\"" . $wgTitle->getDBkey() . "\"";
203 $res = mysql_query( $sql, $wgDBconnection );
205 while ( $q = mysql_fetch_object ( $res ) ) {
206 $list[] = $q->lang_to . ":" . $q->title_to;
208 mysql_free_result( $res );
209 mysql_select_db( $wgDBname, $wgDBconnection ) or die(
210 htmlspecialchars(mysql_error()) );
212 return $list;
215 function supressQuickbar() { $this->mSupressQuickbar = true; }
216 function isQuickbarSupressed() { return $this->mSupressQuickbar; }
218 function addHTML( $text ) { $this->mBodytext .= $text; }
219 function addHeadtext( $text ) { $this->mHeadtext .= $text; }
220 function debug( $text ) { $this->mDebugtext .= $text; }
222 # First pass--just handle <nowiki> sections, pass the rest off
223 # to doWikiPass2() which does all the real work.
226 function addWikiText( $text, $linestart = true )
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 = "";
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];
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];
269 } else {
270 $stripped2 = $stripped;
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];
286 $text = $this->doWikiPass2( $stripped3, $linestart );
288 for ( $i = 1; $i <= $presecs; ++$i ) {
289 $text = preg_replace( "/{$unique3}/", str_replace( '$', '\$', $prelist[$i] ), $text, 1 );
292 for ( $i = 1; $i <= $mathsecs; ++$i ) {
293 $text = preg_replace( "/{$unique2}/", str_replace( '$', '\$', $mathlist[$i] ), $text, 1 );
296 for ( $i = 1; $i <= $nwsecs; ++$i ) {
297 $text = preg_replace( "/{$unique}/", str_replace( '$', '\$', $nwlist[$i] ), $text, 1 );
299 $this->addHTML( $text );
300 wfProfileOut();
303 # Finally, all the text has been munged and accumulated into
304 # the object, let's actually output it:
306 function output()
308 global $wgUser, $wgLang, $wgDebugComments, $wgCookieExpiration;
309 global $wgInputEncoding, $wgOutputEncoding, $wgLanguageCode;
310 wfProfileIn( "OutputPage::output" );
311 $sk = $wgUser->getSkin();
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" );
322 header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
324 header( "Content-type: text/html; charset={$wgOutputEncoding}" );
325 header( "Content-language: {$wgLanguageCode}" );
327 if ( "" != $this->mRedirect ) {
328 header( "Location: {$this->mRedirect}" );
329 wfProfileOut();
330 return;
333 $exp = time() + $wgCookieExpiration;
334 foreach( $this->mCookies as $name => $val ) {
335 setcookie( $name, $val, $exp, "/" );
337 wfProfileOut();
339 wfProfileIn( "OutputPage::output-middle" );
340 $sk->initPage();
341 $this->out( $this->headElement() );
343 $this->out( "\n<body" );
344 $ops = $sk->getBodyOptions();
345 foreach ( $ops as $name => $val ) {
346 $this->out( " $name='$val'" );
348 $this->out( ">\n" );
349 if ( $wgDebugComments ) {
350 $this->out( "<!-- Wiki debugging output:\n" .
351 $this->mDebugtext . "-->\n" );
353 $this->out( $sk->beforeContent() );
354 wfProfileOut();
356 wfProfileIn( "OutputPage::output-bodytext" );
357 $this->out( $this->mBodytext );
358 wfProfileOut();
359 wfProfileIn( "OutputPage::output-after" );
360 $this->out( $sk->afterContent() );
361 wfProfileOut();
363 wfProfileOut(); # A hack - we can't report after here
364 $this->out( $this->reportTime() );
366 $this->out( "\n</body></html>" );
367 flush();
370 function out( $ins )
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; }
379 print $outs;
382 function setEncodings()
384 global $HTTP_SERVER_VARS, $wgInputEncoding, $wgOutputEncoding;
385 global $wgUser, $wgLang;
387 $wgInputEncoding = strtolower( $wgInputEncoding );
388 $s = $HTTP_SERVER_VARS['HTTP_ACCEPT_CHARSET'];
390 if( $wgUser->getOption( 'altencoding' ) ) {
391 $wgLang->setAltEncoding();
392 return;
395 if ( "" == $s ) {
396 $wgOutputEncoding = strtolower( $wgOutputEncoding );
397 return;
399 $a = explode( ",", $s );
400 $best = 0.0;
401 $bestset = "*";
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;
411 if ( $q > $best ) {
412 $bestset = $set;
413 $best = $q;
416 #if ( "*" == $bestset ) { $bestset = "iso-8859-1"; }
417 if ( "*" == $bestset ) { $bestset = $wgOutputEncoding; }
418 $wgOutputEncoding = strtolower( $bestset );
420 # Disable for now
422 $wgOutputEncoding = $wgInputEncoding;
425 function reportTime()
427 global $wgRequestTime, $wgDebugLogFile, $HTTP_SERVER_VARS;
428 global $wgProfiling, $wgProfileStack, $wgUser;
430 list( $usec, $sec ) = explode( " ", microtime() );
431 $now = (float)$sec + (float)$usec;
433 list( $usec, $sec ) = explode( " ", $wgRequestTime );
434 $start = (float)$sec + (float)$usec;
435 $elapsed = $now - $start;
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;
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";
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 );
475 $com = sprintf( "<!-- Time since request: %01.2f secs. -->",
476 $elapsed );
477 return $com;
480 # Note: these arguments are keys into wfMsg(), not text!
482 function errorpage( $title, $msg )
484 global $wgTitle;
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 );
493 $this->mBodytext = "";
494 $this->addHTML( "<p>" . wfMsg( $msg ) . "\n" );
495 $this->returnToMain( false );
497 $this->output();
498 exit;
501 function sysopRequired()
503 global $wgUser;
505 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
506 $this->setPageTitle( wfMsg( "sysoptitle" ) );
507 $this->setRobotpolicy( "noindex,nofollow" );
508 $this->setArticleFlag( false );
509 $this->mBodytext = "";
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();
518 function developerRequired()
520 global $wgUser;
522 $this->setHTMLTitle( wfMsg( "errorpagetitle" ) );
523 $this->setPageTitle( wfMsg( "developertitle" ) );
524 $this->setRobotpolicy( "noindex,nofollow" );
525 $this->setArticleFlag( false );
526 $this->mBodytext = "";
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();
535 function databaseError( $fname )
537 global $wgUser, $wgCommandLineMode;
539 $this->setPageTitle( wfMsg( "databaseerror" ) );
540 $this->setRobotpolicy( "noindex,nofollow" );
541 $this->setArticleFlag( false );
543 if ( $wgCommandLineMode ) {
544 $msg = wfMsg( "dberrortextcl" );
545 } else {
546 $msg = wfMsg( "dberrortextcl" );
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 );
553 if ( $wgCommandLineMode ) {
554 print $msg;
555 exit();
557 $sk = $wgUser->getSkin();
558 $shlink = $sk->makeKnownLink( wfMsg( "searchhelppage" ),
559 wfMsg( "searchingwikipedia" ) );
560 $msg = str_replace( "$5", $shlink, $msg );
562 $this->mBodytext = $msg;
563 $this->output();
564 exit();
567 function readOnlyPage()
569 global $wgUser, $wgReadOnlyFile;
571 $this->setPageTitle( wfMsg( "readonly" ) );
572 $this->setRobotpolicy( "noindex,nofollow" );
573 $this->setArticleFlag( false );
575 $reason = implode( "", file( $wgReadOnlyFile ) );
576 $text = str_replace( "$1", $reason, wfMsg( "readonlytext" ) );
577 $this->addHTML( $text );
578 $this->returnToMain( false );
581 function fatalError( $message )
583 $this->setPageTitle( wfMsg( "internalerror" ) );
584 $this->setRobotpolicy( "noindex,nofollow" );
585 $this->setArticleFlag( false );
587 $this->mBodytext = $message;
588 $this->output();
589 exit;
592 function unexpectedValueError( $name, $val )
594 $msg = str_replace( "$1", $name, wfMsg( "unexpected" ) );
595 $msg = str_replace( "$2", $val, $msg );
596 $this->fatalError( $msg );
599 function fileCopyError( $old, $new )
601 $msg = str_replace( "$1", $old, wfMsg( "filecopyerror" ) );
602 $msg = str_replace( "$2", $new, $msg );
603 $this->fatalError( $msg );
606 function fileRenameError( $old, $new )
608 $msg = str_replace( "$1", $old, wfMsg( "filerenameerror" ) );
609 $msg = str_replace( "$2", $new, $msg );
610 $this->fatalError( $msg );
613 function fileDeleteError( $name )
615 $msg = str_replace( "$1", $name, wfMsg( "filedeleteerror" ) );
616 $this->fatalError( $msg );
619 function fileNotFoundError( $name )
621 $msg = str_replace( "$1", $name, wfMsg( "filenotfound" ) );
622 $this->fatalError( $msg );
625 function returnToMain( $auto = true )
627 global $wgUser, $wgOut, $returnto;
629 $sk = $wgUser->getSkin();
630 if ( "" == $returnto ) {
631 $returnto = wfMsg( "mainpage" );
633 $link = $sk->makeKnownLink( $returnto, "" );
635 $r = str_replace( "$1", $link, wfMsg( "returnto" ) );
636 if ( $auto ) {
637 $wgOut->addMeta( "http:Refresh", "10;url=" .
638 wfLocalUrlE( wfUrlencode( $returnto ) ) );
640 $wgOut->addHTML( "\n<p>$r\n" );
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.
647 function doWikiPass2( $text, $linestart )
649 global $wgUser;
650 wfProfileIn( "OutputPage::doWikiPass2" );
652 $text = $this->removeHTMLtags( $text );
653 $text = $this->replaceVariables( $text );
655 $text = preg_replace( "/(^|\n)-----*/", "\\1<hr>", $text );
656 $text = str_replace ( "<HR>", "<hr>", $text );
658 $text = $this->doQuotes( $text );
659 $text = $this->doHeadings( $text );
660 $text = $this->doBlockLevels( $text, $linestart );
662 $text = $this->replaceExternalLinks( $text );
663 $text = $this->replaceInternalLinks ( $text );
665 $text = $this->magicISBN( $text );
666 $text = $this->magicRFC( $text );
667 $text = $this->autoNumberHeadings( $text );
669 $sk = $wgUser->getSkin();
670 $text = $sk->transformContent( $text );
672 wfProfileOut();
673 return $text;
676 /* private */ function doQuotes( $text )
678 $text = preg_replace( "/'''(.+)'''/mU", "<strong>\$1</strong>", $text );
679 $text = preg_replace( "/''(.+)''/mU", "<em>\$1</em>", $text );
680 return $text;
683 /* private */ function doHeadings( $text )
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 );
690 return $text;
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.
697 /* private */ function replaceExternalLinks( $text )
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;
710 /* private */ function subReplaceExternalLinks( $s, $protocol, $autonumber )
712 global $wgUser, $printable;
713 global $wgAllowExternalImages;
716 $unique = "4jzAfzB8hNvf4sqyO9Edd8pSmk9rE2in0Tgw3";
717 $uc = "A-Za-z0-9_\\/~%\\-+&*#?!=()@\\x80-\\xFF";
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";
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}]|$)/";
734 $e2 = "/(^|[^\\[])({$protocol}:)(([".$uc."]|[".$sep."][".$uc."])+)([^". $uc . $sep. "]|[".$sep."]|$)/";
735 $sk = $wgUser->getSkin();
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 );
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 );
747 $a = explode( "[{$protocol}:", " " . $s );
748 $s = array_shift( $a );
749 $s = substr( $s, 1 );
751 $e1 = "/^([{$uc}"."{$sep}]+)](.*)\$/sD";
752 $e2 = "/^([{$uc}"."{$sep}]+)\\s+([^\\]]+)](.*)\$/sD";
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;
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}";
774 return $s;
777 /* private */ function replaceInternalLinks( $s )
779 global $wgTitle, $wgUser, $wgLang;
780 global $wgLinkCache, $wgInterwikiMagic;
781 global $wgNamespacesWithSubpages;
782 wfProfileIn( $fname = "OutputPage::replaceInternalLinks" );
784 wfProfileIn( "$fname-setup" );
785 $tc = Title::legalChars() . "#";
786 $sk = $wgUser->getSkin();
788 $a = explode( "[[", " " . $s );
789 $s = array_shift( $a );
790 $s = substr( $s, 1 );
792 $e1 = "/^([{$tc}]+)\\|([^]]+)]](.*)\$/sD";
793 $e2 = "/^([{$tc}]+)]](.*)\$/sD";
794 wfProfileOut();
796 wfProfileIn( "$fname-loop" );
797 foreach ( $a as $line ) {
798 if ( preg_match( $e1, $line, $m ) ) { # page with alternate text
800 $text = $m[2];
801 $trail = $m[3];
803 } else if ( preg_match( $e2, $line, $m ) ) { # page with normal text
805 $text = "";
806 $trail = $m[2];
809 else { # Invalid form; output directly
810 $s .= "[[" . $line ;
811 continue;
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];
818 } else {
819 $noslash=substr($m[1],1);
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
829 } else { # no subpage
830 $link = $m[1];
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(); }
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(); }
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;
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 );
874 wfProfileOut();
875 wfProfileOut();
876 return $s;
879 # Some functions here used by doBlockLevels()
881 /* private */ function closeParagraph()
883 $result = "";
884 if ( 0 != strcmp( "p", $this->mLastSection ) &&
885 0 != strcmp( "", $this->mLastSection ) ) {
886 $result = "</" . $this->mLastSection . ">";
888 $this->mLastSection = "";
889 return $result;
891 # getCommon() returns the length of the longest common substring
892 # of both arguments, starting at the beginning of both.
894 /* private */ function getCommon( $st1, $st2 )
896 $fl = strlen( $st1 );
897 $shorter = strlen( $st2 );
898 if ( $fl < $shorter ) { $shorter = $fl; }
900 for ( $i = 0; $i < $shorter; ++$i ) {
901 if ( $st1{$i} != $st2{$i} ) { break; }
903 return $i;
905 # These next three functions open, continue, and close the list
906 # element appropriate to the prefix character passed into them.
908 /* private */ function openList( $char )
910 $result = $this->closeParagraph();
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;
919 else { $result = "<!-- ERR 1 -->"; }
921 return $result;
924 /* private */ function nextItem( $char )
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>";
938 return "<!-- ERR 2 -->";
941 /* private */function closeList( $char )
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>";
953 return "<!-- ERR 3 -->";
956 /* private */ function doBlockLevels( $text, $linestart )
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.
963 $a = explode( "\n", $text );
964 $text = $lastPref = "";
965 $this->mDTopen = $inBlockElem = false;
967 if ( ! $linestart ) { $text .= array_shift( $a ); }
968 foreach ( $a as $t ) {
969 if ( "" != $text ) { $text .= "\n"; }
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 );
978 if ( 0 != $npl && 0 == strcmp( $lastPref, $pref2 ) ) {
979 $text .= $this->nextItem( substr( $pref, -1 ) );
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 );
989 } else if (0 != $npl || 0 != $opl) {
990 $cpl = $this->getCommon( $pref, $lastPref );
992 while ( $cpl < $opl ) {
993 $text .= $this->closeList( $lastPref{$opl-1} );
994 --$opl;
996 if ( $npl <= $cpl && $cpl > 0 ) {
997 $text .= $this->nextItem( $pref{$cpl-1} );
999 while ( $npl > $cpl ) {
1000 $char = substr( $pref, $cpl, 1 );
1001 $text .= $this->openList( $char );
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 );
1011 ++$cpl;
1013 $lastPref = $pref2;
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;
1021 if ( ! $inBlockElem ) {
1022 if ( " " == $t{0} ) {
1023 $newSection = "pre";
1024 # $t = wfEscapeHTML( $t );
1026 else { $newSection = "p"; }
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 . ">";
1038 $this->mLastSection = $newSection;
1040 if ( $inBlockElem &&
1041 preg_match( "/(<\\/table|<\\/blockquote|<\\/h1|<\\/h2|<\\/h3|<\\/h4|<\\/h5|<\\/h6)/i", $t ) ) {
1042 $inBlockElem = false;
1045 $text .= $t;
1047 while ( $npl ) {
1048 $text .= $this->closeList( $pref2{$npl-1} );
1049 --$npl;
1051 if ( "" != $this->mLastSection ) {
1052 if ( "p" != $this->mLastSection ) {
1053 $text .= "</" . $this->mLastSection . ">";
1055 $this->mLastSection = "";
1057 wfProfileOut();
1058 return $text;
1061 /* private */ function replaceVariables( $text )
1063 global $wgLang;
1064 wfProfileIn( "OutputPage:replaceVariables" );
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 );
1081 if ( false !== strstr( $text, "{{NUMBEROFARTICLES}}" ) ) {
1082 $v = wfNumberOfArticles();
1083 $text = str_replace( "{{NUMBEROFARTICLES}}", $v, $text );
1085 wfProfileOut();
1086 return $text;
1089 /* private */ function removeHTMLtags( $text )
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"
1099 $htmlsingle = array(
1100 "br", "p", "hr", "li", "dt", "dd"
1102 $htmlnest = array( # Tags that can be nested--??
1103 "table", "tr", "td", "th", "div", "blockquote", "ol", "ul",
1104 "dl", "font", "big", "small", "sub", "sup"
1106 $tabletags = array( # Can only appear inside table
1107 "td", "th", "tr"
1110 $htmlsingle = array_merge( $tabletags, $htmlsingle );
1111 $htmlelements = array_merge( $htmlsingle, $htmlpairs );
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 */
1126 # Remove HTML comments
1127 $text = preg_replace( "/<!--.*-->/sU", "", $text );
1129 $bits = explode( "<", $text );
1130 $text = array_shift( $bits );
1131 $tagstack = array(); $tablestack = array();
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 );
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 );
1153 $newparams = "";
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();
1168 array_push( $tagstack, $t );
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);
1176 if ( ! $badtag ) {
1177 $rest = str_replace( ">", "&gt;", $rest );
1178 $text .= "<$slash$t$newparams$brace$rest";
1179 continue;
1182 $text .= "&lt;" . str_replace( ">", "&gt;", $x);
1184 # Close off any remaining tags
1185 while ( $t = array_pop( $tagstack ) ) {
1186 $text .= "</$t>\n";
1187 if ( $t == "table" ) { $tagstack = array_pop( $tablestack ); }
1189 wfProfileOut();
1190 return $text;
1193 /* private */ function autoNumberHeadings( $text )
1195 global $wgUser;
1196 if ( 1 != $wgUser->getOption( "numberheadings" ) ) {
1197 return $text;
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;
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 );
1227 return $t . $text;
1230 /* private */ function magicISBN( $text )
1232 global $wgLang;
1234 $a = split( "ISBN ", " $text" );
1235 if ( count ( $a ) < 2 ) return $text;
1236 $text = substr( array_shift( $a ), 1);
1237 $valid = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1239 foreach ( $a as $x ) {
1240 $isbn = $blank = "" ;
1241 while ( " " == $x{0} ) {
1242 $blank .= " ";
1243 $x = substr( $x, 1 );
1245 while ( strstr( $valid, $x{0} ) != false ) {
1246 $isbn .= $x{0};
1247 $x = substr( $x, 1 );
1249 $num = str_replace( "-", "", $isbn );
1250 $num = str_replace( " ", "", $num );
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;
1260 return $text;
1263 /* private */ function magicRFC( $text )
1265 return $text;
1268 /* private */ function headElement()
1270 global $wgDocType, $wgUser, $wgLanguageCode, $wgOutputEncoding;
1272 $ret = "<!DOCTYPE HTML PUBLIC \"$wgDocType\">\n";
1274 if ( "" == $this->mHTMLtitle ) {
1275 $this->mHTMLtitle = $this->mPagetitle;
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";
1286 $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\">\n";
1288 $p = $this->mRobotpolicy;
1289 if ( "" == $p ) { $p = "index,follow"; }
1290 $ret .= "<meta name=\"robots\" content=\"$p\">\n";
1292 if ( count( $this->mKeywords ) > 0 ) {
1293 $ret .= "<meta name=\"keywords\" content=\"" .
1294 implode( ",", $this->mKeywords ) . "\">\n";
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";
1302 $sk = $wgUser->getSkin();
1303 $ret .= $sk->getHeadScripts();
1304 $ret .= $sk->getUserStyles();
1306 $ret .= "</head>\n";
1307 return $ret;