Title: Refactor JS/CSS page handling to be more sane

Change-Id: Ia7837dc614dcc8896a7d4b6d663dc45b6bd4f7ee
This commit is contained in:
James D. Forrester 2018-02-12 16:15:30 -08:00 committed by Jforrester
parent db8f62e57d
commit 6d4e15476c
11 changed files with 336 additions and 176 deletions

View file

@ -233,6 +233,12 @@ changes to languages because of Phabricator reports.
* CommentStore::getCommentLegacy * CommentStore::getCommentLegacy
* CommentStore::insert * CommentStore::insert
* CommentStore::insertWithTemplate * CommentStore::insertWithTemplate
* The following methods in Title have been renamed, and the old ones are deprecated:
* Title::getSkinFromCssJsSubpage  use ::getSkinFromConfigSubpage
* Title::isCssOrJsPage  use ::isSiteConfigPage
* Title::isCssJsSubpage  use ::isUserConfigPage
* Title::isCssSubpage use ::isUserCssConfigPage
* Title::isJsSubpage use ::isUserJsConfigPage
* The method ResourceLoaderModule::getPosition(), deprecated in 1.29, has been removed. * The method ResourceLoaderModule::getPosition(), deprecated in 1.29, has been removed.
== Compatibility == == Compatibility ==

View file

@ -5813,6 +5813,7 @@ $wgGrantPermissions['editpage']['changetags'] = true;
$wgGrantPermissions['editprotected'] = $wgGrantPermissions['editpage']; $wgGrantPermissions['editprotected'] = $wgGrantPermissions['editpage'];
$wgGrantPermissions['editprotected']['editprotected'] = true; $wgGrantPermissions['editprotected']['editprotected'] = true;
// FIXME: Rename editmycssjs to editmyconfig
$wgGrantPermissions['editmycssjs'] = $wgGrantPermissions['editpage']; $wgGrantPermissions['editmycssjs'] = $wgGrantPermissions['editpage'];
$wgGrantPermissions['editmycssjs']['editmyusercss'] = true; $wgGrantPermissions['editmycssjs']['editmyusercss'] = true;
$wgGrantPermissions['editmycssjs']['editmyuserjs'] = true; $wgGrantPermissions['editmycssjs']['editmyuserjs'] = true;

View file

