This patch reworks RestrictionStore::getCascadeProtectionSourcesInternal to return a third and fourth array: * One for cascading restrictions originating from templatelinks * Another for those originating from imagelinks They are used in PermissionManager::checkCascadingSourcesRestrictions to differentiate cascading protection of file content and file page, but could also be used in the future by action=info and other callers. Bug: T24521 Bug: T62109 Bug: T140010 Change-Id: Ia5863f418538106f4fd657c672298ff6ac835805 (cherry picked from commit 7a4952ef2c5d593fae9419bad39f3e9894f42adf)
681 lines
21 KiB
PHP
681 lines
21 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\EditPage;
|
|
|
|
use LogEventsList;
|
|
use MediaWiki\Block\Block;
|
|
use MediaWiki\Block\DatabaseBlockStore;
|
|
use MediaWiki\Config\Config;
|
|
use MediaWiki\Html\Html;
|
|
use MediaWiki\Language\RawMessage;
|
|
use MediaWiki\Linker\LinkRenderer;
|
|
use MediaWiki\MainConfigNames;
|
|
use MediaWiki\Page\ProperPageIdentity;
|
|
use MediaWiki\Permissions\Authority;
|
|
use MediaWiki\Permissions\PermissionManager;
|
|
use MediaWiki\Permissions\RestrictionStore;
|
|
use MediaWiki\Revision\RevisionRecord;
|
|
use MediaWiki\SpecialPage\SpecialPage;
|
|
use MediaWiki\SpecialPage\SpecialPageFactory;
|
|
use MediaWiki\Title\NamespaceInfo;
|
|
use MediaWiki\Title\Title;
|
|
use MediaWiki\User\TempUser\TempUserCreator;
|
|
use MediaWiki\User\UserFactory;
|
|
use MediaWiki\User\UserNameUtils;
|
|
use MediaWiki\User\UserRigorOptions;
|
|
use MediaWiki\Utils\UrlUtils;
|
|
use MessageLocalizer;
|
|
use RepoGroup;
|
|
use Skin;
|
|
use SkinFactory;
|
|
use Wikimedia\Rdbms\IConnectionProvider;
|
|
use Wikimedia\Rdbms\ReadOnlyMode;
|
|
|
|
/**
|
|
* Provides the intro messages (edit notices and others) to be displayed before an edit form.
|
|
*
|
|
* Used by EditPage, and may be used by extensions providing alternative editors.
|
|
*
|
|
* @since 1.41
|
|
*/
|
|
class IntroMessageBuilder {
|
|
|
|
use ParametersHelper;
|
|
|
|
// Parameters for getIntroMessages()
|
|
public const MORE_FRAMES = 1;
|
|
public const LESS_FRAMES = 2;
|
|
|
|
private Config $config;
|
|
private LinkRenderer $linkRenderer;
|
|
private PermissionManager $permManager;
|
|
private UserNameUtils $userNameUtils;
|
|
private TempUserCreator $tempUserCreator;
|
|
private UserFactory $userFactory;
|
|
private RestrictionStore $restrictionStore;
|
|
private DatabaseBlockStore $blockStore;
|
|
private ReadOnlyMode $readOnlyMode;
|
|
private SpecialPageFactory $specialPageFactory;
|
|
private RepoGroup $repoGroup;
|
|
private NamespaceInfo $namespaceInfo;
|
|
private SkinFactory $skinFactory;
|
|
private IConnectionProvider $dbProvider;
|
|
private UrlUtils $urlUtils;
|
|
|
|
public function __construct(
|
|
Config $config,
|
|
LinkRenderer $linkRenderer,
|
|
PermissionManager $permManager,
|
|
UserNameUtils $userNameUtils,
|
|
TempUserCreator $tempUserCreator,
|
|
UserFactory $userFactory,
|
|
RestrictionStore $restrictionStore,
|
|
DatabaseBlockStore $blockStore,
|
|
ReadOnlyMode $readOnlyMode,
|
|
SpecialPageFactory $specialPageFactory,
|
|
RepoGroup $repoGroup,
|
|
NamespaceInfo $namespaceInfo,
|
|
SkinFactory $skinFactory,
|
|
IConnectionProvider $dbProvider,
|
|
UrlUtils $urlUtils
|
|
) {
|
|
$this->config = $config;
|
|
$this->linkRenderer = $linkRenderer;
|
|
$this->permManager = $permManager;
|
|
$this->userNameUtils = $userNameUtils;
|
|
$this->tempUserCreator = $tempUserCreator;
|
|
$this->userFactory = $userFactory;
|
|
$this->restrictionStore = $restrictionStore;
|
|
$this->blockStore = $blockStore;
|
|
$this->readOnlyMode = $readOnlyMode;
|
|
$this->specialPageFactory = $specialPageFactory;
|
|
$this->repoGroup = $repoGroup;
|
|
$this->namespaceInfo = $namespaceInfo;
|
|
$this->skinFactory = $skinFactory;
|
|
$this->dbProvider = $dbProvider;
|
|
$this->urlUtils = $urlUtils;
|
|
}
|
|
|
|
/**
|
|
* Wrapper for LogEventsList::showLogExtract() that returns the string with the output.
|
|
*
|
|
* LogEventsList::showLogExtract() has some side effects affecting the global state (main request
|
|
* context), which should not be relied upon.
|
|
*
|
|
* @param string|array $types See LogEventsList::showLogExtract()
|
|
* @param string $page See LogEventsList::showLogExtract()
|
|
* @param string $user See LogEventsList::showLogExtract()
|
|
* @param array $param See LogEventsList::showLogExtract()
|
|
* @return string
|
|
*/
|
|
private function getLogExtract( $types = [], $page = '', $user = '', $param = [] ): string {
|
|
$outString = '';
|
|
LogEventsList::showLogExtract( $outString, $types, $page, $user, $param );
|
|
return $outString;
|
|
}
|
|
|
|
/**
|
|
* Return intro messages to be shown before an edit form.
|
|
*
|
|
* The message identifiers used as array keys are stable. Callers of this method may recognize
|
|
* specific messages and omit them when displaying, if they're not applicable to some interface or
|
|
* if they provide the same information in an alternative way.
|
|
*
|
|
* Callers should load the 'mediawiki.interface.helpers.styles' ResourceLoader module, as some of
|
|
* the possible messages rely on those styles.
|
|
*
|
|
* @param int $frames Some intro messages come with optional wrapper frames.
|
|
* Pass IntroMessageBuilder::MORE_FRAMES to include the frames whenever possible,
|
|
* or IntroMessageBuilder::LESS_FRAMES to omit them whenever possible.
|
|
* @param string[] $skip Identifiers of messages not to generate
|
|
* @param MessageLocalizer $localizer
|
|
* @param ProperPageIdentity $page Page being viewed
|
|
* @param RevisionRecord|null $revRecord Revision being viewed, null if page doesn't exist
|
|
* @param Authority $performer
|
|
* @param string|null $editIntro
|
|
* @param string|null $returnToQuery
|
|
* @param bool $preview
|
|
* @param string|null $section
|
|
* @return array<string,string> Ordered map of identifiers to message HTML
|
|
*/
|
|
public function getIntroMessages(
|
|
int $frames,
|
|
array $skip,
|
|
MessageLocalizer $localizer,
|
|
ProperPageIdentity $page,
|
|
?RevisionRecord $revRecord,
|
|
Authority $performer,
|
|
?string $editIntro,
|
|
?string $returnToQuery,
|
|
bool $preview,
|
|
?string $section = null
|
|
): array {
|
|
$title = Title::newFromPageIdentity( $page );
|
|
$messages = new IntroMessageList( $frames, $skip );
|
|
|
|
$this->addOldRevisionWarning( $messages, $localizer, $revRecord );
|
|
|
|
if ( !$preview ) {
|
|
$this->addCodeEditingIntro( $messages, $localizer, $title, $performer );
|
|
$this->addSharedRepoHint( $messages, $localizer, $page );
|
|
$this->addUserWarnings( $messages, $localizer, $title, $performer );
|
|
$this->addEditIntro( $messages, $localizer, $page, $performer, $editIntro, $section );
|
|
$this->addRecreateWarning( $messages, $localizer, $page );
|
|
}
|
|
|
|
$this->addTalkPageText( $messages, $localizer, $title );
|
|
$this->addEditNotices( $messages, $localizer, $title, $revRecord );
|
|
|
|
$this->addReadOnlyWarning( $messages, $localizer );
|
|
$this->addAnonEditWarning( $messages, $localizer, $title, $performer, $returnToQuery, $preview );
|
|
$this->addUserConfigPageInfo( $messages, $localizer, $title, $performer, $preview );
|
|
$this->addPageProtectionWarningHeaders( $messages, $localizer, $page );
|
|
$this->addHeaderCopyrightWarning( $messages, $localizer );
|
|
|
|
return $messages->getList();
|
|
}
|
|
|
|
/**
|
|
* Adds introduction to code editing.
|
|
*/
|
|
private function addCodeEditingIntro(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
Title $title,
|
|
Authority $performer
|
|
): void {
|
|
$isUserJsConfig = $title->isUserJsConfigPage();
|
|
$namespace = $title->getNamespace();
|
|
$intro = '';
|
|
|
|
if (
|
|
$title->isUserConfigPage() &&
|
|
$title->isSubpageOf( Title::makeTitle( NS_USER, $performer->getUser()->getName() ) )
|
|
) {
|
|
$isUserCssConfig = $title->isUserCssConfigPage();
|
|
$isUserJsonConfig = $title->isUserJsonConfigPage();
|
|
$isUserJsConfig = $title->isUserJsConfigPage();
|
|
|
|
if ( $isUserCssConfig ) {
|
|
$warning = 'usercssispublic';
|
|
} elseif ( $isUserJsonConfig ) {
|
|
$warning = 'userjsonispublic';
|
|
} else {
|
|
$warning = 'userjsispublic';
|
|
}
|
|
|
|
$warningText = $localizer->msg( $warning )->parse();
|
|
$intro .= $warningText ? Html::rawElement(
|
|
'div',
|
|
[ 'class' => 'mw-userconfigpublic' ],
|
|
$warningText
|
|
) : '';
|
|
|
|
}
|
|
$codeMsg = $localizer->msg( 'editpage-code-message' );
|
|
$codeMessageText = $codeMsg->isDisabled() ? '' : $codeMsg->parseAsBlock();
|
|
$isJavaScript = $title->hasContentModel( CONTENT_MODEL_JAVASCRIPT );
|
|
$isCSS = $title->hasContentModel( CONTENT_MODEL_CSS );
|
|
|
|
if ( $namespace === NS_MEDIAWIKI ) {
|
|
$interfaceMsg = $localizer->msg( 'editinginterface' );
|
|
$interfaceMsgText = $interfaceMsg->parse();
|
|
# Show a warning if editing an interface message
|
|
$intro .= $interfaceMsgText ? Html::rawElement(
|
|
'div',
|
|
[ 'class' => 'mw-editinginterface' ],
|
|
$interfaceMsgText
|
|
) : '';
|
|
# If this is a default message (but not css, json, or js),
|
|
# show a hint that it is translatable on translatewiki.net
|
|
if (
|
|
!$isCSS
|
|
&& !$title->hasContentModel( CONTENT_MODEL_JSON )
|
|
&& !$isJavaScript
|
|
) {
|
|
$defaultMessageText = $title->getDefaultMessageText();
|
|
if ( $defaultMessageText !== false ) {
|
|
$translateInterfaceText = $localizer->msg( 'translateinterface' )->parse();
|
|
$intro .= $translateInterfaceText ? Html::rawElement(
|
|
'div',
|
|
[ 'class' => 'mw-translateinterface' ],
|
|
$translateInterfaceText
|
|
) : '';
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( $isUserJsConfig ) {
|
|
$userConfigDangerousMsg = $localizer->msg( 'userjsdangerous' )->parse();
|
|
$intro .= $userConfigDangerousMsg ? Html::rawElement(
|
|
'div',
|
|
[ 'class' => 'mw-userconfigdangerous' ],
|
|
$userConfigDangerousMsg
|
|
) : '';
|
|
}
|
|
|
|
// If the wiki page contains JavaScript or CSS link add message specific to code.
|
|
if ( $isJavaScript || $isCSS ) {
|
|
$intro .= $codeMessageText;
|
|
}
|
|
|
|
$messages->addWithKey(
|
|
'code-editing-intro',
|
|
$intro,
|
|
// While semantically this is a warning, given the impact of editing these pages,
|
|
// it's best to deter users who don't understand what they are doing by
|
|
// acknowledging the danger here. This is a potentially destructive action
|
|
// so requires destructive coloring.
|
|
Html::errorBox( '$1' )
|
|
);
|
|
}
|
|
|
|
private function addSharedRepoHint(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
ProperPageIdentity $page
|
|
): void {
|
|
$namespace = $page->getNamespace();
|
|
if ( $namespace === NS_FILE ) {
|
|
# Show a hint to shared repo
|
|
$file = $this->repoGroup->findFile( $page );
|
|
if ( $file && !$file->isLocal() ) {
|
|
$descUrl = $file->getDescriptionUrl();
|
|
# there must be a description url to show a hint to shared repo
|
|
if ( $descUrl ) {
|
|
if ( !$page->exists() ) {
|
|
$messages->add(
|
|
$localizer->msg(
|
|
'sharedupload-desc-create',
|
|
$file->getRepo()->getDisplayName(),
|
|
$descUrl
|
|
),
|
|
"<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>"
|
|
);
|
|
} else {
|
|
$messages->add(
|
|
$localizer->msg(
|
|
'sharedupload-desc-edit',
|
|
$file->getRepo()->getDisplayName(),
|
|
$descUrl
|
|
),
|
|
"<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function addUserWarnings(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
Title $title,
|
|
Authority $performer
|
|
): void {
|
|
$namespace = $title->getNamespace();
|
|
# Show a warning message when someone creates/edits a user (talk) page but the user does not exist
|
|
# Show log extract when the user is currently blocked
|
|
if ( $namespace === NS_USER || $namespace === NS_USER_TALK ) {
|
|
$username = explode( '/', $title->getText(), 2 )[0];
|
|
// Allow IP users
|
|
$validation = UserRigorOptions::RIGOR_NONE;
|
|
$user = $this->userFactory->newFromName( $username, $validation );
|
|
$ip = $this->userNameUtils->isIP( $username );
|
|
$block = $this->blockStore->newFromTarget( $user, $user );
|
|
|
|
$userExists = ( $user && $user->isRegistered() );
|
|
if ( $userExists && $user->isHidden() && !$performer->isAllowed( 'hideuser' ) ) {
|
|
// If the user exists, but is hidden, and the viewer cannot see hidden
|
|
// users, pretend like they don't exist at all. See T120883
|
|
$userExists = false;
|
|
}
|
|
|
|
if ( !$userExists && !$ip ) {
|
|
$messages->addWithKey(
|
|
'userpage-userdoesnotexist',
|
|
// This wrapper frame, for whatever reason, is not optional
|
|
Html::warningBox(
|
|
$localizer->msg( 'userpage-userdoesnotexist', wfEscapeWikiText( $username ) )->parse(),
|
|
'mw-userpage-userdoesnotexist'
|
|
)
|
|
);
|
|
} elseif (
|
|
$block !== null &&
|
|
$block->getType() !== Block::TYPE_AUTO &&
|
|
(
|
|
$block->isSitewide() ||
|
|
$this->permManager->isBlockedFrom(
|
|
// @phan-suppress-next-line PhanTypeMismatchArgumentNullable False positive
|
|
$user,
|
|
$title,
|
|
true
|
|
)
|
|
)
|
|
) {
|
|
// Show log extract if the user is sitewide blocked or is partially
|
|
// blocked and not allowed to edit their user page or user talk page
|
|
$messages->addWithKey(
|
|
'blocked-notice-logextract',
|
|
$this->getLogExtract(
|
|
'block',
|
|
$this->namespaceInfo->getCanonicalName( NS_USER ) . ':' . $block->getTargetName(),
|
|
'',
|
|
[
|
|
'lim' => 1,
|
|
'showIfEmpty' => false,
|
|
'msgKey' => [
|
|
'blocked-notice-logextract',
|
|
$user->getName() # Support GENDER in notice
|
|
],
|
|
]
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to add a custom edit intro, or use the standard one if this is not possible.
|
|
*/
|
|
private function addEditIntro(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
ProperPageIdentity $page,
|
|
Authority $performer,
|
|
?string $editIntro,
|
|
?string $section
|
|
): void {
|
|
if ( ( $editIntro === null || $editIntro === '' ) && $section === 'new' ) {
|
|
// Custom edit intro for new sections
|
|
$editIntro = 'MediaWiki:addsection-editintro';
|
|
}
|
|
if ( $editIntro !== null && $editIntro !== '' ) {
|
|
$introTitle = Title::newFromText( $editIntro );
|
|
|
|
// (T334855) Use SpecialMyLanguage redirect so that nonexistent translated pages can
|
|
// fall back to the corresponding page in a suitable language
|
|
$introTitle = $this->getTargetTitleIfSpecialMyLanguage( $introTitle );
|
|
|
|
if ( $this->isPageExistingAndViewable( $introTitle, $performer ) ) {
|
|
$messages->addWithKey(
|
|
'editintro',
|
|
$localizer->msg( new RawMessage(
|
|
// Added using template syntax, to take <noinclude>'s into account.
|
|
'<div class="mw-editintro">{{:' . $introTitle->getFullText() . '}}</div>'
|
|
) )
|
|
// Parse as content to enable language conversion (T353870)
|
|
->inContentLanguage()
|
|
->parse()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( !$page->exists() ) {
|
|
$helpLink = $this->urlUtils->expand(
|
|
Skin::makeInternalOrExternalUrl(
|
|
$localizer->msg( 'helppage' )->inContentLanguage()->text()
|
|
),
|
|
PROTO_CURRENT
|
|
);
|
|
if ( $performer->getUser()->isRegistered() ) {
|
|
$messages->add(
|
|
$localizer->msg( 'newarticletext', $helpLink ),
|
|
// Suppress the external link icon, consider the help url an internal one
|
|
"<div class=\"mw-newarticletext plainlinks\">\n$1\n</div>"
|
|
);
|
|
} else {
|
|
$messages->add(
|
|
$localizer->msg( 'newarticletextanon', $helpLink ),
|
|
// Suppress the external link icon, consider the help url an internal one
|
|
"<div class=\"mw-newarticletextanon plainlinks\">\n$1\n</div>"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function addRecreateWarning(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
ProperPageIdentity $page
|
|
): void {
|
|
# Give a notice if the user is editing a deleted/moved page...
|
|
if ( !$page->exists() ) {
|
|
$dbr = $this->dbProvider->getReplicaDatabase();
|
|
|
|
$messages->addWithKey(
|
|
'recreate-moveddeleted-warn',
|
|
$this->getLogExtract( [ 'delete', 'move', 'merge' ], $page, '', [
|
|
'lim' => 10,
|
|
'conds' => [ $dbr->expr( 'log_action', '!=', 'revision' ) ],
|
|
'showIfEmpty' => false,
|
|
'msgKey' => [ 'recreate-moveddeleted-warn' ],
|
|
] )
|
|
);
|
|
}
|
|
}
|
|
|
|
private function addTalkPageText(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
Title $title
|
|
): void {
|
|
if ( $title->isTalkPage() ) {
|
|
$messages->add( $localizer->msg( 'talkpagetext' ) );
|
|
}
|
|
}
|
|
|
|
private function addEditNotices(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
Title $title,
|
|
?RevisionRecord $revRecord
|
|
): void {
|
|
$editNotices = $title->getEditNotices( $revRecord ? $revRecord->getId() : 0 );
|
|
if ( count( $editNotices ) ) {
|
|
foreach ( $editNotices as $key => $html ) {
|
|
$messages->addWithKey( $key, $html );
|
|
}
|
|
} else {
|
|
$msg = $localizer->msg( 'editnotice-notext' );
|
|
if ( !$msg->isDisabled() ) {
|
|
$messages->addWithKey(
|
|
'editnotice-notext',
|
|
Html::rawElement(
|
|
'div',
|
|
[ 'class' => 'mw-editnotice-notext' ],
|
|
$msg->parseAsBlock()
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function addOldRevisionWarning(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
?RevisionRecord $revRecord
|
|
): void {
|
|
if ( $revRecord && !$revRecord->isCurrent() ) {
|
|
// This wrapper frame is not optional (T337071)
|
|
$messages->addWithKey( 'editingold', Html::warningBox( $localizer->msg( 'editingold' )->parse() ) );
|
|
}
|
|
}
|
|
|
|
private function addReadOnlyWarning(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer
|
|
): void {
|
|
if ( $this->readOnlyMode->isReadOnly() ) {
|
|
$messages->add(
|
|
$localizer->msg( 'readonlywarning', $this->readOnlyMode->getReason() ),
|
|
"<div id=\"mw-read-only-warning\">\n$1\n</div>"
|
|
);
|
|
}
|
|
}
|
|
|
|
private function addAnonEditWarning(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
Title $title,
|
|
Authority $performer,
|
|
?string $returnToQuery,
|
|
bool $preview
|
|
): void {
|
|
if ( !$performer->getUser()->isRegistered() ) {
|
|
$tempUserCreateActive = $this->tempUserCreator->shouldAutoCreate( $performer, 'edit' );
|
|
if ( !$preview ) {
|
|
$messages->addWithKey(
|
|
'anoneditwarning',
|
|
$localizer->msg(
|
|
$tempUserCreateActive ? 'autocreate-edit-warning' : 'anoneditwarning',
|
|
// Log-in link
|
|
SpecialPage::getTitleFor( 'Userlogin' )->getFullURL( [
|
|
'returnto' => $title->getPrefixedDBkey(),
|
|
'returntoquery' => $returnToQuery,
|
|
] ),
|
|
// Sign-up link
|
|
SpecialPage::getTitleFor( 'CreateAccount' )->getFullURL( [
|
|
'returnto' => $title->getPrefixedDBkey(),
|
|
'returntoquery' => $returnToQuery,
|
|
] )
|
|
)->parse(),
|
|
Html::warningBox( '$1', 'mw-anon-edit-warning' )
|
|
);
|
|
} else {
|
|
$messages->addWithKey(
|
|
'anoneditwarning',
|
|
$localizer->msg( $tempUserCreateActive ? 'autocreate-preview-warning' : 'anonpreviewwarning' )
|
|
->parse(),
|
|
Html::warningBox( '$1', 'mw-anon-preview-warning' ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether the user entered a skin name in uppercase,
|
|
* e.g. "User:Example/Monobook.css" instead of "monobook.css"
|
|
*/
|
|
private function isWrongCaseUserConfigPage( Title $title ): bool {
|
|
if ( $title->isUserCssConfigPage() || $title->isUserJsConfigPage() ) {
|
|
$name = $title->getSkinFromConfigSubpage();
|
|
$skins = array_merge(
|
|
array_keys( $this->skinFactory->getInstalledSkins() ),
|
|
[ 'common' ]
|
|
);
|
|
return !in_array( $name, $skins, true )
|
|
&& in_array( strtolower( $name ), $skins, true );
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private function addUserConfigPageInfo(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
Title $title,
|
|
Authority $performer,
|
|
bool $preview
|
|
): void {
|
|
if ( $title->isUserConfigPage() ) {
|
|
# Check the skin exists
|
|
if ( $this->isWrongCaseUserConfigPage( $title ) ) {
|
|
$messages->add(
|
|
$localizer->msg( 'userinvalidconfigtitle', $title->getSkinFromConfigSubpage() ),
|
|
Html::errorBox( '$1', '', 'mw-userinvalidconfigtitle' )
|
|
);
|
|
}
|
|
if ( $title->isSubpageOf( Title::makeTitle( NS_USER, $performer->getUser()->getName() ) ) ) {
|
|
$isUserCssConfig = $title->isUserCssConfigPage();
|
|
$isUserJsonConfig = $title->isUserJsonConfigPage();
|
|
$isUserJsConfig = $title->isUserJsConfigPage();
|
|
|
|
if ( !$preview ) {
|
|
if ( $isUserCssConfig && $this->config->get( MainConfigNames::AllowUserCss ) ) {
|
|
$messages->add(
|
|
$localizer->msg( 'usercssyoucanpreview' ),
|
|
"<div id='mw-usercssyoucanpreview'>\n$1\n</div>"
|
|
);
|
|
} elseif ( $isUserJsonConfig /* No comparable 'AllowUserJson' */ ) {
|
|
$messages->add(
|
|
$localizer->msg( 'userjsonyoucanpreview' ),
|
|
"<div id='mw-userjsonyoucanpreview'>\n$1\n</div>"
|
|
);
|
|
} elseif ( $isUserJsConfig && $this->config->get( MainConfigNames::AllowUserJs ) ) {
|
|
$messages->add(
|
|
$localizer->msg( 'userjsyoucanpreview' ),
|
|
"<div id='mw-userjsyoucanpreview'>\n$1\n</div>"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function addPageProtectionWarningHeaders(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer,
|
|
ProperPageIdentity $page
|
|
): void {
|
|
if ( $this->restrictionStore->isProtected( $page, 'edit' ) &&
|
|
$this->permManager->getNamespaceRestrictionLevels(
|
|
$page->getNamespace()
|
|
) !== [ '' ]
|
|
) {
|
|
# Is the title semi-protected?
|
|
if ( $this->restrictionStore->isSemiProtected( $page ) ) {
|
|
$noticeMsg = 'semiprotectedpagewarning';
|
|
} else {
|
|
# Then it must be protected based on static groups (regular)
|
|
$noticeMsg = 'protectedpagewarning';
|
|
}
|
|
$messages->addWithKey(
|
|
$noticeMsg,
|
|
$this->getLogExtract( 'protect', $page, '', [ 'lim' => 1, 'msgKey' => [ $noticeMsg ] ] )
|
|
);
|
|
}
|
|
if ( $this->restrictionStore->isCascadeProtected( $page ) ) {
|
|
# Is this page under cascading protection from some source pages?
|
|
$tlCascadeSources = $this->restrictionStore->getCascadeProtectionSources( $page )[2];
|
|
if ( $tlCascadeSources ) {
|
|
$htmlList = '';
|
|
# Explain, and list the titles responsible
|
|
foreach ( $tlCascadeSources as $source ) {
|
|
$htmlList .= Html::rawElement( 'li', [], $this->linkRenderer->makeLink( $source ) );
|
|
}
|
|
$messages->addWithKey(
|
|
'cascadeprotectedwarning',
|
|
$localizer->msg( 'cascadeprotectedwarning', count( $tlCascadeSources ) )->parse() .
|
|
( $htmlList ? Html::rawElement( 'ul', [], $htmlList ) : '' ),
|
|
Html::warningBox( '$1', 'mw-cascadeprotectedwarning' )
|
|
);
|
|
}
|
|
}
|
|
if ( !$page->exists() && $this->restrictionStore->getRestrictions( $page, 'create' ) ) {
|
|
$messages->addWithKey(
|
|
'titleprotectedwarning',
|
|
$this->getLogExtract(
|
|
'protect', $page,
|
|
'',
|
|
[
|
|
'lim' => 1,
|
|
'showIfEmpty' => false,
|
|
'msgKey' => [ 'titleprotectedwarning' ],
|
|
'wrap' => "<div class=\"mw-titleprotectedwarning\">\n$1</div>"
|
|
]
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
private function addHeaderCopyrightWarning(
|
|
IntroMessageList $messages,
|
|
MessageLocalizer $localizer
|
|
): void {
|
|
$messages->add(
|
|
$localizer->msg( 'editpage-head-copy-warn' ),
|
|
"<div class='editpage-head-copywarn'>\n$1\n</div>"
|
|
);
|
|
}
|
|
}
|