diff --git a/src/js/_enqueues/admin/media.js b/src/js/_enqueues/admin/media.js index 13d46e485b..b724b7d666 100644 --- a/src/js/_enqueues/admin/media.js +++ b/src/js/_enqueues/admin/media.js @@ -141,7 +141,10 @@ * @return {void} */ $( function() { - var settings, $mediaGridWrap = $( '#wp-media-grid' ); + var settings, + $mediaGridWrap = $( '#wp-media-grid' ), + copyAttachmentURLClipboard = new ClipboardJS( '.copy-attachment-url.media-library' ), + copyAttachmentURLSuccessTimeout; // Opens a manage media frame into the grid. if ( $mediaGridWrap.length && window.wp && window.wp.media ) { @@ -205,5 +208,35 @@ $( '.find-box-inside' ).on( 'click', 'tr', function() { $( this ).find( '.found-radio input' ).prop( 'checked', true ); }); + + /** + * Handles media list copy media URL button. + * + * @since 6.0.0 + * + * @param {MouseEvent} event A click event. + * @return {void} + */ + copyAttachmentURLClipboard.on( 'success', function( event ) { + var triggerElement = $( event.trigger ), + successElement = $( '.success', triggerElement.closest( '.copy-to-clipboard-container' ) ); + + // Clear the selection and move focus back to the trigger. + event.clearSelection(); + // Handle ClipboardJS focus bug, see https://github.com/zenorocha/clipboard.js/issues/680. + triggerElement.trigger( 'focus' ); + + // Show success visual feedback. + clearTimeout( copyAttachmentURLSuccessTimeout ); + successElement.removeClass( 'hidden' ); + + // Hide success visual feedback after 3 seconds since last success and unfocus the trigger. + copyAttachmentURLSuccessTimeout = setTimeout( function() { + successElement.addClass( 'hidden' ); + }, 3000 ); + + // Handle success audible feedback. + wp.a11y.speak( wp.i18n.__( 'The file URL has been copied to your clipboard' ) ); + } ); }); })( jQuery ); diff --git a/src/wp-admin/css/list-tables.css b/src/wp-admin/css/list-tables.css index a39a3b725c..37bf3da948 100644 --- a/src/wp-admin/css/list-tables.css +++ b/src/wp-admin/css/list-tables.css @@ -384,6 +384,23 @@ table.media .column-title .filename { margin-bottom: 0.2em; } +/* Media Copy to clipboard row action */ +.media .row-actions .copy-to-clipboard-container { + display: inline; + position: relative; +} + +.media .row-actions .copy-to-clipboard-container .success { + position: absolute; + left: 50%; + transform: translate(-50%, -100%); + background: #000; + color: #fff; + border-radius: 5px; + margin: 0; + padding: 2px 5px; +} + /* @todo: pick a consistent list table selector */ .wp-list-table a { transition: none; diff --git a/src/wp-admin/includes/class-wp-media-list-table.php b/src/wp-admin/includes/class-wp-media-list-table.php index d425bccfbf..d1234929c4 100644 --- a/src/wp-admin/includes/class-wp-media-list-table.php +++ b/src/wp-admin/includes/class-wp-media-list-table.php @@ -806,6 +806,15 @@ class WP_Media_List_Table extends WP_List_Table { esc_attr( sprintf( __( 'View “%s”' ), $att_title ) ), __( 'View' ) ); + + $actions['copy'] = sprintf( + '', + esc_url( wp_get_attachment_url( $post->ID ) ), + /* translators: %s: Attachment title. */ + esc_attr( sprintf( __( 'Copy “%s” URL to clipboard' ), $att_title ) ), + __( 'Copy URL to clipboard' ), + __( 'Copied!' ) + ); } } diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 25615b77a9..0193d91868 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -1351,7 +1351,7 @@ function wp_default_scripts( $scripts ) { $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" ); $scripts->add( 'media-grid', "/wp-includes/js/media-grid$suffix.js", array( 'media-editor' ), false, 1 ); - $scripts->add( 'media', "/wp-admin/js/media$suffix.js", array( 'jquery' ), false, 1 ); + $scripts->add( 'media', "/wp-admin/js/media$suffix.js", array( 'jquery', 'clipboard', 'wp-i18n', 'wp-a11y' ), false, 1 ); $scripts->set_translations( 'media' ); $scripts->add( 'image-edit', "/wp-admin/js/image-edit$suffix.js", array( 'jquery', 'jquery-ui-core', 'json2', 'imgareaselect', 'wp-a11y' ), false, 1 );