[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/js/ -> user-profile.js (source)

   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( '&nbsp;' );
 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);


Generated : Fri Jun 27 08:20:01 2025 Cross-referenced by PHPXref