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