* @var string The contents of <h1> */
private $mPageTitle = '';
+ /**
+ * @var string The displayed title of the page. Different from page title
+ * if overridden by display title magic word or hooks. Can contain safe
+ * HTML. Different from page title which may contain messages such as
+ * "Editing X" which is displayed in h1. This can be used for other places
+ * where the page name is referred on the page.
+ */
+ private $displayTitle;
+
/**
* @var string Contains all of the "<body>" content. Should be private we
* got set/get accessors and the append() method.
* @var bool Is the displayed content related to the source of the
* corresponding wiki article.
*/
- private $mIsarticle = false;
+ private $mIsArticle = false;
/** @var bool Stores "article flag" toggle. */
private $mIsArticleRelated = true;
*/
public $mNoGallery = false;
- /** @var string */
- private $mPageTitleActionText = '';
-
/** @var int Cache stuff. Looks like mEnableClientCache */
protected $mCdnMaxage = 0;
/** @var int Upper limit on mCdnMaxage */
private $mIndexPolicy = 'index';
private $mFollowPolicy = 'follow';
+
+ /**
+ * @var array Headers that cause the cache to vary. Key is header name, value is an array of
+ * options for the Key header.
+ */
private $mVaryHeader = [
'Accept-Encoding' => [ 'match=gzip' ],
];
return $this->mLinktags;
}
- /**
- * Add a new \<link\> with "rel" attribute set to "meta"
- *
- * @param array $linkarr Associative array mapping attribute names to their
- * values, both keys and values will be escaped, and the
- * "rel" attribute will be automatically added
- */
- function addMetadataLink( array $linkarr ) {
- $linkarr['rel'] = $this->getMetadataAttribute();
- $this->addLink( $linkarr );
- }
-
/**
* Set the URL to be used for the <link rel=canonical>. This should be used
* in preference to addLink(), to avoid duplicate link tags.
return $this->mCanonicalUrl;
}
- /**
- * Get the value of the "rel" attribute for metadata links
- *
- * @return string
- */
- public function getMetadataAttribute() {
- # note: buggy CC software only reads first "meta" link
- static $haveMeta = false;
- if ( $haveMeta ) {
- return 'alternate meta';
- } else {
- $haveMeta = true;
- return 'meta';
- }
- }
-
/**
* Add raw HTML to the list of scripts (including \<script\> tag, etc.)
* Internal use only. Use OutputPage::addModules() or OutputPage::addJsConfigVars()
# this breaks strtotime().
$clientHeader = preg_replace( '/;.*$/', '', $clientHeader );
- Wikimedia\suppressWarnings(); // E_STRICT system time bitching
+ Wikimedia\suppressWarnings(); // E_STRICT system time warnings
$clientHeaderTime = strtotime( $clientHeader );
Wikimedia\restoreWarnings();
if ( !$clientHeaderTime ) {
}
}
- /**
- * Set the new value of the "action text", this will be added to the
- * "HTML title", separated from it with " - ".
- *
- * @param string $text New value of the "action text"
- */
- public function setPageTitleActionText( $text ) {
- $this->mPageTitleActionText = $text;
- }
-
- /**
- * Get the value of the "action text"
- *
- * @return string
- */
- public function getPageTitleActionText() {
- return $this->mPageTitleActionText;
- }
-
/**
* "HTML title" means the contents of "<title>".
* It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file.
return $this->mPageTitle;
}
+ /**
+ * Same as page title but only contains name of the page, not any other text.
+ *
+ * @since 1.32
+ * @param string $html Page title text.
+ * @see OutputPage::setPageTitle
+ */
+ public function setDisplayTitle( $html ) {
+ $this->displayTitle = $html;
+ }
+
+ /**
+ * Returns page display title.
+ *
+ * Performs some normalization, but this not as strict the magic word.
+ *
+ * @since 1.32
+ * @return string HTML
+ */
+ public function getDisplayTitle() {
+ $html = $this->displayTitle;
+ if ( $html === null ) {
+ $html = $this->getTitle()->getPrefixedText();
+ }
+
+ return Sanitizer::normalizeCharReferences( Sanitizer::removeHTMLtags( $html ) );
+ }
+
+ /**
+ * Returns page display title without namespace prefix if possible.
+ *
+ * @since 1.32
+ * @return string HTML
+ */
+ public function getUnprefixedDisplayTitle() {
+ $text = $this->getDisplayTitle();
+ $nsPrefix = $this->getTitle()->getNsText() . ':';
+ $prefix = preg_quote( $nsPrefix, '/' );
+
+ return preg_replace( "/^$prefix/i", '', $text );
+ }
+
/**
* Set the Title object to use
*
* corresponding article on the wiki
* Setting true will cause the change "article related" toggle to true
*
- * @param bool $v
+ * @param bool $newVal
*/
- public function setArticleFlag( $v ) {
- $this->mIsarticle = $v;
- if ( $v ) {
- $this->mIsArticleRelated = $v;
+ public function setArticleFlag( $newVal ) {
+ $this->mIsArticle = $newVal;
+ if ( $newVal ) {
+ $this->mIsArticleRelated = $newVal;
}
}
* @return bool
*/
public function isArticle() {
- return $this->mIsarticle;
+ return $this->mIsArticle;
}
/**
* Set whether this page is related an article on the wiki
* Setting false will cause the change of "article flag" toggle to false
*
- * @param bool $v
+ * @param bool $newVal
*/
- public function setArticleRelated( $v ) {
- $this->mIsArticleRelated = $v;
- if ( !$v ) {
- $this->mIsarticle = false;
+ public function setArticleRelated( $newVal ) {
+ $this->mIsArticleRelated = $newVal;
+ if ( !$newVal ) {
+ $this->mIsArticle = false;
}
}
* (e.g. 'fr:Test page')
*/
public function addLanguageLinks( array $newLinkArray ) {
- $this->mLanguageLinks += $newLinkArray;
+ $this->mLanguageLinks = array_merge( $this->mLanguageLinks, $newLinkArray );
}
/**
* @param array $categories Mapping category name => sort key
*/
public function addCategoryLinks( array $categories ) {
- global $wgContLang;
-
- if ( !is_array( $categories ) || count( $categories ) == 0 ) {
+ if ( !$categories ) {
return;
}
'OutputPageMakeCategoryLinks',
[ &$outputPage, $categories, &$this->mCategoryLinks ] )
) {
- $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
+ $services = MediaWikiServices::getInstance();
+ $linkRenderer = $services->getLinkRenderer();
foreach ( $categories as $category => $type ) {
// array keys will cast numeric category names to ints, so cast back to string
$category = (string)$category;
if ( !$title ) {
continue;
}
- $wgContLang->findVariantLink( $category, $title, true );
+ $services->getContentLanguage()->findVariantLink( $category, $title, true );
if ( $category != $origcategory && array_key_exists( $category, $categories ) ) {
continue;
}
- $text = $wgContLang->convertHtml( $title->getText() );
+ $text = $services->getContentLanguage()->convertHtml( $title->getText() );
$this->mCategories[$type][] = $title->getText();
$this->mCategoryLinks[$type][] = $linkRenderer->makeLink( $title, new HtmlArmor( $text ) );
}
* Set the revision ID which will be seen by the wiki text parser
* for things such as embedded {{REVISIONID}} variable use.
*
- * @param int|null $revid An positive integer, or null
+ * @param int|null $revid A positive integer, or null
* @return mixed Previous value
*/
public function setRevisionId( $revid ) {
$val = is_null( $revid ) ? null : intval( $revid );
- return wfSetVar( $this->mRevisionId, $val );
+ return wfSetVar( $this->mRevisionId, $val, true );
}
/**
* @return mixed Previous value
*/
public function setRevisionTimestamp( $timestamp ) {
- return wfSetVar( $this->mRevisionTimestamp, $timestamp );
+ return wfSetVar( $this->mRevisionTimestamp, $timestamp, true );
}
/**
/**
* Set the displayed file version
*
- * @param File|bool $file
+ * @param File|null $file
* @return mixed Previous value
*/
public function setFileVersion( $file ) {
* Add wikitext with a custom Title object
*
* @param string $text Wikitext
- * @param Title &$title
+ * @param Title $title
* @param bool $linestart Is this the start of a line?
*/
- public function addWikiTextWithTitle( $text, &$title, $linestart = true ) {
+ public function addWikiTextWithTitle( $text, Title $title, $linestart = true ) {
$this->addWikiTextTitle( $text, $title, $linestart );
}
* Add wikitext with a custom Title object and tidy enabled.
*
* @param string $text Wikitext
- * @param Title &$title
+ * @param Title $title
* @param bool $linestart Is this the start of a line?
*/
- function addWikiTextTitleTidy( $text, &$title, $linestart = true ) {
+ function addWikiTextTitleTidy( $text, Title $title, $linestart = true ) {
$this->addWikiTextTitle( $text, $title, $linestart, true );
}
}
/**
- * Add wikitext with a custom Title object
+ * Add wikitext with a custom Title object.
+ * Output is unwrapped.
*
* @param string $text Wikitext
* @param Title $title
$this->addParserOutput( $parserOutput, [
'enableSectionEditLinks' => false,
+ 'wrapperDivClass' => '',
] );
}
* @since 1.24
* @param ParserOutput $parserOutput
*/
- public function addParserOutputMetadata( $parserOutput ) {
- $this->mLanguageLinks += $parserOutput->getLanguageLinks();
+ public function addParserOutputMetadata( ParserOutput $parserOutput ) {
+ $this->mLanguageLinks =
+ array_merge( $this->mLanguageLinks, $parserOutput->getLanguageLinks() );
$this->addCategoryLinks( $parserOutput->getCategories() );
$this->setIndicators( $parserOutput->getIndicators() );
$this->mNewSectionLink = $parserOutput->getNewSection();
foreach ( $parserOutput->getOutputHooks() as $hookInfo ) {
list( $hookName, $data ) = $hookInfo;
if ( isset( $parserOutputHooks[$hookName] ) ) {
- call_user_func( $parserOutputHooks[$hookName], $this, $parserOutput, $data );
+ $parserOutputHooks[$hookName]( $this, $parserOutput, $data );
}
}
* @param ParserOutput $parserOutput
* @param array $poOptions Options to ParserOutput::getText()
*/
- public function addParserOutputContent( $parserOutput, $poOptions = [] ) {
+ public function addParserOutputContent( ParserOutput $parserOutput, $poOptions = [] ) {
$this->addParserOutputText( $parserOutput, $poOptions );
$this->addModules( $parserOutput->getModules() );
* @param ParserOutput $parserOutput
* @param array $poOptions Options to ParserOutput::getText()
*/
- public function addParserOutputText( $parserOutput, $poOptions = [] ) {
+ public function addParserOutputText( ParserOutput $parserOutput, $poOptions = [] ) {
$text = $parserOutput->getText( $poOptions );
// Avoid PHP 7.1 warning of passing $this by reference
$outputPage = $this;
* @param ParserOutput $parserOutput
* @param array $poOptions Options to ParserOutput::getText()
*/
- function addParserOutput( $parserOutput, $poOptions = [] ) {
+ function addParserOutput( ParserOutput $parserOutput, $poOptions = [] ) {
$this->addParserOutputMetadata( $parserOutput );
$this->addParserOutputText( $parserOutput, $poOptions );
}
*
* @param string $text
* @param bool $linestart Is this the start of a line?
- * @param bool $interface Use interface language ($wgLang instead of
- * $wgContLang) while parsing language sensitive magic words like GRAMMAR and PLURAL.
- * This also disables LanguageConverter.
+ * @param bool $interface Use interface language (instead of content language) while parsing
+ * language sensitive magic words like GRAMMAR and PLURAL. This also disables
+ * LanguageConverter.
* @param Language|null $language Target language object, will override $interface
* @throws MWException
* @return string HTML
*
* @param string $text
* @param bool $linestart Is this the start of a line?
- * @param bool $interface Use interface language ($wgLang instead of
- * $wgContLang) while parsing language sensitive magic
- * words like GRAMMAR and PLURAL
+ * @param bool $interface Use interface language (instead of content language) while parsing
+ * language sensitive magic words like GRAMMAR and PLURAL
* @return string HTML
*/
public function parseInline( $text, $linestart = true, $interface = false ) {
}
/**
- * Lower the value of the "s-maxage" part of the "Cache-control" HTTP header
+ * Set the value of the "s-maxage" part of the "Cache-control" HTTP header to $maxage if that is
+ * lower than the current s-maxage. Either way, $maxage is now an upper limit on s-maxage, so
+ * that future calls to setCdnMaxage() will no longer be able to raise the s-maxage above
+ * $maxage.
*
* @param int $maxage Maximum cache time on the CDN, in seconds
* @since 1.27
* TTL is 90% of the age of the object, subject to the min and max.
*
* @param string|int|float|bool|null $mtime Last-Modified timestamp
- * @param int $minTTL Mimimum TTL in seconds [default: 1 minute]
+ * @param int $minTTL Minimum TTL in seconds [default: 1 minute]
* @param int $maxTTL Maximum TTL in seconds [default: $wgSquidMaxage]
- * @return int TTL in seconds
+ * @return int TTL in seconds passed to lowerCdnMaxage() (may not be the same as the new
+ * s-maxage)
* @since 1.28
*/
public function adaptCdnTTL( $mtime, $minTTL = 0, $maxTTL = 0 ) {
/**
* Use enableClientCache(false) to force it to send nocache headers
*
- * @param bool $state
+ * @param bool|null $state New value, or null to not set the value
*
- * @return bool
+ * @return bool Old value
*/
public function enableClientCache( $state ) {
return wfSetVar( $this->mEnableClientCache, $state );
}
/**
- * Get the list of cookies that will influence on the cache
+ * Get the list of cookie names that will influence the cache
*
* @return array
*/
if ( !is_array( $option ) ) {
$option = [];
}
- $this->mVaryHeader[$header] = array_unique( array_merge( $this->mVaryHeader[$header], $option ) );
+ $this->mVaryHeader[$header] =
+ array_unique( array_merge( $this->mVaryHeader[$header], $option ) );
}
/**
}
/**
- * T23672: Add Accept-Language to Vary and Key headers
- * if there's no 'variant' parameter existed in GET.
+ * T23672: Add Accept-Language to Vary and Key headers if there's no 'variant' parameter in GET.
*
* For example:
- * /w/index.php?title=Main_page should always be served; but
- * /w/index.php?title=Main_page&variant=zh-cn should never be served.
+ * /w/index.php?title=Main_page will vary based on Accept-Language; but
+ * /w/index.php?title=Main_page&variant=zh-cn will not.
*/
- function addAcceptLanguage() {
+ private function addAcceptLanguage() {
$title = $this->getTitle();
if ( !$title instanceof Title ) {
return;
foreach ( $variants as $variant ) {
if ( $variant === $lang->getCode() ) {
continue;
- } else {
- $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 = LanguageCode::bcp47( $variant );
- if ( $variantBCP47 !== $variant ) {
- $aloption[] = 'substr=' . $variantBCP47;
- }
+ }
+
+ $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 = LanguageCode::bcp47( $variant );
+ if ( $variantBCP47 !== $variant ) {
+ $aloption[] = "substr=$variantBCP47";
}
}
$this->addVaryHeader( 'Accept-Language', $aloption );
* @throws MWException
*/
public function output( $return = false ) {
- global $wgContLang;
-
if ( $this->mDoNothing ) {
return $return ? '' : null;
}
ob_start();
$response->header( 'Content-type: ' . $config->get( 'MimeType' ) . '; charset=UTF-8' );
- $response->header( 'Content-language: ' . $wgContLang->getHtmlCode() );
+ $response->header( 'Content-language: ' .
+ MediaWikiServices::getInstance()->getContentLanguage()->getHtmlCode() );
if ( !$this->mArticleBodyOnly ) {
$sk = $this->getSkin();
-
- if ( $sk->shouldPreloadLogo() ) {
- $this->addLogoPreloadLinkHeaders();
- }
}
$linkHeader = $this->getLinkHeader();
$this->rlClientContext = new DerivativeResourceLoaderContext( $this->rlClientContext );
$this->rlClientContext->setContentOverrideCallback( function ( Title $title ) {
foreach ( $this->contentOverrideCallbacks as $callback ) {
- $content = call_user_func( $callback, $title );
+ $content = $callback( $title );
if ( $content !== null ) {
+ $text = ContentHandler::getContentText( $content );
+ if ( strpos( $text, '</script>' ) !== false ) {
+ // Proactively replace this so that we can display a message
+ // to the user, instead of letting it go to Html::inlineScript(),
+ // where it would be considered a server-side issue.
+ $titleFormatted = $title->getPrefixedText();
+ $content = new JavaScriptContent(
+ Xml::encodeJsCall( 'mw.log.error', [
+ "Cannot preview $titleFormatted due to script-closing tag."
+ ] )
+ );
+ }
return $content;
}
}
* @return string The doctype, opening "<html>", and head element.
*/
public function headElement( Skin $sk, $includeStyle = true ) {
- global $wgContLang;
-
$userdir = $this->getLanguage()->getDir();
- $sitedir = $wgContLang->getDir();
+ $sitedir = MediaWikiServices::getInstance()->getContentLanguage()->getDir();
$pieces = [];
$pieces[] = Html::htmlHeader( Sanitizer::mergeAttributes(
* @return array
*/
public function getJSVars() {
- global $wgContLang;
-
$curRevisionId = 0;
$articleId = 0;
$canonicalSpecialPageName = false; # T23115
+ $services = MediaWikiServices::getInstance();
$title = $this->getTitle();
$ns = $title->getNamespace();
if ( $ns == NS_SPECIAL ) {
list( $canonicalSpecialPageName, /*...*/ ) =
- SpecialPageFactory::resolveAlias( $title->getDBkey() );
+ $services->getSpecialPageFactory()->
+ resolveAlias( $title->getDBkey() );
} elseif ( $this->canUseWikiPage() ) {
$wikiPage = $this->getWikiPage();
$curRevisionId = $wikiPage->getLatest();
'wgRelevantPageName' => $relevantTitle->getPrefixedDBkey(),
'wgRelevantArticleId' => $relevantTitle->getArticleID(),
'wgRequestId' => WebRequest::getRequestId(),
+ 'wgCSPNonce' => $this->getCSPNonce(),
];
if ( $user->isLoggedIn() ) {
$vars['wgUserNewMsgRevisionId'] = $user->getNewMessageRevisionId();
}
- if ( $wgContLang->hasVariants() ) {
- $vars['wgUserVariant'] = $wgContLang->getPreferredVariant();
+ $contLang = $services->getContentLanguage();
+ if ( $contLang->hasVariants() ) {
+ $vars['wgUserVariant'] = $contLang->getPreferredVariant();
}
// Same test as SkinTemplate
$vars['wgIsProbablyEditable'] = $title->quickUserCan( 'edit', $user )
] );
}
- /**
- * Add Link headers for preloading the wiki's logo.
- *
- * @since 1.26
- */
- protected function addLogoPreloadLinkHeaders() {
- $logo = ResourceLoaderSkinModule::getLogo( $this->getConfig() );
-
- $tags = [];
- $logosPerDppx = [];
- $logos = [];
-
- if ( !is_array( $logo ) ) {
- // No media queries required if we only have one variant
- $this->addLinkHeader( '<' . $logo . '>;rel=preload;as=image' );
- return;
- }
-
- if ( isset( $logo['svg'] ) ) {
- // No media queries required if we only have a 1x and svg variant
- // because all preload-capable browsers support SVGs
- $this->addLinkHeader( '<' . $logo['svg'] . '>;rel=preload;as=image' );
- return;
- }
-
- foreach ( $logo as $dppx => $src ) {
- // Keys are in this format: "1.5x"
- $dppx = substr( $dppx, 0, -1 );
- $logosPerDppx[$dppx] = $src;
- }
-
- // Because PHP can't have floats as array keys
- uksort( $logosPerDppx, function ( $a , $b ) {
- $a = floatval( $a );
- $b = floatval( $b );
- // Sort from smallest to largest (e.g. 1x, 1.5x, 2x)
- return $a <=> $b;
- } );
-
- foreach ( $logosPerDppx as $dppx => $src ) {
- $logos[] = [ 'dppx' => $dppx, 'src' => $src ];
- }
-
- $logosCount = count( $logos );
- // Logic must match ResourceLoaderSkinModule:
- // - 1x applies to resolution < 1.5dppx
- // - 1.5x applies to resolution >= 1.5dppx && < 2dppx
- // - 2x applies to resolution >= 2dppx
- // Note that min-resolution and max-resolution are both inclusive.
- for ( $i = 0; $i < $logosCount; $i++ ) {
- if ( $i === 0 ) {
- // Smallest dppx
- // min-resolution is ">=" (larger than or equal to)
- // "not min-resolution" is essentially "<"
- $media_query = 'not all and (min-resolution: ' . $logos[ 1 ]['dppx'] . 'dppx)';
- } elseif ( $i !== $logosCount - 1 ) {
- // In between
- // Media query expressions can only apply "not" to the entire expression
- // (e.g. can't express ">= 1.5 and not >= 2).
- // Workaround: Use <= 1.9999 in place of < 2.
- $upper_bound = floatval( $logos[ $i + 1 ]['dppx'] ) - 0.000001;
- $media_query = '(min-resolution: ' . $logos[ $i ]['dppx'] .
- 'dppx) and (max-resolution: ' . $upper_bound . 'dppx)';
- } else {
- // Largest dppx
- $media_query = '(min-resolution: ' . $logos[ $i ]['dppx'] . 'dppx)';
- }
-
- $this->addLinkHeader(
- '<' . $logos[$i]['src'] . '>;rel=preload;as=image;media=' . $media_query
- );
- }
- }
-
/**
* Get (and set if not yet set) the CSP nonce.
*