[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 /** 2 * @output wp-admin/js/user-profile.js 3 */ 4 5 /* global ajaxurl, pwsL10n, userProfileL10n, ClipboardJS */ 6 (function($) { 7 var updateLock = false, 8 isSubmitting = false, 9 __ = wp.i18n.__, 10 clipboard = new ClipboardJS( '.application-password-display .copy-button' ), 11 $pass1Row, 12 $pass1, 13 $pass2, 14 $weakRow, 15 $weakCheckbox, 16 $toggleButton, 17 $submitButtons, 18 $submitButton, 19 currentPass, 20 $form, 21 originalFormContent, 22 $passwordWrapper, 23 successTimeout; 24 25 function generatePassword() { 26 if ( typeof zxcvbn !== 'function' ) { 27 setTimeout( generatePassword, 50 ); 28 return; 29 } else if ( ! $pass1.val() || $passwordWrapper.hasClass( 'is-open' ) ) { 30 // zxcvbn loaded before user entered password, or generating new password. 31 $pass1.val( $pass1.data( 'pw' ) ); 32 $pass1.trigger( 'pwupdate' ); 33 showOrHideWeakPasswordCheckbox(); 34 } else { 35 // zxcvbn loaded after the user entered password, check strength. 36 check_pass_strength(); 37 showOrHideWeakPasswordCheckbox(); 38 } 39 40 /* 41 * This works around a race condition when zxcvbn loads quickly and 42 * causes `generatePassword()` to run prior to the toggle button being 43 * bound. 44 */ 45 bindToggleButton(); 46 47 // Install screen. 48 if ( 1 !== parseInt( $toggleButton.data( 'start-masked' ), 10 ) ) { 49 // Show the password not masked if admin_password hasn't been posted yet. 50 $pass1.attr( 'type', 'text' ); 51 } else { 52 // Otherwise, mask the password. 53 $toggleButton.trigger( 'click' ); 54 } 55 56 // Once zxcvbn loads, passwords strength is known. 57 $( '#pw-weak-text-label' ).text( __( 'Confirm use of weak password' ) ); 58 59 // Focus the password field if not the install screen. 60 if ( 'mailserver_pass' !== $pass1.prop('id' ) && ! $('#weblog_title').length ) { 61 $( $pass1 ).trigger( 'focus' ); 62 } 63 } 64 65 function bindPass1() { 66 currentPass = $pass1.val(); 67 68 if ( 1 === parseInt( $pass1.data( 'reveal' ), 10 ) ) { 69 generatePassword(); 70 } 71 72 $pass1.on( 'input' + ' pwupdate', function () { 73 if ( $pass1.val() === currentPass ) { 74 return; 75 } 76 77 currentPass = $pass1.val(); 78 79 // Refresh password strength area. 80 $pass1.removeClass( 'short bad good strong' ); 81 showOrHideWeakPasswordCheckbox(); 82 } ); 83 } 84 85 function resetToggle( show ) { 86 $toggleButton 87 .attr({ 88 'aria-label': show ? __( 'Show password' ) : __( 'Hide password' ) 89 }) 90 .find( '.text' ) 91 .text( show ? __( 'Show' ) : __( 'Hide' ) ) 92 .end() 93 .find( '.dashicons' ) 94 .removeClass( show ? 'dashicons-hidden' : 'dashicons-visibility' ) 95 .addClass( show ? 'dashicons-visibility' : 'dashicons-hidden' ); 96 } 97 98 function bindToggleButton() { 99 if ( !! $toggleButton ) { 100 // Do not rebind. 101 return; 102 } 103 $toggleButton = $pass1Row.find('.wp-hide-pw'); 104 105 // Toggle between showing and hiding the password. 106 $toggleButton.show().on( 'click', function () { 107 if ( 'password' === $pass1.attr( 'type' ) ) { 108 $pass1.attr( 'type', 'text' ); 109 resetToggle( false ); 110 } else { 111 $pass1.attr( 'type', 'password' ); 112 resetToggle( true ); 113 } 114 }); 115 116 // Ensure the password input type is set to password when the form is submitted. 117 $pass1Row.closest( 'form' ).on( 'submit', function() { 118 if ( $pass1.attr( 'type' ) === 'text' ) { 119 $pass1.attr( 'type', 'password' ); 120 resetToggle( true ); 121 } 122 } ); 123 } 124 125 /** 126 * Handle the password reset button. Sets up an ajax callback to trigger sending 127 * a password reset email. 128 */ 129 function bindPasswordResetLink() { 130 $( '#generate-reset-link' ).on( 'click', function() { 131 var $this = $(this), 132 data = { 133 'user_id': userProfileL10n.user_id, // The user to send a reset to. 134 'nonce': userProfileL10n.nonce // Nonce to validate the action. 135 }; 136 137 // Remove any previous error messages. 138 $this.parent().find( '.notice-error' ).remove(); 139 140 // Send the reset request. 141 var resetAction = wp.ajax.post( 'send-password-reset', data ); 142 143 // Handle reset success. 144 resetAction.done( function( response ) { 145 addInlineNotice( $this, true, response ); 146 } ); 147 148 // Handle reset failure. 149 resetAction.fail( function( response ) { 150 addInlineNotice( $this, false, response ); 151 } ); 152 153 }); 154 155 } 156 157 /** 158 * Helper function to insert an inline notice of success or failure. 159 * 160 * @param {jQuery Object} $this The button element: the message will be inserted 161 * above this button 162 * @param {bool} success Whether the message is a success message. 163 * @param {string} message The message to insert. 164 */ 165 function addInlineNotice( $this, success, message ) { 166 var resultDiv = $( '<div />', { 167 role: 'alert' 168 } ); 169 170 // Set up the notice div. 171 resultDiv.addClass( 'notice inline' ); 172 173 // Add a class indicating success or failure. 174 resultDiv.addClass( 'notice-' + ( success ? 'success' : 'error' ) ); 175 176 // Add the message, wrapping in a p tag, with a fadein to highlight each message. 177 resultDiv.text( $( $.parseHTML( message ) ).text() ).wrapInner( '<p />'); 178 179 // Disable the button when the callback has succeeded. 180 $this.prop( 'disabled', success ); 181 182 // Remove any previous notices. 183 $this.siblings( '.notice' ).remove(); 184 185 // Insert the notice. 186 $this.before( resultDiv ); 187 } 188 189 function bindPasswordForm() { 190 var $generateButton, 191 $cancelButton; 192 193 $pass1Row = $( '.user-pass1-wrap, .user-pass-wrap, .mailserver-pass-wrap, .reset-pass-submit' ); 194 195 // Hide the confirm password field when JavaScript support is enabled. 196 $('.user-pass2-wrap').hide(); 197 198 $submitButton = $( '#submit, #wp-submit' ).on( 'click', function () { 199 updateLock = false; 200 }); 201 202 $submitButtons = $submitButton.add( ' #createusersub' ); 203 204 $weakRow = $( '.pw-weak' ); 205 $weakCheckbox = $weakRow.find( '.pw-checkbox' ); 206 $weakCheckbox.on( 'change', function() { 207 $submitButtons.prop( 'disabled', ! $weakCheckbox.prop( 'checked' ) ); 208 } ); 209 210 $pass1 = $('#pass1, #mailserver_pass'); 211 if ( $pass1.length ) { 212 bindPass1(); 213 } else { 214 // Password field for the login form. 215 $pass1 = $( '#user_pass' ); 216 } 217 218 /* 219 * Fix a LastPass mismatch issue, LastPass only changes pass2. 220 * 221 * This fixes the issue by copying any changes from the hidden 222 * pass2 field to the pass1 field, then running check_pass_strength. 223 */ 224 $pass2 = $( '#pass2' ).on( 'input', function () { 225 if ( $pass2.val().length > 0 ) { 226 $pass1.val( $pass2.val() ); 227 $pass2.val(''); 228 currentPass = ''; 229 $pass1.trigger( 'pwupdate' ); 230 } 231 } ); 232 233 // Disable hidden inputs to prevent autofill and submission. 234 if ( $pass1.is( ':hidden' ) ) { 235 $pass1.prop( 'disabled', true ); 236 $pass2.prop( 'disabled', true ); 237 } 238 239 $passwordWrapper = $pass1Row.find( '.wp-pwd' ); 240 $generateButton = $pass1Row.find( 'button.wp-generate-pw' ); 241 242 bindToggleButton(); 243 244 $generateButton.show(); 245 $generateButton.on( 'click', function () { 246 updateLock = true; 247 248 // Make sure the password fields are shown. 249 $generateButton.not( '.skip-aria-expanded' ).attr( 'aria-expanded', 'true' ); 250 $passwordWrapper 251 .show() 252 .addClass( 'is-open' ); 253 254 // Enable the inputs when showing. 255 $pass1.attr( 'disabled', false ); 256 $pass2.attr( 'disabled', false ); 257 258 // Set the password to the generated value. 259 generatePassword(); 260 261 // Show generated password in plaintext by default. 262 resetToggle ( false ); 263 264 // Generate the next password and cache. 265 wp.ajax.post( 'generate-password' ) 266 .done( function( data ) { 267 $pass1.data( 'pw', data ); 268 } ); 269 } ); 270 271 $cancelButton = $pass1Row.find( 'button.wp-cancel-pw' ); 272 $cancelButton.on( 'click', function () { 273 updateLock = false; 274 275 // Disable the inputs when hiding to prevent autofill and submission. 276 $pass1.prop( 'disabled', true ); 277 $pass2.prop( 'disabled', true ); 278 279 // Clear password field and update the UI. 280 $pass1.val( '' ).trigger( 'pwupdate' ); 281 resetToggle( false ); 282 283 // Hide password controls. 284 $passwordWrapper 285 .hide() 286 .removeClass( 'is-open' ); 287 288 // Stop an empty password from being submitted as a change. 289 $submitButtons.prop( 'disabled', false ); 290 291 $generateButton.attr( 'aria-expanded', 'false' ); 292 } ); 293 294 $pass1Row.closest( 'form' ).on( 'submit', function () { 295 updateLock = false; 296 297 $pass1.prop( 'disabled', false ); 298 $pass2.prop( 'disabled', false ); 299 $pass2.val( $pass1.val() ); 300 }); 301 } 302 303 function check_pass_strength() { 304 var pass1 = $('#pass1').val(), strength; 305 306 $('#pass-strength-result').removeClass('short bad good strong empty'); 307 if ( ! pass1 || '' === pass1.trim() ) { 308 $( '#pass-strength-result' ).addClass( 'empty' ).html( ' ' ); 309 return; 310 } 311 312 strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputDisallowedList(), pass1 ); 313 314 switch ( strength ) { 315 case -1: 316 $( '#pass-strength-result' ).addClass( 'bad' ).html( pwsL10n.unknown ); 317 break; 318 case 2: 319 $('#pass-strength-result').addClass('bad').html( pwsL10n.bad ); 320 break; 321 case 3: 322 $('#pass-strength-result').addClass('good').html( pwsL10n.good ); 323 break; 324 case 4: 325 $('#pass-strength-result').addClass('strong').html( pwsL10n.strong ); 326 break; 327 case 5: 328 $('#pass-strength-result').addClass('short').html( pwsL10n.mismatch ); 329 break; 330 default: 331 $('#pass-strength-result').addClass('short').html( pwsL10n.short ); 332 } 333 } 334 335 function showOrHideWeakPasswordCheckbox() { 336 var passStrengthResult = $('#pass-strength-result'); 337 338 if ( passStrengthResult.length ) { 339 var passStrength = passStrengthResult[0]; 340 341 if ( passStrength.className ) { 342 $pass1.addClass( passStrength.className ); 343 if ( $( passStrength ).is( '.short, .bad' ) ) { 344 if ( ! $weakCheckbox.prop( 'checked' ) ) { 345 $submitButtons.prop( 'disabled', true ); 346 } 347 $weakRow.show(); 348 } else { 349 if ( $( passStrength ).is( '.empty' ) ) { 350 $submitButtons.prop( 'disabled', true ); 351 $weakCheckbox.prop( 'checked', false ); 352 } else { 353 $submitButtons.prop( 'disabled', false ); 354 } 355 $weakRow.hide(); 356 } 357 } 358 } 359 } 360 361 // Debug information copy section. 362 clipboard.on( 'success', function( e ) { 363 var triggerElement = $( e.trigger ), 364 successElement = $( '.success', triggerElement.closest( '.application-password-display' ) ); 365 366 // Clear the selection and move focus back to the trigger. 367 e.clearSelection(); 368 369 // Show success visual feedback. 370 clearTimeout( successTimeout ); 371 successElement.removeClass( 'hidden' ); 372 373 // Hide success visual feedback after 3 seconds since last success. 374 successTimeout = setTimeout( function() { 375 successElement.addClass( 'hidden' ); 376 }, 3000 ); 377 378 // Handle success audible feedback. 379 wp.a11y.speak( __( 'Application password has been copied to your clipboard.' ) ); 380 } ); 381 382 $( function() { 383 var $colorpicker, $stylesheet, user_id, current_user_id, 384 select = $( '#display_name' ), 385 current_name = select.val(), 386 greeting = $( '#wp-admin-bar-my-account' ).find( '.display-name' ); 387 388 $( '#pass1' ).val( '' ).on( 'input' + ' pwupdate', check_pass_strength ); 389 $('#pass-strength-result').show(); 390 $('.color-palette').on( 'click', function() { 391 $(this).siblings('input[name="admin_color"]').prop('checked', true); 392 }); 393 394 if ( select.length ) { 395 $('#first_name, #last_name, #nickname').on( 'blur.user_profile', function() { 396 var dub = [], 397 inputs = { 398 display_nickname : $('#nickname').val() || '', 399 display_username : $('#user_login').val() || '', 400 display_firstname : $('#first_name').val() || '', 401 display_lastname : $('#last_name').val() || '' 402 }; 403 404 if ( inputs.display_firstname && inputs.display_lastname ) { 405 inputs.display_firstlast = inputs.display_firstname + ' ' + inputs.display_lastname; 406 inputs.display_lastfirst = inputs.display_lastname + ' ' + inputs.display_firstname; 407 } 408 409 $.each( $('option', select), function( i, el ){ 410 dub.push( el.value ); 411 }); 412 413 $.each(inputs, function( id, value ) { 414 if ( ! value ) { 415 return; 416 } 417 418 var val = value.replace(/<\/?[a-z][^>]*>/gi, ''); 419 420 if ( inputs[id].length && $.inArray( val, dub ) === -1 ) { 421 dub.push(val); 422 $('<option />', { 423 'text': val 424 }).appendTo( select ); 425 } 426 }); 427 }); 428 429 /** 430 * Replaces "Howdy, *" in the admin toolbar whenever the display name dropdown is updated for one's own profile. 431 */ 432 select.on( 'change', function() { 433 if ( user_id !== current_user_id ) { 434 return; 435 } 436 437 var display_name = this.value.trim() || current_name; 438 439 greeting.text( display_name ); 440 } ); 441 } 442 443 $colorpicker = $( '#color-picker' ); 444 $stylesheet = $( '#colors-css' ); 445 user_id = $( 'input#user_id' ).val(); 446 current_user_id = $( 'input[name="checkuser_id"]' ).val(); 447 448 $colorpicker.on( 'click.colorpicker', '.color-option', function() { 449 var colors, 450 $this = $(this); 451 452 if ( $this.hasClass( 'selected' ) ) { 453 return; 454 } 455 456 $this.siblings( '.selected' ).removeClass( 'selected' ); 457 $this.addClass( 'selected' ).find( 'input[type="radio"]' ).prop( 'checked', true ); 458 459 // Set color scheme. 460 if ( user_id === current_user_id ) { 461 // Load the colors stylesheet. 462 // The default color scheme won't have one, so we'll need to create an element. 463 if ( 0 === $stylesheet.length ) { 464 $stylesheet = $( '<link rel="stylesheet" />' ).appendTo( 'head' ); 465 } 466 $stylesheet.attr( 'href', $this.children( '.css_url' ).val() ); 467 468 // Repaint icons. 469 if ( typeof wp !== 'undefined' && wp.svgPainter ) { 470 try { 471 colors = JSON.parse( $this.children( '.icon_colors' ).val() ); 472 } catch ( error ) {} 473 474 if ( colors ) { 475 wp.svgPainter.setColors( colors ); 476 wp.svgPainter.paint(); 477 } 478 } 479 480 // Update user option. 481 $.post( ajaxurl, { 482 action: 'save-user-color-scheme', 483 color_scheme: $this.children( 'input[name="admin_color"]' ).val(), 484 nonce: $('#color-nonce').val() 485 }).done( function( response ) { 486 if ( response.success ) { 487 $( 'body' ).removeClass( response.data.previousScheme ).addClass( response.data.currentScheme ); 488 } 489 }); 490 } 491 }); 492 493 bindPasswordForm(); 494 bindPasswordResetLink(); 495 $submitButtons.on( 'click', function() { 496 isSubmitting = true; 497 }); 498 499 $form = $( '#your-profile, #createuser' ); 500 originalFormContent = $form.serialize(); 501 }); 502 503 $( '#destroy-sessions' ).on( 'click', function( e ) { 504 var $this = $(this); 505 506 wp.ajax.post( 'destroy-sessions', { 507 nonce: $( '#_wpnonce' ).val(), 508 user_id: $( '#user_id' ).val() 509 }).done( function( response ) { 510 $this.prop( 'disabled', true ); 511 $this.siblings( '.notice' ).remove(); 512 $this.before( '<div class="notice notice-success inline" role="alert"><p>' + response.message + '</p></div>' ); 513 }).fail( function( response ) { 514 $this.siblings( '.notice' ).remove(); 515 $this.before( '<div class="notice notice-error inline" role="alert"><p>' + response.message + '</p></div>' ); 516 }); 517 518 e.preventDefault(); 519 }); 520 521 window.generatePassword = generatePassword; 522 523 // Warn the user if password was generated but not saved. 524 $( window ).on( 'beforeunload', function () { 525 if ( true === updateLock ) { 526 return __( 'Your new password has not been saved.' ); 527 } 528 if ( originalFormContent !== $form.serialize() && ! isSubmitting ) { 529 return __( 'The changes you made will be lost if you navigate away from this page.' ); 530 } 531 }); 532 533 /* 534 * We need to generate a password as soon as the Reset Password page is loaded, 535 * to avoid double clicking the button to retrieve the first generated password. 536 * See ticket #39638. 537 */ 538 $( function() { 539 if ( $( '.reset-pass-submit' ).length ) { 540 $( '.reset-pass-submit button.wp-generate-pw' ).trigger( 'click' ); 541 } 542 }); 543 544 })(jQuery);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Fri Jun 27 08:20:01 2025 | Cross-referenced by PHPXref |