/**
* 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();
+ # 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
$sk = $wgUser->getSkin();
- foreach ( $categories as $category => $unused ) {
+ foreach ( $categories as $category => $type ) {
$title = Title::makeTitleSafe( NS_CATEGORY, $category );
$text = $wgContLang->convertHtml( $title->getText() );
- $this->mCategoryLinks[] = $sk->makeLinkObj( $title, $text );
+ $this->mCategoryLinks[$type][] = $sk->makeLinkObj( $title, $text );
}
}
// Versioning...
$this->mTemplateIds += (array)$parserOutput->mTemplateIds;
- # Display title
+ // 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;
$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 );
}
* @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 );
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() {
# 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
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' );
}
$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;
* @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;
+ global $wgUser, $wgTitle;
$skin = $wgUser->getSkin();
$this->setRobotpolicy( 'noindex,nofollow' );
} 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 );
}
* @return string HTML tag links to be put in the header.
*/
public function getHeadLinks() {
- global $wgRequest;
+ global $wgRequest, $wgFeed;
$ret = '';
foreach ( $this->mMetatags as $tag ) {
if ( 0 == strcasecmp( 'http:', substr( $tag[0], 0, 5 ) ) ) {
$ret .= " />\n";
}
- 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.
- global $wgTitle;
-
+ if( $wgFeed ) {
+ 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.
+ global $wgTitle;
+
+ $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
+ # 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(
- $format,
- $link,
- wfMsg( "page-{$format}-feed", $wgTitle->getPrefixedText() ) ); # Used messages: 'page-rss-feed' and 'page-atom-feed' (for an easier grep)
+ 'atom',
+ $rctitle->getFullURL( 'feed=atom' ),
+ wfMsg( 'site-atom-feed', $wgSitename ) );
}
- # 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 ) );
-
return $ret;
}