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