/** @var int Cache stuff. Looks like mEnableClientCache */
protected $mSquidMaxage = 0;
+ /** @var int Upper limit on mSquidMaxage */
+ protected $mCdnMaxageLimit = INF;
/**
* @var bool Controls if anti-clickjacking / frame-breaking headers will
private $mIndexPolicy = 'index';
private $mFollowPolicy = 'follow';
private $mVaryHeader = array(
- 'Accept-Encoding' => array( 'list-contains=gzip' ),
+ 'Accept-Encoding' => array( 'match=gzip' ),
);
/**
* @param int $maxage Maximum cache time on the Squid, in seconds.
*/
public function setSquidMaxage( $maxage ) {
- $this->mSquidMaxage = $maxage;
+ $this->mSquidMaxage = min( $maxage, $this->mCdnMaxageLimit );
+ }
+
+ /**
+ * Lower the value of the "s-maxage" part of the "Cache-control" HTTP header
+ *
+ * @param int $maxage Maximum cache time on the Squid, in seconds
+ * @since 1.27
+ */
+ public function lowerCdnMaxage( $maxage ) {
+ $this->mCdnMaxageLimit = min( $maxage, $this->mCdnMaxageLimit );
+ $this->setSquidMaxage( $this->mSquidMaxage );
}
/**
* @return bool
*/
function haveCacheVaryCookies() {
- $cookieHeader = $this->getRequest()->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 ) !== false ) {
+ $request = $this->getRequest();
+ foreach ( $this->getCacheVaryCookies() as $cookieName ) {
+ if ( $request->getCookie( $cookieName, '', '' ) !== '' ) {
wfDebug( __METHOD__ . ": found $cookieName\n" );
return true;
}
* Add an HTTP header that will influence on the cache
*
* @param string $header Header name
- * @param string[]|null $option Options for X-Vary-Options. Possible options are:
- * - "string-contains=$XXX" varies on whether the header value as a string
- * contains $XXX as a substring.
- * - "list-contains=$XXX" varies on whether the header value as a
- * comma-separated list contains $XXX as one of the list items.
+ * @param string[]|null $option Options for the Key header. See
+ * https://datatracker.ietf.org/doc/draft-fielding-http-key/
+ * for the list of valid options.
*/
public function addVaryHeader( $header, array $option = null ) {
if ( !array_key_exists( $header, $this->mVaryHeader ) ) {
}
/**
- * Get a complete X-Vary-Options header
+ * Get a complete Key header
*
* @return string
*/
- public function getXVO() {
+ public function getKeyHeader() {
$cvCookies = $this->getCacheVaryCookies();
$cookiesOption = array();
foreach ( $cvCookies as $cookieName ) {
- $cookiesOption[] = 'string-contains=' . $cookieName;
+ $cookiesOption[] = 'param=' . $cookieName;
}
$this->addVaryHeader( 'Cookie', $cookiesOption );
}
$headers[] = $newheader;
}
- $xvo = 'X-Vary-Options: ' . implode( ',', $headers );
+ $key = 'Key: ' . implode( ',', $headers );
- return $xvo;
+ return $key;
}
/**
- * bug 21672: Add Accept-Language to Vary and XVO headers
+ * T23672: Add Accept-Language to Vary and Key headers
* if there's no 'variant' parameter existed in GET.
*
* For example:
if ( $variant === $lang->getCode() ) {
continue;
} else {
- $aloption[] = 'string-contains=' . $variant;
+ $aloption[] = 'substr=' . $variant;
// IE and some other browsers use BCP 47 standards in
// their Accept-Language header, like "zh-CN" or "zh-Hant".
// We should handle these too.
$variantBCP47 = wfBCP47( $variant );
if ( $variantBCP47 !== $variant ) {
- $aloption[] = 'string-contains=' . $variantBCP47;
+ $aloption[] = 'substr=' . $variantBCP47;
}
}
}
# maintain different caches for logged-in users and non-logged in ones
$response->header( $this->getVaryHeader() );
- if ( $config->get( 'UseXVO' ) ) {
- # Add an X-Vary-Options header for Squid with Wikimedia patches
- $response->header( $this->getXVO() );
+ if ( $config->get( 'UseKeyHeader' ) ) {
+ $response->header( $this->getKeyHeader() );
}
if ( $this->mEnableClientCache ) {
function getExternalHeadScripts() {
$links = array();
- // Startup - this provides the client with the module manifest and loads jquery and mediawiki base modules
+ // Startup - this provides the client with the module
+ // manifest and loads jquery and mediawiki base modules
$links[] = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS );
return self::getHtmlFromLoaderLinks( $links );
// This needs to be TYPE_COMBINED so these modules are properly wrapped
// in mw.loader.implement() calls and deferred until mw.user is available
$embedScripts = array( 'user.options' );
- $links[] = $this->makeResourceLoaderLink( $embedScripts, ResourceLoaderModule::TYPE_COMBINED );
+ $links[] = $this->makeResourceLoaderLink(
+ $embedScripts,
+ ResourceLoaderModule::TYPE_COMBINED
+ );
// Separate user.tokens as otherwise caching will be allowed (T84960)
- $links[] = $this->makeResourceLoaderLink( 'user.tokens', ResourceLoaderModule::TYPE_COMBINED );
+ $links[] = $this->makeResourceLoaderLink(
+ 'user.tokens',
+ ResourceLoaderModule::TYPE_COMBINED
+ );
// Modules requests - let the client calculate dependencies and batch requests as it likes
// Only load modules that have marked themselves for loading at the top
// FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded
// asynchronously and may arrive *after* the inline script here. So the previewed code
// may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js.
- // Similarly, when previewing ./common.js and the user module does arrive first, it will
- // arrive without common.js and the inline script runs after. Thus running common after
- // the excluded subpage.
+ // Similarly, when previewing ./common.js and the user module does arrive first,
+ // it will arrive without common.js and the inline script runs after.
+ // Thus running common after the excluded subpage.
} else {
// Include the user module normally, i.e., raw to avoid it being wrapped in a closure.
$links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_COMBINED );
}
// Group JS is only enabled if site JS is enabled.
- $links[] = $this->makeResourceLoaderLink( 'user.groups', ResourceLoaderModule::TYPE_COMBINED );
+ $links[] = $this->makeResourceLoaderLink(
+ 'user.groups',
+ ResourceLoaderModule::TYPE_COMBINED
+ );
return self::getHtmlFromLoaderLinks( $links );
}
* @return bool
*/
public function userCanPreview() {
- if ( $this->getRequest()->getVal( 'action' ) != 'submit'
- || !$this->getRequest()->wasPosted()
- || !$this->getUser()->matchEditToken(
- $this->getRequest()->getVal( 'wpEditToken' ) )
- ) {
+ $request = $this->getRequest();
+ if ( $request->getVal( 'action' ) !== 'submit' || !$request->wasPosted() ) {
+ return false;
+ }
+
+ $user = $this->getUser();
+ if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) {
return false;
}
- if ( !$this->getTitle()->isJsSubpage() && !$this->getTitle()->isCssSubpage() ) {
+
+ $title = $this->getTitle();
+ if ( !$title->isJsSubpage() && !$title->isCssSubpage() ) {
return false;
}
- if ( !$this->getTitle()->isSubpageOf( $this->getUser()->getUserPage() ) ) {
+ if ( !$title->isSubpageOf( $user->getUserPage() ) ) {
// Don't execute another user's CSS or JS on preview (T85855)
return false;
}
- return !count( $this->getTitle()->getUserPermissionsErrors( 'edit', $this->getUser() ) );
+ $errors = $title->getUserPermissionsErrors( 'edit', $user );
+ if ( count( $errors ) !== 0 ) {
+ return false;
+ }
+
+ return true;
}
/**
if ( $config->get( 'UniversalEditButton' ) && $this->isArticleRelated() ) {
$user = $this->getUser();
if ( $this->getTitle()->quickUserCan( 'edit', $user )
- && ( $this->getTitle()->exists() || $this->getTitle()->quickUserCan( 'create', $user ) ) ) {
+ && ( $this->getTitle()->exists() ||
+ $this->getTitle()->quickUserCan( 'create', $user ) )
+ ) {
// Original UniversalEditButton
$msg = $this->msg( 'edit' )->text();
$tags['universal-edit-button'] = Html::element( 'link', array(
$tags['rsd'] = Html::element( 'link', array(
'rel' => 'EditURI',
'type' => 'application/rsd+xml',
- // Output a protocol-relative URL here if $wgServer is protocol-relative
- // Whether RSD accepts relative or protocol-relative URLs is completely undocumented, though
+ // Output a protocol-relative URL here if $wgServer is protocol-relative.
+ // Whether RSD accepts relative or protocol-relative URLs is completely
+ // undocumented, though.
'href' => wfExpandUrl( wfAppendQuery(
wfScript( 'api' ),
array( 'action' => 'rsd' ) ),
$tags["variant-$variant"] = Html::element( 'link', array(
'rel' => 'alternate',
'hreflang' => wfBCP47( $variant ),
- 'href' => $this->getTitle()->getLocalURL( array( 'variant' => $variant ) ) )
+ 'href' => $this->getTitle()->getLocalURL(
+ array( 'variant' => $variant ) )
+ )
);
}
# x-default link per https://support.google.com/webmasters/answer/189077?hl=en
$format,
$link,
# Used messages: 'page-rss-feed' and 'page-atom-feed' (for an easier grep)
- $this->msg( "page-{$format}-feed", $this->getTitle()->getPrefixedText() )->text()
+ $this->msg(
+ "page-{$format}-feed", $this->getTitle()->getPrefixedText()
+ )->text()
);
}
continue;
}
$group = $module->getGroup();
- // Modules in groups other than the ones needing special treatment (see $styles assignment)
+ // Modules in groups other than the ones needing special treatment
+ // (see $styles assignment)
// will be placed in the "other" style category.
$styles[isset( $styles[$group] ) ? $group : 'other'][] = $name;
}
// statically added styles from other modules. So the order has to be
// other, dynamic, site, private, user. Add statically added styles for
// other modules
- $links[] = $this->makeResourceLoaderLink( $styles['other'], ResourceLoaderModule::TYPE_STYLES );
+ $links[] = $this->makeResourceLoaderLink(
+ $styles['other'],
+ ResourceLoaderModule::TYPE_STYLES
+ );
// Add normal styles added through addStyle()/addInlineStyle() here
$links[] = implode( "\n", $this->buildCssLinksArray() ) . $this->mInlineStyles;
- // Add marker tag to mark the place where the client-side loader should inject dynamic styles
+ // Add marker tag to mark the place where the client-side
+ // loader should inject dynamic styles
// We use a <meta> tag with a made-up name for this because that's valid HTML
$links[] = Html::element(
'meta',
$url = $style;
} else {
$config = $this->getConfig();
- $url = $config->get( 'StylePath' ) . '/' . $style . '?' . $config->get( 'StyleVersion' );
+ $url = $config->get( 'StylePath' ) . '/' . $style . '?' .
+ $config->get( 'StyleVersion' );
}
$link = Html::linkedStyle( $url, $media );
$themes = ExtensionRegistry::getInstance()->getAttribute( 'SkinOOUIThemes' );
// Make keys (skin names) lowercase for case-insensitive matching.
$themes = array_change_key_case( $themes, CASE_LOWER );
- $theme = isset( $themes[ $skinName ] ) ? $themes[ $skinName ] : 'MediaWiki';
+ $theme = isset( $themes[$skinName] ) ? $themes[$skinName] : 'MediaWiki';
// For example, 'OOUI\MediaWikiTheme'.
$themeClass = "OOUI\\{$theme}Theme";
OOUI\Theme::setSingleton( new $themeClass() );