Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add copy button when creating new application password Fix/62019 #7382

Open
wants to merge 7 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions src/js/_enqueues/admin/user-profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
* @output wp-admin/js/user-profile.js
*/

/* global ajaxurl, pwsL10n, userProfileL10n */
/* global ajaxurl, pwsL10n, userProfileL10n, ClipboardJS */
(function($) {
var updateLock = false,
isSubmitting = false,
__ = wp.i18n.__,
clipboard = new ClipboardJS( '.application-password-display .copy-button' ),
$pass1Row,
$pass1,
$pass2,
Expand All @@ -18,7 +19,8 @@
currentPass,
$form,
originalFormContent,
$passwordWrapper;
$passwordWrapper,
successTimeout;

function generatePassword() {
if ( typeof zxcvbn !== 'function' ) {
Expand Down Expand Up @@ -346,6 +348,27 @@
}
}

// Debug information copy section.
clipboard.on( 'success', function( e ) {
var triggerElement = $( e.trigger ),
successElement = $( '.success', triggerElement.closest( '.application-password-display' ) );

// Clear the selection and move focus back to the trigger.
e.clearSelection();

// Show success visual feedback.
clearTimeout( successTimeout );
successElement.removeClass( 'hidden' );

// Hide success visual feedback after 3 seconds since last success.
successTimeout = setTimeout( function() {
successElement.addClass( 'hidden' );
}, 3000 );

// Handle success audible feedback.
wp.a11y.speak( __( 'Application password has been copied to your clipboard.' ) );
} );

$( function() {
var $colorpicker, $stylesheet, user_id, current_user_id,
select = $( '#display_name' ),
Expand Down
5 changes: 5 additions & 0 deletions src/wp-admin/css/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,11 @@ a#remove-post-thumbnail:hover,
border: none;
}

.application-password-display .success {
color: #007017;
margin-left: 0.5rem;
}

/*------------------------------------------------------------------------------
3.0 - Actions
------------------------------------------------------------------------------*/
Expand Down
1 change: 1 addition & 0 deletions src/wp-admin/css/forms.css
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,7 @@ table.form-table td .updated p {
}

.application-password-display input.code {
margin-bottom: 6px;
width: 19em;
}

Expand Down
2 changes: 2 additions & 0 deletions src/wp-admin/user-edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,8 @@
?>
</label>
<input id="new-application-password-value" type="text" class="code" readonly="readonly" value="{{ data.password }}" />
<button type="button" class="button copy-button" data-clipboard-text="{{ data.password }}"><?php _e( 'Copy' ); ?></button>
<span class="success hidden" aria-hidden="true"><?php _e( 'Copied!' ); ?></span>
</p>
<p><?php _e( 'Be sure to save this in a safe location. You will not be able to retrieve it.' ); ?></p>
<button type="button" class="notice-dismiss">
Expand Down
2 changes: 1 addition & 1 deletion src/wp-includes/script-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -1213,7 +1213,7 @@ function wp_default_scripts( $scripts ) {
$scripts->add( 'auth-app', "/wp-admin/js/auth-app$suffix.js", array( 'jquery', 'wp-api-request', 'wp-i18n', 'wp-hooks' ), false, 1 );
$scripts->set_translations( 'auth-app' );

$scripts->add( 'user-profile', "/wp-admin/js/user-profile$suffix.js", array( 'jquery', 'password-strength-meter', 'wp-util' ), false, 1 );
$scripts->add( 'user-profile', "/wp-admin/js/user-profile$suffix.js", array( 'clipboard', 'jquery', 'password-strength-meter', 'wp-util', 'wp-a11y' ), false, 1 );
$scripts->set_translations( 'user-profile' );
$user_id = isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : 0;
did_action( 'init' ) && $scripts->localize(
Expand Down
7 changes: 6 additions & 1 deletion tests/e2e/specs/profile/applications-passwords.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
await applicationPasswords.create();

const [ app ] = await applicationPasswords.get();
expect( app['name']).toBe( TEST_APPLICATION_NAME );

Check failure on line 26 in tests/e2e/specs/profile/applications-passwords.test.js

View workflow job for this annotation

GitHub Actions / Test with SCRIPT_DEBUG disabled / Run E2E tests

[chromium] › profile/applications-passwords.test.js:19:6 › Manage applications passwords › should correctly create a new application password

2) [chromium] › profile/applications-passwords.test.js:19:6 › Manage applications passwords › should correctly create a new application password Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── TypeError: Cannot read properties of undefined (reading 'name') 24 | 25 | const [ app ] = await applicationPasswords.get(); > 26 | expect( app['name']).toBe( TEST_APPLICATION_NAME ); | ^ 27 | 28 | const successMessage = page.getByRole( 'alert' ); 29 | at /home/runner/work/wordpress-develop/wordpress-develop/tests/e2e/specs/profile/applications-passwords.test.js:26:14

const successMessage = page.getByRole( 'alert' );

Expand All @@ -31,7 +31,12 @@
await expect(
successMessage
).toContainText(
`Your new password for ${TEST_APPLICATION_NAME} is: \n\nBe sure to save this in a safe location. You will not be able to retrieve it.`
`Your new password for ${TEST_APPLICATION_NAME} is:`
);
await expect(
successMessage
).toContainText(
`Be sure to save this in a safe location. You will not be able to retrieve it.`
);
} );

Expand Down Expand Up @@ -107,7 +112,7 @@
}

async create(applicationName = TEST_APPLICATION_NAME) {
await this.admin.visitAdminPage( '/profile.php' );

Check failure on line 115 in tests/e2e/specs/profile/applications-passwords.test.js

View workflow job for this annotation

GitHub Actions / Test with SCRIPT_DEBUG disabled / Run E2E tests

[chromium] › profile/applications-passwords.test.js:19:6 › Manage applications passwords › should correctly create a new application password

2) [chromium] › profile/applications-passwords.test.js:19:6 › Manage applications passwords › should correctly create a new application password Error: Not logged in 113 | 114 | async create(applicationName = TEST_APPLICATION_NAME) { > 115 | await this.admin.visitAdminPage( '/profile.php' ); | ^ 116 | 117 | const newPasswordField = this.page.getByRole( 'textbox', { name: 'New Application Password Name' } ); 118 | await expect( newPasswordField ).toBeVisible(); at Admin.visitAdminPage (/home/runner/work/wordpress-develop/wordpress-develop/node_modules/@wordpress/e2e-test-utils-playwright/src/admin/visit-admin-page.ts:36:9) at ApplicationPasswords.create (/home/runner/work/wordpress-develop/wordpress-develop/tests/e2e/specs/profile/applications-passwords.test.js:115:3) at /home/runner/work/wordpress-develop/wordpress-develop/tests/e2e/specs/profile/applications-passwords.test.js:23:3

Check failure on line 115 in tests/e2e/specs/profile/applications-passwords.test.js

View workflow job for this annotation

GitHub Actions / Test with SCRIPT_DEBUG enabled / Run E2E tests

[chromium] › profile/applications-passwords.test.js:19:6 › Manage applications passwords › should correctly create a new application password

2) [chromium] › profile/applications-passwords.test.js:19:6 › Manage applications passwords › should correctly create a new application password Error: Not logged in 113 | 114 | async create(applicationName = TEST_APPLICATION_NAME) { > 115 | await this.admin.visitAdminPage( '/profile.php' ); | ^ 116 | 117 | const newPasswordField = this.page.getByRole( 'textbox', { name: 'New Application Password Name' } ); 118 | await expect( newPasswordField ).toBeVisible(); at Admin.visitAdminPage (/home/runner/work/wordpress-develop/wordpress-develop/node_modules/@wordpress/e2e-test-utils-playwright/src/admin/visit-admin-page.ts:36:9) at ApplicationPasswords.create (/home/runner/work/wordpress-develop/wordpress-develop/tests/e2e/specs/profile/applications-passwords.test.js:115:3) at /home/runner/work/wordpress-develop/wordpress-develop/tests/e2e/specs/profile/applications-passwords.test.js:23:3

Check failure on line 115 in tests/e2e/specs/profile/applications-passwords.test.js

View workflow job for this annotation

GitHub Actions / Test with SCRIPT_DEBUG enabled / Run E2E tests

[chromium] › profile/applications-passwords.test.js:19:6 › Manage applications passwords › should correctly create a new application password

2) [chromium] › profile/applications-passwords.test.js:19:6 › Manage applications passwords › should correctly create a new application password Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: Not logged in 113 | 114 | async create(applicationName = TEST_APPLICATION_NAME) { > 115 | await this.admin.visitAdminPage( '/profile.php' ); | ^ 116 | 117 | const newPasswordField = this.page.getByRole( 'textbox', { name: 'New Application Password Name' } ); 118 | await expect( newPasswordField ).toBeVisible(); at Admin.visitAdminPage (/home/runner/work/wordpress-develop/wordpress-develop/node_modules/@wordpress/e2e-test-utils-playwright/src/admin/visit-admin-page.ts:36:9) at ApplicationPasswords.create (/home/runner/work/wordpress-develop/wordpress-develop/tests/e2e/specs/profile/applications-passwords.test.js:115:3) at /home/runner/work/wordpress-develop/wordpress-develop/tests/e2e/specs/profile/applications-passwords.test.js:23:3

const newPasswordField = this.page.getByRole( 'textbox', { name: 'New Application Password Name' } );
await expect( newPasswordField ).toBeVisible();
Expand Down
Loading