? modules/.DS_Store ? modules/system/.DS_Store ? modules/user/.user.module.swp Index: modules/system/system.css =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.css,v retrieving revision 1.51 diff -u -p -r1.51 system.css --- modules/system/system.css 5 May 2008 21:10:48 -0000 1.51 +++ modules/system/system.css 25 Sep 2008 21:39:24 -0000 @@ -516,36 +516,45 @@ html.js .js-hide { /* ** Password strength indicator */ -span.password-strength { - visibility: hidden; +.password-strength-title { + float: left; +} +#password-indicator { + border: 1px solid silver; + float: right; + height: 0.9em; + margin: 0.4em 0.85em 0 0; + width: 6.3em; +} +#password-indicator div { + height: 100%; + width: 0%; + background-color: #96F6AA; } input.password-field { - margin-right: 10px; /* LTR */ + margin-right: 0.8em; /* LTR */ } div.password-description { - padding: 0 2px; - margin: 4px 0 0 0; + padding: 0.2em 0.5em; + margin: 0.4em 0 0 0; font-size: 0.85em; - max-width: 500px; + width: 38.7em; + border: 1px solid silver; } div.password-description ul { margin-bottom: 0; } .password-parent { margin: 0 0 0 0; + float: left; } + /* ** Password confirmation checker */ -input.password-confirm { - margin-right: 10px; /* LTR */ -} .confirm-parent { - margin: 5px 0 0 0; + margin: 0.5em 0 0 0; } -span.password-confirm { +div.password-confirm { visibility: hidden; } -span.password-confirm span { - font-weight: normal; -} Index: modules/user/user.js =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.js,v retrieving revision 1.6 diff -u -p -r1.6 user.js --- modules/user/user.js 12 Sep 2007 18:29:32 -0000 1.6 +++ modules/user/user.js 25 Sep 2008 21:39:24 -0000 @@ -5,111 +5,80 @@ * that its confirmation is correct. */ Drupal.behaviors.password = function(context) { + var translate = Drupal.settings.password; $("input.password-field:not(.password-processed)", context).each(function() { var passwordInput = $(this).addClass('password-processed'); - var parent = $(this).parent(); - // Wait this number of milliseconds before checking password. - var monitorDelay = 700; + var innerWrapper = $(this).parent(); + var outerWrapper = $(this).parent().parent(); // Add the password strength layers. - $(this).after(''+ translate.strengthTitle +' ').parent(); - var passwordStrength = $("span.password-strength", parent); + var passwordStrength = $("span.password-strength", innerWrapper); var passwordResult = $("span.password-result", passwordStrength); - parent.addClass("password-parent"); + innerWrapper.addClass("password-parent"); + + // Add the description box at the end. + var passwordMeter = '
' + translate.strengthTitle + '
'; + $(".password-confirm", outerWrapper).after('
'); + $(innerWrapper).append(passwordMeter); + var passwordDescription = $("div.password-description", outerWrapper).hide(); // Add the password confirmation layer. var outerItem = $(this).parent().parent(); - $("input.password-confirm", outerItem).after(''+ translate["confirmTitle"] +' ').parent().addClass("confirm-parent"); - var confirmInput = $("input.password-confirm", outerItem); - var confirmResult = $("span.password-confirm", outerItem); + $("input.password-confirm", outerWrapper).after('
' + translate["confirmTitle"] + '
').parent().addClass("confirm-parent"); + var confirmInput = $("input.password-confirm", outerWrapper); + var confirmResult = $("div.password-confirm", outerWrapper); var confirmChild = $("span", confirmResult); - // Add the description box at the end. - $(confirmInput).parent().after('
'); - var passwordDescription = $("div.password-description", $(this).parent().parent()).hide(); - - // Check the password fields. + // Check the password strength. var passwordCheck = function () { - // Remove timers for a delayed check if they exist. - if (this.timer) { - clearTimeout(this.timer); - } - - // Verify that there is a password to check. - if (!passwordInput.val()) { - passwordStrength.css({ visibility: "hidden" }); - passwordDescription.hide(); - return; - } // Evaluate password strength. - var result = Drupal.evaluatePasswordStrength(passwordInput.val()); - passwordResult.html(result.strength == "" ? "" : translate[result.strength +"Strength"]); - // Map the password strength to the relevant drupal CSS class. - var classMap = { low: "error", medium: "warning", high: "ok" }; - var newClass = classMap[result.strength] || ""; - - // Remove the previous styling if any exists; add the new class. - if (this.passwordClass) { - passwordResult.removeClass(this.passwordClass); - passwordDescription.removeClass(this.passwordClass); - } passwordDescription.html(result.message); - passwordResult.addClass(newClass); - if (result.strength == "high") { + + if (result.strength == 100) { passwordDescription.hide(); } else { - passwordDescription.addClass(newClass); + passwordDescription.show(); } - this.passwordClass = newClass; - // Check that password and confirmation match. + // adjust the length of the strength indicator + $("#indicator").css('width', result.strength + '%'); - // Hide the result layer if confirmation is empty, otherwise show the layer. - confirmResult.css({ visibility: (confirmInput.val() == "" ? "hidden" : "visible") }); + passwordCheckMatch(); + }; - var success = passwordInput.val() == confirmInput.val(); + // Check that password and confirmation match. + var passwordCheckMatch = function () { - // Remove the previous styling if any exists. - if (this.confirmClass) { - confirmChild.removeClass(this.confirmClass); - } + if (confirmInput.val()) { + var success = passwordInput.val() == confirmInput.val(); - // Fill in the correct message and set the class accordingly. - var confirmClass = success ? "ok" : "error"; - confirmChild.html(translate["confirm"+ (success ? "Success" : "Failure")]).addClass(confirmClass); - this.confirmClass = confirmClass; - - // Show the indicator and tips. - passwordStrength.css({ visibility: "visible" }); - passwordDescription.show(); - }; + //show the result + confirmResult.css({ visibility: "visible" }); - // Do a delayed check on the password fields. - var passwordDelayedCheck = function() { - // Postpone the check since the user is most likely still typing. - if (this.timer) { - clearTimeout(this.timer); + // Remove the previous styling if any exists. + if (this.confirmClass) { + confirmChild.removeClass(this.confirmClass); + } + + // Fill in the correct message and set the class accordingly. + var confirmClass = success ? "ok" : 'error'; + confirmChild.html(translate["confirm" + (success ? "Success" : "Failure")]).addClass(confirmClass); + this.confirmClass = confirmClass; } - - // When the user clears the field, hide the tips immediately. - if (!passwordInput.val()) { - passwordStrength.css({ visibility: "hidden" }); - passwordDescription.hide(); - return; + else { + confirmResult.css({ visibility: "hidden" }); } + } - // Schedule the actual check. - this.timer = setTimeout(passwordCheck, monitorDelay); - }; // Monitor keyup and blur events. // Blur must be used because a mouse paste does not trigger keyup. - passwordInput.keyup(passwordDelayedCheck).blur(passwordCheck); - confirmInput.keyup(passwordDelayedCheck).blur(passwordCheck); + passwordInput.keyup(passwordCheck).focus(passwordCheck).blur(passwordCheck); + confirmInput.keyup(passwordCheckMatch).blur(passwordCheckMatch); }); }; @@ -119,51 +88,66 @@ Drupal.behaviors.password = function(con * Returns the estimated strength and the relevant output message. */ Drupal.evaluatePasswordStrength = function(value) { - var strength = "", msg = "", translate = Drupal.settings.password; + var weaknesses = 0, strength = 100, msg = [], translate = Drupal.settings.password; var hasLetters = value.match(/[a-zA-Z]+/); var hasNumbers = value.match(/[0-9]+/); var hasPunctuation = value.match(/[^a-zA-Z0-9]+/); var hasCasing = value.match(/[a-z]+.*[A-Z]+|[A-Z]+.*[a-z]+/); - // Check if the password is blank. - if (!value.length) { - strength = ""; - msg = ""; - } - // Check if length is less than 6 characters. - else if (value.length < 6) { - strength = "low"; - msg = translate.tooShort; + // Lose 10 points for every character less than 6 + if (value.length < 6) { + msg.push(translate.tooShort); + strength -= (6 - value.length) * 10; + } + + // count weaknesses + + if (!hasLetters) { + msg.push(translate.addLowerCase); + weaknesses++; + } + + if (!hasCasing) { + msg.push(translate.addUpperCase); + weaknesses++; + } + + if (!hasNumbers) { + msg.push(translate.addNumbers); + weaknesses++; } + + if (!hasPunctuation) { + msg.push(translate.addPunctuation); + weaknesses++; + } + + // apply penalty for each weakness (balanced against length penalty) + switch (weaknesses) { + case 1: + strength -= 12.5; + break; + case 2: + strength -= 25; + break; + case 3: + strength -= 40; + break; + case 4: + strength -= 40; + break; + } + // Check if password is the same as the username (convert both to lowercase). - else if (value.toLowerCase() == translate.username.toLowerCase()) { - strength = "low"; - msg = translate.sameAsUsername; - } - // Check if it contains letters, numbers, punctuation, and upper/lower case. - else if (hasLetters && hasNumbers && hasPunctuation && hasCasing) { - strength = "high"; - } - // Password is not secure enough so construct the medium-strength message. - else { - // Extremely bad passwords still count as low. - var count = (hasLetters ? 1 : 0) + (hasNumbers ? 1 : 0) + (hasPunctuation ? 1 : 0) + (hasCasing ? 1 : 0); - strength = count > 1 ? "medium" : "low"; - - msg = []; - if (!hasLetters || !hasCasing) { - msg.push(translate.addLetters); - } - if (!hasNumbers) { - msg.push(translate.addNumbers); - } - if (!hasPunctuation) { - msg.push(translate.addPunctuation); - } - msg = translate.needsMoreVariation +""; + if (value.toLowerCase() == translate.username.toLowerCase()) { + msg.push(translate.sameAsUsername); + // passwords the same as username are always very weak. + strength = 5; } + //assemble the final message. + msg = translate.hasWeaknesses + ""; return { strength: strength, message: msg }; }; Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.924 diff -u -p -r1.924 user.module --- modules/user/user.module 21 Sep 2008 15:08:39 -0000 1.924 +++ modules/user/user.module 25 Sep 2008 21:39:26 -0000 @@ -2135,15 +2135,13 @@ function _user_password_dynamic_validati drupal_add_js(array( 'password' => array( 'strengthTitle' => t('Password strength:'), - 'lowStrength' => t('Low'), - 'mediumStrength' => t('Medium'), - 'highStrength' => t('High'), - 'tooShort' => t('It is recommended to choose a password that contains at least six characters. It should include numbers, punctuation, and both upper and lowercase letters.'), - 'needsMoreVariation' => t('The password does not include enough variation to be secure. Try:'), - 'addLetters' => t('Adding both upper and lowercase letters.'), - 'addNumbers' => t('Adding numbers.'), - 'addPunctuation' => t('Adding punctuation.'), - 'sameAsUsername' => t('It is recommended to choose a password different from the username.'), + 'hasWeaknesses' => t('To make your password more secure:'), + 'tooShort' => t('Make it at least 6 characters'), + 'addLowerCase' => t('Add lowercase letters'), + 'addUpperCase' => t('Add uppercase letters'), + 'addNumbers' => t('Add numbers'), + 'addPunctuation' => t('Add punctuation'), + 'sameAsUsername' => t('Make it different from your username'), 'confirmSuccess' => t('Yes'), 'confirmFailure' => t('No'), 'confirmTitle' => t('Passwords match:'),