@ -239,19 +239,19 @@ class EditPage {
public $isConflict = false; public $isConflict = false;
/** /**
* @deprecated since 1.30 use Title::isCssJsSubpage() * @deprecated since 1.30 use Title::isUserConfigPage()
* @var bool * @var bool
*/ */
public $isCssJsSubpage = false; public $isCssJsSubpage = false;
/** /**
* @deprecated since 1.30 use Title::isCssSubpage() * @deprecated since 1.30 use Title::isUserCssConfigPage()
* @var bool * @var bool
*/ */
public $isCssSubpage = false; public $isCssSubpage = false;
/** /**
* @deprecated since 1.30 use Title::isJsSubpage() * @deprecated since 1.30 use Title::isUserJsConfigPage()
* @var bool * @var bool
*/ */
public $isJsSubpage = false; public $isJsSubpage = false;
@ -663,10 +663,10 @@ class EditPage {
// css / js subpages of user pages get a special treatment // css / js subpages of user pages get a special treatment
// The following member variables are deprecated since 1.30, // The following member variables are deprecated since 1.30,
// the functions should be used instead. // the functions should be used instead.
$this->isCssJsSubpage = $this->mTitle->isCssJsSubpage(); $this->isCssJsSubpage = $this->mTitle->isUserConfigPage();
$this->isCssSubpage = $this->mTitle->isCssSubpage(); $this->isCssSubpage = $this->mTitle->isUserCssConfigPage();
$this->isJsSubpage = $this->mTitle->isJsSubpage(); $this->isJsSubpage = $this->mTitle->isUserJsConfigPage();
$this->isWrongCaseCssJsPage = $this->isWrongCaseCssJsPage(); $this->isWrongCaseCssJsPage = $this->isWrongCaseUserConfigPage();
# Show applicable editing introductions # Show applicable editing introductions
if ( $this->formtype == 'initial' || $this->firsttime ) { if ( $this->formtype == 'initial' || $this->firsttime ) {
@ -877,9 +877,9 @@ class EditPage {
* *
* @return bool * @return bool
*/ */
protected function isWrongCaseCssJsPage() { protected function isWrongCaseUserConfigPage() {
if ( $this->mTitle->isCssJsSubpage() ) { if ( $this->mTitle->isUserConfigPage() ) {
$name = $this->mTitle->getSkinFromCssJsSubpage(); $name = $this->mTitle->getSkinFromConfigSubpage();
$skins = array_merge( $skins = array_merge(
array_keys( Skin::getSkinNames() ), array_keys( Skin::getSkinNames() ),
[ 'common' ] [ 'common' ]
@ -2879,7 +2879,7 @@ ERROR;
$out->addHTML( $editConflictHelper->getEditFormHtmlBeforeContent() ); $out->addHTML( $editConflictHelper->getEditFormHtmlBeforeContent() );
} }
if ( !$this->mTitle->isCssJsSubpage() && $showToolbar && $user->getOption( 'showtoolbar' ) ) { if ( !$this->mTitle->isUserConfigPage() && $showToolbar && $user->getOption( 'showtoolbar' ) ) {
$out->addHTML( self::getEditToolbar( $this->mTitle ) ); $out->addHTML( self::getEditToolbar( $this->mTitle ) );
} }
@ -3116,22 +3116,26 @@ ERROR;
); );
} }
} else { } else {
if ( $this->mTitle->isCssJsSubpage() ) { if ( $this->mTitle->isUserConfigPage() ) {
# Check the skin exists # Check the skin exists
if ( $this->isWrongCaseCssJsPage() ) { if ( $this->isWrongCaseUserConfigPage() ) {
$out->wrapWikiMsg( $out->wrapWikiMsg(
"<div class='error' id='mw-userinvalidcssjstitle'>\n$1\n</div>", "<div class='error' id='mw-userinvalidconfigtitle'>\n$1\n</div>",
[ 'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() ] [ 'userinvalidconfigtitle', $this->mTitle->getSkinFromConfigSubpage() ]
); );
} }
if ( $this->getTitle()->isSubpageOf( $user->getUserPage() ) ) { if ( $this->getTitle()->isSubpageOf( $user->getUserPage() ) ) {
$isCssSubpage = $this->mTitle->isCssSubpage(); $isUserCssConfig = $this->mTitle->isUserCssConfigPage();
$out->wrapWikiMsg( '<div class="mw-usercssjspublic">$1</div>',
$isCssSubpage ? 'usercssispublic' : 'userjsispublic' $warning = $isUserCssConfig
); ? 'usercssispublic'
: 'userjsispublic';
$out->wrapWikiMsg( '<div class="mw-userconfigpublic">$1</div>', $warning );
if ( $this->formtype !== 'preview' ) { if ( $this->formtype !== 'preview' ) {
$config = $this->context->getConfig(); $config = $this->context->getConfig();
if ( $isCssSubpage && $config->get( 'AllowUserCss' ) ) { if ( $isUserCssConfig && $config->get( 'AllowUserCss' ) ) {
$out->wrapWikiMsg( $out->wrapWikiMsg(
"<div id='mw-usercssyoucanpreview'>\n$1\n</div>", "<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
[ 'usercssyoucanpreview' ] [ 'usercssyoucanpreview' ]
@ -3913,10 +3917,10 @@ ERROR;
} }
# don't parse non-wikitext pages, show message about preview # don't parse non-wikitext pages, show message about preview
if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) { if ( $this->mTitle->isUserConfigPage() || $this->mTitle->isSiteConfigPage() ) {
if ( $this->mTitle->isCssJsSubpage() ) { if ( $this->mTitle->isUserConfigPage() ) {
$level = 'user'; $level = 'user';
} elseif ( $this->mTitle->isCssOrJsPage() ) { } elseif ( $this->mTitle->isSiteConfigPage() ) {
$level = 'site'; $level = 'site';
} else { } else {
$level = false; $level = false;

View file

@ -2945,14 +2945,14 @@ class OutputPage extends ContextSource {
private function isUserJsPreview() { private function isUserJsPreview() {
return $this->getConfig()->get( 'AllowUserJs' ) return $this->getConfig()->get( 'AllowUserJs' )
&& $this->getTitle() && $this->getTitle()
&& $this->getTitle()->isJsSubpage() && $this->getTitle()->isUserJsConfigPage()
&& $this->userCanPreview(); && $this->userCanPreview();
} }
protected function isUserCssPreview() { protected function isUserCssPreview() {
return $this->getConfig()->get( 'AllowUserCss' ) return $this->getConfig()->get( 'AllowUserCss' )
&& $this->getTitle() && $this->getTitle()
&& $this->getTitle()->isCssSubpage() && $this->getTitle()->isUserCssConfigPage()
&& $this->userCanPreview(); && $this->userCanPreview();
} }
@ -3204,7 +3204,10 @@ class OutputPage extends ContextSource {
} }
$title = $this->getTitle(); $title = $this->getTitle();
if ( !$title->isJsSubpage() && !$title->isCssSubpage() ) { if (
!$title->isUserJsConfigPage()
&& !$title->isUserCssConfigPage()
) {
return false; return false;
} }
if ( !$title->isSubpageOf( $user->getUserPage() ) ) { if ( !$title->isSubpageOf( $user->getUserPage() ) ) {

View file

@ -1303,22 +1303,52 @@ class Title implements LinkTarget {
* show "inactive" CSS or JS. * show "inactive" CSS or JS.
* *
* @return bool * @return bool
* @todo FIXME: Rename to isSiteConfigPage() and remove deprecated hook * @since 1.31
*/ */
public function isCssOrJsPage() { public function isSiteConfigPage() {
$isCssOrJsPage = NS_MEDIAWIKI == $this->mNamespace return (
&& ( $this->hasContentModel( CONTENT_MODEL_CSS ) NS_MEDIAWIKI == $this->mNamespace
|| $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ); && (
$this->hasContentModel( CONTENT_MODEL_CSS )
return $isCssOrJsPage; || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
)
);
} }
/** /**
* Is this a .css or .js subpage of a user page?
* @return bool * @return bool
* @todo FIXME: Rename to isUserConfigPage() * @deprecated Since 1.31; use ::isSiteConfigPage() instead
*/
public function isCssOrJsPage() {
// wfDeprecated( __METHOD__, '1.31' );
return ( NS_MEDIAWIKI == $this->mNamespace
&& ( $this->hasContentModel( CONTENT_MODEL_CSS )
|| $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
}
/**
* Is this a "config" (.css or .js) sub-page of a user page?
*
* @return bool
* @since 1.31
*/
public function isUserConfigPage() {
return (
NS_USER == $this->mNamespace
&& $this->isSubpage()
&& (
$this->hasContentModel( CONTENT_MODEL_CSS )
|| $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
)
);
}
/**
* @return bool
* @deprecated Since 1.31; use ::isUserConfigPage() instead
*/ */
public function isCssJsSubpage() { public function isCssJsSubpage() {
// wfDeprecated( __METHOD__, '1.31' );
return ( NS_USER == $this->mNamespace && $this->isSubpage() return ( NS_USER == $this->mNamespace && $this->isSubpage()
&& ( $this->hasContentModel( CONTENT_MODEL_CSS ) && ( $this->hasContentModel( CONTENT_MODEL_CSS )
|| $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) ); || $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ) );
@ -1328,8 +1358,9 @@ class Title implements LinkTarget {
* Trim down a .css or .js subpage title to get the corresponding skin name * Trim down a .css or .js subpage title to get the corresponding skin name
* *
* @return string Containing skin name from .css or .js subpage title * @return string Containing skin name from .css or .js subpage title
* @since 1.31
*/ */
public function getSkinFromCssJsSubpage() { public function getSkinFromConfigSubpage() {
$subpage = explode( '/', $this->mTextform ); $subpage = explode( '/', $this->mTextform );
$subpage = $subpage[count( $subpage ) - 1]; $subpage = $subpage[count( $subpage ) - 1];
$lastdot = strrpos( $subpage, '.' ); $lastdot = strrpos( $subpage, '.' );
@ -1340,23 +1371,58 @@ class Title implements LinkTarget {
} }
/** /**
* Is this a .css subpage of a user page? * @deprecated Since 1.31; use ::getSkinFromConfigSubpage() instead
* @return string Containing skin name from .css or .js subpage title
*/
public function getSkinFromCssJsSubpage() {
wfDeprecated( __METHOD__, '1.31' );
return $this->getSkinFromConfigSubpage();
}
/**
* Is this a CSS "config" sub-page of a user page?
* *
* @return bool * @return bool
* @since 1.31
*/
public function isUserCssConfigPage() {
return (
NS_USER == $this->mNamespace
&& $this->isSubpage()
&& $this->hasContentModel( CONTENT_MODEL_CSS )
);
}
/**
* @deprecated Since 1.31; use ::isUserCssConfigPage()
* @return bool
*/ */
public function isCssSubpage() { public function isCssSubpage() {
return ( NS_USER == $this->mNamespace && $this->isSubpage() // wfDeprecated( __METHOD__, '1.31' );
&& $this->hasContentModel( CONTENT_MODEL_CSS ) ); return $this->isUserCssConfigPage();
} }
/** /**
* Is this a .js subpage of a user page? * Is this a .js subpage of a user page?
* *
* @return bool * @return bool
* @since 1.31
*/
public function isUserJsConfigPage() {
return (
NS_USER == $this->mNamespace
&& $this->isSubpage()
&& $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
);
}
/**
* @deprecated Since 1.31; use ::isUserCssConfigPage()
* @return bool
*/ */
public function isJsSubpage() { public function isJsSubpage() {
return ( NS_USER == $this->mNamespace && $this->isSubpage() // wfDeprecated( __METHOD__, '1.31' );
&& $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT ) ); return $this->isUserJsConfigPage();
} }
/** /**
@ -2260,20 +2326,33 @@ class Title implements LinkTarget {
* *
* @return array List of errors * @return array List of errors
*/ */
private function checkCSSandJSPermissions( $action, $user, $errors, $rigor, $short ) { private function checkUserConfigPermissions( $action, $user, $errors, $rigor, $short ) {
# Protect css/js subpages of user pages # Protect css/js subpages of user pages
# XXX: this might be better using restrictions # XXX: this might be better using restrictions
if ( $action != 'patrol' ) { if ( $action != 'patrol' ) {
if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) { if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) { if (
$this->isUserCssConfigPage()
&& !$user->isAllowedAny( 'editmyusercss', 'editusercss' )
) {
$errors[] = [ 'mycustomcssprotected', $action ]; $errors[] = [ 'mycustomcssprotected', $action ];
} elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) { } elseif (
$this->isUserJsConfigPage()
&& !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
) {
$errors[] = [ 'mycustomjsprotected', $action ]; $errors[] = [ 'mycustomjsprotected', $action ];
} }
} else { } else {
if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) { if (
$this->isUserCssConfigPage()
&& !$user->isAllowed( 'editusercss' )
) {
$errors[] = [ 'customcssprotected', $action ]; $errors[] = [ 'customcssprotected', $action ];
} elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) { } elseif (
$this->isUserJsConfigPage()
&& !$user->isAllowed( 'edituserjs' )
) {
$errors[] = [ 'customjsprotected', $action ]; $errors[] = [ 'customjsprotected', $action ];
} }
} }
@ -2330,7 +2409,7 @@ class Title implements LinkTarget {
* @return array List of errors * @return array List of errors
*/ */
private function checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short ) { private function checkCascadingSourcesRestrictions( $action, $user, $errors, $rigor, $short ) {
if ( $rigor !== 'quick' && !$this->isCssJsSubpage() ) { if ( $rigor !== 'quick' && !$this->isUserConfigPage() ) {
# We /could/ use the protection level on the source page, but it's # We /could/ use the protection level on the source page, but it's
# fairly ugly as we have to establish a precedence hierarchy for pages # fairly ugly as we have to establish a precedence hierarchy for pages
# included by multiple cascade-protected pages. So just restrict # included by multiple cascade-protected pages. So just restrict
@ -2611,7 +2690,7 @@ class Title implements LinkTarget {
'checkReadPermissions', 'checkReadPermissions',
'checkUserBlock', // for wgBlockDisablesLogin 'checkUserBlock', // for wgBlockDisablesLogin
]; ];
# Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions # Don't call checkSpecialsAndNSPermissions or checkUserConfigPermissions
# here as it will lead to duplicate error messages. This is okay to do # here as it will lead to duplicate error messages. This is okay to do
# since anywhere that checks for create will also check for edit, and # since anywhere that checks for create will also check for edit, and
# those checks are called for edit. # those checks are called for edit.
@ -2629,7 +2708,7 @@ class Title implements LinkTarget {
'checkQuickPermissions', 'checkQuickPermissions',
'checkPermissionHooks', 'checkPermissionHooks',
'checkSpecialsAndNSPermissions', 'checkSpecialsAndNSPermissions',
'checkCSSandJSPermissions', 'checkUserConfigPermissions',
'checkPageRestrictions', 'checkPageRestrictions',
'checkCascadingSourcesRestrictions', 'checkCascadingSourcesRestrictions',
'checkActionPermissions', 'checkActionPermissions',
@ -3743,9 +3822,9 @@ class Title implements LinkTarget {
} }
// If we are looking at a css/js user subpage, purge the action=raw. // If we are looking at a css/js user subpage, purge the action=raw.
if ( $this->isJsSubpage() ) { if ( $this->isUserJsConfigPage() ) {
$urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' ); $urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' );
} elseif ( $this->isCssSubpage() ) { } elseif ( $this->isUserCssConfigPage() ) {
$urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' ); $urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' );
} }

View file

@ -705,7 +705,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
'type' => 'info', 'type' => 'info',
'raw' => true, 'raw' => true,
'default' => $context->getLanguage()->pipeList( $linkTools ), 'default' => $context->getLanguage()->pipeList( $linkTools ),
'label-message' => 'prefs-common-css-js', 'label-message' => 'prefs-common-config',
'section' => 'rendering/skin', 'section' => 'rendering/skin',
]; ];
} }

View file

@ -29,8 +29,8 @@ use Wikimedia\Rdbms\IDatabase;
* Abstraction for ResourceLoader modules which pull from wiki pages * Abstraction for ResourceLoader modules which pull from wiki pages
* *
* This can only be used for wiki pages in the MediaWiki and User namespaces, * This can only be used for wiki pages in the MediaWiki and User namespaces,
* because of its dependence on the functionality of Title::isCssJsSubpage * because of its dependence on the functionality of Title::isUserConfigPage()
* and Title::isCssOrJsPage(). * and Title::isSiteConfigPage().
* *
* This module supports being used as a placeholder for a module on a remote wiki. * This module supports being used as a placeholder for a module on a remote wiki.
* To do so, getDB() must be overloaded to return a foreign database object that * To do so, getDB() must be overloaded to return a foreign database object that
@ -450,7 +450,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
} elseif ( $new && in_array( $new->getContentFormat(), $formats ) ) { } elseif ( $new && in_array( $new->getContentFormat(), $formats ) ) {
$purge = true; $purge = true;
} else { } else {
$purge = ( $title->isCssOrJsPage() || $title->isCssJsSubpage() ); $purge = ( $title->isSiteConfigPage() || $title->isUserConfigPage() );
} }
if ( $purge ) { if ( $purge ) {

View file

@ -684,7 +684,7 @@
"userjspreview": "<strong>Remember that you are only testing/previewing your user JavaScript.\nIt has not yet been saved!</strong>", "userjspreview": "<strong>Remember that you are only testing/previewing your user JavaScript.\nIt has not yet been saved!</strong>",
"sitecsspreview": "<strong>Remember that you are only previewing this CSS.\nIt has not yet been saved!</strong>", "sitecsspreview": "<strong>Remember that you are only previewing this CSS.\nIt has not yet been saved!</strong>",
"sitejspreview": "<strong>Remember that you are only previewing this JavaScript code.\nIt has not yet been saved!</strong>", "sitejspreview": "<strong>Remember that you are only previewing this JavaScript code.\nIt has not yet been saved!</strong>",
"userinvalidcssjstitle": "<strong>Warning:</strong> There is no skin \"$1\".\nCustom .css and .js pages use a lowercase title, e.g. {{ns:user}}:Foo/vector.css as opposed to {{ns:user}}:Foo/Vector.css.", "userinvalidconfigtitle": "<strong>Warning:</strong> There is no skin \"$1\".\nCustom .css and .js pages use a lowercase title, e.g. {{ns:user}}:Foo/vector.css as opposed to {{ns:user}}:Foo/Vector.css.",
"updated": "(Updated)", "updated": "(Updated)",
"note": "<strong>Note:</strong>", "note": "<strong>Note:</strong>",
"previewnote": "<strong>Remember that this is only a preview.</strong>\nYour changes have not yet been saved!", "previewnote": "<strong>Remember that this is only a preview.</strong>\nYour changes have not yet been saved!",
@ -1073,7 +1073,7 @@
"prefs-files": "Files", "prefs-files": "Files",
"prefs-custom-css": "Custom CSS", "prefs-custom-css": "Custom CSS",
"prefs-custom-js": "Custom JavaScript", "prefs-custom-js": "Custom JavaScript",
"prefs-common-css-js": "Shared CSS/JavaScript for all skins:", "prefs-common-config": "Shared CSS/JavaScript for all skins:",
"prefs-reset-intro": "You can use this page to reset your preferences to the site defaults.\nThis cannot be undone.", "prefs-reset-intro": "You can use this page to reset your preferences to the site defaults.\nThis cannot be undone.",
"prefs-emailconfirm-label": "Email confirmation:", "prefs-emailconfirm-label": "Email confirmation:",
"youremail": "Email:", "youremail": "Email:",

View file

@ -880,7 +880,7 @@
"userjspreview": "Text displayed on preview of every user .js subpage", "userjspreview": "Text displayed on preview of every user .js subpage",
"sitecsspreview": "Text displayed on preview of .css pages in MediaWiki namespace.\n\nSee also:\n* {{msg-mw|Usercsspreview}}", "sitecsspreview": "Text displayed on preview of .css pages in MediaWiki namespace.\n\nSee also:\n* {{msg-mw|Usercsspreview}}",
"sitejspreview": "Text displayed on preview of .js pages in MediaWiki namespace", "sitejspreview": "Text displayed on preview of .js pages in MediaWiki namespace",
"userinvalidcssjstitle": "Parameters:\n* $1 - skin name", "userinvalidconfigtitle": "Parameters:\n* $1 - skin name",
"updated": "{{Identical|Updated}}", "updated": "{{Identical|Updated}}",
"note": "{{Identical|Note}}", "note": "{{Identical|Note}}",
"previewnote": "Note displayed when clicking on Show preview", "previewnote": "Note displayed when clicking on Show preview",
@ -1269,7 +1269,7 @@
"prefs-files": "Title of a tab in [[Special:Preferences]].\n{{Identical|File}}", "prefs-files": "Title of a tab in [[Special:Preferences]].\n{{Identical|File}}",
"prefs-custom-css": "visible on [[Special:Preferences]] -[Skins].\n{{Identical|Custom CSS}}", "prefs-custom-css": "visible on [[Special:Preferences]] -[Skins].\n{{Identical|Custom CSS}}",
"prefs-custom-js": "visible on [[Special:Preferences]] -[Skins].\n{{Identical|Custom JavaScript}}", "prefs-custom-js": "visible on [[Special:Preferences]] -[Skins].\n{{Identical|Custom JavaScript}}",
"prefs-common-css-js": "Used as label in [[Special:Preferences#mw-prefsection-rendering|preferences]], tab \"Appearance\", section \"Skin\".\n\nSee also:\n* {{msg-mw|Globalcssjs-custom-css-js}}", "prefs-common-config": "Used as label in [[Special:Preferences#mw-prefsection-rendering|preferences]], tab \"Appearance\", section \"Skin\".\n\nSee also:\n* {{msg-mw|Globalcssjs-custom-css-js}}",
"prefs-reset-intro": "Used in [[Special:Preferences/reset]].", "prefs-reset-intro": "Used in [[Special:Preferences/reset]].",
"prefs-emailconfirm-label": "Sub-heading in [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}.", "prefs-emailconfirm-label": "Sub-heading in [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}.",
"youremail": "Label of the e-mail text box of the \"E-mail options\" section of [[Special:Preferences]].\nAlso used on create account form.\n\n{{Identical|E-mail}}", "youremail": "Label of the e-mail text box of the \"E-mail options\" section of [[Special:Preferences]].\nAlso used on create account form.\n\n{{Identical|E-mail}}",

View file

@ -164,7 +164,7 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
$this->assertTrue( $title->hasContentModel( $expectedModelId ) ); $this->assertTrue( $title->hasContentModel( $expectedModelId ) );
} }
public static function provideIsCssOrJsPage() { public static function provideIsSiteConfigPage() {
return [ return [
[ 'Help:Foo', false ], [ 'Help:Foo', false ],
[ 'Help:Foo.js', false ], [ 'Help:Foo.js', false ],
@ -173,6 +173,8 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
[ 'User:Foo.js', false ], [ 'User:Foo.js', false ],
[ 'User:Foo/bar.js', false ], [ 'User:Foo/bar.js', false ],
[ 'User:Foo/bar.css', false ], [ 'User:Foo/bar.css', false ],
[ 'User:Foo/bar.JS', false ],
[ 'User:Foo/bar.CSS', false ],
[ 'User talk:Foo/bar.css', false ], [ 'User talk:Foo/bar.css', false ],
[ 'User:Foo/bar.js.xxx', false ], [ 'User:Foo/bar.js.xxx', false ],
[ 'User:Foo/bar.xxx', false ], [ 'User:Foo/bar.xxx', false ],
@ -180,6 +182,40 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
[ 'MediaWiki:Foo.css', true ], [ 'MediaWiki:Foo.css', true ],
[ 'MediaWiki:Foo.JS', false ], [ 'MediaWiki:Foo.JS', false ],
[ 'MediaWiki:Foo.CSS', false ], [ 'MediaWiki:Foo.CSS', false ],
[ 'MediaWiki:Foo/bar.css', true ],
[ 'MediaWiki:Foo.css.xxx', false ],
[ 'TEST-JS:Foo', false ],
[ 'TEST-JS:Foo.js', false ],
];
}
/**
* @dataProvider provideIsSiteConfigPage
* @covers Title::isSiteConfigPage
*/
public function testSiteConfigPage( $title, $expectedBool ) {
$title = Title::newFromText( $title );
$this->assertEquals( $expectedBool, $title->isSiteConfigPage() );
}
public static function provideIsUserConfigPage() {
return [
[ 'Help:Foo', false ],
[ 'Help:Foo.js', false ],
[ 'Help:Foo/bar.js', false ],
[ 'User:Foo', false ],
[ 'User:Foo.js', false ],
[ 'User:Foo/bar.js', true ],
[ 'User:Foo/bar.JS', false ],
[ 'User:Foo/bar.css', true ],
[ 'User:Foo/bar.CSS', false ],
[ 'User talk:Foo/bar.css', false ],
[ 'User:Foo/bar.js.xxx', false ],
[ 'User:Foo/bar.xxx', false ],
[ 'MediaWiki:Foo.js', false ],
[ 'MediaWiki:Foo.css', false ],
[ 'MediaWiki:Foo.JS', false ],
[ 'MediaWiki:Foo.CSS', false ],
[ 'MediaWiki:Foo.css.xxx', false ], [ 'MediaWiki:Foo.css.xxx', false ],
[ 'TEST-JS:Foo', false ], [ 'TEST-JS:Foo', false ],
[ 'TEST-JS:Foo.js', false ], [ 'TEST-JS:Foo.js', false ],
@ -187,44 +223,15 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
} }
/** /**
* @dataProvider provideIsCssOrJsPage * @dataProvider provideIsUserConfigPage
* @covers Title::isCssOrJsPage * @covers Title::isUserConfigPage
*/ */
public function testIsCssOrJsPage( $title, $expectedBool ) { public function testIsUserConfigPage( $title, $expectedBool ) {
$title = Title::newFromText( $title ); $title = Title::newFromText( $title );
$this->assertEquals( $expectedBool, $title->isCssOrJsPage() ); $this->assertEquals( $expectedBool, $title->isUserConfigPage() );
} }
public static function provideIsCssJsSubpage() { public static function provideIsUserCssConfigPage() {
return [
[ 'Help:Foo', false ],
[ 'Help:Foo.js', false ],
[ 'Help:Foo/bar.js', false ],
[ 'User:Foo', false ],
[ 'User:Foo.js', false ],
[ 'User:Foo/bar.js', true ],
[ 'User:Foo/bar.css', true ],
[ 'User talk:Foo/bar.css', false ],
[ 'User:Foo/bar.js.xxx', false ],
[ 'User:Foo/bar.xxx', false ],
[ 'MediaWiki:Foo.js', false ],
[ 'User:Foo/bar.JS', false ],
[ 'User:Foo/bar.CSS', false ],
[ 'TEST-JS:Foo', false ],
[ 'TEST-JS:Foo.js', false ],
];
}
/**
* @dataProvider provideIsCssJsSubpage
* @covers Title::isCssJsSubpage
*/
public function testIsCssJsSubpage( $title, $expectedBool ) {
$title = Title::newFromText( $title );
$this->assertEquals( $expectedBool, $title->isCssJsSubpage() );
}
public static function provideIsCssSubpage() {
return [ return [
[ 'Help:Foo', false ], [ 'Help:Foo', false ],
[ 'Help:Foo.css', false ], [ 'Help:Foo.css', false ],
@ -237,33 +244,35 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
} }
/** /**
* @dataProvider provideIsCssSubpage * @dataProvider provideIsUserCssConfigPage
* @covers Title::isCssSubpage * @covers Title::isUserCssConfigPage
*/ */
public function testIsCssSubpage( $title, $expectedBool ) { public function testIsUserCssConfigPage( $title, $expectedBool ) {
$title = Title::newFromText( $title ); $title = Title::newFromText( $title );
$this->assertEquals( $expectedBool, $title->isCssSubpage() ); $this->assertEquals( $expectedBool, $title->isUserCssConfigPage() );
} }
public static function provideIsJsSubpage() { public static function provideIsUserJsConfigPage() {
return [ return [
[ 'Help:Foo', false ], [ 'Help:Foo', false ],
[ 'Help:Foo.css', false ], [ 'Help:Foo.css', false ],
[ 'User:Foo', false ], [ 'User:Foo', false ],
[ 'User:Foo.js', false ], [ 'User:Foo.js', false ],
[ 'User:Foo.json', false ],
[ 'User:Foo.css', false ], [ 'User:Foo.css', false ],
[ 'User:Foo/bar.js', true ], [ 'User:Foo/bar.js', true ],
[ 'User:Foo/bar.json', false ],
[ 'User:Foo/bar.css', false ], [ 'User:Foo/bar.css', false ],
]; ];
} }
/** /**
* @dataProvider provideIsJsSubpage * @dataProvider provideIsUserJsConfigPage
* @covers Title::isJsSubpage * @covers Title::isUserJsConfigPage
*/ */
public function testIsJsSubpage( $title, $expectedBool ) { public function testIsUserJsConfigPage( $title, $expectedBool ) {
$title = Title::newFromText( $title ); $title = Title::newFromText( $title );
$this->assertEquals( $expectedBool, $title->isJsSubpage() ); $this->assertEquals( $expectedBool, $title->isUserJsConfigPage() );
} }
public static function provideIsWikitextPage() { public static function provideIsWikitextPage() {
@ -279,13 +288,14 @@ class TitleMethodsTest extends MediaWikiLangTestCase {
[ 'User:Foo/bar.js.xxx', true ], [ 'User:Foo/bar.js.xxx', true ],
[ 'User:Foo/bar.xxx', true ], [ 'User:Foo/bar.xxx', true ],
[ 'MediaWiki:Foo.js', false ], [ 'MediaWiki:Foo.js', false ],
[ 'MediaWiki:Foo.css', false ],
[ 'MediaWiki:Foo/bar.css', false ],
[ 'User:Foo/bar.JS', true ], [ 'User:Foo/bar.JS', true ],
[ 'User:Foo/bar.CSS', true ], [ 'User:Foo/bar.CSS', true ],
[ 'MediaWiki:Foo.css', false ],
[ 'MediaWiki:Foo.JS', true ],
[ 'MediaWiki:Foo.CSS', true ],
[ 'MediaWiki:Foo.css.xxx', true ],
[ 'TEST-JS:Foo', false ], [ 'TEST-JS:Foo', false ],
[ 'TEST-JS:Foo.js', false ], [ 'TEST-JS:Foo.js', false ],
[ 'TEST-JS_TALK:Foo.js', true ],
]; ];
} }

View file

@ -96,6 +96,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
/** /**
* @todo This test method should be split up into separate test methods and * @todo This test method should be split up into separate test methods and
* data providers * data providers
* @covers Title::checkQuickPermissions
*/ */
public function testQuickPermissions() { public function testQuickPermissions() {
global $wgContLang; global $wgContLang;
@ -386,6 +387,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
/** /**
* @todo This test method should be split up into separate test methods and * @todo This test method should be split up into separate test methods and
* data providers * data providers
* @covers Title::checkSpecialsAndNSPermissions
*/ */
public function testSpecialsAndNSPermissions() { public function testSpecialsAndNSPermissions() {
global $wgNamespaceProtection; global $wgNamespaceProtection;
@ -442,91 +444,139 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
/** /**
* @todo This test method should be split up into separate test methods and * @todo This test method should be split up into separate test methods and
* data providers * data providers
* @covers Title::checkUserConfigPermissions
*/ */
public function testCssAndJavascriptPermissions() { public function testJsConfigEditPermissions() {
$this->setUser( $this->userName ); $this->setUser( $this->userName );
$this->setTitle( NS_USER, $this->userName . '/test.js' ); $this->setTitle( NS_USER, $this->userName . '/test.js' );
$this->runCSSandJSPermissions( $this->runConfigEditPermissions(
[ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ], [ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ], [ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ], [ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ], [ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ] [ [ 'badaccess-group0' ] ]
); );
$this->setTitle( NS_USER, $this->userName . '/test.css' );
$this->runCSSandJSPermissions(
[ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ]
);
$this->setTitle( NS_USER, $this->altUserName . '/test.js' );
$this->runCSSandJSPermissions(
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ]
);
$this->setTitle( NS_USER, $this->altUserName . '/test.css' );
$this->runCSSandJSPermissions(
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ]
);
$this->setTitle( NS_USER, $this->altUserName . '/tempo' );
$this->runCSSandJSPermissions(
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ]
);
}
protected function runCSSandJSPermissions( $result0, $result1, $result2, $result3, $result4 ) {
$this->setUserPerm( '' );
$this->assertEquals( $result0,
$this->title->getUserPermissionsErrors( 'bogus',
$this->user ) );
$this->setUserPerm( 'editmyusercss' );
$this->assertEquals( $result1,
$this->title->getUserPermissionsErrors( 'bogus',
$this->user ) );
$this->setUserPerm( 'editmyuserjs' );
$this->assertEquals( $result2,
$this->title->getUserPermissionsErrors( 'bogus',
$this->user ) );
$this->setUserPerm( 'editusercss' );
$this->assertEquals( $result3,
$this->title->getUserPermissionsErrors( 'bogus',
$this->user ) );
$this->setUserPerm( 'edituserjs' );
$this->assertEquals( $result4,
$this->title->getUserPermissionsErrors( 'bogus',
$this->user ) );
$this->setUserPerm( [ 'edituserjs', 'editusercss' ] );
$this->assertEquals( [ [ 'badaccess-group0' ] ],
$this->title->getUserPermissionsErrors( 'bogus',
$this->user ) );
} }
/** /**
* @todo This test method should be split up into separate test methods and * @todo This test method should be split up into separate test methods and
* data providers * data providers
* @covers Title::checkUserConfigPermissions
*/
public function testCssConfigEditPermissions() {
$this->setUser( $this->userName );
$this->setTitle( NS_USER, $this->userName . '/test.css' );
$this->runConfigEditPermissions(
[ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ]
);
}
/**
* @todo This test method should be split up into separate test methods and
* data providers
* @covers Title::checkUserConfigPermissions
*/
public function testOtherJsConfigEditPermissions() {
$this->setUser( $this->userName );
$this->setTitle( NS_USER, $this->altUserName . '/test.js' );
$this->runConfigEditPermissions(
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ]
);
}
/**
* @todo This test method should be split up into separate test methods and
* data providers
* @covers Title::checkUserConfigPermissions
*/
public function testOtherCssConfigEditPermissions() {
$this->setUser( $this->userName );
$this->setTitle( NS_USER, $this->altUserName . '/test.css' );
$this->runConfigEditPermissions(
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ]
);
}
/**
* @todo This test method should be split up into separate test methods and
* data providers
* @covers Title::checkUserConfigPermissions
*/
public function testOtherNonConfigEditPermissions() {
$this->setUser( $this->userName );
$this->setTitle( NS_USER, $this->altUserName . '/tempo' );
$this->runConfigEditPermissions(
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ]
);
}
protected function runConfigEditPermissions(
$resultNone,
$resultMyCss,
$resultMyJs,
$resultUserCss,
$resultUserJs
) {
$this->setUserPerm( '' );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultNone, $result );
$this->setUserPerm( 'editmyusercss' );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultMyCss, $result );
$this->setUserPerm( 'editmyuserjs' );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultMyJs, $result );
$this->setUserPerm( 'editusercss' );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultUserCss, $result );
$this->setUserPerm( 'edituserjs' );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultUserJs, $result );
$this->setUserPerm( [ 'edituserjs', 'editusercss' ] );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( [ [ 'badaccess-group0' ] ], $result );
}
/**
* @todo This test method should be split up into separate test methods and
* data providers
* @covers Title::checkPageRestrictions
*/ */
public function testPageRestrictions() { public function testPageRestrictions() {
global $wgContLang; global $wgContLang;
@ -619,6 +669,9 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
$this->user ) ); $this->user ) );
} }
/**
* @covers Title::checkCascadingSourcesRestrictions
*/
public function testCascadingSourcesRestrictions() { public function testCascadingSourcesRestrictions() {
$this->setTitle( NS_MAIN, "test page" ); $this->setTitle( NS_MAIN, "test page" );
$this->setUserPerm( [ "edit", "bogus" ] ); $this->setUserPerm( [ "edit", "bogus" ] );
@ -648,6 +701,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
/** /**
* @todo This test method should be split up into separate test methods and * @todo This test method should be split up into separate test methods and
* data providers * data providers
* @covers Title::checkActionPermissions
*/ */
public function testActionPermissions() { public function testActionPermissions() {
$this->setUserPerm( [ "createpage" ] ); $this->setUserPerm( [ "createpage" ] );
@ -720,6 +774,9 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
$this->title->userCan( 'move-target', $this->user ) ); $this->title->userCan( 'move-target', $this->user ) );
} }
/**
* @covers Title::checkUserBlock
*/
public function testUserBlock() { public function testUserBlock() {
global $wgEmailConfirmToEdit, $wgEmailAuthentication; global $wgEmailConfirmToEdit, $wgEmailAuthentication;
$wgEmailConfirmToEdit = true; $wgEmailConfirmToEdit = true;