/** @var bool Boolean for initialisation on demand */
public $mRestrictionsLoaded = false;
- /** @var string Text form including namespace/interwiki, initialised on demand */
- protected $mPrefixedText = null;
+ /**
+ * Text form including namespace/interwiki, initialised on demand
+ *
+ * Only public to share cache with TitleFormatter
+ *
+ * @private
+ * @var string
+ */
+ public $prefixedText = null;
/** @var mixed Cached value for getTitleProtection (create protection) */
public $mTitleProtection;
*/
public function isSpecial( $name ) {
if ( $this->isSpecialPage() ) {
- list( $thisName, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform );
+ list( $thisName, /* $subpage */ ) =
+ MediaWikiServices::getInstance()->getSpecialPageFactory()->
+ resolveAlias( $this->mDbkeyform );
if ( $name == $thisName ) {
return true;
}
*/
public function fixSpecialName() {
if ( $this->isSpecialPage() ) {
- list( $canonicalName, $par ) = SpecialPageFactory::resolveAlias( $this->mDbkeyform );
+ $spFactory = MediaWikiServices::getInstance()->getSpecialPageFactory();
+ list( $canonicalName, $par ) = $spFactory->resolveAlias( $this->mDbkeyform );
if ( $canonicalName ) {
- $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par );
+ $localName = $spFactory->getLocalNameFor( $canonicalName, $par );
if ( $localName != $this->mDbkeyform ) {
return self::makeTitle( NS_SPECIAL, $localName );
}
);
}
+ /**
+ * Is this a message which can contain raw HTML?
+ *
+ * @return bool
+ * @since 1.32
+ */
+ public function isRawHtmlMessage() {
+ global $wgRawHtmlMessages;
+
+ if ( !$this->inNamespace( NS_MEDIAWIKI ) ) {
+ return false;
+ }
+ $message = lcfirst( $this->getRootTitle()->getDBkey() );
+ return in_array( $message, $wgRawHtmlMessages, true );
+ }
+
/**
* Is this a talk page of some sort?
*
if ( $nsText === false ) {
// See T165149. Awkward, but better than erroneously linking to the main namespace.
$nsText = MediaWikiServices::getInstance()->getContentLanguage()->
- getNsText( NS_SPECIAL ) . ":Badtitle/NS{$this->mNamespace}";
+ getNsText( NS_SPECIAL ) . ":Badtitle/NS{$this->mNamespace}";
}
$p .= $nsText . ':';
* @return string The prefixed title, with spaces
*/
public function getPrefixedText() {
- if ( $this->mPrefixedText === null ) {
+ if ( $this->prefixedText === null ) {
$s = $this->prefix( $this->mTextform );
$s = strtr( $s, '_', ' ' );
- $this->mPrefixedText = $s;
+ $this->prefixedText = $s;
}
- return $this->mPrefixedText;
+ return $this->prefixedText;
}
/**
$error = [ 'sitejsonprotected', $action ];
} elseif ( $this->isSiteJsConfigPage() && !$user->isAllowed( 'editsitejs' ) ) {
$error = [ 'sitejsprotected', $action ];
+ } elseif ( $this->isRawHtmlMessage() ) {
+ // Raw HTML can be used to deploy CSS or JS so require rights for both.
+ if ( !$user->isAllowed( 'editsitejs' ) ) {
+ $error = [ 'sitejsprotected', $action ];
+ } elseif ( !$user->isAllowed( 'editsitecss' ) ) {
+ $error = [ 'sitecssprotected', $action ];
+ }
}
if ( $error ) {
# Protect css/json/js subpages of user pages
# XXX: this might be better using restrictions
- if ( $action != 'patrol' ) {
- if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
- if (
- $this->isUserCssConfigPage()
- && !$user->isAllowedAny( 'editmyusercss', 'editusercss' )
- ) {
- $errors[] = [ 'mycustomcssprotected', $action ];
- } elseif (
- $this->isUserJsonConfigPage()
- && !$user->isAllowedAny( 'editmyuserjson', 'edituserjson' )
- ) {
- $errors[] = [ 'mycustomjsonprotected', $action ];
- } elseif (
- $this->isUserJsConfigPage()
- && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
- ) {
- $errors[] = [ 'mycustomjsprotected', $action ];
- }
- } else {
+ if ( $action === 'patrol' ) {
+ return $errors;
+ }
+
+ if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
+ // Users need editmyuser* to edit their own CSS/JSON/JS subpages.
+ if (
+ $this->isUserCssConfigPage()
+ && !$user->isAllowedAny( 'editmyusercss', 'editusercss' )
+ ) {
+ $errors[] = [ 'mycustomcssprotected', $action ];
+ } elseif (
+ $this->isUserJsonConfigPage()
+ && !$user->isAllowedAny( 'editmyuserjson', 'edituserjson' )
+ ) {
+ $errors[] = [ 'mycustomjsonprotected', $action ];
+ } elseif (
+ $this->isUserJsConfigPage()
+ && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
+ ) {
+ $errors[] = [ 'mycustomjsprotected', $action ];
+ }
+ } else {
+ // Users need editmyuser* to edit their own CSS/JSON/JS subpages, except for
+ // deletion/suppression which cannot be used for attacks and we want to avoid the
+ // situation where an unprivileged user can post abusive content on their subpages
+ // and only very highly privileged users could remove it.
+ if ( !in_array( $action, [ 'delete', 'deleterevision', 'suppressrevision' ], true ) ) {
if (
$this->isUserCssConfigPage()
&& !$user->isAllowed( 'editusercss' )
} elseif ( $this->isSpecialPage() ) {
# If it's a special page, ditch the subpage bit and check again
$name = $this->mDbkeyform;
- list( $name, /* $subpage */ ) = SpecialPageFactory::resolveAlias( $name );
+ list( $name, /* $subpage */ ) =
+ MediaWikiServices::getInstance()->getSpecialPageFactory()->
+ resolveAlias( $name );
if ( $name ) {
$pure = SpecialPage::getTitleFor( $name )->getPrefixedText();
if ( in_array( $pure, $wgWhitelistRead, true ) ) {
$id = $this->getArticleID();
if ( $id ) {
$cache = ObjectCache::getMainWANInstance();
+ $fname = __METHOD__;
$rows = $cache->getWithSetCallback(
// Page protections always leave a new null revision
$cache->makeKey( 'page-restrictions', $id, $this->getLatestRevID() ),
$cache::TTL_DAY,
- function ( $curValue, &$ttl, array &$setOpts ) {
+ function ( $curValue, &$ttl, array &$setOpts ) use ( $fname ) {
$dbr = wfGetDB( DB_REPLICA );
$setOpts += Database::getCacheSetOptions( $dbr );
'page_restrictions',
[ 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ],
[ 'pr_page' => $this->getArticleID() ],
- __METHOD__
+ $fname
)
);
}
$revQuery['joins']
);
if ( $row ) {
- return new Revision( $row );
+ return new Revision( $row, 0, $this );
}
}
return null;
return (bool)wfFindFile( $this );
case NS_SPECIAL:
// valid special page
- return SpecialPageFactory::exists( $this->mDbkeyform );
+ return MediaWikiServices::getInstance()->getSpecialPageFactory()->
+ exists( $this->mDbkeyform );
case NS_MAIN:
// selflink, possibly with fragment
return $this->mDbkeyform == '';
}
/**
- * Get the default message text or false if the message doesn't exist
+ * Get the default (plain) message contents for an page that overrides an
+ * interface message key.
+ *
+ * Primary use cases:
+ *
+ * - Article:
+ * - Show default when viewing the page. The Article::getSubstituteContent
+ * method displays the default message content, instead of the
+ * 'noarticletext' placeholder message normally used.
+ *
+ * - EditPage:
+ * - Title of edit page. When creating an interface message override,
+ * the editor is told they are "Editing the page", instead of
+ * "Creating the page". (EditPage::setHeaders)
+ * - Edit notice. The 'translateinterface' edit notice is shown when creating
+ * or editing a an interface message override. (EditPage::showIntro)
+ * - Opening the editor. The contents of the localisation message are used
+ * as contents of the editor when creating a new page in the MediaWiki
+ * namespace. This simplifies the process for editors when "changing"
+ * an interface message by creating an override. (EditPage::getContentObject)
+ * - Showing a diff. The left-hand side of a diff when an editor is
+ * previewing their changes before saving the creation of a page in the
+ * MediaWiki namespace. (EditPage::showDiff)
+ * - Disallowing a save. When attempting to create a a MediaWiki-namespace
+ * page with the proposed content matching the interface message default,
+ * the save is rejected, the same way we disallow blank pages from being
+ * created. (EditPage::internalAttemptSave)
+ *
+ * - ApiEditPage:
+ * - Default content, when using the 'prepend' or 'append' feature.
+ *
+ * - SkinTemplate:
+ * - Label the create action as "Edit", if the page can be an override.
*
* @return string|bool
*/