was configured with 'any'.
=== New features in 1.31 ===
+* (T76554) User sub-pages named ….json are now protected in the same way that ….js
+ and ….css pages are, so that configuration options can safely be placed there.
* Wikimedia\Rdbms\IDatabase->select() and similar methods now support
joins with parentheses for grouping.
* As a first pass in standardizing dialog boxes across the MediaWiki product,
$wgGroupPermissions['user']['reupload-shared'] = true;
$wgGroupPermissions['user']['minoredit'] = true;
$wgGroupPermissions['user']['editmyusercss'] = true;
+$wgGroupPermissions['user']['editmyuserjson'] = true;
$wgGroupPermissions['user']['editmyuserjs'] = true;
$wgGroupPermissions['user']['purge'] = true;
$wgGroupPermissions['user']['sendemail'] = true;
$wgGroupPermissions['sysop']['undelete'] = true;
$wgGroupPermissions['sysop']['editinterface'] = true;
$wgGroupPermissions['sysop']['editusercss'] = true;
+$wgGroupPermissions['sysop']['edituserjson'] = true;
$wgGroupPermissions['sysop']['edituserjs'] = true;
$wgGroupPermissions['sysop']['import'] = true;
$wgGroupPermissions['sysop']['importupload'] = true;
// FIXME: Rename editmycssjs to editmyconfig
$wgGrantPermissions['editmycssjs'] = $wgGrantPermissions['editpage'];
$wgGrantPermissions['editmycssjs']['editmyusercss'] = true;
+$wgGrantPermissions['editmycssjs']['editmyuserjson'] = true;
$wgGrantPermissions['editmycssjs']['editmyuserjs'] = true;
$wgGrantPermissions['editmyoptions']['editmyoptions'] = true;
$wgGrantPermissions['editinterface'] = $wgGrantPermissions['editpage'];
$wgGrantPermissions['editinterface']['editinterface'] = true;
$wgGrantPermissions['editinterface']['editusercss'] = true;
+$wgGrantPermissions['editinterface']['edituserjson'] = true;
$wgGrantPermissions['editinterface']['edituserjs'] = true;
$wgGrantPermissions['createeditmovepage'] = $wgGrantPermissions['editpage'];
if ( $namespace == NS_MEDIAWIKI ) {
# Show a warning if editing an interface message
$out->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1\n</div>", 'editinginterface' );
- # If this is a default message (but not css or js),
+ # If this is a default message (but not css, json, or js),
# show a hint that it is translatable on translatewiki.net
- if ( !$this->mTitle->hasContentModel( CONTENT_MODEL_CSS )
+ if (
+ !$this->mTitle->hasContentModel( CONTENT_MODEL_CSS )
+ && !$this->mTitle->hasContentModel( CONTENT_MODEL_JSON )
&& !$this->mTitle->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
) {
$defaultMessageText = $this->mTitle->getDefaultMessageText();
}
if ( $this->getTitle()->isSubpageOf( $user->getUserPage() ) ) {
$isUserCssConfig = $this->mTitle->isUserCssConfigPage();
+ $isUserJsonConfig = $this->mTitle->isUserJsonConfigPage();
+ $isUserJsConfig = $this->mTitle->isUserJsConfigPage();
$warning = $isUserCssConfig
? 'usercssispublic'
- : 'userjsispublic';
+ : ( $isUserJsonConfig ? 'userjsonispublic' : 'userjsispublic' );
$out->wrapWikiMsg( '<div class="mw-userconfigpublic">$1</div>', $warning );
"<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
[ 'usercssyoucanpreview' ]
);
- }
-
- if ( $this->mTitle->isJsSubpage() && $config->get( 'AllowUserJs' ) ) {
+ } elseif ( $isUserJsonConfig /* No comparable 'AllowUserJson' */ ) {
+ $out->wrapWikiMsg(
+ "<div id='mw-userjsonyoucanpreview'>\n$1\n</div>",
+ [ 'userjsonyoucanpreview' ]
+ );
+ } elseif ( $isUserJsConfig && $config->get( 'AllowUserJs' ) ) {
$out->wrapWikiMsg(
"<div id='mw-userjsyoucanpreview'>\n$1\n</div>",
[ 'userjsyoucanpreview' ]
if ( $level === 'user' && !$config->get( 'AllowUserCss' ) ) {
$format = false;
}
+ } elseif ( $content->getModel() == CONTENT_MODEL_JSON ) {
+ $format = 'json';
+ if ( $level === 'user' /* No comparable 'AllowUserJson' */ ) {
+ $format = false;
+ }
} elseif ( $content->getModel() == CONTENT_MODEL_JAVASCRIPT ) {
$format = 'js';
if ( $level === 'user' && !$config->get( 'AllowUserJs' ) ) {
}
# Used messages to make sure grep find them:
- # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
+ # Messages: usercsspreview, userjsonpreview, userjspreview,
+ # sitecsspreview, sitejsonpreview, sitejspreview
if ( $level && $format ) {
$note = "<div id='mw-{$level}{$format}preview'>" .
$this->context->msg( "{$level}{$format}preview" )->text() .
}
/**
- * Could this page contain custom CSS or JavaScript for the global UI.
- * This is generally true for pages in the MediaWiki namespace having CONTENT_MODEL_CSS
- * or CONTENT_MODEL_JAVASCRIPT.
+ * Could this MediaWiki namespace page contain custom CSS, JSON, or JavaScript for the
+ * global UI. This is generally true for pages in the MediaWiki namespace having
+ * CONTENT_MODEL_CSS, CONTENT_MODEL_JSON, or CONTENT_MODEL_JAVASCRIPT.
*
- * This method does *not* return true for per-user JS/CSS. Use isCssJsSubpage()
+ * This method does *not* return true for per-user JS/JSON/CSS. Use isUserConfigPage()
* for that!
*
- * Note that this method should not return true for pages that contain and
- * show "inactive" CSS or JS.
+ * Note that this method should not return true for pages that contain and show
+ * "inactive" CSS, JSON, or JS.
*
* @return bool
* @since 1.31
NS_MEDIAWIKI == $this->mNamespace
&& (
$this->hasContentModel( CONTENT_MODEL_CSS )
+ || $this->hasContentModel( CONTENT_MODEL_JSON )
|| $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
)
);
/**
* @return bool
- * @deprecated Since 1.31; use ::isSiteConfigPage() instead
+ * @deprecated Since 1.31; use ::isSiteConfigPage() instead (which also checks for JSON pages)
*/
public function isCssOrJsPage() {
wfDeprecated( __METHOD__, '1.31' );
}
/**
- * Is this a "config" (.css or .js) sub-page of a user page?
+ * Is this a "config" (.css, .json, or .js) sub-page of a user page?
*
* @return bool
* @since 1.31
&& $this->isSubpage()
&& (
$this->hasContentModel( CONTENT_MODEL_CSS )
+ || $this->hasContentModel( CONTENT_MODEL_JSON )
|| $this->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
)
);
/**
* @return bool
- * @deprecated Since 1.31; use ::isUserConfigPage() instead
+ * @deprecated Since 1.31; use ::isUserConfigPage() instead (which also checks for JSON pages)
*/
public function isCssJsSubpage() {
wfDeprecated( __METHOD__, '1.31' );
}
/**
- * Trim down a .css or .js subpage title to get the corresponding skin name
+ * Trim down a .css, .json, 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, .json, or .js subpage title
* @since 1.31
*/
public function getSkinFromConfigSubpage() {
$subpage = $subpage[count( $subpage ) - 1];
$lastdot = strrpos( $subpage, '.' );
if ( $lastdot === false ) {
- return $subpage; # Never happens: only called for names ending in '.css' or '.js'
+ return $subpage; # Never happens: only called for names ending in '.css'/'.json'/'.js'
}
return substr( $subpage, 0, $lastdot );
}
/**
* @deprecated Since 1.31; use ::getSkinFromConfigSubpage() instead
- * @return string Containing skin name from .css or .js subpage title
+ * @return string Containing skin name from .css, .json, or .js subpage title
*/
public function getSkinFromCssJsSubpage() {
wfDeprecated( __METHOD__, '1.31' );
}
/**
- * Is this a .js subpage of a user page?
+ * Is this a JSON "config" sub-page of a user page?
+ *
+ * @return bool
+ * @since 1.31
+ */
+ public function isUserJsonConfigPage() {
+ return (
+ NS_USER == $this->mNamespace
+ && $this->isSubpage()
+ && $this->hasContentModel( CONTENT_MODEL_JSON )
+ );
+ }
+
+ /**
+ * Is this a JS "config" sub-page of a user page?
*
* @return bool
* @since 1.31
}
/**
- * Check CSS/JS sub-page permissions
+ * Check CSS/JSON/JS sub-page permissions
*
* @param string $action The action to check
* @param User $user User to check
* @return array List of errors
*/
private function checkUserConfigPermissions( $action, $user, $errors, $rigor, $short ) {
- # Protect css/js subpages of user pages
+ # Protect css/json/js subpages of user pages
# XXX: this might be better using restrictions
if ( $action != 'patrol' ) {
&& !$user->isAllowedAny( 'editmyusercss', 'editusercss' )
) {
$errors[] = [ 'mycustomcssprotected', $action ];
+ } elseif (
+ $this->isUserJsonConfigPage()
+ && !$user->isAllowedAny( 'editmyuserjson', 'edituserjson' )
+ ) {
+ $errors[] = [ 'mycustomjsonprotected', $action ];
} elseif (
$this->isUserJsConfigPage()
&& !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
&& !$user->isAllowed( 'editusercss' )
) {
$errors[] = [ 'customcssprotected', $action ];
+ } elseif (
+ $this->isUserJsonConfigPage()
+ && !$user->isAllowed( 'edituserjson' )
+ ) {
+ $errors[] = [ 'customjsonprotected', $action ];
} elseif (
$this->isUserJsConfigPage()
&& !$user->isAllowed( 'edituserjs' )
// If we are looking at a css/js user subpage, purge the action=raw.
if ( $this->isUserJsConfigPage() ) {
$urls[] = $this->getInternalURL( 'action=raw&ctype=text/javascript' );
+ } elseif ( $this->isUserJsonConfigPage() ) {
+ $urls[] = $this->getInternalURL( 'action=raw&ctype=application/json' );
} elseif ( $this->isUserCssConfigPage() ) {
$urls[] = $this->getInternalURL( 'action=raw&ctype=text/css' );
}
$maxage = $request->getInt( 'maxage', $config->get( 'SquidMaxage' ) );
$smaxage = $request->getIntOrNull( 'smaxage' );
if ( $smaxage === null ) {
- if ( $contentType == 'text/css' || $contentType == 'text/javascript' ) {
- // CSS/JS raw content has its own CDN max age configuration.
- // Note: Title::getCdnUrls() includes action=raw for css/js pages,
- // so if using the canonical url, this will get HTCP purges.
+ if (
+ $contentType == 'text/css' ||
+ $contentType == 'application/json' ||
+ $contentType == 'text/javascript'
+ ) {
+ // CSS/JSON/JS raw content has its own CDN max age configuration.
+ // Note: Title::getCdnUrls() includes action=raw for css/json/js
+ // pages, so if using the canonical url, this will get HTCP purges.
$smaxage = intval( $config->get( 'ForcedRawSMaxage' ) );
} else {
// No CDN cache for anything else
}
if ( $content === null || $content === false ) {
- // section not found (or section not supported, e.g. for JS and CSS)
+ // section not found (or section not supported, e.g. for JS, JSON, and CSS)
$text = false;
} else {
$text = $content->getNativeData();
'text/x-wiki',
'text/javascript',
'text/css',
+ // FIXME: Should we still allow Zope editing? External editing feature was dropped
'application/x-zope-edit',
'application/json'
];
'editmyoptions',
'editmyprivateinfo',
'editmyusercss',
+ 'editmyuserjson',
'editmyuserjs',
'editmywatchlist',
'editsemiprotected',
'editusercss',
+ 'edituserjson',
'edituserjs',
'hideuser',
'import',
"cascadeprotected": "This page has been protected from editing because it is transcluded in the following {{PLURAL:$1|page, which is|pages, which are}} protected with the \"cascading\" option turned on:\n$2",
"namespaceprotected": "You do not have permission to edit pages in the <strong>$1</strong> namespace.",
"customcssprotected": "You do not have permission to edit this CSS page because it contains another user's personal settings.",
+ "customjsonprotected": "You do not have permission to edit this JSON page because it contains another user's personal settings.",
"customjsprotected": "You do not have permission to edit this JavaScript page because it contains another user's personal settings.",
"mycustomcssprotected": "You do not have permission to edit this CSS page.",
+ "mycustomjsonprotected": "You do not have permission to edit this JSON page.",
"mycustomjsprotected": "You do not have permission to edit this JavaScript page.",
"myprivateinfoprotected": "You do not have permission to edit your private information.",
"mypreferencesprotected": "You do not have permission to edit your preferences.",
"blocked-notice-logextract": "This user is currently blocked.\nThe latest block log entry is provided below for reference:",
"clearyourcache": "<strong>Note:</strong> After saving, you may have to bypass your browser's cache to see the changes.\n* <strong>Firefox / Safari:</strong> Hold <em>Shift</em> while clicking <em>Reload</em>, or press either <em>Ctrl-F5</em> or <em>Ctrl-R</em> (<em>⌘-R</em> on a Mac)\n* <strong>Google Chrome:</strong> Press <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> on a Mac)\n* <strong>Internet Explorer:</strong> Hold <em>Ctrl</em> while clicking <em>Refresh</em>, or press <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Go to <em>Menu → Settings</em> (<em>Opera → Preferences</em> on a Mac) and then to <em>Privacy & security → Clear browsing data → Cached images and files</em>.",
"usercssyoucanpreview": "<strong>Tip:</strong> Use the \"{{int:showpreview}}\" button to test your new CSS before saving.",
+ "userjsonyoucanpreview": "<strong>Tip:</strong> Use the \"{{int:showpreview}}\" button to test your new JSON before saving.",
"userjsyoucanpreview": "<strong>Tip:</strong> Use the \"{{int:showpreview}}\" button to test your new JavaScript before saving.",
"usercsspreview": "<strong>Remember that you are only previewing your user CSS.\nIt has not yet been saved!</strong>",
+ "userjsonpreview": "<strong>Remember that you are only testing/previewing your user JSON config.\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>",
+ "sitejsonpreview": "<strong>Remember that you are only previewing this JSON config.\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>",
- "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.",
+ "userinvalidconfigtitle": "<strong>Warning:</strong> There is no skin \"$1\".\nCustom .css, .json, and .js pages use a lowercase title, e.g. {{ns:user}}:Foo/vector.css as opposed to {{ns:user}}:Foo/Vector.css.",
"updated": "(Updated)",
"note": "<strong>Note:</strong>",
"previewnote": "<strong>Remember that this is only a preview.</strong>\nYour changes have not yet been saved!",
"default": "default",
"prefs-files": "Files",
"prefs-custom-css": "Custom CSS",
+ "prefs-custom-json": "Custom JSON",
"prefs-custom-js": "Custom JavaScript",
- "prefs-common-config": "Shared CSS/JavaScript for all skins:",
+ "prefs-common-config": "Shared CSS/JSON/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-emailconfirm-label": "Email confirmation:",
"youremail": "Email:",
"right-editcontentmodel": "Edit the content model of a page",
"right-editinterface": "Edit the user interface",
"right-editusercss": "Edit other users' CSS files",
+ "right-edituserjson": "Edit other users' JSON files",
"right-edituserjs": "Edit other users' JavaScript files",
"right-editmyusercss": "Edit your own user CSS files",
+ "right-editmyuserjson": "Edit your own user JSON files",
"right-editmyuserjs": "Edit your own user JavaScript files",
"right-viewmywatchlist": "View your own watchlist",
"right-editmywatchlist": "Edit your own watchlist. Note some actions will still add pages even without this right.",
"grant-createaccount": "Create accounts",
"grant-createeditmovepage": "Create, edit, and move pages",
"grant-delete": "Delete pages, revisions, and log entries",
- "grant-editinterface": "Edit the MediaWiki namespace and user CSS/JavaScript",
- "grant-editmycssjs": "Edit your user CSS/JavaScript",
+ "grant-editinterface": "Edit the MediaWiki namespace and user CSS/JSON/JavaScript",
+ "grant-editmycssjs": "Edit your user CSS/JSON/JavaScript",
"grant-editmyoptions": "Edit your user preferences",
"grant-editmywatchlist": "Edit your watchlist",
"grant-editpage": "Edit existing pages",
"group-bot.css": "/* CSS placed here will affect bots only */",
"group-sysop.css": "/* CSS placed here will affect sysops only */",
"group-bureaucrat.css": "/* CSS placed here will affect bureaucrats only */",
+ "common.json": "/* Any JSON here will be loaded for all users on every page load. */",
"common.js": "/* Any JavaScript here will be loaded for all users on every page load. */",
"group-autoconfirmed.js": "/* Any JavaScript here will be loaded for autoconfirmed users only */",
"group-user.js": "/* Any JavaScript here will be loaded for registered users only */",
"unlinkaccounts-success": "The account was unlinked.",
"authenticationdatachange-ignored": "The authentication data change was not handled. Maybe no provider was configured?",
"userjsispublic": "Please note: JavaScript subpages should not contain confidential data as they are viewable by other users.",
+ "userjsonispublic": "Please note: JSON subpages should not contain confidential data as they are viewable by other users.",
"usercssispublic": "Please note: CSS subpages should not contain confidential data as they are viewable by other users.",
"restrictionsfield-badip": "Invalid IP address or range: $1",
"restrictionsfield-label": "Allowed IP ranges:",
"cascadeprotected": "Parameters:\n* $1 - number of cascade-protected pages, used for PLURAL\n* $2 - list of cascade-protected pages\n* $3 - (Unused) the action the user attempted to perform",
"namespaceprotected": "Parameters:\n* $1 - namespace name\n* $2 - (Unused) the action the user attempted to perform",
"customcssprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
+ "customjsonprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
"customjsprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
"mycustomcssprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
+ "mycustomjsonprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
"mycustomjsprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
"myprivateinfoprotected": "Used as error message.",
"mypreferencesprotected": "Used as error message.",
"userpage-userdoesnotexist": "Error message displayed when trying to edit or create a page or a subpage that belongs to a user who is not registered on the wiki.\n\nParameters:\n* $1 - a username\n{{Identical|Userdoesnotexist}}",
"userpage-userdoesnotexist-view": "Shown in user pages of non-existing users. See for example [{{canonicalurl:User:Foo}} User:Foo].\n\nParameters:\n* $1 - a username\n{{Identical|Userdoesnotexist}}",
"blocked-notice-logextract": "{{gender}}\nParameters:\n* $1 - (Optional) the name of the blocked user. Can be used for GENDER.",
- "clearyourcache": "Text at the top of .js/.css pages.\n\nWhen translating browser function names, check how they are translated in the localized versions of these web browsers in your language. If a browser is not translated to it, use English or another language in which browsers are most commonly used by the speakers of your language.",
- "usercssyoucanpreview": "Text displayed on every CSS page.\n\nSee also:\n* {{msg-mw|Userjsyoucanpreview}}\n* {{msg-mw|Showpreview}}",
- "userjsyoucanpreview": "Text displayed on every JavaScript page.\n\nSee also:\n* {{msg-mw|Usercssyoucanpreview}}\n* {{msg-mw|Showpreview}}",
+ "clearyourcache": "Text at the top of .js/.json/.css pages.\n\nWhen translating browser function names, check how they are translated in the localized versions of these web browsers in your language. If a browser is not translated to it, use English or another language in which browsers are most commonly used by the speakers of your language.",
+ "usercssyoucanpreview": "Text displayed on every CSS page.\n\nSee also:\n* {{msg-mw|Userjsyoucanpreview}}\n* {{msg-mw|Userjsonyoucanpreview}}\n* {{msg-mw|Showpreview}}",
+ "userjsonyoucanpreview": "Text displayed on every JSON page.\n\nSee also:\n* {{msg-mw|Usercssyoucanpreview}}\n* {{msg-mw|Userjsyoucanpreview}}\n* {{msg-mw|Showpreview}}",
+ "userjsyoucanpreview": "Text displayed on every JavaScript page.\n\nSee also:\n* {{msg-mw|Userjsonyoucanpreview}}\n* {{msg-mw|Usercssyoucanpreview}}\n* {{msg-mw|Showpreview}}",
"usercsspreview": "Text displayed on preview of every user .css subpage.\n\nSee also:\n* {{msg-mw|Sitecsspreview}}",
+ "userjsonpreview": "Text displayed on preview of every user .json 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}}",
+ "sitejsonpreview": "Text displayed on preview of .json pages in MediaWiki namespace",
"sitejspreview": "Text displayed on preview of .js pages in MediaWiki namespace",
"userinvalidconfigtitle": "Parameters:\n* $1 - skin name",
"updated": "{{Identical|Updated}}",
"addsection-preload": "{{notranslate}}",
"addsection-editintro": "{{notranslate}}",
"defaultmessagetext": "Caption above the default message text shown on the left-hand side of a diff displayed after clicking \"Show changes\" when creating a new page in the MediaWiki: namespace",
- "content-failed-to-parse": "Error message indicating that the page's content can not be saved because it is syntactically invalid. This may occurr for content types using serialization or a strict markup syntax.\n\nParameters:\n* $1 – content model, any one of the following messages:\n** {{msg-mw|Content-model-wikitext}}\n** {{msg-mw|Content-model-javascript}}\n** {{msg-mw|Content-model-css}}\n** {{msg-mw|Content-model-text}}\n* $2 – content format as MIME type (e.g. <code>text/css</code>)\n* $3 – specific error message",
+ "content-failed-to-parse": "Error message indicating that the page's content can not be saved because it is syntactically invalid. This may occurr for content types using serialization or a strict markup syntax.\n\nParameters:\n* $1 – content model, any one of the following messages:\n** {{msg-mw|Content-model-wikitext}}\n** {{msg-mw|Content-model-javascript}}\n** {{msg-mw|Content-model-css}}\n** {{msg-mw|Content-model-json}}\n** {{msg-mw|Content-model-text}}\n* $2 – content format as MIME type (e.g. <code>text/css</code>)\n* $3 – specific error message",
"invalid-content-data": "Error message indicating that the page's content can not be saved because it is invalid. This may occurr for content types with internal consistency constraints.",
- "content-not-allowed-here": "Error message indicating that the desired content model is not supported in given localtion.\n* $1 - the human readable name of the content model: {{msg-mw|Content-model-wikitext}}, {{msg-mw|Content-model-javascript}}, {{msg-mw|Content-model-css}} or {{msg-mw|Content-model-text}}\n* $2 - the title of the page in question",
+ "content-not-allowed-here": "Error message indicating that the desired content model is not supported in given localtion.\n* $1 - the human readable name of the content model: {{msg-mw|Content-model-wikitext}}, {{msg-mw|Content-model-javascript}}, {{msg-mw|Content-model-json}}, {{msg-mw|Content-model-css}} or {{msg-mw|Content-model-text}}\n* $2 - the title of the page in question",
"editwarning-warning": "Uses {{msg-mw|Prefs-editing}}",
"editpage-invalidcontentmodel-title": "Title of error page shown when using an unrecognized content model on EditPage",
"editpage-invalidcontentmodel-text": "Error message shown when using an unrecognized content model on EditPage. $1 is the user's invalid input",
"default": "{{Identical|Default}}",
"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-json": "visible on [[Special:Preferences]] -[Skins].\n{{Identical|Custom JSON}}",
"prefs-custom-js": "visible on [[Special:Preferences]] -[Skins].\n{{Identical|Custom JavaScript}}",
"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]].",
"right-editcontentmodel": "{{doc-right|editcontentmodel}}",
"right-editinterface": "{{doc-right|editinterface}}",
"right-editusercss": "{{doc-right|editusercss}}\nSee also:\n* {{msg-mw|Right-editmyusercss}}",
+ "right-edituserjson": "{{doc-right|edituserjson}}\nSee also:\n* {{msg-mw|Right-editmyuserjson}}",
"right-edituserjs": "{{doc-right|edituserjs}}\nSee also:\n* {{msg-mw|Right-editmyuserjs}}",
"right-editmyusercss": "{{doc-right|editmyusercss}}\nSee also:\n* {{msg-mw|Right-editusercss}}",
+ "right-editmyuserjson": "{{doc-right|editmyuserjson}}\nSee also:\n* {{msg-mw|Right-edituserjson}}",
"right-editmyuserjs": "{{doc-right|editmyuserjs}}\nSee also:\n* {{msg-mw|Right-edituserjs}}",
"right-viewmywatchlist": "{{doc-right|viewmywatchlist}}",
"right-editmywatchlist": "{{doc-right|editmywatchlist}}",
"group-bot.css": "{{doc-group|bot|css}}",
"group-sysop.css": "{{doc-group|sysop|css}}",
"group-bureaucrat.css": "{{doc-group|bureaucrat|css}}",
+ "common.json": "{{optional}}\nJSON for all users.",
"common.js": "{{optional}}\nJS for all users.",
"group-autoconfirmed.js": "{{doc-group|autoconfirmed|js}}",
"group-user.js": "{{doc-group|user|js}}",
"unlinkaccounts": "Title of the special page [[Special:UnlinkAccounts]] which allows the user to remove linked remote accounts.",
"unlinkaccounts-success": "Account unlinking form success message",
"authenticationdatachange-ignored": "Shown when authentication data change was unsuccessful due to configuration problems.\n\nCf. e.g. {{msg-mw|Passwordreset-ignored}}.",
- "userjsispublic": "A reminder to users that Javascript subpages are not preferences but normal pages, and thus can be viewed by other users and the general public. This message is shown to a user whenever they are editing a subpage in their own user-space that ends in .js. See also {{msg-mw|usercssispublic}}.",
- "usercssispublic": "A reminder to users that CSS subpages are not preferences but normal pages, and thus can be viewed by other users and the general public. This message is shown to a user whenever they are editing a subpage in their own user-space that ends in .css. See also {{msg-mw|userjsispublic}}",
+ "userjsispublic": "A reminder to users that Javascript subpages are not preferences but normal pages, and thus can be viewed by other users and the general public. This message is shown to a user whenever they are editing a subpage in their own user-space that ends in .js. See also {{msg-mw|usercssispublic}} and {{msg-mw|userjsonispublic}}.",
+ "userjsonispublic": "A reminder to users that JSON subpages are not preferences but normal pages, and thus can be viewed by other users and the general public. This message is shown to a user whenever they are editing a subpage in their own user-space that ends in .json. See also {{msg-mw|userjsispublic}} and {{msg-mw|usercssispublic}}",
+ "usercssispublic": "A reminder to users that CSS subpages are not preferences but normal pages, and thus can be viewed by other users and the general public. This message is shown to a user whenever they are editing a subpage in their own user-space that ends in .css. See also {{msg-mw|userjsispublic}} and {{msg-mw|userjsonispublic}}",
"restrictionsfield-badip": "An error message shown when one entered an invalid IP address or range in a restrictions field (such as Special:BotPassword). $1 is the IP address.",
"restrictionsfield-label": "Field label shown for restriction fields (e.g. on Special:BotPassword).",
"restrictionsfield-help": "Placeholder text displayed in restriction fields (e.g. on Special:BotPassword).",
[ 'User:Foo', false ],
[ 'User:Foo.js', false ],
[ 'User:Foo/bar.js', false ],
+ [ 'User:Foo/bar.json', false ],
[ 'User:Foo/bar.css', false ],
[ 'User:Foo/bar.JS', false ],
+ [ 'User:Foo/bar.JSON', false ],
[ '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', true ],
+ [ 'MediaWiki:Foo.json', true ],
[ 'MediaWiki:Foo.css', true ],
[ 'MediaWiki:Foo.JS', false ],
+ [ 'MediaWiki:Foo.JSON', false ],
[ 'MediaWiki:Foo.CSS', false ],
[ 'MediaWiki:Foo/bar.css', true ],
[ 'MediaWiki:Foo.css.xxx', false ],
[ 'User:Foo.js', false ],
[ 'User:Foo/bar.js', true ],
[ 'User:Foo/bar.JS', false ],
+ [ 'User:Foo/bar.json', true ],
+ [ 'User:Foo/bar.JSON', 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.json', false ],
[ 'MediaWiki:Foo.css', false ],
[ 'MediaWiki:Foo.JS', false ],
+ [ 'MediaWiki:Foo.JSON', false ],
[ 'MediaWiki:Foo.CSS', false ],
[ 'MediaWiki:Foo.css.xxx', false ],
[ 'TEST-JS:Foo', false ],
[ 'Help:Foo.css', false ],
[ 'User:Foo', false ],
[ 'User:Foo.js', false ],
+ [ 'User:Foo.json', false ],
[ 'User:Foo.css', false ],
[ 'User:Foo/bar.js', false ],
+ [ 'User:Foo/bar.json', false ],
[ 'User:Foo/bar.css', true ],
];
}
[ 'User:Foo', true ],
[ 'User:Foo.js', true ],
[ 'User:Foo/bar.js', false ],
+ [ 'User:Foo/bar.json', false ],
[ 'User:Foo/bar.css', false ],
[ 'User talk:Foo/bar.css', true ],
[ 'User:Foo/bar.js.xxx', true ],
[ 'User:Foo/bar.xxx', true ],
[ 'MediaWiki:Foo.js', false ],
[ 'User:Foo/bar.JS', true ],
+ [ 'User:Foo/bar.JSON', true ],
[ 'User:Foo/bar.CSS', true ],
+ [ 'MediaWiki:Foo.json', false ],
[ 'MediaWiki:Foo.css', false ],
[ 'MediaWiki:Foo.JS', true ],
+ [ 'MediaWiki:Foo.JSON', true ],
[ 'MediaWiki:Foo.CSS', true ],
[ 'MediaWiki:Foo.css.xxx', true ],
[ 'TEST-JS:Foo', false ],
$this->runConfigEditPermissions(
[ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
+ [ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomjsprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ]
);
}
+ /**
+ * @todo This test method should be split up into separate test methods and
+ * data providers
+ * @covers Title::checkUserConfigPermissions
+ */
+ public function testJsonConfigEditPermissions() {
+ $this->setUser( $this->userName );
+
+ $this->setTitle( NS_USER, $this->userName . '/test.json' );
+ $this->runConfigEditPermissions(
+ [ [ 'badaccess-group0' ], [ 'mycustomjsonprotected', 'bogus' ] ],
+
+ [ [ 'badaccess-group0' ], [ 'mycustomjsonprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ] ],
+ [ [ 'badaccess-group0' ], [ 'mycustomjsonprotected', 'bogus' ] ],
+
+ [ [ 'badaccess-group0' ], [ 'mycustomjsonprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ] ],
+ [ [ 'badaccess-group0' ], [ 'mycustomjsonprotected', 'bogus' ] ]
+ );
+ }
+
/**
* @todo This test method should be split up into separate test methods and
* data providers
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
+ [ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'mycustomcssprotected', 'bogus' ] ]
);
}
$this->runConfigEditPermissions(
[ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ], [ 'customjsprotected', 'bogus' ] ],
[ [ '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 testOtherJsonConfigEditPermissions() {
+ $this->setUser( $this->userName );
+
+ $this->setTitle( NS_USER, $this->altUserName . '/test.json' );
+ $this->runConfigEditPermissions(
+ [ [ 'badaccess-group0' ], [ 'customjsonprotected', 'bogus' ] ],
+
+ [ [ 'badaccess-group0' ], [ 'customjsonprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ], [ 'customjsonprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ], [ 'customjsonprotected', 'bogus' ] ],
+
+ [ [ 'badaccess-group0' ], [ 'customjsonprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ] ],
+ [ [ 'badaccess-group0' ], [ 'customjsonprotected', 'bogus' ] ]
+ );
+ }
+
/**
* @todo This test method should be split up into separate test methods and
* data providers
$this->runConfigEditPermissions(
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
+ [ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ] ],
+ [ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ],
[ [ 'badaccess-group0' ], [ 'customcssprotected', 'bogus' ] ]
);
}
$this->runConfigEditPermissions(
[ [ 'badaccess-group0' ] ],
+ [ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
+ [ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ],
[ [ 'badaccess-group0' ] ]
);
protected function runConfigEditPermissions(
$resultNone,
$resultMyCss,
+ $resultMyJson,
$resultMyJs,
$resultUserCss,
+ $resultUserJson,
$resultUserJs
) {
$this->setUserPerm( '' );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultMyCss, $result );
+ $this->setUserPerm( 'editmyuserjson' );
+ $result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
+ $this->assertEquals( $resultMyJson, $result );
+
$this->setUserPerm( 'editmyuserjs' );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultMyJs, $result );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultUserCss, $result );
+ $this->setUserPerm( 'edituserjson' );
+ $result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
+ $this->assertEquals( $resultUserJson, $result );
+
$this->setUserPerm( 'edituserjs' );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( $resultUserJs, $result );
- $this->setUserPerm( [ 'edituserjs', 'editusercss' ] );
+ $this->setUserPerm( [ 'edituserjs', 'edituserjson', 'editusercss' ] );
$result = $this->title->getUserPermissionsErrors( 'bogus', $this->user );
$this->assertEquals( [ [ 'badaccess-group0' ] ], $result );
}