* @todo document
*/
class OutputPage extends ContextSource {
- /** @var array Should be private. Used with addMeta() which adds "<meta>" */
+ /** @var string[][] Should be private. Used with addMeta() which adds "<meta>" */
protected $mMetatags = [];
/** @var array */
protected $mLinktags = [];
- /** @var bool */
+ /** @var string|bool */
protected $mCanonicalUrl = false;
/**
- * @var string The contents of <h1> */
+ * @var string The contents of <h1>
+ */
private $mPageTitle = '';
/**
* a OutputPage tied to that context.
* @param IContextSource $context
*/
- function __construct( IContextSource $context ) {
+ public function __construct( IContextSource $context ) {
$this->setContext( $context );
}
* @param string $name Name of the meta tag
* @param string $val Value of the meta tag
*/
- function addMeta( $name, $val ) {
- array_push( $this->mMetatags, [ $name, $val ] );
+ public function addMeta( $name, $val ) {
+ $this->mMetatags[] = [ $name, $val ];
}
/**
*
* @param array $linkarr Associative array of attributes.
*/
- function addLink( array $linkarr ) {
- array_push( $this->mLinktags, $linkarr );
+ public function addLink( array $linkarr ) {
+ $this->mLinktags[] = $linkarr;
}
/**
* in preference to addLink(), to avoid duplicate link tags.
* @param string $url
*/
- function setCanonicalUrl( $url ) {
+ public function setCanonicalUrl( $url ) {
$this->mCanonicalUrl = $url;
}
*
* @param string $script Raw HTML
*/
- function addScript( $script ) {
+ public function addScript( $script ) {
$this->mScripts .= $script;
}
*
* @return array
*/
- function getHeadItemsArray() {
+ public function getHeadItemsArray() {
return $this->mHeadItems;
}
* @param Title $t
*/
public function setTitle( Title $t ) {
+ // @phan-suppress-next-next-line PhanUndeclaredMethod
+ // @fixme Not all implementations of IContextSource have this method!
$this->getContext()->setTitle( $t );
}
* @param ParserOptions|null $options Either the ParserOption to use or null to only get the
* current ParserOption object. This parameter is deprecated since 1.31.
* @return ParserOptions
+ * @suppress PhanUndeclaredProperty For isBogus
*/
public function parserOptions( $options = null ) {
if ( $options !== null ) {
return $this->mRevisionId;
}
+ /**
+ * Whether the revision displayed is the latest revision of the page
+ *
+ * @since 1.34
+ * @return bool
+ */
+ public function isRevisionCurrent() {
+ return $this->mRevisionId == 0 || $this->mRevisionId == $this->getTitle()->getLatestRevID();
+ }
+
/**
* Set the timestamp of the revision which will be displayed. This is used
* to avoid a extra DB call in Skin::lastModified().
* @param string $text Wikitext
* @param Title $title
* @param bool $linestart Is this the start of a line?
- * @param bool $tidy Whether to use tidy.
- * Setting this to false (or omitting it) is deprecated
- * since 1.32; all wikitext should be tidied.
* @param bool $interface Whether it is an interface message
* (for example disables conversion)
* @param string $wrapperClass if not empty, wraps the output in
* a `<div class="$wrapperClass">`
- * @private
*/
private function addWikiTextTitleInternal(
$text, Title $title, $linestart, $interface, $wrapperClass = null
* @param ParserOutput $parserOutput
* @param array $poOptions Options to ParserOutput::getText()
*/
- function addParserOutput( ParserOutput $parserOutput, $poOptions = [] ) {
+ public function addParserOutput( ParserOutput $parserOutput, $poOptions = [] ) {
$this->addParserOutputMetadata( $parserOutput );
$this->addParserOutputText( $parserOutput, $poOptions );
}
}
/**
- * Get TTL in [$minTTL,$maxTTL] in pass it to lowerCdnMaxage()
+ * Get TTL in [$minTTL,$maxTTL] and pass it to lowerCdnMaxage()
*
* This sets and returns $minTTL if $mtime is false or null. Otherwise,
* the TTL is higher the older the $mtime timestamp is. Essentially, the
$maxTTL = $maxTTL ?: $this->getConfig()->get( 'CdnMaxAge' );
if ( $mtime === null || $mtime === false ) {
- return $minTTL; // entity does not exist
+ return; // entity does not exist
}
- $age = MWTimestamp::time() - wfTimestamp( TS_UNIX, $mtime );
+ $age = MWTimestamp::time() - (int)wfTimestamp( TS_UNIX, $mtime );
$adaptiveTTL = max( 0.9 * $age, $minTTL );
$adaptiveTTL = min( $adaptiveTTL, $maxTTL );
*
* @return array
*/
- function getCacheVaryCookies() {
+ public function getCacheVaryCookies() {
if ( self::$cacheVaryCookies === null ) {
$config = $this->getConfig();
self::$cacheVaryCookies = array_values( array_unique( array_merge(
*
* @return bool
*/
- function haveCacheVaryCookies() {
+ public function haveCacheVaryCookies() {
$request = $this->getRequest();
foreach ( $this->getCacheVaryCookies() as $cookieName ) {
if ( $request->getCookie( $cookieName, '', '' ) !== '' ) {
/**
* Return a Link: header. Based on the values of $mLinkHeader.
*
- * @return string
+ * @return string|false
*/
public function getLinkHeader() {
if ( !$this->mLinkHeader ) {
$this->mCdnMaxage != 0 &&
!$this->haveCacheVaryCookies()
) {
- if ( $config->get( 'UseESI' ) ) {
- wfDeprecated( '$wgUseESI = true', '1.33' );
- # We'll purge the proxy cache explicitly, but require end user agents
- # to revalidate against the proxy on each visit.
- # Surrogate-Control controls our CDN, Cache-Control downstream caches
- wfDebug( __METHOD__ .
- ": proxy caching with ESI; {$this->mLastModified} **", 'private' );
- # 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={$config->get( 'CdnMaxAge' )}" .
- "+{$this->mCdnMaxage}, content=\"ESI/1.0\""
- );
- $response->header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' );
- } else {
- # We'll purge the proxy cache for anons explicitly, but require end user agents
- # to revalidate against the proxy on each visit.
- # IMPORTANT! The CDN needs to replace the Cache-Control header with
- # Cache-Control: s-maxage=0, must-revalidate, max-age=0
- wfDebug( __METHOD__ .
- ": local proxy caching; {$this->mLastModified} **", 'private' );
- # 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->mCdnMaxage}, must-revalidate, max-age=0" );
- }
+ # We'll purge the proxy cache for anons explicitly, but require end user agents
+ # to revalidate against the proxy on each visit.
+ # IMPORTANT! The CDN needs to replace the Cache-Control header with
+ # Cache-Control: s-maxage=0, must-revalidate, max-age=0
+ wfDebug( __METHOD__ .
+ ": local proxy caching; {$this->mLastModified} **", 'private' );
+ # 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->mCdnMaxage}, must-revalidate, max-age=0" );
} else {
# We do want clients to cache if they can, but they *must* check for updates
# on revisiting the page.
* and optionally an custom HTML title (content of the "<title>" tag).
*
* @param string|Message $pageTitle Will be passed directly to setPageTitle()
- * @param string|Message $htmlTitle Will be passed directly to setHTMLTitle();
+ * @param string|Message|false $htmlTitle Will be passed directly to setHTMLTitle();
* optional, if not passed the "<title>" attribute will be
* based on $pageTitle
*/
* @param string|null $action Action that was denied or null if unknown
*/
public function showPermissionsErrorPage( array $errors, $action = null ) {
+ $services = MediaWikiServices::getInstance();
+ $permissionManager = $services->getPermissionManager();
foreach ( $errors as $key => $error ) {
$errors[$key] = (array)$error;
}
// 1. the user is not logged in
// 2. the only error is insufficient permissions (i.e. no block or something else)
// 3. the error can be avoided simply by logging in
+
if ( in_array( $action, [ 'read', 'edit', 'createpage', 'createtalk', 'upload' ] )
&& $this->getUser()->isAnon() && count( $errors ) == 1 && isset( $errors[0][0] )
&& ( $errors[0][0] == 'badaccess-groups' || $errors[0][0] == 'badaccess-group0' )
- && ( User::groupHasPermission( 'user', $action )
- || User::groupHasPermission( 'autoconfirmed', $action ) )
+ && ( $permissionManager->groupHasPermission( 'user', $action )
+ || $permissionManager->groupHasPermission( 'autoconfirmed', $action ) )
) {
$displayReturnto = null;
}
}
- $services = MediaWikiServices::getInstance();
-
$title = SpecialPage::getTitleFor( 'Userlogin' );
$linkRenderer = $services->getLinkRenderer();
$loginUrl = $title->getLinkURL( $query, false, PROTO_RELATIVE );
$this->prepareErrorPage( $this->msg( 'loginreqtitle' ) );
$this->addHTML( $this->msg( $msg )->rawParams( $loginLink )->params( $loginUrl )->parse() );
- $permissionManager = $services->getPermissionManager();
-
# Don't return to a page the user can't read otherwise
# we'll end up in a pointless loop
if ( $displayReturnto && $permissionManager->userCan(
/**
* Add a "return to" link pointing to a specified title
*
- * @param Title $title Title to link
+ * @param LinkTarget $title Title to link
* @param array $query Query string parameters
* @param string|null $text Text of the link (input is not escaped)
* @param array $options Options array to pass to Linker
* @return string The doctype, opening "<html>", and head element.
*/
public function headElement( Skin $sk, $includeStyle = true ) {
+ $config = $this->getConfig();
$userdir = $this->getLanguage()->getDir();
$sitedir = MediaWikiServices::getInstance()->getContentLanguage()->getDir();
$pieces = [];
- $pieces[] = Html::htmlHeader( Sanitizer::mergeAttributes(
+ $htmlAttribs = Sanitizer::mergeAttributes(
$this->getRlClient()->getDocumentAttributes(),
$sk->getHtmlElementAttributes()
- ) );
+ );
+ $pieces[] = Html::htmlHeader( $htmlAttribs );
$pieces[] = Html::openElement( 'head' );
if ( $this->getHTMLTitle() == '' ) {
$this->setHTMLTitle( $this->msg( 'pagetitle', $this->getPageTitle() )->inContentLanguage() );
}
- if ( !Html::isXmlMimeType( $this->getConfig()->get( 'MimeType' ) ) ) {
+ if ( !Html::isXmlMimeType( $config->get( 'MimeType' ) ) ) {
// Add <meta charset="UTF-8">
// This should be before <title> since it defines the charset used by
// text including the text inside <title>.
}
$pieces[] = Html::element( 'title', null, $this->getHTMLTitle() );
- $pieces[] = $this->getRlClient()->getHeadHtml();
+ $pieces[] = $this->getRlClient()->getHeadHtml( $htmlAttribs['class'] ?? null );
$pieces[] = $this->buildExemptModules();
$pieces = array_merge( $pieces, array_values( $this->getHeadLinksArray() ) );
$pieces = array_merge( $pieces, array_values( $this->mHeadItems ) );
+ // This library is intended to run on older browsers that MediaWiki no longer
+ // supports as Grade A. For these Grade C browsers, we provide an experience
+ // using only HTML and CSS. But, where standards-compliant browsers are able to
+ // style unknown HTML elements without issue, old IE ignores these styles.
+ // The html5shiv library fixes that.
// Use an IE conditional comment to serve the script only to old IE
+ $shivUrl = $config->get( 'ResourceBasePath' ) . '/resources/lib/html5shiv/html5shiv.js';
$pieces[] = '<!--[if lt IE 9]>' .
- ResourceLoaderClientHtml::makeLoad(
- new ResourceLoaderContext(
- $this->getResourceLoader(),
- new FauxRequest( [] )
- ),
- [ 'html5shiv' ],
- ResourceLoaderModule::TYPE_SCRIPTS,
- [ 'raw' => '1', 'sync' => '1' ],
- $this->getCSPNonce()
- ) .
+ Html::linkedScript( $shivUrl, $this->getCSPNonce() ) .
'<![endif]-->';
$pieces[] = Html::closeElement( 'head' );
$title = $this->getTitle();
$ns = $title->getNamespace();
- $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
+ $nsInfo = $services->getNamespaceInfo();
$canonicalNamespace = $nsInfo->exists( $ns )
? $nsInfo->getCanonicalName( $ns )
: $title->getNsText();
$vars['wgUserId'] = $user->getId();
$vars['wgUserEditCount'] = $user->getEditCount();
$userReg = $user->getRegistration();
- $vars['wgUserRegistration'] = $userReg ? wfTimestamp( TS_UNIX, $userReg ) * 1000 : null;
+ $vars['wgUserRegistration'] = $userReg ? (int)wfTimestamp( TS_UNIX, $userReg ) * 1000 : null;
// Get the revision ID of the oldest new message on the user's talk
// page. This can be used for constructing new message alerts on
// the client side.
$vars['wgUserVariant'] = $contLang->getPreferredVariant();
}
// Same test as SkinTemplate
- $vars['wgIsProbablyEditable'] = $title->quickUserCan( 'edit', $user )
- && ( $title->exists() || $title->quickUserCan( 'create', $user ) );
+ $vars['wgIsProbablyEditable'] = $this->userCanEditOrCreate( $user, $title );
- $vars['wgRelevantPageIsProbablyEditable'] = $relevantTitle
- && $relevantTitle->quickUserCan( 'edit', $user )
- && ( $relevantTitle->exists() || $relevantTitle->quickUserCan( 'create', $user ) );
+ $vars['wgRelevantPageIsProbablyEditable'] = $relevantTitle &&
+ $this->userCanEditOrCreate( $user, $relevantTitle );
foreach ( $title->getRestrictionTypes() as $type ) {
// Following keys are set in $vars:
return true;
}
+ /**
+ * @param User $user
+ * @param LinkTarget $title
+ * @return bool
+ */
+ private function userCanEditOrCreate(
+ User $user,
+ LinkTarget $title
+ ) {
+ $pm = MediaWikiServices::getInstance()->getPermissionManager();
+ return $pm->quickUserCan( 'edit', $user, $title )
+ && ( $this->getTitle()->exists() ||
+ $pm->quickUserCan( 'create', $user, $title ) );
+ }
+
/**
* @return array Array in format "link name or number => 'link html'".
*/
# Universal edit button
if ( $config->get( 'UniversalEditButton' ) && $this->isArticleRelated() ) {
- $user = $this->getUser();
- if ( $this->getTitle()->quickUserCan( 'edit', $user )
- && ( $this->getTitle()->exists() ||
- $this->getTitle()->quickUserCan( 'create', $user ) )
- ) {
+ if ( $this->userCanEditOrCreate( $this->getUser(), $this->getTitle() ) ) {
// Original UniversalEditButton
$msg = $this->msg( 'edit' )->text();
$tags['universal-edit-button'] = Html::element( 'link', [