diff -r 34716fd837a4 -r be944660c56a wp/wp-admin/js/user-profile.js --- a/wp/wp-admin/js/user-profile.js Tue Dec 15 15:52:01 2020 +0100 +++ b/wp/wp-admin/js/user-profile.js Wed Sep 21 18:19:35 2022 +0200 @@ -2,7 +2,7 @@ * @output wp-admin/js/user-profile.js */ -/* global ajaxurl, pwsL10n */ +/* global ajaxurl, pwsL10n, userProfileL10n */ (function($) { var updateLock = false, __ = wp.i18n.__, @@ -14,27 +14,30 @@ $toggleButton, $submitButtons, $submitButton, - currentPass; + currentPass, + $passwordWrapper; function generatePassword() { if ( typeof zxcvbn !== 'function' ) { setTimeout( generatePassword, 50 ); return; - } else if ( ! $pass1.val() ) { - // zxcvbn loaded before user entered password. + } else if ( ! $pass1.val() || $passwordWrapper.hasClass( 'is-open' ) ) { + // zxcvbn loaded before user entered password, or generating new password. $pass1.val( $pass1.data( 'pw' ) ); $pass1.trigger( 'pwupdate' ); showOrHideWeakPasswordCheckbox(); - } - else { + } else { // zxcvbn loaded after the user entered password, check strength. check_pass_strength(); showOrHideWeakPasswordCheckbox(); } + // Install screen. if ( 1 !== parseInt( $toggleButton.data( 'start-masked' ), 10 ) ) { + // Show the password not masked if admin_password hasn't been posted yet. $pass1.attr( 'type', 'text' ); } else { + // Otherwise, mask the password. $toggleButton.trigger( 'click' ); } @@ -56,6 +59,7 @@ currentPass = $pass1.val(); + // Refresh password strength area. $pass1.removeClass( 'short bad good strong' ); showOrHideWeakPasswordCheckbox(); } ); @@ -84,21 +88,76 @@ $pass1.attr( 'type', 'password' ); resetToggle( true ); } - - $pass1.focus(); - - if ( ! _.isUndefined( $pass1[0].setSelectionRange ) ) { - $pass1[0].setSelectionRange( 0, 100 ); - } }); } + /** + * Handle the password reset button. Sets up an ajax callback to trigger sending + * a password reset email. + */ + function bindPasswordRestLink() { + $( '#generate-reset-link' ).on( 'click', function() { + var $this = $(this), + data = { + 'user_id': userProfileL10n.user_id, // The user to send a reset to. + 'nonce': userProfileL10n.nonce // Nonce to validate the action. + }; + + // Remove any previous error messages. + $this.parent().find( '.notice-error' ).remove(); + + // Send the reset request. + var resetAction = wp.ajax.post( 'send-password-reset', data ); + + // Handle reset success. + resetAction.done( function( response ) { + addInlineNotice( $this, true, response ); + } ); + + // Handle reset failure. + resetAction.fail( function( response ) { + addInlineNotice( $this, false, response ); + } ); + + }); + + } + + /** + * Helper function to insert an inline notice of success or failure. + * + * @param {jQuery Object} $this The button element: the message will be inserted + * above this button + * @param {bool} success Whether the message is a success message. + * @param {string} message The message to insert. + */ + function addInlineNotice( $this, success, message ) { + var resultDiv = $( '
' ); + + // Set up the notice div. + resultDiv.addClass( 'notice inline' ); + + // Add a class indicating success or failure. + resultDiv.addClass( 'notice-' + ( success ? 'success' : 'error' ) ); + + // Add the message, wrapping in a p tag, with a fadein to highlight each message. + resultDiv.text( $( $.parseHTML( message ) ).text() ).wrapInner( '

'); + + // Disable the button when the callback has succeeded. + $this.prop( 'disabled', success ); + + // Remove any previous notices. + $this.siblings( '.notice' ).remove(); + + // Insert the notice. + $this.before( resultDiv ); + } + function bindPasswordForm() { - var $passwordWrapper, - $generateButton, + var $generateButton, $cancelButton; - $pass1Row = $( '.user-pass1-wrap, .user-pass-wrap' ); + $pass1Row = $( '.user-pass1-wrap, .user-pass-wrap, .reset-pass-submit' ); // Hide the confirm password field when JavaScript support is enabled. $('.user-pass2-wrap').hide(); @@ -111,7 +170,7 @@ $weakRow = $( '.pw-weak' ); $weakCheckbox = $weakRow.find( '.pw-checkbox' ); - $weakCheckbox.change( function() { + $weakCheckbox.on( 'change', function() { $submitButtons.prop( 'disabled', ! $weakCheckbox.prop( 'checked' ) ); } ); @@ -123,7 +182,7 @@ $pass1 = $( '#user_pass' ); } - /** + /* * Fix a LastPass mismatch issue, LastPass only changes pass2. * * This fixes the issue by copying any changes from the hidden @@ -149,57 +208,52 @@ bindToggleButton(); - if ( $generateButton.length ) { - $passwordWrapper.hide(); - } - $generateButton.show(); $generateButton.on( 'click', function () { updateLock = true; - $generateButton.hide(); - $passwordWrapper.show(); + // Make sure the password fields are shown. + $generateButton.attr( 'aria-expanded', 'true' ); + $passwordWrapper + .show() + .addClass( 'is-open' ); // Enable the inputs when showing. $pass1.attr( 'disabled', false ); $pass2.attr( 'disabled', false ); - if ( $pass1.val().length === 0 ) { - generatePassword(); - } + // Set the password to the generated value. + generatePassword(); + + // Show generated password in plaintext by default. + resetToggle ( false ); + + // Generate the next password and cache. + wp.ajax.post( 'generate-password' ) + .done( function( data ) { + $pass1.data( 'pw', data ); + } ); } ); $cancelButton = $pass1Row.find( 'button.wp-cancel-pw' ); $cancelButton.on( 'click', function () { updateLock = false; - // Clear any entered password. - $pass1.val( '' ); - - // Generate a new password. - wp.ajax.post( 'generate-password' ) - .done( function( data ) { - $pass1.data( 'pw', data ); - } ); - - $generateButton.show().focus(); - $passwordWrapper.hide(); - - $weakRow.hide( 0, function () { - $weakCheckbox.removeProp( 'checked' ); - } ); - // Disable the inputs when hiding to prevent autofill and submission. $pass1.prop( 'disabled', true ); $pass2.prop( 'disabled', true ); + // Clear password field and update the UI. + $pass1.val( '' ).trigger( 'pwupdate' ); resetToggle( false ); - if ( $pass1Row.closest( 'form' ).is( '#your-profile' ) ) { - // Clear password field to prevent update. - $pass1.val( '' ).trigger( 'pwupdate' ); - $submitButtons.prop( 'disabled', false ); - } + // Hide password controls. + $passwordWrapper + .hide() + .removeClass( 'is-open' ); + + // Stop an empty password from being submitted as a change. + $submitButtons.prop( 'disabled', false ); } ); $pass1Row.closest( 'form' ).on( 'submit', function () { @@ -215,7 +269,7 @@ var pass1 = $('#pass1').val(), strength; $('#pass-strength-result').removeClass('short bad good strong empty'); - if ( ! pass1 ) { + if ( ! pass1 || '' === pass1.trim() ) { $( '#pass-strength-result' ).addClass( 'empty' ).html( ' ' ); return; } @@ -265,7 +319,7 @@ } } - $(document).ready( function() { + $( function() { var $colorpicker, $stylesheet, user_id, current_user_id, select = $( '#display_name' ), current_name = select.val(), @@ -273,12 +327,12 @@ $( '#pass1' ).val( '' ).on( 'input' + ' pwupdate', check_pass_strength ); $('#pass-strength-result').show(); - $('.color-palette').click( function() { + $('.color-palette').on( 'click', function() { $(this).siblings('input[name="admin_color"]').prop('checked', true); }); if ( select.length ) { - $('#first_name, #last_name, #nickname').bind( 'blur.user_profile', function() { + $('#first_name, #last_name, #nickname').on( 'blur.user_profile', function() { var dub = [], inputs = { display_nickname : $('#nickname').val() || '', @@ -320,7 +374,7 @@ return; } - var display_name = $.trim( this.value ) || current_name; + var display_name = this.value.trim() || current_name; greeting.text( display_name ); } ); @@ -354,7 +408,7 @@ // Repaint icons. if ( typeof wp !== 'undefined' && wp.svgPainter ) { try { - colors = $.parseJSON( $this.children( '.icon_colors' ).val() ); + colors = JSON.parse( $this.children( '.icon_colors' ).val() ); } catch ( error ) {} if ( colors ) { @@ -377,6 +431,7 @@ }); bindPasswordForm(); + bindPasswordRestLink(); }); $( '#destroy-sessions' ).on( 'click', function( e ) { @@ -399,11 +454,22 @@ window.generatePassword = generatePassword; - /* Warn the user if password was generated but not saved */ + // Warn the user if password was generated but not saved. $( window ).on( 'beforeunload', function () { if ( true === updateLock ) { return __( 'Your new password has not been saved.' ); } } ); + /* + * We need to generate a password as soon as the Reset Password page is loaded, + * to avoid double clicking the button to retrieve the first generated password. + * See ticket #39638. + */ + $( function() { + if ( $( '.reset-pass-submit' ).length ) { + $( '.reset-pass-submit button.wp-generate-pw' ).trigger( 'click' ); + } + }); + })(jQuery);