Initial Drupal 11 with DDEV setup
This commit is contained in:
369
web/core/modules/user/js/user.js
Normal file
369
web/core/modules/user/js/user.js
Normal file
@ -0,0 +1,369 @@
|
||||
/**
|
||||
* @file
|
||||
* User behaviors.
|
||||
*/
|
||||
|
||||
(($, Drupal) => {
|
||||
/**
|
||||
* An object containing CSS classes used for password widget.
|
||||
*
|
||||
* @type {object}
|
||||
* @prop {string} passwordParent - A CSS class for the parent element.
|
||||
* @prop {string} passwordsMatch - A CSS class indicating password match.
|
||||
* @prop {string} passwordsNotMatch - A CSS class indicating passwords
|
||||
* doesn't match.
|
||||
* @prop {string} passwordWeak - A CSS class indicating weak password
|
||||
* strength.
|
||||
* @prop {string} passwordFair - A CSS class indicating fair password
|
||||
* strength.
|
||||
* @prop {string} passwordGood - A CSS class indicating good password
|
||||
* strength.
|
||||
* @prop {string} passwordStrong - A CSS class indicating strong password
|
||||
* strength.
|
||||
* @prop {string} widgetInitial - Initial CSS class that should be removed
|
||||
* on a state change.
|
||||
* @prop {string} passwordEmpty - A CSS class indicating password has not
|
||||
* been filled.
|
||||
* @prop {string} passwordFilled - A CSS class indicating password has
|
||||
* been filled.
|
||||
* @prop {string} confirmEmpty - A CSS class indicating password
|
||||
* confirmation has not been filled.
|
||||
* @prop {string} confirmFilled - A CSS class indicating password
|
||||
* confirmation has been filled.
|
||||
*/
|
||||
Drupal.user = {
|
||||
password: {
|
||||
css: {
|
||||
passwordParent: 'password-parent',
|
||||
passwordsMatch: 'ok',
|
||||
passwordsNotMatch: 'error',
|
||||
passwordWeak: 'is-weak',
|
||||
passwordFair: 'is-fair',
|
||||
passwordGood: 'is-good',
|
||||
passwordStrong: 'is-strong',
|
||||
widgetInitial: '',
|
||||
passwordEmpty: '',
|
||||
passwordFilled: '',
|
||||
confirmEmpty: '',
|
||||
confirmFilled: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Attach handlers to evaluate the strength of any password fields and to
|
||||
* check that its confirmation is correct.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches password strength indicator and other relevant validation to
|
||||
* password fields.
|
||||
*/
|
||||
Drupal.behaviors.password = {
|
||||
attach(context, settings) {
|
||||
const cssClasses = Drupal.user.password.css;
|
||||
once('password', 'input.js-password-field', context).forEach((value) => {
|
||||
const $mainInput = $(value);
|
||||
const $mainInputParent = $mainInput
|
||||
.parent()
|
||||
.addClass(cssClasses.passwordParent);
|
||||
const $passwordWidget = $mainInput.closest(
|
||||
'.js-form-type-password-confirm',
|
||||
);
|
||||
const $confirmInput = $passwordWidget.find('input.js-password-confirm');
|
||||
const $passwordConfirmMessage = $(
|
||||
Drupal.theme('passwordConfirmMessage', settings.password),
|
||||
);
|
||||
|
||||
const $passwordMatchStatus = $passwordConfirmMessage
|
||||
.find('[data-drupal-selector="password-match-status-text"]')
|
||||
.first();
|
||||
|
||||
const $confirmInputParent = $confirmInput
|
||||
.parent()
|
||||
.addClass('confirm-parent')
|
||||
.append($passwordConfirmMessage);
|
||||
|
||||
// List of classes to be removed from the strength bar on a state
|
||||
// change.
|
||||
const passwordStrengthBarClassesToRemove = [
|
||||
cssClasses.passwordWeak || '',
|
||||
cssClasses.passwordFair || '',
|
||||
cssClasses.passwordGood || '',
|
||||
cssClasses.passwordStrong || '',
|
||||
]
|
||||
.join(' ')
|
||||
.trim();
|
||||
|
||||
// List of classes to be removed from the text wrapper on a state
|
||||
// change.
|
||||
const confirmTextWrapperClassesToRemove = [
|
||||
cssClasses.passwordsMatch || '',
|
||||
cssClasses.passwordsNotMatch || '',
|
||||
]
|
||||
.join(' ')
|
||||
.trim();
|
||||
|
||||
// List of classes to be removed from the widget on a state change.
|
||||
const widgetClassesToRemove = [
|
||||
cssClasses.widgetInitial || '',
|
||||
cssClasses.passwordEmpty || '',
|
||||
cssClasses.passwordFilled || '',
|
||||
cssClasses.confirmEmpty || '',
|
||||
cssClasses.confirmFilled || '',
|
||||
]
|
||||
.join(' ')
|
||||
.trim();
|
||||
|
||||
const password = {};
|
||||
|
||||
// If the password strength indicator is enabled, add its markup.
|
||||
if (settings.password.showStrengthIndicator) {
|
||||
const $passwordStrength = $(
|
||||
Drupal.theme('passwordStrength', settings.password),
|
||||
);
|
||||
password.$strengthBar = $passwordStrength
|
||||
.find('[data-drupal-selector="password-strength-indicator"]')
|
||||
.first();
|
||||
password.$strengthTextWrapper = $passwordStrength
|
||||
.find('[data-drupal-selector="password-strength-text"]')
|
||||
.first();
|
||||
password.$suggestions = $(
|
||||
Drupal.theme('passwordSuggestions', settings.password, []),
|
||||
);
|
||||
|
||||
password.$suggestions.hide();
|
||||
$mainInputParent.append($passwordStrength);
|
||||
$confirmInputParent.after(password.$suggestions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds classes to the widget indicating if the elements are filled.
|
||||
*/
|
||||
const addWidgetClasses = () => {
|
||||
$passwordWidget
|
||||
.addClass(
|
||||
$mainInput[0].value
|
||||
? cssClasses.passwordFilled
|
||||
: cssClasses.passwordEmpty,
|
||||
)
|
||||
.addClass(
|
||||
$confirmInput[0].value
|
||||
? cssClasses.confirmFilled
|
||||
: cssClasses.confirmEmpty,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check that password and confirmation inputs match.
|
||||
*
|
||||
* @param {string} confirmInputVal
|
||||
* The value of the confirm input.
|
||||
*/
|
||||
const passwordCheckMatch = (confirmInputVal) => {
|
||||
const passwordsAreMatching = $mainInput[0].value === confirmInputVal;
|
||||
const confirmClass = passwordsAreMatching
|
||||
? cssClasses.passwordsMatch
|
||||
: cssClasses.passwordsNotMatch;
|
||||
const confirmMessage = passwordsAreMatching
|
||||
? settings.password.confirmSuccess
|
||||
: settings.password.confirmFailure;
|
||||
|
||||
// Update the success message and set the class if needed.
|
||||
if (
|
||||
!$passwordMatchStatus.hasClass(confirmClass) ||
|
||||
!$passwordMatchStatus.html() === confirmMessage
|
||||
) {
|
||||
if (confirmTextWrapperClassesToRemove) {
|
||||
$passwordMatchStatus.removeClass(
|
||||
confirmTextWrapperClassesToRemove,
|
||||
);
|
||||
}
|
||||
$passwordMatchStatus.html(confirmMessage).addClass(confirmClass);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the password strength.
|
||||
*/
|
||||
const passwordCheck = () => {
|
||||
if (settings.password.showStrengthIndicator) {
|
||||
// Evaluate the password strength.
|
||||
const result = Drupal.evaluatePasswordStrength(
|
||||
$mainInput[0].value,
|
||||
settings.password,
|
||||
);
|
||||
const $currentPasswordSuggestions = $(
|
||||
Drupal.theme(
|
||||
'passwordSuggestions',
|
||||
settings.password,
|
||||
result.messageTips,
|
||||
),
|
||||
);
|
||||
|
||||
// Update the suggestions for how to improve the password if needed.
|
||||
if (
|
||||
password.$suggestions.html() !==
|
||||
$currentPasswordSuggestions.html()
|
||||
) {
|
||||
password.$suggestions.replaceWith($currentPasswordSuggestions);
|
||||
password.$suggestions = $currentPasswordSuggestions.toggle(
|
||||
// Only show the description box if a weakness exists in the
|
||||
// password.
|
||||
result.strength !== 100,
|
||||
);
|
||||
}
|
||||
|
||||
if (passwordStrengthBarClassesToRemove) {
|
||||
password.$strengthBar.removeClass(
|
||||
passwordStrengthBarClassesToRemove,
|
||||
);
|
||||
}
|
||||
// Adjust the length of the strength indicator.
|
||||
password.$strengthBar[0].style.width = `${result.strength}%`;
|
||||
password.$strengthBar.addClass(result.indicatorClass);
|
||||
|
||||
// Update the strength indication text.
|
||||
password.$strengthTextWrapper.html(result.indicatorText);
|
||||
}
|
||||
|
||||
// Check the value in the confirm input and show results.
|
||||
if ($confirmInput[0].value) {
|
||||
passwordCheckMatch($confirmInput[0].value);
|
||||
$passwordConfirmMessage[0].style.visibility = 'visible';
|
||||
} else {
|
||||
$passwordConfirmMessage[0].style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
if (widgetClassesToRemove) {
|
||||
$passwordWidget.removeClass(widgetClassesToRemove);
|
||||
addWidgetClasses();
|
||||
}
|
||||
};
|
||||
|
||||
if (widgetClassesToRemove) {
|
||||
addWidgetClasses();
|
||||
}
|
||||
|
||||
// Monitor input events.
|
||||
$mainInput.on('input', passwordCheck);
|
||||
$confirmInput.on('input', passwordCheck);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate the strength of a user's password.
|
||||
*
|
||||
* Returns the estimated strength and the relevant output message.
|
||||
*
|
||||
* @param {string} password
|
||||
* The password to evaluate.
|
||||
* @param {object} passwordSettings
|
||||
* A password settings object containing the text to display and the CSS
|
||||
* classes for each strength level.
|
||||
*
|
||||
* @return {object}
|
||||
* An object containing strength, message, indicatorText and indicatorClass.
|
||||
*/
|
||||
Drupal.evaluatePasswordStrength = (password, passwordSettings) => {
|
||||
password = password.trim();
|
||||
let indicatorText;
|
||||
let indicatorClass;
|
||||
let weaknesses = 0;
|
||||
let strength = 100;
|
||||
let msg = [];
|
||||
|
||||
const hasLowercase = /[a-z]/.test(password);
|
||||
const hasUppercase = /[A-Z]/.test(password);
|
||||
const hasNumbers = /[0-9]/.test(password);
|
||||
const hasPunctuation = /[^a-zA-Z0-9]/.test(password);
|
||||
|
||||
// If there is a username edit box on the page, compare password to that,
|
||||
// otherwise use value from the database.
|
||||
const $usernameBox = $('input.username');
|
||||
const username =
|
||||
$usernameBox.length > 0
|
||||
? $usernameBox[0].value
|
||||
: passwordSettings.username;
|
||||
|
||||
// Lose 5 points for every character less than 12, plus a 30 point penalty.
|
||||
if (password.length < 12) {
|
||||
msg.push(passwordSettings.tooShort);
|
||||
strength -= (12 - password.length) * 5 + 30;
|
||||
}
|
||||
|
||||
// Count weaknesses.
|
||||
if (!hasLowercase) {
|
||||
msg.push(passwordSettings.addLowerCase);
|
||||
weaknesses += 1;
|
||||
}
|
||||
if (!hasUppercase) {
|
||||
msg.push(passwordSettings.addUpperCase);
|
||||
weaknesses += 1;
|
||||
}
|
||||
if (!hasNumbers) {
|
||||
msg.push(passwordSettings.addNumbers);
|
||||
weaknesses += 1;
|
||||
}
|
||||
if (!hasPunctuation) {
|
||||
msg.push(passwordSettings.addPunctuation);
|
||||
weaknesses += 1;
|
||||
}
|
||||
|
||||
// Apply penalty for each weakness (balanced against length penalty).
|
||||
switch (weaknesses) {
|
||||
case 1:
|
||||
strength -= 12.5;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strength -= 25;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
strength -= 40;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if password is the same as the username.
|
||||
if (password !== '' && password.toLowerCase() === username.toLowerCase()) {
|
||||
msg.push(passwordSettings.sameAsUsername);
|
||||
// Passwords the same as username are always very weak.
|
||||
strength = 5;
|
||||
}
|
||||
|
||||
const cssClasses = Drupal.user.password.css;
|
||||
|
||||
// Based on the strength, work out what text should be shown by the
|
||||
// password strength meter.
|
||||
if (strength < 60) {
|
||||
indicatorText = passwordSettings.weak;
|
||||
indicatorClass = cssClasses.passwordWeak;
|
||||
} else if (strength < 70) {
|
||||
indicatorText = passwordSettings.fair;
|
||||
indicatorClass = cssClasses.passwordFair;
|
||||
} else if (strength < 80) {
|
||||
indicatorText = passwordSettings.good;
|
||||
indicatorClass = cssClasses.passwordGood;
|
||||
} else if (strength <= 100) {
|
||||
indicatorText = passwordSettings.strong;
|
||||
indicatorClass = cssClasses.passwordStrong;
|
||||
}
|
||||
|
||||
// Assemble the final message while keeping the original message array.
|
||||
const messageTips = msg;
|
||||
msg = `${passwordSettings.hasWeaknesses}<ul><li>${msg.join(
|
||||
'</li><li>',
|
||||
)}</li></ul>`;
|
||||
|
||||
return {
|
||||
strength,
|
||||
indicatorText,
|
||||
indicatorClass,
|
||||
messageTips,
|
||||
};
|
||||
};
|
||||
})(jQuery, Drupal);
|
||||
Reference in New Issue
Block a user