Нема описа

user-profile.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. /**
  2. * @output wp-admin/js/user-profile.js
  3. */
  4. /* global ajaxurl, pwsL10n, userProfileL10n */
  5. (function($) {
  6. var updateLock = false,
  7. __ = wp.i18n.__,
  8. $pass1Row,
  9. $pass1,
  10. $pass2,
  11. $weakRow,
  12. $weakCheckbox,
  13. $toggleButton,
  14. $submitButtons,
  15. $submitButton,
  16. currentPass,
  17. $passwordWrapper;
  18. function generatePassword() {
  19. if ( typeof zxcvbn !== 'function' ) {
  20. setTimeout( generatePassword, 50 );
  21. return;
  22. } else if ( ! $pass1.val() || $passwordWrapper.hasClass( 'is-open' ) ) {
  23. // zxcvbn loaded before user entered password, or generating new password.
  24. $pass1.val( $pass1.data( 'pw' ) );
  25. $pass1.trigger( 'pwupdate' );
  26. showOrHideWeakPasswordCheckbox();
  27. } else {
  28. // zxcvbn loaded after the user entered password, check strength.
  29. check_pass_strength();
  30. showOrHideWeakPasswordCheckbox();
  31. }
  32. // Install screen.
  33. if ( 1 !== parseInt( $toggleButton.data( 'start-masked' ), 10 ) ) {
  34. // Show the password not masked if admin_password hasn't been posted yet.
  35. $pass1.attr( 'type', 'text' );
  36. } else {
  37. // Otherwise, mask the password.
  38. $toggleButton.trigger( 'click' );
  39. }
  40. // Once zxcvbn loads, passwords strength is known.
  41. $( '#pw-weak-text-label' ).text( __( 'Confirm use of weak password' ) );
  42. }
  43. function bindPass1() {
  44. currentPass = $pass1.val();
  45. if ( 1 === parseInt( $pass1.data( 'reveal' ), 10 ) ) {
  46. generatePassword();
  47. }
  48. $pass1.on( 'input' + ' pwupdate', function () {
  49. if ( $pass1.val() === currentPass ) {
  50. return;
  51. }
  52. currentPass = $pass1.val();
  53. // Refresh password strength area.
  54. $pass1.removeClass( 'short bad good strong' );
  55. showOrHideWeakPasswordCheckbox();
  56. } );
  57. }
  58. function resetToggle( show ) {
  59. $toggleButton
  60. .attr({
  61. 'aria-label': show ? __( 'Show password' ) : __( 'Hide password' )
  62. })
  63. .find( '.text' )
  64. .text( show ? __( 'Show' ) : __( 'Hide' ) )
  65. .end()
  66. .find( '.dashicons' )
  67. .removeClass( show ? 'dashicons-hidden' : 'dashicons-visibility' )
  68. .addClass( show ? 'dashicons-visibility' : 'dashicons-hidden' );
  69. }
  70. function bindToggleButton() {
  71. $toggleButton = $pass1Row.find('.wp-hide-pw');
  72. $toggleButton.show().on( 'click', function () {
  73. if ( 'password' === $pass1.attr( 'type' ) ) {
  74. $pass1.attr( 'type', 'text' );
  75. resetToggle( false );
  76. } else {
  77. $pass1.attr( 'type', 'password' );
  78. resetToggle( true );
  79. }
  80. });
  81. }
  82. /**
  83. * Handle the password reset button. Sets up an ajax callback to trigger sending
  84. * a password reset email.
  85. */
  86. function bindPasswordRestLink() {
  87. $( '#generate-reset-link' ).on( 'click', function() {
  88. var $this = $(this),
  89. data = {
  90. 'user_id': userProfileL10n.user_id, // The user to send a reset to.
  91. 'nonce': userProfileL10n.nonce // Nonce to validate the action.
  92. };
  93. // Remove any previous error messages.
  94. $this.parent().find( '.notice-error' ).remove();
  95. // Send the reset request.
  96. var resetAction = wp.ajax.post( 'send-password-reset', data );
  97. // Handle reset success.
  98. resetAction.done( function( response ) {
  99. addInlineNotice( $this, true, response );
  100. } );
  101. // Handle reset failure.
  102. resetAction.fail( function( response ) {
  103. addInlineNotice( $this, false, response );
  104. } );
  105. });
  106. }
  107. /**
  108. * Helper function to insert an inline notice of success or failure.
  109. *
  110. * @param {jQuery Object} $this The button element: the message will be inserted
  111. * above this button
  112. * @param {bool} success Whether the message is a success message.
  113. * @param {string} message The message to insert.
  114. */
  115. function addInlineNotice( $this, success, message ) {
  116. var resultDiv = $( '<div />' );
  117. // Set up the notice div.
  118. resultDiv.addClass( 'notice inline' );
  119. // Add a class indicating success or failure.
  120. resultDiv.addClass( 'notice-' + ( success ? 'success' : 'error' ) );
  121. // Add the message, wrapping in a p tag, with a fadein to highlight each message.
  122. resultDiv.text( $( $.parseHTML( message ) ).text() ).wrapInner( '<p />');
  123. // Disable the button when the callback has succeeded.
  124. $this.prop( 'disabled', success );
  125. // Remove any previous notices.
  126. $this.siblings( '.notice' ).remove();
  127. // Insert the notice.
  128. $this.before( resultDiv );
  129. }
  130. function bindPasswordForm() {
  131. var $generateButton,
  132. $cancelButton;
  133. $pass1Row = $( '.user-pass1-wrap, .user-pass-wrap, .reset-pass-submit' );
  134. // Hide the confirm password field when JavaScript support is enabled.
  135. $('.user-pass2-wrap').hide();
  136. $submitButton = $( '#submit, #wp-submit' ).on( 'click', function () {
  137. updateLock = false;
  138. });
  139. $submitButtons = $submitButton.add( ' #createusersub' );
  140. $weakRow = $( '.pw-weak' );
  141. $weakCheckbox = $weakRow.find( '.pw-checkbox' );
  142. $weakCheckbox.on( 'change', function() {
  143. $submitButtons.prop( 'disabled', ! $weakCheckbox.prop( 'checked' ) );
  144. } );
  145. $pass1 = $('#pass1');
  146. if ( $pass1.length ) {
  147. bindPass1();
  148. } else {
  149. // Password field for the login form.
  150. $pass1 = $( '#user_pass' );
  151. }
  152. /*
  153. * Fix a LastPass mismatch issue, LastPass only changes pass2.
  154. *
  155. * This fixes the issue by copying any changes from the hidden
  156. * pass2 field to the pass1 field, then running check_pass_strength.
  157. */
  158. $pass2 = $( '#pass2' ).on( 'input', function () {
  159. if ( $pass2.val().length > 0 ) {
  160. $pass1.val( $pass2.val() );
  161. $pass2.val('');
  162. currentPass = '';
  163. $pass1.trigger( 'pwupdate' );
  164. }
  165. } );
  166. // Disable hidden inputs to prevent autofill and submission.
  167. if ( $pass1.is( ':hidden' ) ) {
  168. $pass1.prop( 'disabled', true );
  169. $pass2.prop( 'disabled', true );
  170. }
  171. $passwordWrapper = $pass1Row.find( '.wp-pwd' );
  172. $generateButton = $pass1Row.find( 'button.wp-generate-pw' );
  173. bindToggleButton();
  174. $generateButton.show();
  175. $generateButton.on( 'click', function () {
  176. updateLock = true;
  177. // Make sure the password fields are shown.
  178. $generateButton.attr( 'aria-expanded', 'true' );
  179. $passwordWrapper
  180. .show()
  181. .addClass( 'is-open' );
  182. // Enable the inputs when showing.
  183. $pass1.attr( 'disabled', false );
  184. $pass2.attr( 'disabled', false );
  185. // Set the password to the generated value.
  186. generatePassword();
  187. // Show generated password in plaintext by default.
  188. resetToggle ( false );
  189. // Generate the next password and cache.
  190. wp.ajax.post( 'generate-password' )
  191. .done( function( data ) {
  192. $pass1.data( 'pw', data );
  193. } );
  194. } );
  195. $cancelButton = $pass1Row.find( 'button.wp-cancel-pw' );
  196. $cancelButton.on( 'click', function () {
  197. updateLock = false;
  198. // Disable the inputs when hiding to prevent autofill and submission.
  199. $pass1.prop( 'disabled', true );
  200. $pass2.prop( 'disabled', true );
  201. // Clear password field and update the UI.
  202. $pass1.val( '' ).trigger( 'pwupdate' );
  203. resetToggle( false );
  204. // Hide password controls.
  205. $passwordWrapper
  206. .hide()
  207. .removeClass( 'is-open' );
  208. // Stop an empty password from being submitted as a change.
  209. $submitButtons.prop( 'disabled', false );
  210. } );
  211. $pass1Row.closest( 'form' ).on( 'submit', function () {
  212. updateLock = false;
  213. $pass1.prop( 'disabled', false );
  214. $pass2.prop( 'disabled', false );
  215. $pass2.val( $pass1.val() );
  216. });
  217. }
  218. function check_pass_strength() {
  219. var pass1 = $('#pass1').val(), strength;
  220. $('#pass-strength-result').removeClass('short bad good strong empty');
  221. if ( ! pass1 || '' === pass1.trim() ) {
  222. $( '#pass-strength-result' ).addClass( 'empty' ).html( '&nbsp;' );
  223. return;
  224. }
  225. strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputDisallowedList(), pass1 );
  226. switch ( strength ) {
  227. case -1:
  228. $( '#pass-strength-result' ).addClass( 'bad' ).html( pwsL10n.unknown );
  229. break;
  230. case 2:
  231. $('#pass-strength-result').addClass('bad').html( pwsL10n.bad );
  232. break;
  233. case 3:
  234. $('#pass-strength-result').addClass('good').html( pwsL10n.good );
  235. break;
  236. case 4:
  237. $('#pass-strength-result').addClass('strong').html( pwsL10n.strong );
  238. break;
  239. case 5:
  240. $('#pass-strength-result').addClass('short').html( pwsL10n.mismatch );
  241. break;
  242. default:
  243. $('#pass-strength-result').addClass('short').html( pwsL10n['short'] );
  244. }
  245. }
  246. function showOrHideWeakPasswordCheckbox() {
  247. var passStrength = $('#pass-strength-result')[0];
  248. if ( passStrength.className ) {
  249. $pass1.addClass( passStrength.className );
  250. if ( $( passStrength ).is( '.short, .bad' ) ) {
  251. if ( ! $weakCheckbox.prop( 'checked' ) ) {
  252. $submitButtons.prop( 'disabled', true );
  253. }
  254. $weakRow.show();
  255. } else {
  256. if ( $( passStrength ).is( '.empty' ) ) {
  257. $submitButtons.prop( 'disabled', true );
  258. $weakCheckbox.prop( 'checked', false );
  259. } else {
  260. $submitButtons.prop( 'disabled', false );
  261. }
  262. $weakRow.hide();
  263. }
  264. }
  265. }
  266. $( function() {
  267. var $colorpicker, $stylesheet, user_id, current_user_id,
  268. select = $( '#display_name' ),
  269. current_name = select.val(),
  270. greeting = $( '#wp-admin-bar-my-account' ).find( '.display-name' );
  271. $( '#pass1' ).val( '' ).on( 'input' + ' pwupdate', check_pass_strength );
  272. $('#pass-strength-result').show();
  273. $('.color-palette').on( 'click', function() {
  274. $(this).siblings('input[name="admin_color"]').prop('checked', true);
  275. });
  276. if ( select.length ) {
  277. $('#first_name, #last_name, #nickname').on( 'blur.user_profile', function() {
  278. var dub = [],
  279. inputs = {
  280. display_nickname : $('#nickname').val() || '',
  281. display_username : $('#user_login').val() || '',
  282. display_firstname : $('#first_name').val() || '',
  283. display_lastname : $('#last_name').val() || ''
  284. };
  285. if ( inputs.display_firstname && inputs.display_lastname ) {
  286. inputs.display_firstlast = inputs.display_firstname + ' ' + inputs.display_lastname;
  287. inputs.display_lastfirst = inputs.display_lastname + ' ' + inputs.display_firstname;
  288. }
  289. $.each( $('option', select), function( i, el ){
  290. dub.push( el.value );
  291. });
  292. $.each(inputs, function( id, value ) {
  293. if ( ! value ) {
  294. return;
  295. }
  296. var val = value.replace(/<\/?[a-z][^>]*>/gi, '');
  297. if ( inputs[id].length && $.inArray( val, dub ) === -1 ) {
  298. dub.push(val);
  299. $('<option />', {
  300. 'text': val
  301. }).appendTo( select );
  302. }
  303. });
  304. });
  305. /**
  306. * Replaces "Howdy, *" in the admin toolbar whenever the display name dropdown is updated for one's own profile.
  307. */
  308. select.on( 'change', function() {
  309. if ( user_id !== current_user_id ) {
  310. return;
  311. }
  312. var display_name = this.value.trim() || current_name;
  313. greeting.text( display_name );
  314. } );
  315. }
  316. $colorpicker = $( '#color-picker' );
  317. $stylesheet = $( '#colors-css' );
  318. user_id = $( 'input#user_id' ).val();
  319. current_user_id = $( 'input[name="checkuser_id"]' ).val();
  320. $colorpicker.on( 'click.colorpicker', '.color-option', function() {
  321. var colors,
  322. $this = $(this);
  323. if ( $this.hasClass( 'selected' ) ) {
  324. return;
  325. }
  326. $this.siblings( '.selected' ).removeClass( 'selected' );
  327. $this.addClass( 'selected' ).find( 'input[type="radio"]' ).prop( 'checked', true );
  328. // Set color scheme.
  329. if ( user_id === current_user_id ) {
  330. // Load the colors stylesheet.
  331. // The default color scheme won't have one, so we'll need to create an element.
  332. if ( 0 === $stylesheet.length ) {
  333. $stylesheet = $( '<link rel="stylesheet" />' ).appendTo( 'head' );
  334. }
  335. $stylesheet.attr( 'href', $this.children( '.css_url' ).val() );
  336. // Repaint icons.
  337. if ( typeof wp !== 'undefined' && wp.svgPainter ) {
  338. try {
  339. colors = JSON.parse( $this.children( '.icon_colors' ).val() );
  340. } catch ( error ) {}
  341. if ( colors ) {
  342. wp.svgPainter.setColors( colors );
  343. wp.svgPainter.paint();
  344. }
  345. }
  346. // Update user option.
  347. $.post( ajaxurl, {
  348. action: 'save-user-color-scheme',
  349. color_scheme: $this.children( 'input[name="admin_color"]' ).val(),
  350. nonce: $('#color-nonce').val()
  351. }).done( function( response ) {
  352. if ( response.success ) {
  353. $( 'body' ).removeClass( response.data.previousScheme ).addClass( response.data.currentScheme );
  354. }
  355. });
  356. }
  357. });
  358. bindPasswordForm();
  359. bindPasswordRestLink();
  360. });
  361. $( '#destroy-sessions' ).on( 'click', function( e ) {
  362. var $this = $(this);
  363. wp.ajax.post( 'destroy-sessions', {
  364. nonce: $( '#_wpnonce' ).val(),
  365. user_id: $( '#user_id' ).val()
  366. }).done( function( response ) {
  367. $this.prop( 'disabled', true );
  368. $this.siblings( '.notice' ).remove();
  369. $this.before( '<div class="notice notice-success inline"><p>' + response.message + '</p></div>' );
  370. }).fail( function( response ) {
  371. $this.siblings( '.notice' ).remove();
  372. $this.before( '<div class="notice notice-error inline"><p>' + response.message + '</p></div>' );
  373. });
  374. e.preventDefault();
  375. });
  376. window.generatePassword = generatePassword;
  377. // Warn the user if password was generated but not saved.
  378. $( window ).on( 'beforeunload', function () {
  379. if ( true === updateLock ) {
  380. return __( 'Your new password has not been saved.' );
  381. }
  382. } );
  383. /*
  384. * We need to generate a password as soon as the Reset Password page is loaded,
  385. * to avoid double clicking the button to retrieve the first generated password.
  386. * See ticket #39638.
  387. */
  388. $( function() {
  389. if ( $( '.reset-pass-submit' ).length ) {
  390. $( '.reset-pass-submit button.wp-generate-pw' ).trigger( 'click' );
  391. }
  392. });
  393. })(jQuery);