diff options
Diffstat (limited to 'includes/OutputPage.php')
-rw-r--r-- | includes/OutputPage.php | 451 |
1 files changed, 287 insertions, 164 deletions
diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 1fddeb7d..8226cb2f 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -1,8 +1,6 @@ <?php if ( ! defined( 'MEDIAWIKI' ) ) die( 1 ); -/** - */ /** * @todo document @@ -26,7 +24,7 @@ class OutputPage { var $mFeedLinksAppendQuery = false; var $mEnableClientCache = true; var $mArticleBodyOnly = false; - + var $mNewSectionLink = false; var $mNoGallery = false; var $mPageTitleActionText = ''; @@ -59,13 +57,13 @@ class OutputPage { $this->mNewSectionLink = false; $this->mTemplateIds = array(); } - + public function redirect( $url, $responsecode = '302' ) { # Strip newlines as a paranoia check for header injection in PHP<5.1.2 $this->mRedirect = str_replace( "\n", '', $url ); $this->mRedirectCode = $responsecode; } - + public function getRedirect() { return $this->mRedirect; } @@ -87,10 +85,26 @@ class OutputPage { $this->addLink( array( 'rel' => 'stylesheet', - 'href' => $wgStylePath . '/' . $style . '?' . $wgStyleVersion ) ); + 'href' => $wgStylePath . '/' . $style . '?' . $wgStyleVersion, + 'type' => 'text/css' ) ); } /** + * Add a JavaScript file out of skins/common, or a given relative path. + * @param string $file filename in skins/common or complete on-server path (/foo/bar.js) + */ + function addScriptFile( $file ) { + global $wgStylePath, $wgStyleVersion, $wgJsMimeType; + if( substr( $file, 0, 1 ) == '/' ) { + $path = $file; + } else { + $path = "{$wgStylePath}/common/{$file}"; + } + $encPath = htmlspecialchars( $path ); + $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"$path?$wgStyleVersion\"></script>\n" ); + } + + /** * Add a self-contained script tag with the given contents * @param string $script JavaScript text, no <script> tags */ @@ -99,8 +113,8 @@ class OutputPage { $this->mScripts .= "<script type=\"$wgJsMimeType\">/*<![CDATA[*/\n$script\n/*]]>*/</script>"; } - function getScript() { - return $this->mScripts . $this->getHeadItems(); + function getScript() { + return $this->mScripts . $this->getHeadItems(); } function getHeadItems() { @@ -145,18 +159,17 @@ class OutputPage { */ function checkLastModified ( $timestamp ) { global $wgCachePages, $wgCacheEpoch, $wgUser, $wgRequest; - $fname = 'OutputPage::checkLastModified'; if ( !$timestamp || $timestamp == '19700101000000' ) { - wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP\n" ); + wfDebug( __METHOD__ . ": CACHE DISABLED, NO TIMESTAMP\n" ); return; } if( !$wgCachePages ) { - wfDebug( "$fname: CACHE DISABLED\n", false ); + wfDebug( __METHOD__ . ": CACHE DISABLED\n", false ); return; } if( $wgUser->getOption( 'nocache' ) ) { - wfDebug( "$fname: USER DISABLED CACHE\n", false ); + wfDebug( __METHOD__ . ": USER DISABLED CACHE\n", false ); return; } @@ -168,34 +181,34 @@ class OutputPage { # Wed, 20 Aug 2003 06:51:19 GMT; length=5202 # this breaks strtotime(). $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] ); - + wfSuppressWarnings(); // E_STRICT system time bitching $modsinceTime = strtotime( $modsince ); wfRestoreWarnings(); - + $ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 ); - wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", false ); - wfDebug( "$fname: -- we might send Last-Modified : $lastmod\n", false ); + wfDebug( __METHOD__ . ": -- client send If-Modified-Since: " . $modsince . "\n", false ); + wfDebug( __METHOD__ . ": -- we might send Last-Modified : $lastmod\n", false ); if( ($ismodsince >= $timestamp ) && $wgUser->validateCache( $ismodsince ) && $ismodsince >= $wgCacheEpoch ) { # Make sure you're in a place you can leave when you call us! $wgRequest->response()->header( "HTTP/1.0 304 Not Modified" ); $this->mLastModified = $lastmod; $this->sendCacheControl(); - wfDebug( "$fname: CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false ); + wfDebug( __METHOD__ . ": CACHED client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false ); $this->disable(); - + // Don't output a compressed blob when using ob_gzhandler; // it's technically against HTTP spec and seems to confuse // Firefox when the response gets split over two packets. wfClearOutputBuffers(); - + return true; } else { - wfDebug( "$fname: READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false ); + wfDebug( __METHOD__ . ": READY client: $ismodsince ; user: $wgUser->mTouched ; page: $timestamp ; site $wgCacheEpoch\n", false ); $this->mLastModified = $lastmod; } } else { - wfDebug( "$fname: client did not send If-Modified-Since header\n", false ); + wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", false ); $this->mLastModified = $lastmod; } } @@ -228,6 +241,7 @@ class OutputPage { public function getHTMLTitle() { return $this->mHTMLtitle; } public function getPageTitle() { return $this->mPagetitle; } public function setSubtitle( $str ) { $this->mSubtitle = /*$this->parse(*/$str/*)*/; } // @bug 2514 + public function appendSubtitle( $str ) { $this->mSubtitle .= /*$this->parse(*/$str/*)*/; } // @bug 2514 public function getSubtitle() { return $this->mSubtitle; } public function isArticle() { return $this->mIsarticle; } public function setPrintable() { $this->mPrintable = true; } @@ -270,23 +284,49 @@ class OutputPage { /** * Add an array of categories, with names in the keys */ - public function addCategoryLinks($categories) { + public function addCategoryLinks( $categories ) { global $wgUser, $wgContLang; - if ( !is_array( $categories ) ) { + if ( !is_array( $categories ) || count( $categories ) == 0 ) { return; } - # Add the links to the link cache in a batch + + # Add the links to a LinkBatch $arr = array( NS_CATEGORY => $categories ); $lb = new LinkBatch; $lb->setArray( $arr ); - $lb->execute(); - $sk = $wgUser->getSkin(); - foreach ( $categories as $category => $unused ) { - $title = Title::makeTitleSafe( NS_CATEGORY, $category ); - $text = $wgContLang->convertHtml( $title->getText() ); - $this->mCategoryLinks[] = $sk->makeLinkObj( $title, $text ); + # Fetch existence plus the hiddencat property + $dbr = wfGetDB( DB_SLAVE ); + $pageTable = $dbr->tableName( 'page' ); + $where = $lb->constructSet( 'page', $dbr ); + $propsTable = $dbr->tableName( 'page_props' ); + $sql = "SELECT page_id, page_namespace, page_title, page_len, page_is_redirect, pp_value + FROM $pageTable LEFT JOIN $propsTable ON pp_propname='hiddencat' AND pp_page=page_id WHERE $where"; + $res = $dbr->query( $sql, __METHOD__ ); + + # Add the results to the link cache + $lb->addResultToCache( LinkCache::singleton(), $res ); + + # Set all the values to 'normal'. This can be done with array_fill_keys in PHP 5.2.0+ + $categories = array_combine( array_keys( $categories ), + array_fill( 0, count( $categories ), 'normal' ) ); + + # Mark hidden categories + foreach ( $res as $row ) { + if ( isset( $row->pp_value ) ) { + $categories[$row->page_title] = 'hidden'; + } + } + + # Add the remaining categories to the skin + if ( wfRunHooks( 'OutputPageMakeCategoryLinks', array( &$this, $categories, &$this->mCategoryLinks ) ) ) { + $sk = $wgUser->getSkin(); + foreach ( $categories as $category => $type ) { + $title = Title::makeTitleSafe( NS_CATEGORY, $category ); + $text = $wgContLang->convertHtml( $title->getText() ); + $this->mCategoryLinks[$type][] = $sk->makeLinkObj( $title, $text ); + } } } @@ -308,6 +348,7 @@ class OutputPage { /* @deprecated */ public function setParserOptions( $options ) { + wfDeprecated( __METHOD__ ); return $this->parserOptions( $options ); } @@ -353,22 +394,21 @@ class OutputPage { public function addWikiTextTitle($text, &$title, $linestart, $tidy = false) { global $wgParser; - $fname = 'OutputPage:addWikiTextTitle'; - wfProfileIn($fname); + wfProfileIn( __METHOD__ ); - wfIncrStats('pcache_not_possible'); + wfIncrStats( 'pcache_not_possible' ); $popts = $this->parserOptions(); - $oldTidy = $popts->setTidy($tidy); + $oldTidy = $popts->setTidy( $tidy ); $parserOutput = $wgParser->parse( $text, $title, $popts, $linestart, true, $this->mRevisionId ); - + $popts->setTidy( $oldTidy ); $this->addParserOutput( $parserOutput ); - wfProfileOut($fname); + wfProfileOut( __METHOD__ ); } /** @@ -387,13 +427,13 @@ class OutputPage { $this->mNoGallery = $parserOutput->getNoGallery(); $this->mHeadItems = array_merge( $this->mHeadItems, (array)$parserOutput->mHeadItems ); // Versioning... - $this->mTemplateIds += (array)$parserOutput->mTemplateIds; - - # Display title + $this->mTemplateIds = wfArrayMerge( $this->mTemplateIds, (array)$parserOutput->mTemplateIds ); + + // Display title if( ( $dt = $parserOutput->getDisplayTitle() ) !== false ) $this->setPageTitle( $dt ); - # Hooks registered in the object + // Hooks registered in the object global $wgParserOutputHooks; foreach ( $parserOutput->getOutputHooks() as $hookInfo ) { list( $hookName, $data ) = $hookInfo; @@ -428,13 +468,15 @@ class OutputPage { public function addPrimaryWikiText( $text, $article, $cache = true ) { global $wgParser, $wgUser; + wfDeprecated( __METHOD__ ); + $popts = $this->parserOptions(); $popts->setTidy(true); $parserOutput = $wgParser->parse( $text, $article->mTitle, $popts, true, true, $this->mRevisionId ); $popts->setTidy(false); if ( $cache && $article && $parserOutput->getCacheTime() != -1 ) { - $parserCache =& ParserCache::singleton(); + $parserCache = ParserCache::singleton(); $parserCache->save( $parserOutput, $article, $wgUser ); } @@ -446,6 +488,7 @@ class OutputPage { */ public function addSecondaryWikiText( $text, $linestart = true ) { global $wgTitle; + wfDeprecated( __METHOD__ ); $this->addWikiTextTitleTidy($text, $wgTitle, $linestart); } @@ -494,7 +537,7 @@ class OutputPage { * @return bool True if successful, else false. */ public function tryParserCache( &$article, $user ) { - $parserCache =& ParserCache::singleton(); + $parserCache = ParserCache::singleton(); $parserOutput = $parserCache->get( $article, $user ); if ( $parserOutput !== false ) { $this->addParserOutput( $parserOutput ); @@ -519,27 +562,70 @@ class OutputPage { return wfSetVar( $this->mEnableClientCache, $state ); } - function uncacheableBecauseRequestvars() { + function getCacheVaryCookies() { + global $wgCookiePrefix, $wgCacheVaryCookies; + static $cookies; + if ( $cookies === null ) { + $cookies = array_merge( + array( + "{$wgCookiePrefix}Token", + "{$wgCookiePrefix}LoggedOut", + session_name() + ), + $wgCacheVaryCookies + ); + wfRunHooks('GetCacheVaryCookies', array( $this, &$cookies ) ); + } + return $cookies; + } + + function uncacheableBecauseRequestVars() { global $wgRequest; return $wgRequest->getText('useskin', false) === false && $wgRequest->getText('uselang', false) === false; } + /** + * Check if the request has a cache-varying cookie header + * If it does, it's very important that we don't allow public caching + */ + function haveCacheVaryCookies() { + global $wgRequest, $wgCookiePrefix; + $cookieHeader = $wgRequest->getHeader( 'cookie' ); + if ( $cookieHeader === false ) { + return false; + } + $cvCookies = $this->getCacheVaryCookies(); + foreach ( $cvCookies as $cookieName ) { + # Check for a simple string match, like the way squid does it + if ( strpos( $cookieHeader, $cookieName ) ) { + wfDebug( __METHOD__.": found $cookieName\n" ); + return true; + } + } + wfDebug( __METHOD__.": no cache-varying cookies found\n" ); + return false; + } + /** Get a complete X-Vary-Options header */ public function getXVO() { global $wgCookiePrefix; - return 'X-Vary-Options: ' . - # User ID cookie - "Cookie;string-contains={$wgCookiePrefix}UserID;" . - # Session cookie - 'string-contains=' . session_name() . ',' . - # Encoding checks for gzip only - 'Accept-Encoding;list-contains=gzip'; + $cvCookies = $this->getCacheVaryCookies(); + $xvo = 'X-Vary-Options: Accept-Encoding;list-contains=gzip,Cookie;'; + $first = true; + foreach ( $cvCookies as $cookieName ) { + if ( $first ) { + $first = false; + } else { + $xvo .= ';'; + } + $xvo .= 'string-contains=' . $cookieName; + } + return $xvo; } public function sendCacheControl() { global $wgUseSquid, $wgUseESI, $wgUseETag, $wgSquidMaxage, $wgRequest; - $fname = 'OutputPage::sendCacheControl'; $response = $wgRequest->response(); if ($wgUseETag && $this->mETag) @@ -552,15 +638,15 @@ class OutputPage { # Add an X-Vary-Options header for Squid with Wikimedia patches $response->header( $this->getXVO() ); - if( !$this->uncacheableBecauseRequestvars() && $this->mEnableClientCache ) { + if( !$this->uncacheableBecauseRequestVars() && $this->mEnableClientCache ) { if( $wgUseSquid && session_id() == '' && - ! $this->isPrintable() && $this->mSquidMaxage != 0 ) + ! $this->isPrintable() && $this->mSquidMaxage != 0 && !$this->haveCacheVaryCookies() ) { if ( $wgUseESI ) { # We'll purge the proxy cache explicitly, but require end user agents # to revalidate against the proxy on each visit. # Surrogate-Control controls our Squid, Cache-Control downstream caches - wfDebug( "$fname: proxy caching with ESI; {$this->mLastModified} **\n", false ); + wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", false ); # start with a shorter timeout for initial testing # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"'); $response->header( 'Surrogate-Control: max-age='.$wgSquidMaxage.'+'.$this->mSquidMaxage.', content="ESI/1.0"'); @@ -570,7 +656,7 @@ class OutputPage { # to revalidate against the proxy on each visit. # IMPORTANT! The Squid needs to replace the Cache-Control header with # Cache-Control: s-maxage=0, must-revalidate, max-age=0 - wfDebug( "$fname: local proxy caching; {$this->mLastModified} **\n", false ); + wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", false ); # start with a shorter timeout for initial testing # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" ); $response->header( 'Cache-Control: s-maxage='.$this->mSquidMaxage.', must-revalidate, max-age=0' ); @@ -578,13 +664,13 @@ class OutputPage { } else { # We do want clients to cache if they can, but they *must* check for updates # on revisiting the page. - wfDebug( "$fname: private caching; {$this->mLastModified} **\n", false ); + wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", false ); $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' ); $response->header( "Cache-Control: private, must-revalidate, max-age=0" ); } if($this->mLastModified) $response->header( "Last-modified: {$this->mLastModified}" ); } else { - wfDebug( "$fname: no caching **\n", false ); + wfDebug( __METHOD__ . ": no caching **\n", false ); # In general, the absence of a last modified header should be enough to prevent # the client from using its cache. We send a few other things just to make sure. @@ -601,14 +687,14 @@ class OutputPage { public function output() { global $wgUser, $wgOutputEncoding, $wgRequest; global $wgContLanguageCode, $wgDebugRedirects, $wgMimeType; - global $wgJsMimeType, $wgStylePath, $wgUseAjax, $wgAjaxSearch, $wgAjaxWatch; - global $wgServer, $wgStyleVersion; + global $wgJsMimeType, $wgUseAjax, $wgAjaxSearch, $wgAjaxWatch; + global $wgServer, $wgEnableMWSuggest; if( $this->mDoNothing ){ return; } - $fname = 'OutputPage::output'; - wfProfileIn( $fname ); + + wfProfileIn( __METHOD__ ); if ( '' != $this->mRedirect ) { # Standards require redirect URLs to be absolute @@ -631,7 +717,7 @@ class OutputPage { } else { $wgRequest->response()->header( 'Location: '.$this->mRedirect ); } - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return; } elseif ( $this->mStatusCode ) @@ -692,20 +778,27 @@ class OutputPage { $sk = $wgUser->getSkin(); if ( $wgUseAjax ) { - $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajax.js?$wgStyleVersion\"></script>\n" ); + $this->addScriptFile( 'ajax.js' ); wfRunHooks( 'AjaxAddScript', array( &$this ) ); if( $wgAjaxSearch && $wgUser->getBoolOption( 'ajaxsearch' ) ) { - $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxsearch.js?$wgStyleVersion\"></script>\n" ); + $this->addScriptFile( 'ajaxsearch.js' ); $this->addScript( "<script type=\"{$wgJsMimeType}\">hookEvent(\"load\", sajax_onload);</script>\n" ); } if( $wgAjaxWatch && $wgUser->isLoggedIn() ) { - $this->addScript( "<script type=\"{$wgJsMimeType}\" src=\"{$wgStylePath}/common/ajaxwatch.js?$wgStyleVersion\"></script>\n" ); + $this->addScriptFile( 'ajaxwatch.js' ); + } + + if ( $wgEnableMWSuggest && !$wgUser->getOption( 'disablesuggest', false ) ){ + $this->addScriptFile( 'mwsuggest.js' ); } } - + + if( $wgUser->getBoolOption( 'editsectiononrightclick' ) ) { + $this->addScriptFile( 'rightclickedit.js' ); + } # Buffer output; final headers may depend on later processing @@ -720,6 +813,10 @@ class OutputPage { if ($this->mArticleBodyOnly) { $this->out($this->mBodytext); } else { + // Hook that allows last minute changes to the output page, e.g. + // adding of CSS or Javascript by extensions. + wfRunHooks( 'BeforePageDisplay', array( &$this, &$sk ) ); + wfProfileIn( 'Output-skin' ); $sk->outputPage( $this ); wfProfileOut( 'Output-skin' ); @@ -727,7 +824,7 @@ class OutputPage { $this->sendCacheControl(); ob_end_flush(); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); } /** @@ -767,6 +864,7 @@ class OutputPage { * @deprecated */ public function reportTime() { + wfDeprecated( __METHOD__ ); $time = wfReportTime(); return $time; } @@ -828,8 +926,8 @@ class OutputPage { # Don't auto-return to special pages if( $return ) { - $return = $wgTitle->getNamespace() > -1 ? $wgTitle->getPrefixedText() : NULL; - $this->returnToMain( false, $return ); + $return = $wgTitle->getNamespace() > -1 ? $wgTitle : NULL; + $this->returnToMain( null, $return ); } } @@ -852,12 +950,12 @@ class OutputPage { $this->enableClientCache( false ); $this->mRedirect = ''; $this->mBodytext = ''; - + array_unshift( $params, 'parse' ); array_unshift( $params, $msg ); $this->addHtml( call_user_func_array( 'wfMsgExt', $params ) ); - - $this->returnToMain( false ); + + $this->returnToMain(); } /** @@ -865,7 +963,7 @@ class OutputPage { * * @param array $errors Error message keys */ - public function showPermissionsErrorPage( $errors ) + public function showPermissionsErrorPage( $errors, $action = null ) { global $wgTitle; @@ -878,14 +976,15 @@ class OutputPage { $this->enableClientCache( false ); $this->mRedirect = ''; $this->mBodytext = ''; - $this->addWikiText( $this->formatPermissionsErrorMessage( $errors ) ); + $this->addWikiText( $this->formatPermissionsErrorMessage( $errors, $action ) ); } /** @deprecated */ public function errorpage( $title, $msg ) { + wfDeprecated( __METHOD__ ); throw new ErrorPageError( $title, $msg ); } - + /** * Display an error page indicating that a given version of MediaWiki is * required to use it @@ -942,7 +1041,7 @@ class OutputPage { $message = wfMsgHtml( 'badaccess-groups', $groups ); } $this->addHtml( $message ); - $this->returnToMain( false ); + $this->returnToMain(); } /** @@ -973,22 +1072,22 @@ class OutputPage { } $skin = $wgUser->getSkin(); - + $this->setPageTitle( wfMsg( 'loginreqtitle' ) ); $this->setHtmlTitle( wfMsg( 'errorpagetitle' ) ); $this->setRobotPolicy( 'noindex,nofollow' ); $this->setArticleFlag( false ); - + $loginTitle = SpecialPage::getTitleFor( 'Userlogin' ); $loginLink = $skin->makeKnownLinkObj( $loginTitle, wfMsgHtml( 'loginreqlink' ), 'returnto=' . $wgTitle->getPrefixedUrl() ); $this->addHtml( wfMsgWikiHtml( 'loginreqpagetext', $loginLink ) ); $this->addHtml( "\n<!--" . $wgTitle->getPrefixedUrl() . "-->" ); - + # Don't return to the main page if the user can't read it # otherwise we'll end up in a pointless loop $mainPage = Title::newMainPage(); if( $mainPage->userCanRead() ) - $this->returnToMain( true, $mainPage ); + $this->returnToMain( null, $mainPage ); } /** @deprecated */ @@ -1000,8 +1099,14 @@ class OutputPage { * @param array $errors An array of arrays returned by Title::getUserPermissionsErrors * @return string The wikitext error-messages, formatted into a list. */ - public function formatPermissionsErrorMessage( $errors ) { - $text = wfMsgNoTrans( 'permissionserrorstext', count( $errors ) ) . "\n\n"; + public function formatPermissionsErrorMessage( $errors, $action = null ) { + if ($action == null) { + $text = wfMsgNoTrans( 'permissionserrorstext', count($errors)). "\n\n"; + } else { + $action_desc = wfMsg( "right-$action" ); + $action_desc[0] = strtolower($action_desc[0]); + $text = wfMsgNoTrans( 'permissionserrorstext-withaction', count($errors), $action_desc ) . "\n\n"; + } if (count( $errors ) > 1) { $text .= '<ul class="permissions-errors">' . "\n"; @@ -1014,7 +1119,7 @@ class OutputPage { } $text .= '</ul>'; } else { - $text .= '<div class="permissions-errors">' . call_user_func_array( 'wfMsgNoTrans', $errors[0]) . '</div>'; + $text .= '<div class="permissions-errors">' . call_user_func_array( 'wfMsgNoTrans', reset( $errors ) ) . '</div>'; } return $text; @@ -1039,8 +1144,8 @@ class OutputPage { * @param bool $protected Is this a permissions error? * @param array $reasons List of reasons for this error, as returned by Title::getUserPermissionsErrors(). */ - public function readOnlyPage( $source = null, $protected = false, $reasons = array() ) { - global $wgUser, $wgReadOnlyFile, $wgReadOnly, $wgTitle; + public function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) { + global $wgUser, $wgTitle; $skin = $wgUser->getSkin(); $this->setRobotpolicy( 'noindex,nofollow' ); @@ -1060,30 +1165,25 @@ class OutputPage { } else { $this->setPageTitle( wfMsg( 'badaccess' ) ); } - $this->addWikiText( $this->formatPermissionsErrorMessage( $reasons ) ); + $this->addWikiText( $this->formatPermissionsErrorMessage( $reasons, $action ) ); } else { // Wiki is read only $this->setPageTitle( wfMsg( 'readonly' ) ); - if ( $wgReadOnly ) { - $reason = $wgReadOnly; - } else { - // Should not happen, user should have called wfReadOnly() first - $reason = file_get_contents( $wgReadOnlyFile ); - } + $reason = wfReadOnlyReason(); $this->addWikiMsg( 'readonlytext', $reason ); } // Show source, if supplied if( is_string( $source ) ) { $this->addWikiMsg( 'viewsourcetext' ); - $text = wfOpenElement( 'textarea', + $text = Xml::openElement( 'textarea', array( 'id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $wgUser->getOption( 'cols' ), 'rows' => $wgUser->getOption( 'rows' ), 'readonly' => 'readonly' ) ); $text .= htmlspecialchars( $source ); - $text .= wfCloseElement( 'textarea' ); + $text .= Xml::closeElement( 'textarea' ); $this->addHTML( $text ); // Show templates used by this article @@ -1096,37 +1196,43 @@ class OutputPage { # link to it. After all, you just tried editing it and couldn't, so # what's there to do there? if( $wgTitle->exists() ) { - $this->returnToMain( false, $wgTitle ); + $this->returnToMain( null, $wgTitle ); } } /** @deprecated */ public function fatalError( $message ) { - throw new FatalError( $message ); + wfDeprecated( __METHOD__ ); + throw new FatalError( $message ); } - + /** @deprecated */ public function unexpectedValueError( $name, $val ) { + wfDeprecated( __METHOD__ ); throw new FatalError( wfMsg( 'unexpected', $name, $val ) ); } /** @deprecated */ public function fileCopyError( $old, $new ) { + wfDeprecated( __METHOD__ ); throw new FatalError( wfMsg( 'filecopyerror', $old, $new ) ); } /** @deprecated */ public function fileRenameError( $old, $new ) { + wfDeprecated( __METHOD__ ); throw new FatalError( wfMsg( 'filerenameerror', $old, $new ) ); } /** @deprecated */ public function fileDeleteError( $name ) { + wfDeprecated( __METHOD__ ); throw new FatalError( wfMsg( 'filedeleteerror', $name ) ); } /** @deprecated */ public function fileNotFoundError( $name ) { + wfDeprecated( __METHOD__ ); throw new FatalError( wfMsg( 'filenotfound', $name ) ); } @@ -1179,11 +1285,11 @@ class OutputPage { */ public function returnToMain( $unused = null, $returnto = NULL ) { global $wgRequest; - + if ( $returnto == NULL ) { $returnto = $wgRequest->getText( 'returnto' ); } - + if ( '' === $returnto ) { $returnto = Title::newMainPage(); } @@ -1251,8 +1357,8 @@ class OutputPage { } $ret .= "xml:lang=\"$wgContLanguageCode\" lang=\"$wgContLanguageCode\" $rtl>\n"; $ret .= "<head>\n<title>" . htmlspecialchars( $this->getHTMLTitle() ) . "</title>\n"; - array_push( $this->mMetatags, array( "http:Content-type", "$wgMimeType; charset={$wgOutputEncoding}" ) ); - + $this->addMeta( "http:Content-type", "$wgMimeType; charset={$wgOutputEncoding}" ); + $ret .= $this->getHeadLinks(); global $wgStylePath; if( $this->isPrintable() ) { @@ -1275,13 +1381,38 @@ class OutputPage { $ret .= "</head>\n"; return $ret; } + + protected function addDefaultMeta() { + global $wgVersion; + $this->addMeta( "generator", "MediaWiki $wgVersion" ); + + $p = $this->mRobotpolicy; + if( $p !== '' && $p != 'index,follow' ) { + // http://www.robotstxt.org/wc/meta-user.html + // Only show if it's different from the default robots policy + $this->addMeta( 'robots', $p ); + } + + if ( count( $this->mKeywords ) > 0 ) { + $strip = array( + "/<.*?>/" => '', + "/_/" => ' ' + ); + $this->addMeta( 'keywords', preg_replace(array_keys($strip), array_values($strip),implode( ",", $this->mKeywords ) ) ); + } + } /** * @return string HTML tag links to be put in the header. */ public function getHeadLinks() { - global $wgRequest; - $ret = ''; + global $wgRequest, $wgFeed; + + // Ideally this should happen earlier, somewhere. :P + $this->addDefaultMeta(); + + $tags = array(); + foreach ( $this->mMetatags as $tag ) { if ( 0 == strcasecmp( 'http:', substr( $tag[0], 0, 5 ) ) ) { $a = 'http-equiv'; @@ -1289,62 +1420,52 @@ class OutputPage { } else { $a = 'name'; } - $ret .= "<meta $a=\"{$tag[0]}\" content=\"{$tag[1]}\" />\n"; - } - - $p = $this->mRobotpolicy; - if( $p !== '' && $p != 'index,follow' ) { - // http://www.robotstxt.org/wc/meta-user.html - // Only show if it's different from the default robots policy - $ret .= "<meta name=\"robots\" content=\"$p\" />\n"; - } - - if ( count( $this->mKeywords ) > 0 ) { - $strip = array( - "/<.*?>/" => '', - "/_/" => ' ' - ); - $ret .= "\t\t<meta name=\"keywords\" content=\"" . - htmlspecialchars(preg_replace(array_keys($strip), array_values($strip),implode( ",", $this->mKeywords ))) . "\" />\n"; + $tags[] = Xml::element( 'meta', + array( + $a => $tag[0], + 'content' => $tag[1] ) ); } foreach ( $this->mLinktags as $tag ) { - $ret .= "\t\t<link"; - foreach( $tag as $attr => $val ) { - $ret .= " $attr=\"" . htmlspecialchars( $val ) . "\""; - } - $ret .= " />\n"; + $tags[] = Xml::element( 'link', $tag ); } - - foreach( $this->getSyndicationLinks() as $format => $link ) { - # Use the page name for the title (accessed through $wgTitle since - # there's no other way). In principle, this could lead to issues - # with having the same name for different feeds corresponding to - # the same page, but we can't avoid that at this low a level. + + if( $wgFeed ) { global $wgTitle; + foreach( $this->getSyndicationLinks() as $format => $link ) { + # Use the page name for the title (accessed through $wgTitle since + # there's no other way). In principle, this could lead to issues + # with having the same name for different feeds corresponding to + # the same page, but we can't avoid that at this low a level. + + $tags[] = $this->feedLink( + $format, + $link, + wfMsg( "page-{$format}-feed", $wgTitle->getPrefixedText() ) ); # Used messages: 'page-rss-feed' and 'page-atom-feed' (for an easier grep) + } - $ret .= $this->feedLink( - $format, - $link, - wfMsg( "page-{$format}-feed", $wgTitle->getPrefixedText() ) ); # Used messages: 'page-rss-feed' and 'page-atom-feed' (for an easier grep) - } + # Recent changes feed should appear on every page (except recentchanges, + # that would be redundant). Put it after the per-page feed to avoid + # changing existing behavior. It's still available, probably via a + # menu in your browser. - # Recent changes feed should appear on every page - # Put it after the per-page feed to avoid changing existing behavior. - # It's still available, probably via a menu in your browser. - global $wgSitename; - $rctitle = SpecialPage::getTitleFor( 'Recentchanges' ); - $ret .= $this->feedLink( - 'rss', - $rctitle->getFullURL( 'feed=rss' ), - wfMsg( 'site-rss-feed', $wgSitename ) ); - $ret .= $this->feedLink( - 'atom', - $rctitle->getFullURL( 'feed=atom' ), - wfMsg( 'site-atom-feed', $wgSitename ) ); + $rctitle = SpecialPage::getTitleFor( 'Recentchanges' ); + if ( $wgTitle->getPrefixedText() != $rctitle->getPrefixedText() ) { + global $wgSitename; + + $tags[] = $this->feedLink( + 'rss', + $rctitle->getFullURL( 'feed=rss' ), + wfMsg( 'site-rss-feed', $wgSitename ) ); + $tags[] = $this->feedLink( + 'atom', + $rctitle->getFullURL( 'feed=atom' ), + wfMsg( 'site-atom-feed', $wgSitename ) ); + } + } - return $ret; + return implode( "\n\t\t", $tags ) . "\n"; } - + /** * Return URLs for each supported syndication format for this page. * @return array associating format keys with URLs @@ -1352,7 +1473,7 @@ class OutputPage { public function getSyndicationLinks() { global $wgTitle, $wgFeedClasses; $links = array(); - + if( $this->isSyndicated() ) { if( is_string( $this->getFeedAppendQuery() ) ) { $appendQuery = "&" . $this->getFeedAppendQuery(); @@ -1366,7 +1487,7 @@ class OutputPage { } return $links; } - + /** * Generate a <link rel/> for an RSS feed. */ @@ -1375,7 +1496,7 @@ class OutputPage { 'rel' => 'alternate', 'type' => "application/$type+xml", 'title' => $text, - 'href' => $url ) ) . "\n"; + 'href' => $url ) ); } /** @@ -1383,7 +1504,7 @@ class OutputPage { * for when rate limiting has triggered. */ public function rateLimited() { - global $wgOut, $wgTitle; + global $wgTitle; $this->setPageTitle(wfMsg('actionthrottled')); $this->setRobotPolicy( 'noindex,follow' ); @@ -1394,9 +1515,9 @@ class OutputPage { $this->setStatusCode(503); $this->addWikiMsg( 'actionthrottledtext' ); - $this->returnToMain( false, $wgTitle ); + $this->returnToMain( null, $wgTitle ); } - + /** * Show an "add new section" link? * @@ -1405,7 +1526,7 @@ class OutputPage { public function showNewSectionLink() { return $this->mNewSectionLink; } - + /** * Show a warning about slave lag * @@ -1452,21 +1573,23 @@ class OutputPage { } /** - * This function takes a number of message/argument specifications, wraps them in + * This function takes a number of message/argument specifications, wraps them in * some overall structure, and then parses the result and adds it to the output. * - * In the $wrap, $1 is replaced with the first message, $2 with the second, and so - * on. The subsequent arguments may either be strings, in which case they are the + * In the $wrap, $1 is replaced with the first message, $2 with the second, and so + * on. The subsequent arguments may either be strings, in which case they are the * message names, or an arrays, in which case the first element is the message name, * and subsequent elements are the parameters to that message. * * The special named parameter 'options' in a message specification array is passed - * through to the $options parameter of wfMsgExt(). + * through to the $options parameter of wfMsgExt(). + * + * Don't use this for messages that are not in users interface language. * * For example: * * $wgOut->wrapWikiMsg( '<div class="error">$1</div>', 'some-error' ); - * + * * Is equivalent to: * * $wgOut->addWikiText( '<div class="error">' . wfMsgNoTrans( 'some-error' ) . '</div>' ); @@ -1491,6 +1614,6 @@ class OutputPage { } $s = str_replace( '$' . ($n+1), wfMsgExt( $name, $options, $args ), $s ); } - $this->addHTML( $this->parse( $s ) ); + $this->addHTML( $this->parse( $s, /*linestart*/true, /*uilang*/true ) ); } } |