* Argon2 password hashing is now available, can be enabled via
$wgPasswordDefault = 'argon2'. It's designed to resist timing attacks
(requires PHP 7.2+) and GPU hacking (7.3+).
+* Special:CreateAccount now warns the user if their chosen username has to be
+ normalized.
=== External library changes in 1.33 ===
"badretype": "The passwords you entered do not match.",
"usernameinprogress": "An account creation for this user name is already in progress.\nPlease wait.",
"userexists": "Username entered already in use.\nPlease choose a different name.",
+ "createacct-normalization": "Your username will be adjusted to \"$2\" due to technical restrictions.",
"loginerror": "Login error",
"createacct-error": "Account creation error",
"createaccounterror": "Could not create account: $1",
"badretype": "Used as error message when the new password and its retype do not match.",
"usernameinprogress": "Used as error message in creating a user account.",
"userexists": "Used as error message in creating a user account.",
+ "createacct-normalization": "Used as warning message on account creation when user name is adjusted silently due to technical restrictions (e.g. first letter capitalized, underscores converted to spaces).\nParameters:\n* $1 - the old username\n* $2 - the new username",
"loginerror": "Used as title of error message.\n{{Identical|Login error}}",
"createacct-error": "Used as heading for the error message.",
"createaccounterror": "Parameters:\n* $1 - an error message",
'createacct-emailrequired',
'noname',
'userexists',
+ 'createacct-normalization',
],
'dependencies' => [
'mediawiki.api',
if ( value === '' ) {
this.currentValue = value;
- this.setErrors( [] );
+ this.setErrors( true, [] );
return;
}
that.currentValue = value;
- if ( info.valid ) {
- that.setErrors( [], forceReplacement );
- } else {
- that.setErrors( info.messages, forceReplacement );
- }
+ that.setErrors( info.valid, info.messages, forceReplacement );
} ).fail( function () {
that.currentValue = null;
- that.setErrors( [] );
+ that.setErrors( true, [] );
} );
return currentRequestInternal;
/**
* Display errors associated with the form element
+ * @param {boolean} valid Whether the input is still valid regardless of the messages
* @param {Array} errors Error messages. Each error message will be appended to a
* `<span>` or `<li>`, as with jQuery.append().
* @param {boolean} [forceReplacement] Set true to force a visual replacement even
* @return {mw.htmlform.Checker}
* @chainable
*/
- mw.htmlform.Checker.prototype.setErrors = function ( errors, forceReplacement ) {
+ mw.htmlform.Checker.prototype.setErrors = function ( valid, errors, forceReplacement ) {
var $oldErrorBox, tagName, showFunc, text, replace,
$errorBox = this.$errorBox;
// FIXME: Use CSS transition
// eslint-disable-next-line no-jquery/no-slide
$errorBox
- .attr( 'class', 'error' )
+ .attr( 'class', valid ? 'warning' : 'error' )
.empty()
.append( errors.map( function ( e ) {
return errors.length === 1 ? e : $( '<li>' ).append( e );
} ) )
.slideDown();
};
- if ( $oldErrorBox !== $errorBox && $oldErrorBox.hasClass( 'error' ) ) {
+ if (
+ $oldErrorBox !== $errorBox &&
+ ( $oldErrorBox.hasClass( 'error' ) || $oldErrorBox.hasClass( 'warning' ) )
+ ) {
// eslint-disable-next-line no-jquery/no-slide
$oldErrorBox.slideUp( showFunc );
} else {
updateForCheckbox();
} );
- // Check if the username is invalid or already taken
+ // Check if the username is invalid or already taken; show username normalisation warning
mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
var $usernameInput = $root.find( '#wpName2' ),
$passwordInput = $root.find( '#wpPassword2' ),
// We could just use .then() if we didn't have to pass on .abort()…
var d, apiPromise;
+ // Leading/trailing/multiple whitespace characters are always stripped in usernames,
+ // this should not require a warning. We do warn about underscores.
+ username = username.replace( / +/g, ' ' ).trim();
+
d = $.Deferred();
apiPromise = api.get( {
action: 'query',
return m.html;
} ) : []
} );
+ } else if ( userinfo.name !== username ) {
+ d.resolve( { valid: true, messages: [
+ mw.message( 'createacct-normalization', username, userinfo.name ).parseDom()
+ ] } );
} else {
d.resolve( { valid: true, messages: [] } );
}