00001 <?php
00002 if ( ! defined( 'MEDIAWIKI' ) )
00003 die( 1 );
00004
00008 class OutputPage {
00009 var $mMetatags = array(), $mKeywords = array(), $mLinktags = array();
00010 var $mExtStyles = array();
00011 var $mPagetitle = '', $mBodytext = '', $mDebugtext = '';
00012 var $mHTMLtitle = '', $mIsarticle = true, $mPrintable = false;
00013 var $mSubtitle = '', $mRedirect = '', $mStatusCode;
00014 var $mLastModified = '', $mETag = false;
00015 var $mCategoryLinks = array(), $mLanguageLinks = array();
00016 var $mScripts = '', $mLinkColours, $mPageLinkTitle = '', $mHeadItems = array();
00017 var $mTemplateIds = array();
00018
00019 var $mAllowUserJs;
00020 var $mSuppressQuickbar = false;
00021 var $mOnloadHandler = '';
00022 var $mDoNothing = false;
00023 var $mContainsOldMagic = 0, $mContainsNewMagic = 0;
00024 var $mIsArticleRelated = true;
00025 protected $mParserOptions = null;
00026 var $mShowFeedLinks = false;
00027 var $mFeedLinksAppendQuery = false;
00028 var $mEnableClientCache = true;
00029 var $mArticleBodyOnly = false;
00030
00031 var $mNewSectionLink = false;
00032 var $mHideNewSectionLink = false;
00033 var $mNoGallery = false;
00034 var $mPageTitleActionText = '';
00035 var $mParseWarnings = array();
00036 var $mSquidMaxage = 0;
00037 var $mRevisionId = null;
00038
00044 var $styles = array();
00045
00046 private $mIndexPolicy = 'index';
00047 private $mFollowPolicy = 'follow';
00048
00053 function __construct() {
00054 global $wgAllowUserJs;
00055 $this->mAllowUserJs = $wgAllowUserJs;
00056 }
00057
00058 public function redirect( $url, $responsecode = '302' ) {
00059 # Strip newlines as a paranoia check for header injection in PHP<5.1.2
00060 $this->mRedirect = str_replace( "\n", '', $url );
00061 $this->mRedirectCode = $responsecode;
00062 }
00063
00064 public function getRedirect() {
00065 return $this->mRedirect;
00066 }
00067
00074 function setStatusCode( $statusCode ) { $this->mStatusCode = $statusCode; }
00075
00083 function addMeta( $name, $val ) {
00084 array_push( $this->mMetatags, array( $name, $val ) );
00085 }
00086
00087 function addKeyword( $text ) { array_push( $this->mKeywords, $text ); }
00088 function addScript( $script ) { $this->mScripts .= "\t\t".$script; }
00089
00090 function addExtensionStyle( $url ) {
00091 $linkarr = array( 'rel' => 'stylesheet', 'href' => $url, 'type' => 'text/css' );
00092 array_push( $this->mExtStyles, $linkarr );
00093 }
00094
00099 function addScriptFile( $file ) {
00100 global $wgStylePath, $wgStyleVersion, $wgJsMimeType;
00101 if( substr( $file, 0, 1 ) == '/' ) {
00102 $path = $file;
00103 } else {
00104 $path = "{$wgStylePath}/common/{$file}";
00105 }
00106 $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"$path?$wgStyleVersion\"></script>\n" );
00107 }
00108
00113 function addInlineScript( $script ) {
00114 global $wgJsMimeType;
00115 $this->mScripts .= "<script type=\"$wgJsMimeType\">/*<![CDATA[*/\n$script\n/*]]>*/</script>";
00116 }
00117
00118 function getScript() {
00119 return $this->mScripts . $this->getHeadItems();
00120 }
00121
00122 function getHeadItems() {
00123 $s = '';
00124 foreach ( $this->mHeadItems as $item ) {
00125 $s .= $item;
00126 }
00127 return $s;
00128 }
00129
00130 function addHeadItem( $name, $value ) {
00131 $this->mHeadItems[$name] = $value;
00132 }
00133
00134 function hasHeadItem( $name ) {
00135 return isset( $this->mHeadItems[$name] );
00136 }
00137
00138 function setETag($tag) { $this->mETag = $tag; }
00139 function setArticleBodyOnly($only) { $this->mArticleBodyOnly = $only; }
00140 function getArticleBodyOnly($only) { return $this->mArticleBodyOnly; }
00141
00142 function addLink( $linkarr ) {
00143 # $linkarr should be an associative array of attributes. We'll escape on output.
00144 array_push( $this->mLinktags, $linkarr );
00145 }
00146
00147 # Get all links added by extensions
00148 function getExtStyle() {
00149 return $this->mExtStyles;
00150 }
00151
00152 function addMetadataLink( $linkarr ) {
00153 # note: buggy CC software only reads first "meta" link
00154 static $haveMeta = false;
00155 $linkarr['rel'] = ($haveMeta) ? 'alternate meta' : 'meta';
00156 $this->addLink( $linkarr );
00157 $haveMeta = true;
00158 }
00159
00169 function checkLastModified( $timestamp ) {
00170 global $wgCachePages, $wgCacheEpoch, $wgUser, $wgRequest;
00171
00172 if ( !$timestamp || $timestamp == '19700101000000' ) {
00173 wfDebug( __METHOD__ . ": CACHE DISABLED, NO TIMESTAMP\n" );
00174 return false;
00175 }
00176 if( !$wgCachePages ) {
00177 wfDebug( __METHOD__ . ": CACHE DISABLED\n", false );
00178 return false;
00179 }
00180 if( $wgUser->getOption( 'nocache' ) ) {
00181 wfDebug( __METHOD__ . ": USER DISABLED CACHE\n", false );
00182 return false;
00183 }
00184
00185 $timestamp = wfTimestamp( TS_MW, $timestamp );
00186 $modifiedTimes = array(
00187 'page' => $timestamp,
00188 'user' => $wgUser->getTouched(),
00189 'epoch' => $wgCacheEpoch
00190 );
00191 wfRunHooks( 'OutputPageCheckLastModified', array( &$modifiedTimes ) );
00192
00193 $maxModified = max( $modifiedTimes );
00194 $this->mLastModified = wfTimestamp( TS_RFC2822, $maxModified );
00195
00196 if( empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
00197 wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", false );
00198 return false;
00199 }
00200
00201 # Make debug info
00202 $info = '';
00203 foreach ( $modifiedTimes as $name => $value ) {
00204 if ( $info !== '' ) {
00205 $info .= ', ';
00206 }
00207 $info .= "$name=" . wfTimestamp( TS_ISO_8601, $value );
00208 }
00209
00210 # IE sends sizes after the date like this:
00211 # Wed, 20 Aug 2003 06:51:19 GMT; length=5202
00212 # this breaks strtotime().
00213 $clientHeader = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
00214
00215 wfSuppressWarnings();
00216 $clientHeaderTime = strtotime( $clientHeader );
00217 wfRestoreWarnings();
00218 if ( !$clientHeaderTime ) {
00219 wfDebug( __METHOD__ . ": unable to parse the client's If-Modified-Since header: $clientHeader\n" );
00220 return false;
00221 }
00222 $clientHeaderTime = wfTimestamp( TS_MW, $clientHeaderTime );
00223
00224 wfDebug( __METHOD__ . ": client sent If-Modified-Since: " .
00225 wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", false );
00226 wfDebug( __METHOD__ . ": effective Last-Modified: " .
00227 wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", false );
00228 if( $clientHeaderTime < $maxModified ) {
00229 wfDebug( __METHOD__ . ": STALE, $info\n", false );
00230 return false;
00231 }
00232
00233 # Not modified
00234 # Give a 304 response code and disable body output
00235 wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", false );
00236 ini_set('zlib.output_compression', 0);
00237 $wgRequest->response()->header( "HTTP/1.1 304 Not Modified" );
00238 $this->sendCacheControl();
00239 $this->disable();
00240
00241
00242
00243
00244 wfClearOutputBuffers();
00245
00246 return true;
00247 }
00248
00249 function setPageTitleActionText( $text ) {
00250 $this->mPageTitleActionText = $text;
00251 }
00252
00253 function getPageTitleActionText () {
00254 if ( isset( $this->mPageTitleActionText ) ) {
00255 return $this->mPageTitleActionText;
00256 }
00257 }
00258
00267 public function setRobotPolicy( $policy ) {
00268 $policy = explode( ',', $policy );
00269 $policy = array_map( 'trim', $policy );
00270
00271 # The default policy is follow, so if nothing is said explicitly, we
00272 # do that.
00273 if( in_array( 'nofollow', $policy ) ) {
00274 $this->mFollowPolicy = 'nofollow';
00275 } else {
00276 $this->mFollowPolicy = 'follow';
00277 }
00278
00279 if( in_array( 'noindex', $policy ) ) {
00280 $this->mIndexPolicy = 'noindex';
00281 } else {
00282 $this->mIndexPolicy = 'index';
00283 }
00284 }
00285
00293 public function setIndexPolicy( $policy ) {
00294 $policy = trim( $policy );
00295 if( in_array( $policy, array( 'index', 'noindex' ) ) ) {
00296 $this->mIndexPolicy = $policy;
00297 }
00298 }
00299
00307 public function setFollowPolicy( $policy ) {
00308 $policy = trim( $policy );
00309 if( in_array( $policy, array( 'follow', 'nofollow' ) ) ) {
00310 $this->mFollowPolicy = $policy;
00311 }
00312 }
00313
00314 public function setHTMLTitle( $name ) { $this->mHTMLtitle = $name; }
00315 public function setPageTitle( $name ) {
00316 global $wgContLang;
00317 $name = $wgContLang->convert( $name, true );
00318 $this->mPagetitle = $name;
00319
00320 $taction = $this->getPageTitleActionText();
00321 if( !empty( $taction ) ) {
00322 $name .= ' - '.$taction;
00323 }
00324
00325 $this->setHTMLTitle( wfMsg( 'pagetitle', $name ) );
00326 }
00327
00328 public function getHTMLTitle() { return $this->mHTMLtitle; }
00329 public function getPageTitle() { return $this->mPagetitle; }
00330 public function setSubtitle( $str ) { $this->mSubtitle = $str; }
00331 public function appendSubtitle( $str ) { $this->mSubtitle .= $str; }
00332 public function getSubtitle() { return $this->mSubtitle; }
00333 public function isArticle() { return $this->mIsarticle; }
00334 public function setPrintable() { $this->mPrintable = true; }
00335 public function isPrintable() { return $this->mPrintable; }
00336 public function setSyndicated( $show = true ) { $this->mShowFeedLinks = $show; }
00337 public function isSyndicated() { return $this->mShowFeedLinks; }
00338 public function setFeedAppendQuery( $val ) { $this->mFeedLinksAppendQuery = $val; }
00339 public function getFeedAppendQuery() { return $this->mFeedLinksAppendQuery; }
00340 public function setOnloadHandler( $js ) { $this->mOnloadHandler = $js; }
00341 public function getOnloadHandler() { return $this->mOnloadHandler; }
00342 public function disable() { $this->mDoNothing = true; }
00343 public function isDisabled() { return $this->mDoNothing; }
00344
00345 public function setArticleRelated( $v ) {
00346 $this->mIsArticleRelated = $v;
00347 if ( !$v ) {
00348 $this->mIsarticle = false;
00349 }
00350 }
00351 public function setArticleFlag( $v ) {
00352 $this->mIsarticle = $v;
00353 if ( $v ) {
00354 $this->mIsArticleRelated = $v;
00355 }
00356 }
00357
00358 public function isArticleRelated() { return $this->mIsArticleRelated; }
00359
00360 public function getLanguageLinks() { return $this->mLanguageLinks; }
00361 public function addLanguageLinks($newLinkArray) {
00362 $this->mLanguageLinks += $newLinkArray;
00363 }
00364 public function setLanguageLinks($newLinkArray) {
00365 $this->mLanguageLinks = $newLinkArray;
00366 }
00367
00368 public function getCategoryLinks() {
00369 return $this->mCategoryLinks;
00370 }
00371
00375 public function addCategoryLinks( $categories ) {
00376 global $wgUser, $wgContLang;
00377
00378 if ( !is_array( $categories ) || count( $categories ) == 0 ) {
00379 return;
00380 }
00381
00382 # Add the links to a LinkBatch
00383 $arr = array( NS_CATEGORY => $categories );
00384 $lb = new LinkBatch;
00385 $lb->setArray( $arr );
00386
00387 # Fetch existence plus the hiddencat property
00388 $dbr = wfGetDB( DB_SLAVE );
00389 $pageTable = $dbr->tableName( 'page' );
00390 $where = $lb->constructSet( 'page', $dbr );
00391 $propsTable = $dbr->tableName( 'page_props' );
00392 $sql = "SELECT page_id, page_namespace, page_title, page_len, page_is_redirect, pp_value
00393 FROM $pageTable LEFT JOIN $propsTable ON pp_propname='hiddencat' AND pp_page=page_id WHERE $where";
00394 $res = $dbr->query( $sql, __METHOD__ );
00395
00396 # Add the results to the link cache
00397 $lb->addResultToCache( LinkCache::singleton(), $res );
00398
00399 # Set all the values to 'normal'. This can be done with array_fill_keys in PHP 5.2.0+
00400 $categories = array_combine( array_keys( $categories ),
00401 array_fill( 0, count( $categories ), 'normal' ) );
00402
00403 # Mark hidden categories
00404 foreach ( $res as $row ) {
00405 if ( isset( $row->pp_value ) ) {
00406 $categories[$row->page_title] = 'hidden';
00407 }
00408 }
00409
00410 # Add the remaining categories to the skin
00411 if ( wfRunHooks( 'OutputPageMakeCategoryLinks', array( &$this, $categories, &$this->mCategoryLinks ) ) ) {
00412 $sk = $wgUser->getSkin();
00413 foreach ( $categories as $category => $type ) {
00414 $origcategory = $category;
00415 $title = Title::makeTitleSafe( NS_CATEGORY, $category );
00416 $wgContLang->findVariantLink( $category, $title, true );
00417 if ( $category != $origcategory )
00418 if ( array_key_exists( $category, $categories ) )
00419 continue;
00420 $text = $wgContLang->convertHtml( $title->getText() );
00421 $this->mCategoryLinks[$type][] = $sk->makeLinkObj( $title, $text );
00422 }
00423 }
00424 }
00425
00426 public function setCategoryLinks($categories) {
00427 $this->mCategoryLinks = array();
00428 $this->addCategoryLinks($categories);
00429 }
00430
00431 public function suppressQuickbar() { $this->mSuppressQuickbar = true; }
00432 public function isQuickbarSuppressed() { return $this->mSuppressQuickbar; }
00433
00434 public function disallowUserJs() { $this->mAllowUserJs = false; }
00435 public function isUserJsAllowed() { return $this->mAllowUserJs; }
00436
00437 public function prependHTML( $text ) { $this->mBodytext = $text . $this->mBodytext; }
00438 public function addHTML( $text ) { $this->mBodytext .= $text; }
00439 public function clearHTML() { $this->mBodytext = ''; }
00440 public function getHTML() { return $this->mBodytext; }
00441 public function debug( $text ) { $this->mDebugtext .= $text; }
00442
00443
00444 public function setParserOptions( $options ) {
00445 wfDeprecated( __METHOD__ );
00446 return $this->parserOptions( $options );
00447 }
00448
00449 public function parserOptions( $options = null ) {
00450 if ( !$this->mParserOptions ) {
00451 $this->mParserOptions = new ParserOptions;
00452 }
00453 return wfSetVar( $this->mParserOptions, $options );
00454 }
00455
00462 public function setRevisionId( $revid ) {
00463 $val = is_null( $revid ) ? null : intval( $revid );
00464 return wfSetVar( $this->mRevisionId, $val );
00465 }
00466
00467 public function getRevisionId() {
00468 return $this->mRevisionId;
00469 }
00470
00479 public function addWikiText( $text, $linestart = true ) {
00480 global $wgTitle;
00481 $this->addWikiTextTitle($text, $wgTitle, $linestart);
00482 }
00483
00484 public function addWikiTextWithTitle($text, &$title, $linestart = true) {
00485 $this->addWikiTextTitle($text, $title, $linestart);
00486 }
00487
00488 function addWikiTextTitleTidy($text, &$title, $linestart = true) {
00489 $this->addWikiTextTitle( $text, $title, $linestart, true );
00490 }
00491
00492 public function addWikiTextTitle($text, &$title, $linestart, $tidy = false) {
00493 global $wgParser;
00494
00495 wfProfileIn( __METHOD__ );
00496
00497 wfIncrStats( 'pcache_not_possible' );
00498
00499 $popts = $this->parserOptions();
00500 $oldTidy = $popts->setTidy( $tidy );
00501
00502 $parserOutput = $wgParser->parse( $text, $title, $popts,
00503 $linestart, true, $this->mRevisionId );
00504
00505 $popts->setTidy( $oldTidy );
00506
00507 $this->addParserOutput( $parserOutput );
00508
00509 wfProfileOut( __METHOD__ );
00510 }
00511
00516 public function addParserOutputNoText( &$parserOutput ) {
00517 global $wgTitle, $wgExemptFromUserRobotsControl, $wgContentNamespaces;
00518
00519 $this->mLanguageLinks += $parserOutput->getLanguageLinks();
00520 $this->addCategoryLinks( $parserOutput->getCategories() );
00521 $this->mNewSectionLink = $parserOutput->getNewSection();
00522 $this->mHideNewSectionLink = $parserOutput->getHideNewSection();
00523
00524 if( is_null( $wgExemptFromUserRobotsControl ) ) {
00525 $bannedNamespaces = $wgContentNamespaces;
00526 } else {
00527 $bannedNamespaces = $wgExemptFromUserRobotsControl;
00528 }
00529 if( !in_array( $wgTitle->getNamespace(), $bannedNamespaces ) ) {
00530 # FIXME (bug 14900): This overrides $wgArticleRobotPolicies, and it
00531 # shouldn't
00532 $this->setIndexPolicy( $parserOutput->getIndexPolicy() );
00533 }
00534
00535 $this->addKeywords( $parserOutput );
00536 $this->mParseWarnings = $parserOutput->getWarnings();
00537 if ( $parserOutput->getCacheTime() == -1 ) {
00538 $this->enableClientCache( false );
00539 }
00540 $this->mNoGallery = $parserOutput->getNoGallery();
00541 $this->mHeadItems = array_merge( $this->mHeadItems, (array)$parserOutput->mHeadItems );
00542
00543 foreach ( (array)$parserOutput->mTemplateIds as $ns => $dbks ) {
00544 if ( isset( $this->mTemplateIds[$ns] ) ) {
00545 $this->mTemplateIds[$ns] = $dbks + $this->mTemplateIds[$ns];
00546 } else {
00547 $this->mTemplateIds[$ns] = $dbks;
00548 }
00549 }
00550
00551 if( ( $dt = $parserOutput->getDisplayTitle() ) !== false )
00552 $this->setPageTitle( $dt );
00553 else if ( ( $title = $parserOutput->getTitleText() ) != '' )
00554 $this->setPageTitle( $title );
00555
00556
00557 global $wgParserOutputHooks;
00558 foreach ( $parserOutput->getOutputHooks() as $hookInfo ) {
00559 list( $hookName, $data ) = $hookInfo;
00560 if ( isset( $wgParserOutputHooks[$hookName] ) ) {
00561 call_user_func( $wgParserOutputHooks[$hookName], $this, $parserOutput, $data );
00562 }
00563 }
00564
00565 wfRunHooks( 'OutputPageParserOutput', array( &$this, $parserOutput ) );
00566 }
00567
00572 function addParserOutput( &$parserOutput ) {
00573 $this->addParserOutputNoText( $parserOutput );
00574 $text = $parserOutput->getText();
00575 wfRunHooks( 'OutputPageBeforeHTML',array( &$this, &$text ) );
00576 $this->addHTML( $text );
00577 }
00578
00588 public function addPrimaryWikiText( $text, $article, $cache = true ) {
00589 global $wgParser, $wgUser;
00590
00591 wfDeprecated( __METHOD__ );
00592
00593 $popts = $this->parserOptions();
00594 $popts->setTidy(true);
00595 $parserOutput = $wgParser->parse( $text, $article->mTitle,
00596 $popts, true, true, $this->mRevisionId );
00597 $popts->setTidy(false);
00598 if ( $cache && $article && $parserOutput->getCacheTime() != -1 ) {
00599 $parserCache = ParserCache::singleton();
00600 $parserCache->save( $parserOutput, $article, $popts);
00601 }
00602
00603 $this->addParserOutput( $parserOutput );
00604 }
00605
00609 public function addSecondaryWikiText( $text, $linestart = true ) {
00610 global $wgTitle;
00611 wfDeprecated( __METHOD__ );
00612 $this->addWikiTextTitleTidy($text, $wgTitle, $linestart);
00613 }
00614
00618 public function addWikiTextTidy( $text, $linestart = true ) {
00619 global $wgTitle;
00620 $this->addWikiTextTitleTidy($text, $wgTitle, $linestart);
00621 }
00622
00623
00629 public function addTemplate( &$template ) {
00630 ob_start();
00631 $template->execute();
00632 $this->addHTML( ob_get_contents() );
00633 ob_end_clean();
00634 }
00635
00643 public function parse( $text, $linestart = true, $interface = false ) {
00644 global $wgParser, $wgTitle;
00645 if( is_null( $wgTitle ) ) {
00646 throw new MWException( 'Empty $wgTitle in ' . __METHOD__ );
00647 }
00648 $popts = $this->parserOptions();
00649 if ( $interface) { $popts->setInterfaceMessage(true); }
00650 $parserOutput = $wgParser->parse( $text, $wgTitle, $popts,
00651 $linestart, true, $this->mRevisionId );
00652 if ( $interface) { $popts->setInterfaceMessage(false); }
00653 return $parserOutput->getText();
00654 }
00655
00657 public function parseInline( $text, $linestart = true, $interface = false ) {
00658 $parsed = $this->parse( $text, $linestart, $interface );
00659
00660 $m = array();
00661 if ( preg_match( '/^<p>(.*)\n?<\/p>\n?/sU', $parsed, $m ) ) {
00662 $parsed = $m[1];
00663 }
00664
00665 return $parsed;
00666 }
00667
00674 public function tryParserCache( &$article ) {
00675 $parserCache = ParserCache::singleton();
00676 $parserOutput = $parserCache->get( $article, $this->parserOptions() );
00677 if ( $parserOutput !== false ) {
00678 $this->addParserOutput( $parserOutput );
00679 return true;
00680 } else {
00681 return false;
00682 }
00683 }
00684
00688 public function setSquidMaxage( $maxage ) {
00689 $this->mSquidMaxage = $maxage;
00690 }
00691
00696 public function enableClientCache( $state ) {
00697 return wfSetVar( $this->mEnableClientCache, $state );
00698 }
00699
00700 function getCacheVaryCookies() {
00701 global $wgCookiePrefix, $wgCacheVaryCookies;
00702 static $cookies;
00703 if ( $cookies === null ) {
00704 $cookies = array_merge(
00705 array(
00706 "{$wgCookiePrefix}Token",
00707 "{$wgCookiePrefix}LoggedOut",
00708 session_name()
00709 ),
00710 $wgCacheVaryCookies
00711 );
00712 wfRunHooks('GetCacheVaryCookies', array( $this, &$cookies ) );
00713 }
00714 return $cookies;
00715 }
00716
00717 function uncacheableBecauseRequestVars() {
00718 global $wgRequest;
00719 return $wgRequest->getText('useskin', false) === false
00720 && $wgRequest->getText('uselang', false) === false;
00721 }
00722
00727 function haveCacheVaryCookies() {
00728 global $wgRequest;
00729 $cookieHeader = $wgRequest->getHeader( 'cookie' );
00730 if ( $cookieHeader === false ) {
00731 return false;
00732 }
00733 $cvCookies = $this->getCacheVaryCookies();
00734 foreach ( $cvCookies as $cookieName ) {
00735 # Check for a simple string match, like the way squid does it
00736 if ( strpos( $cookieHeader, $cookieName ) ) {
00737 wfDebug( __METHOD__.": found $cookieName\n" );
00738 return true;
00739 }
00740 }
00741 wfDebug( __METHOD__.": no cache-varying cookies found\n" );
00742 return false;
00743 }
00744
00746 public function getXVO() {
00747 $cvCookies = $this->getCacheVaryCookies();
00748 $xvo = 'X-Vary-Options: Accept-Encoding;list-contains=gzip,Cookie;';
00749 $first = true;
00750 foreach ( $cvCookies as $cookieName ) {
00751 if ( $first ) {
00752 $first = false;
00753 } else {
00754 $xvo .= ';';
00755 }
00756 $xvo .= 'string-contains=' . $cookieName;
00757 }
00758 return $xvo;
00759 }
00760
00761 public function sendCacheControl() {
00762 global $wgUseSquid, $wgUseESI, $wgUseETag, $wgSquidMaxage, $wgRequest;
00763
00764 $response = $wgRequest->response();
00765 if ($wgUseETag && $this->mETag)
00766 $response->header("ETag: $this->mETag");
00767
00768 # don't serve compressed data to clients who can't handle it
00769 # maintain different caches for logged-in users and non-logged in ones
00770 $response->header( 'Vary: Accept-Encoding, Cookie' );
00771
00772 # Add an X-Vary-Options header for Squid with Wikimedia patches
00773 $response->header( $this->getXVO() );
00774
00775 if( !$this->uncacheableBecauseRequestVars() && $this->mEnableClientCache ) {
00776 if( $wgUseSquid && session_id() == '' &&
00777 ! $this->isPrintable() && $this->mSquidMaxage != 0 && !$this->haveCacheVaryCookies() )
00778 {
00779 if ( $wgUseESI ) {
00780 # We'll purge the proxy cache explicitly, but require end user agents
00781 # to revalidate against the proxy on each visit.
00782 # Surrogate-Control controls our Squid, Cache-Control downstream caches
00783 wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", false );
00784 # start with a shorter timeout for initial testing
00785 # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"');
00786 $response->header( 'Surrogate-Control: max-age='.$wgSquidMaxage.'+'.$this->mSquidMaxage.', content="ESI/1.0"');
00787 $response->header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
00788 } else {
00789 # We'll purge the proxy cache for anons explicitly, but require end user agents
00790 # to revalidate against the proxy on each visit.
00791 # IMPORTANT! The Squid needs to replace the Cache-Control header with
00792 # Cache-Control: s-maxage=0, must-revalidate, max-age=0
00793 wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", false );
00794 # start with a shorter timeout for initial testing
00795 # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" );
00796 $response->header( 'Cache-Control: s-maxage='.$this->mSquidMaxage.', must-revalidate, max-age=0' );
00797 }
00798 } else {
00799 # We do want clients to cache if they can, but they *must* check for updates
00800 # on revisiting the page.
00801 wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", false );
00802 $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
00803 $response->header( "Cache-Control: private, must-revalidate, max-age=0" );
00804 }
00805 if($this->mLastModified) {
00806 $response->header( "Last-Modified: {$this->mLastModified}" );
00807 }
00808 } else {
00809 wfDebug( __METHOD__ . ": no caching **\n", false );
00810
00811 # In general, the absence of a last modified header should be enough to prevent
00812 # the client from using its cache. We send a few other things just to make sure.
00813 $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
00814 $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
00815 $response->header( 'Pragma: no-cache' );
00816 }
00817 }
00818
00823 public function output() {
00824 global $wgUser, $wgOutputEncoding, $wgRequest;
00825 global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType;
00826 global $wgJsMimeType, $wgUseAjax, $wgAjaxWatch;
00827 global $wgEnableMWSuggest, $wgUniversalEditButton;
00828 global $wgArticle, $wgTitle;
00829
00830 if( $this->mDoNothing ){
00831 return;
00832 }
00833
00834 wfProfileIn( __METHOD__ );
00835
00836 if ( '' != $this->mRedirect ) {
00837 # Standards require redirect URLs to be absolute
00838 $this->mRedirect = wfExpandUrl( $this->mRedirect );
00839 if( $this->mRedirectCode == '301') {
00840 if( !$wgDebugRedirects ) {
00841 $wgRequest->response()->header("HTTP/1.1 {$this->mRedirectCode} Moved Permanently");
00842 }
00843 $this->mLastModified = wfTimestamp( TS_RFC2822 );
00844 }
00845
00846 $this->sendCacheControl();
00847
00848 $wgRequest->response()->header("Content-Type: text/html; charset=utf-8");
00849 if( $wgDebugRedirects ) {
00850 $url = htmlspecialchars( $this->mRedirect );
00851 print "<html>\n<head>\n<title>Redirect</title>\n</head>\n<body>\n";
00852 print "<p>Location: <a href=\"$url\">$url</a></p>\n";
00853 print "</body>\n</html>\n";
00854 } else {
00855 $wgRequest->response()->header( 'Location: '.$this->mRedirect );
00856 }
00857 wfProfileOut( __METHOD__ );
00858 return;
00859 }
00860 elseif ( $this->mStatusCode )
00861 {
00862 $statusMessage = array(
00863 100 => 'Continue',
00864 101 => 'Switching Protocols',
00865 102 => 'Processing',
00866 200 => 'OK',
00867 201 => 'Created',
00868 202 => 'Accepted',
00869 203 => 'Non-Authoritative Information',
00870 204 => 'No Content',
00871 205 => 'Reset Content',
00872 206 => 'Partial Content',
00873 207 => 'Multi-Status',
00874 300 => 'Multiple Choices',
00875 301 => 'Moved Permanently',
00876 302 => 'Found',
00877 303 => 'See Other',
00878 304 => 'Not Modified',
00879 305 => 'Use Proxy',
00880 307 => 'Temporary Redirect',
00881 400 => 'Bad Request',
00882 401 => 'Unauthorized',
00883 402 => 'Payment Required',
00884 403 => 'Forbidden',
00885 404 => 'Not Found',
00886 405 => 'Method Not Allowed',
00887 406 => 'Not Acceptable',
00888 407 => 'Proxy Authentication Required',
00889 408 => 'Request Timeout',
00890 409 => 'Conflict',
00891 410 => 'Gone',
00892 411 => 'Length Required',
00893 412 => 'Precondition Failed',
00894 413 => 'Request Entity Too Large',
00895 414 => 'Request-URI Too Large',
00896 415 => 'Unsupported Media Type',
00897 416 => 'Request Range Not Satisfiable',
00898 417 => 'Expectation Failed',
00899 422 => 'Unprocessable Entity',
00900 423 => 'Locked',
00901 424 => 'Failed Dependency',
00902 500 => 'Internal Server Error',
00903 501 => 'Not Implemented',
00904 502 => 'Bad Gateway',
00905 503 => 'Service Unavailable',
00906 504 => 'Gateway Timeout',
00907 505 => 'HTTP Version Not Supported',
00908 507 => 'Insufficient Storage'
00909 );
00910
00911 if ( $statusMessage[$this->mStatusCode] )
00912 $wgRequest->response()->header( 'HTTP/1.1 ' . $this->mStatusCode . ' ' . $statusMessage[$this->mStatusCode] );
00913 }
00914
00915 $sk = $wgUser->getSkin();
00916
00917 if ( $wgUseAjax ) {
00918 $this->addScriptFile( 'ajax.js' );
00919
00920 wfRunHooks( 'AjaxAddScript', array( &$this ) );
00921
00922 if( $wgAjaxWatch && $wgUser->isLoggedIn() ) {
00923 $this->addScriptFile( 'ajaxwatch.js' );
00924 }
00925
00926 if ( $wgEnableMWSuggest && !$wgUser->getOption( 'disablesuggest', false ) ){
00927 $this->addScriptFile( 'mwsuggest.js' );
00928 }
00929 }
00930
00931 if( $wgUser->getBoolOption( 'editsectiononrightclick' ) ) {
00932 $this->addScriptFile( 'rightclickedit.js' );
00933 }
00934
00935 if( $wgUniversalEditButton ) {
00936 if( isset( $wgArticle ) && isset( $wgTitle ) && $wgTitle->quickUserCan( 'edit' )
00937 && ( $wgTitle->exists() || $wgTitle->quickUserCan( 'create' ) ) ) {
00938
00939 $this->addLink( array(
00940 'rel' => 'alternate',
00941 'type' => 'application/x-wiki',
00942 'title' => wfMsg( 'edit' ),
00943 'href' => $wgTitle->getLocalURL( 'action=edit' )
00944 ) );
00945
00946 $this->addLink( array(
00947 'rel' => 'edit',
00948 'title' => wfMsg( 'edit' ),
00949 'href' => $wgTitle->getLocalURL( 'action=edit' )
00950 ) );
00951 }
00952 }
00953
00954 # Buffer output; final headers may depend on later processing
00955 ob_start();
00956
00957 $wgRequest->response()->header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" );
00958 $wgRequest->response()->header( 'Content-language: '.$wgContLanguageCode );
00959
00960 if ($this->mArticleBodyOnly) {
00961 $this->out($this->mBodytext);
00962 } else {
00963
00964
00965 wfRunHooks( 'BeforePageDisplay', array( &$this, &$sk ) );
00966
00967 wfProfileIn( 'Output-skin' );
00968 $sk->outputPage( $this );
00969 wfProfileOut( 'Output-skin' );
00970 }
00971
00972 $this->sendCacheControl();
00973 ob_end_flush();
00974 wfProfileOut( __METHOD__ );
00975 }
00976
00981 public function out( $ins ) {
00982 global $wgInputEncoding, $wgOutputEncoding, $wgContLang;
00983 if ( 0 == strcmp( $wgInputEncoding, $wgOutputEncoding ) ) {
00984 $outs = $ins;
00985 } else {
00986 $outs = $wgContLang->iconv( $wgInputEncoding, $wgOutputEncoding, $ins );
00987 if ( false === $outs ) { $outs = $ins; }
00988 }
00989 print $outs;
00990 }
00991
00995 public static function setEncodings() {
00996 global $wgInputEncoding, $wgOutputEncoding;
00997 global $wgUser, $wgContLang;
00998
00999 $wgInputEncoding = strtolower( $wgInputEncoding );
01000
01001 if ( empty( $_SERVER['HTTP_ACCEPT_CHARSET'] ) ) {
01002 $wgOutputEncoding = strtolower( $wgOutputEncoding );
01003 return;
01004 }
01005 $wgOutputEncoding = $wgInputEncoding;
01006 }
01007
01013 public function reportTime() {
01014 wfDeprecated( __METHOD__ );
01015 $time = wfReportTime();
01016 return $time;
01017 }
01018
01025 function blockedPage( $return = true ) {
01026 global $wgUser, $wgContLang, $wgTitle, $wgLang;
01027
01028 $this->setPageTitle( wfMsg( 'blockedtitle' ) );
01029 $this->setRobotPolicy( 'noindex,nofollow' );
01030 $this->setArticleRelated( false );
01031
01032 $name = User::whoIs( $wgUser->blockedBy() );
01033 $reason = $wgUser->blockedFor();
01034 if( $reason == '' ) {
01035 $reason = wfMsg( 'blockednoreason' );
01036 }
01037 $blockTimestamp = $wgLang->timeanddate( wfTimestamp( TS_MW, $wgUser->mBlock->mTimestamp ), true );
01038 $ip = wfGetIP();
01039
01040 $link = '[[' . $wgContLang->getNsText( NS_USER ) . ":{$name}|{$name}]]";
01041
01042 $blockid = $wgUser->mBlock->mId;
01043
01044 $blockExpiry = $wgUser->mBlock->mExpiry;
01045 if ( $blockExpiry == 'infinity' ) {
01046
01047
01048 $scBlockExpiryOptions = wfMsg( 'ipboptions' );
01049 foreach ( explode( ',', $scBlockExpiryOptions ) as $option ) {
01050 if ( strpos( $option, ":" ) === false )
01051 continue;
01052 list( $show, $value ) = explode( ":", $option );
01053 if ( $value == 'infinite' || $value == 'indefinite' ) {
01054 $blockExpiry = $show;
01055 break;
01056 }
01057 }
01058 } else {
01059 $blockExpiry = $wgLang->timeanddate( wfTimestamp( TS_MW, $blockExpiry ), true );
01060 }
01061
01062 if ( $wgUser->mBlock->mAuto ) {
01063 $msg = 'autoblockedtext';
01064 } else {
01065 $msg = 'blockedtext';
01066 }
01067
01068
01069
01070 $intended = $wgUser->mBlock->mAddress;
01071
01072 $this->addWikiMsg( $msg, $link, $reason, $ip, $name, $blockid, $blockExpiry, $intended, $blockTimestamp );
01073
01074 # Don't auto-return to special pages
01075 if( $return ) {
01076 $return = $wgTitle->getNamespace() > -1 ? $wgTitle : NULL;
01077 $this->returnToMain( null, $return );
01078 }
01079 }
01080
01088 public function showErrorPage( $title, $msg, $params = array() ) {
01089 global $wgTitle;
01090 if ( isset($wgTitle) ) {
01091 $this->mDebugtext .= 'Original title: ' . $wgTitle->getPrefixedText() . "\n";
01092 }
01093 $this->setPageTitle( wfMsg( $title ) );
01094 $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
01095 $this->setRobotPolicy( 'noindex,nofollow' );
01096 $this->setArticleRelated( false );
01097 $this->enableClientCache( false );
01098 $this->mRedirect = '';
01099 $this->mBodytext = '';
01100
01101 array_unshift( $params, 'parse' );
01102 array_unshift( $params, $msg );
01103 $this->addHTML( call_user_func_array( 'wfMsgExt', $params ) );
01104
01105 $this->returnToMain();
01106 }
01107
01113 public function showPermissionsErrorPage( $errors, $action = null )
01114 {
01115 global $wgTitle;
01116
01117 $this->mDebugtext .= 'Original title: ' .
01118 $wgTitle->getPrefixedText() . "\n";
01119 $this->setPageTitle( wfMsg( 'permissionserrors' ) );
01120 $this->setHTMLTitle( wfMsg( 'permissionserrors' ) );
01121 $this->setRobotPolicy( 'noindex,nofollow' );
01122 $this->setArticleRelated( false );
01123 $this->enableClientCache( false );
01124 $this->mRedirect = '';
01125 $this->mBodytext = '';
01126 $this->addWikiText( $this->formatPermissionsErrorMessage( $errors, $action ) );
01127 }
01128
01130 public function errorpage( $title, $msg ) {
01131 wfDeprecated( __METHOD__ );
01132 throw new ErrorPageError( $title, $msg );
01133 }
01134
01141 public function versionRequired( $version ) {
01142 $this->setPageTitle( wfMsg( 'versionrequired', $version ) );
01143 $this->setHTMLTitle( wfMsg( 'versionrequired', $version ) );
01144 $this->setRobotPolicy( 'noindex,nofollow' );
01145 $this->setArticleRelated( false );
01146 $this->mBodytext = '';
01147
01148 $this->addWikiMsg( 'versionrequiredtext', $version );
01149 $this->returnToMain();
01150 }
01151
01157 public function permissionRequired( $permission ) {
01158 global $wgUser, $wgLang;
01159
01160 $this->setPageTitle( wfMsg( 'badaccess' ) );
01161 $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
01162 $this->setRobotPolicy( 'noindex,nofollow' );
01163 $this->setArticleRelated( false );
01164 $this->mBodytext = '';
01165
01166 $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
01167 User::getGroupsWithPermission( $permission ) );
01168 if( $groups ) {
01169 $this->addWikiMsg( 'badaccess-groups',
01170 $wgLang->commaList( $groups ),
01171 count( $groups) );
01172 } else {
01173 $this->addWikiMsg( 'badaccess-group0' );
01174 }
01175 $this->returnToMain();
01176 }
01177
01182 public function sysopRequired() {
01183 throw new MWException( "Call to deprecated OutputPage::sysopRequired() method\n" );
01184 }
01185
01190 public function developerRequired() {
01191 throw new MWException( "Call to deprecated OutputPage::developerRequired() method\n" );
01192 }
01193
01197 public function loginToUse() {
01198 global $wgUser, $wgTitle, $wgContLang;
01199
01200 if( $wgUser->isLoggedIn() ) {
01201 $this->permissionRequired( 'read' );
01202 return;
01203 }
01204
01205 $skin = $wgUser->getSkin();
01206
01207 $this->setPageTitle( wfMsg( 'loginreqtitle' ) );
01208 $this->setHtmlTitle( wfMsg( 'errorpagetitle' ) );
01209 $this->setRobotPolicy( 'noindex,nofollow' );
01210 $this->setArticleFlag( false );
01211
01212 $loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
01213 $loginLink = $skin->makeKnownLinkObj( $loginTitle, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $wgTitle->getPrefixedUrl() );
01214 $this->addHTML( wfMsgWikiHtml( 'loginreqpagetext', $loginLink ) );
01215 $this->addHTML( "\n<!--" . $wgTitle->getPrefixedUrl() . "-->" );
01216
01217 # Don't return to the main page if the user can't read it
01218 # otherwise we'll end up in a pointless loop
01219 $mainPage = Title::newMainPage();
01220 if( $mainPage->userCanRead() )
01221 $this->returnToMain( null, $mainPage );
01222 }
01223
01225 public function databaseError( $fname, $sql, $error, $errno ) {
01226 throw new MWException( "OutputPage::databaseError is obsolete\n" );
01227 }
01228
01233 public function formatPermissionsErrorMessage( $errors, $action = null ) {
01234 if ($action == null) {
01235 $text = wfMsgNoTrans( 'permissionserrorstext', count($errors)). "\n\n";
01236 } else {
01237 global $wgLang;
01238 $action_desc = wfMsg( "action-$action" );
01239 $text = wfMsgNoTrans( 'permissionserrorstext-withaction', count($errors), $action_desc ) . "\n\n";
01240 }
01241
01242 if (count( $errors ) > 1) {
01243 $text .= '<ul class="permissions-errors">' . "\n";
01244
01245 foreach( $errors as $error )
01246 {
01247 $text .= '<li>';
01248 $text .= call_user_func_array( 'wfMsgNoTrans', $error );
01249 $text .= "</li>\n";
01250 }
01251 $text .= '</ul>';
01252 } else {
01253 $text .= '<div class="permissions-errors">' . call_user_func_array( 'wfMsgNoTrans', reset( $errors ) ) . '</div>';
01254 }
01255
01256 return $text;
01257 }
01258
01278 public function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) {
01279 global $wgUser, $wgTitle;
01280 $skin = $wgUser->getSkin();
01281
01282 $this->setRobotPolicy( 'noindex,nofollow' );
01283 $this->setArticleRelated( false );
01284
01285
01286
01287 if ( $protected && empty($reasons) ) {
01288 $reasons[] = array( 'badaccess-group0' );
01289 }
01290
01291 if ( !empty($reasons) ) {
01292
01293 if( $source ) {
01294 $this->setPageTitle( wfMsg( 'viewsource' ) );
01295 $this->setSubtitle( wfMsg( 'viewsourcefor', $skin->makeKnownLinkObj( $wgTitle ) ) );
01296 } else {
01297 $this->setPageTitle( wfMsg( 'badaccess' ) );
01298 }
01299 $this->addWikiText( $this->formatPermissionsErrorMessage( $reasons, $action ) );
01300 } else {
01301
01302 $this->setPageTitle( wfMsg( 'readonly' ) );
01303 $reason = wfReadOnlyReason();
01304 $this->wrapWikiMsg( '<div class="mw-readonly-error">$1</div>', array( 'readonlytext', $reason ) );
01305 }
01306
01307
01308 if( is_string( $source ) ) {
01309 $this->addWikiMsg( 'viewsourcetext' );
01310 $text = Xml::openElement( 'textarea',
01311 array( 'id' => 'wpTextbox1',
01312 'name' => 'wpTextbox1',
01313 'cols' => $wgUser->getOption( 'cols' ),
01314 'rows' => $wgUser->getOption( 'rows' ),
01315 'readonly' => 'readonly' ) );
01316 $text .= htmlspecialchars( $source );
01317 $text .= Xml::closeElement( 'textarea' );
01318 $this->addHTML( $text );
01319
01320
01321 $skin = $wgUser->getSkin();
01322 $article = new Article( $wgTitle );
01323 $this->addHTML( "<div class='templatesUsed'>
01324 {$skin->formatTemplates( $article->getUsedTemplates() )}
01325 </div>
01326 " );
01327 }
01328
01329 # If the title doesn't exist, it's fairly pointless to print a return
01330 # link to it. After all, you just tried editing it and couldn't, so
01331 # what's there to do there?
01332 if( $wgTitle->exists() ) {
01333 $this->returnToMain( null, $wgTitle );
01334 }
01335 }
01336
01338 public function fatalError( $message ) {
01339 wfDeprecated( __METHOD__ );
01340 throw new FatalError( $message );
01341 }
01342
01344 public function unexpectedValueError( $name, $val ) {
01345 wfDeprecated( __METHOD__ );
01346 throw new FatalError( wfMsg( 'unexpected', $name, $val ) );
01347 }
01348
01350 public function fileCopyError( $old, $new ) {
01351 wfDeprecated( __METHOD__ );
01352 throw new FatalError( wfMsg( 'filecopyerror', $old, $new ) );
01353 }
01354
01356 public function fileRenameError( $old, $new ) {
01357 wfDeprecated( __METHOD__ );
01358 throw new FatalError( wfMsg( 'filerenameerror', $old, $new ) );
01359 }
01360
01362 public function fileDeleteError( $name ) {
01363 wfDeprecated( __METHOD__ );
01364 throw new FatalError( wfMsg( 'filedeleteerror', $name ) );
01365 }
01366
01368 public function fileNotFoundError( $name ) {
01369 wfDeprecated( __METHOD__ );
01370 throw new FatalError( wfMsg( 'filenotfound', $name ) );
01371 }
01372
01373 public function showFatalError( $message ) {
01374 $this->setPageTitle( wfMsg( "internalerror" ) );
01375 $this->setRobotPolicy( "noindex,nofollow" );
01376 $this->setArticleRelated( false );
01377 $this->enableClientCache( false );
01378 $this->mRedirect = '';
01379 $this->mBodytext = $message;
01380 }
01381
01382 public function showUnexpectedValueError( $name, $val ) {
01383 $this->showFatalError( wfMsg( 'unexpected', $name, $val ) );
01384 }
01385
01386 public function showFileCopyError( $old, $new ) {
01387 $this->showFatalError( wfMsg( 'filecopyerror', $old, $new ) );
01388 }
01389
01390 public function showFileRenameError( $old, $new ) {
01391 $this->showFatalError( wfMsg( 'filerenameerror', $old, $new ) );
01392 }
01393
01394 public function showFileDeleteError( $name ) {
01395 $this->showFatalError( wfMsg( 'filedeleteerror', $name ) );
01396 }
01397
01398 public function showFileNotFoundError( $name ) {
01399 $this->showFatalError( wfMsg( 'filenotfound', $name ) );
01400 }
01401
01407 public function addReturnTo( $title ) {
01408 global $wgUser;
01409 $this->addLink( array( 'rel' => 'next', 'href' => $title->getFullUrl() ) );
01410 $link = wfMsg( 'returnto', $wgUser->getSkin()->makeLinkObj( $title ) );
01411 $this->addHTML( "<p>{$link}</p>\n" );
01412 }
01413
01421 public function returnToMain( $unused = null, $returnto = NULL ) {
01422 global $wgRequest;
01423
01424 if ( $returnto == NULL ) {
01425 $returnto = $wgRequest->getText( 'returnto' );
01426 }
01427
01428 if ( '' === $returnto ) {
01429 $returnto = Title::newMainPage();
01430 }
01431
01432 if ( is_object( $returnto ) ) {
01433 $titleObj = $returnto;
01434 } else {
01435 $titleObj = Title::newFromText( $returnto );
01436 }
01437 if ( !is_object( $titleObj ) ) {
01438 $titleObj = Title::newMainPage();
01439 }
01440
01441 $this->addReturnTo( $titleObj );
01442 }
01443
01450 private function addKeywords( &$parserOutput ) {
01451 global $wgTitle;
01452 $this->addKeyword( $wgTitle->getPrefixedText() );
01453 $count = 1;
01454 $links2d =& $parserOutput->getLinks();
01455 if ( !is_array( $links2d ) ) {
01456 return;
01457 }
01458 foreach ( $links2d as $dbkeys ) {
01459 foreach( $dbkeys as $dbkey => $unused ) {
01460 $this->addKeyword( $dbkey );
01461 if ( ++$count > 10 ) {
01462 break 2;
01463 }
01464 }
01465 }
01466 }
01467
01471 public function headElement( Skin $sk ) {
01472 global $wgDocType, $wgDTD, $wgContLanguageCode, $wgOutputEncoding, $wgMimeType;
01473 global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces;
01474 global $wgUser, $wgContLang, $wgUseTrackbacks, $wgTitle, $wgStyleVersion;
01475
01476 $this->addMeta( "http:Content-type", "$wgMimeType; charset={$wgOutputEncoding}" );
01477 $this->addStyle( 'common/wikiprintable.css', 'print' );
01478 $sk->setupUserCss( $this );
01479
01480 $ret = '';
01481
01482 if( $wgMimeType == 'text/xml' || $wgMimeType == 'application/xhtml+xml' || $wgMimeType == 'application/xml' ) {
01483 $ret .= "<?xml version=\"1.0\" encoding=\"$wgOutputEncoding\" ?" . ">\n";
01484 }
01485
01486 $ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\"\n \"$wgDTD\">\n";
01487
01488 if ( '' == $this->getHTMLTitle() ) {
01489 $this->setHTMLTitle( wfMsg( 'pagetitle', $this->getPageTitle() ));
01490 }
01491
01492 $rtl = $wgContLang->isRTL() ? " dir='RTL'" : '';
01493 $ret .= "<html xmlns=\"{$wgXhtmlDefaultNamespace}\" ";
01494 foreach($wgXhtmlNamespaces as $tag => $ns) {
01495 $ret .= "xmlns:{$tag}=\"{$ns}\" ";
01496 }
01497 $ret .= "xml:lang=\"$wgContLanguageCode\" lang=\"$wgContLanguageCode\" $rtl>\n";
01498 $ret .= "<head>\n<title>" . htmlspecialchars( $this->getHTMLTitle() ) . "</title>\n\t\t";
01499 $ret .= implode( "\t\t", array(
01500 $this->getHeadLinks(),
01501 $this->buildCssLinks(),
01502 $sk->getHeadScripts( $this->mAllowUserJs ),
01503 $this->mScripts,
01504 $this->getHeadItems(),
01505 ));
01506 if( $sk->usercss ){
01507 $ret .= "<style type='text/css'>{$sk->usercss}</style>";
01508 }
01509
01510 if ($wgUseTrackbacks && $this->isArticleRelated())
01511 $ret .= $wgTitle->trackbackRDF();
01512
01513 $ret .= "</head>\n";
01514 return $ret;
01515 }
01516
01517 protected function addDefaultMeta() {
01518 global $wgVersion;
01519 $this->addMeta( 'http:Content-Style-Type', 'text/css' );
01520 $this->addMeta( 'generator', "MediaWiki $wgVersion" );
01521
01522 $p = "{$this->mIndexPolicy},{$this->mFollowPolicy}";
01523 if( $p !== 'index,follow' ) {
01524
01525
01526 $this->addMeta( 'robots', $p );
01527 }
01528
01529 if ( count( $this->mKeywords ) > 0 ) {
01530 $strip = array(
01531 "/<.*?" . ">/" => '',
01532 "/_/" => ' '
01533 );
01534 $this->addMeta( 'keywords', preg_replace(array_keys($strip), array_values($strip),implode( ",", $this->mKeywords ) ) );
01535 }
01536 }
01537
01541 public function getHeadLinks() {
01542 global $wgRequest, $wgFeed;
01543
01544
01545 $this->addDefaultMeta();
01546
01547 $tags = array();
01548
01549 foreach ( $this->mMetatags as $tag ) {
01550 if ( 0 == strcasecmp( 'http:', substr( $tag[0], 0, 5 ) ) ) {
01551 $a = 'http-equiv';
01552 $tag[0] = substr( $tag[0], 5 );
01553 } else {
01554 $a = 'name';
01555 }
01556 $tags[] = Xml::element( 'meta',
01557 array(
01558 $a => $tag[0],
01559 'content' => $tag[1] ) );
01560 }
01561 foreach ( $this->mLinktags as $tag ) {
01562 $tags[] = Xml::element( 'link', $tag );
01563 }
01564
01565 if( $wgFeed ) {
01566 global $wgTitle;
01567 foreach( $this->getSyndicationLinks() as $format => $link ) {
01568 # Use the page name for the title (accessed through $wgTitle since
01569 # there's no other way). In principle, this could lead to issues
01570 # with having the same name for different feeds corresponding to
01571 # the same page, but we can't avoid that at this low a level.
01572
01573 $tags[] = $this->feedLink(
01574 $format,
01575 $link,
01576 wfMsg( "page-{$format}-feed", $wgTitle->getPrefixedText() ) ); # Used messages: 'page-rss-feed' and 'page-atom-feed' (for an easier grep)
01577 }
01578
01579 # Recent changes feed should appear on every page (except recentchanges,
01580 # that would be redundant). Put it after the per-page feed to avoid
01581 # changing existing behavior. It's still available, probably via a
01582 # menu in your browser. Some sites might have a different feed they'd
01583 # like to promote instead of the RC feed (maybe like a "Recent New Articles"
01584 # or "Breaking news" one). For this, we see if $wgOverrideSiteFeed is defined.
01585 # If so, use it instead.
01586
01587 global $wgOverrideSiteFeed, $wgSitename, $wgFeedClasses;
01588 $rctitle = SpecialPage::getTitleFor( 'Recentchanges' );
01589
01590 if ( $wgOverrideSiteFeed ) {
01591 foreach ( $wgOverrideSiteFeed as $type => $feedUrl ) {
01592 $tags[] = $this->feedLink (
01593 $type,
01594 htmlspecialchars( $feedUrl ),
01595 wfMsg( "site-{$type}-feed", $wgSitename ) );
01596 }
01597 }
01598 else if ( $wgTitle->getPrefixedText() != $rctitle->getPrefixedText() ) {
01599 foreach( $wgFeedClasses as $format => $class ) {
01600 $tags[] = $this->feedLink(
01601 $format,
01602 $rctitle->getLocalURL( "feed={$format}" ),
01603 wfMsg( "site-{$format}-feed", $wgSitename ) ); # For grep: 'site-rss-feed', 'site-atom-feed'.
01604 }
01605 }
01606 }
01607
01608 return implode( "\n\t\t", $tags ) . "\n";
01609 }
01610
01615 public function getSyndicationLinks() {
01616 global $wgTitle, $wgFeedClasses;
01617 $links = array();
01618
01619 if( $this->isSyndicated() ) {
01620 if( is_string( $this->getFeedAppendQuery() ) ) {
01621 $appendQuery = "&" . $this->getFeedAppendQuery();
01622 } else {
01623 $appendQuery = "";
01624 }
01625
01626 foreach( $wgFeedClasses as $format => $class ) {
01627 $links[$format] = $wgTitle->getLocalUrl( "feed=$format{$appendQuery}" );
01628 }
01629 }
01630 return $links;
01631 }
01632
01636 private function feedLink( $type, $url, $text ) {
01637 return Xml::element( 'link', array(
01638 'rel' => 'alternate',
01639 'type' => "application/$type+xml",
01640 'title' => $text,
01641 'href' => $url ) );
01642 }
01643
01652 public function addStyle( $style, $media='', $condition='', $dir='' ) {
01653 $options = array();
01654 if( $media )
01655 $options['media'] = $media;
01656 if( $condition )
01657 $options['condition'] = $condition;
01658 if( $dir )
01659 $options['dir'] = $dir;
01660 $this->styles[$style] = $options;
01661 }
01662
01667 public function buildCssLinks() {
01668 $links = array();
01669 foreach( $this->styles as $file => $options ) {
01670 $link = $this->styleLink( $file, $options );
01671 if( $link )
01672 $links[] = $link;
01673 }
01674
01675 return implode( "\n\t\t", $links );
01676 }
01677
01678 protected function styleLink( $style, $options ) {
01679 global $wgRequest;
01680
01681 if( isset( $options['dir'] ) ) {
01682 global $wgContLang;
01683 $siteDir = $wgContLang->isRTL() ? 'rtl' : 'ltr';
01684 if( $siteDir != $options['dir'] )
01685 return '';
01686 }
01687
01688 if( isset( $options['media'] ) ) {
01689 $media = $this->transformCssMedia( $options['media'] );
01690 if( is_null( $media ) ) {
01691 return '';
01692 }
01693 } else {
01694 $media = '';
01695 }
01696
01697 if( substr( $style, 0, 1 ) == '/' ||
01698 substr( $style, 0, 5 ) == 'http:' ||
01699 substr( $style, 0, 6 ) == 'https:' ) {
01700 $url = $style;
01701 } else {
01702 global $wgStylePath, $wgStyleVersion;
01703 $url = $wgStylePath . '/' . $style . '?' . $wgStyleVersion;
01704 }
01705
01706 $attribs = array(
01707 'rel' => 'stylesheet',
01708 'href' => $url,
01709 'type' => 'text/css' );
01710 if( $media ) {
01711 $attribs['media'] = $media;
01712 }
01713
01714 $link = Xml::element( 'link', $attribs );
01715
01716 if( isset( $options['condition'] ) ) {
01717 $condition = htmlspecialchars( $options['condition'] );
01718 $link = "<!--[if $condition]>$link<![endif]-->";
01719 }
01720 return $link;
01721 }
01722
01723 function transformCssMedia( $media ) {
01724 global $wgRequest, $wgHandheldForIPhone;
01725
01726
01727 $switches = array(
01728 'printable' => 'print',
01729 'handheld' => 'handheld',
01730 );
01731 foreach( $switches as $switch => $targetMedia ) {
01732 if( $wgRequest->getBool( $switch ) ) {
01733 if( $media == $targetMedia ) {
01734 $media = '';
01735 } elseif( $media == 'screen' ) {
01736 return null;
01737 }
01738 }
01739 }
01740
01741
01742 if( $wgHandheldForIPhone ) {
01743 $mediaAliases = array(
01744 'screen' => 'screen and (min-device-width: 481px)',
01745 'handheld' => 'handheld, only screen and (max-device-width: 480px)',
01746 );
01747
01748 if( isset( $mediaAliases[$media] ) ) {
01749 $media = $mediaAliases[$media];
01750 }
01751 }
01752
01753 return $media;
01754 }
01755
01760 public function rateLimited() {
01761 global $wgTitle;
01762
01763 $this->setPageTitle(wfMsg('actionthrottled'));
01764 $this->setRobotPolicy( 'noindex,follow' );
01765 $this->setArticleRelated( false );
01766 $this->enableClientCache( false );
01767 $this->mRedirect = '';
01768 $this->clearHTML();
01769 $this->setStatusCode(503);
01770 $this->addWikiMsg( 'actionthrottledtext' );
01771
01772 $this->returnToMain( null, $wgTitle );
01773 }
01774
01780 public function showNewSectionLink() {
01781 return $this->mNewSectionLink;
01782 }
01783
01789 public function forceHideNewSectionLink() {
01790 return $this->mHideNewSectionLink;
01791 }
01792
01802 public function showLagWarning( $lag ) {
01803 global $wgSlaveLagWarning, $wgSlaveLagCritical;
01804 if( $lag >= $wgSlaveLagWarning ) {
01805 $message = $lag < $wgSlaveLagCritical
01806 ? 'lag-warn-normal'
01807 : 'lag-warn-high';
01808 $warning = wfMsgExt( $message, 'parse', $lag );
01809 $this->addHTML( "<div class=\"mw-{$message}\">\n{$warning}\n</div>\n" );
01810 }
01811 }
01812
01819 public function addWikiMsg( ) {
01820 $args = func_get_args();
01821 $name = array_shift( $args );
01822 $this->addWikiMsgArray( $name, $args );
01823 }
01824
01832 public function addWikiMsgArray( $name, $args, $options = array() ) {
01833 $options[] = 'parse';
01834 $text = wfMsgExt( $name, $options, $args );
01835 $this->addHTML( $text );
01836 }
01837
01860 public function wrapWikiMsg( $wrap ) {
01861 $msgSpecs = func_get_args();
01862 array_shift( $msgSpecs );
01863 $msgSpecs = array_values( $msgSpecs );
01864 $s = $wrap;
01865 foreach ( $msgSpecs as $n => $spec ) {
01866 $options = array();
01867 if ( is_array( $spec ) ) {
01868 $args = $spec;
01869 $name = array_shift( $args );
01870 if ( isset( $args['options'] ) ) {
01871 $options = $args['options'];
01872 unset( $args['options'] );
01873 }
01874 } else {
01875 $args = array();
01876 $name = $spec;
01877 }
01878 $s = str_replace( '$' . ( $n + 1 ), wfMsgExt( $name, $options, $args ), $s );
01879 }
01880 $this->addHTML( $this->parse( $s, true, true ) );
01881 }
01882 }