Last active
April 9, 2025 09:36
-
-
Save damiencarbery/625366a1fe75bf46fa22a04b5d7b47e0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Plugin Name: Copy WooCommerce order shipping address to clipboard | |
Plugin URI: https://www.damiencarbery.com/2022/11/copy-woocommerce-order-shipping-address-to-clipboard/ | |
Description: Copy the shipping (or billing) address, email, phone number, full name or order number to the clipboard for pasting in another application. A WooCommerce Community member asked how to add a button to the order admin page to copy the shipping address to the clipboard. Asked on <a href="https://www.facebook.com/groups/advanced.woocommerce/permalink/6233848903296143/">WooCommerce Community Facebook group</a>. | |
Author: Damien Carbery | |
Version: 0.7.20250409 | |
WC tested up to: 9.7.1 | |
Requires Plugins: woocommerce | |
*/ | |
defined( 'ABSPATH' ) || exit; | |
use Automattic\WooCommerce\Utilities\OrderUtil; | |
class CopyOrderAddressToClipboard { | |
// Returns an instance of this class. | |
public static function get_instance() { | |
if ( null == self::$instance ) { | |
self::$instance = new self; | |
} | |
return self::$instance; | |
} | |
// Initialize the plugin variables. | |
public function __construct() { | |
$this->init(); | |
} | |
// Set up WordPress specfic actions. | |
public function init() { | |
// Declare that this plugin supports WooCommerce HPOS. | |
add_action( 'before_woocommerce_init', function() { | |
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { | |
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); | |
} | |
} ); | |
// Write the order address and JavaScript code to the page. | |
add_action( 'woocommerce_admin_order_data_after_shipping_address', array( $this, 'add_copy_data_and_icon' ) ); | |
// Add extra data fields to the Backbone modal to eliminate front end extrating of order data. | |
add_filter( 'woocommerce_admin_order_preview_get_order_details', array( $this, 'add_backbone_data' ), 10, 2 ); | |
// Add the Copy buttons to the bottom of the preview modal dialog. | |
add_action( 'woocommerce_admin_order_preview_end', array( $this, 'add_copy_buttons_to_backbone_modal' ) ); | |
// Load the JS on the Orders screen. | |
add_action( 'admin_head', array( $this, 'order_details_modal_script' ) ); | |
} | |
public function add_copy_data_and_icon( $order ) { | |
// Retrieve the shipping address. | |
$formattedAddress = apply_filters( 'dcwd_copy_data_shipping_address', $order->get_formatted_shipping_address(), $order ); | |
// If shipping empty (e.g. only virtual products in the order) get billing address. | |
if ( empty( $formattedAddress ) ) { | |
$formattedAddress = apply_filters( 'dcwd_copy_data_billing_address', $order->get_formatted_billing_address(), $order ); | |
} | |
$emailAddress = apply_filters( 'dcwd_copy_data_billing_email', $order->get_billing_email(), $order ); | |
$phoneNumber = apply_filters( 'dcwd_copy_data_billing_phone', $order->get_billing_phone(), $order ); | |
$orderNumber = $order->get_order_number(); | |
$fullName = apply_filters( 'dcwd_copy_data_billing_full_name', $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(), $order ); | |
?> | |
<style> | |
.dcwd-copyToClipboard.copied:after { content: 'Copied'; color: green; padding-left: 0.5em; font-size: 70%; font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; } | |
</style> | |
<script> | |
jQuery(document).ready(function( $ ) { | |
// Add the link markup before the Edit Address icon in the Shipping section. | |
$( '<a href="#" class="dcwd-copyToClipboard dcwd-copyAddress dashicons dashicons-external" title="Copy address to clipboard"></a>' ).insertBefore( '.order_data_column:nth-child(3) a.edit_address' ); | |
$( '<a href="#" class="dcwd-copyToClipboard dcwd-copyEmail dashicons dashicons-external" title="Copy email address to clipboard"></a>' ).insertAfter( '.order_data_column:nth-child(2) .address p:nth-child(2) a' ); | |
$( '<a href="#" class="dcwd-copyToClipboard dcwd-copyPhone dashicons dashicons-external" title="Copy phone number to clipboard"></a>' ).insertAfter( '.order_data_column:nth-child(2) .address p:nth-child(3) a' ); | |
$('<a href="#" class="dcwd-copyToClipboard dcwd-copyFullName dashicons dashicons-external" title="Copy full name to clipboard"></a>' ).insertBefore( '.order_data_column:nth-child(2) .address br:first-child' ); | |
// Use .append (which has opposite syntax to .insertAfter) to put the icon beside the .woocommerce-order-data__heading content. | |
$( '.woocommerce-order-data__heading' ).append( '<a href="#" class="dcwd-copyToClipboard dcwd-copyOrderNum dashicons dashicons-external" title="Copy order number to clipboard"></a>' ); | |
// Add icon beside each order item name. | |
$( '#order_line_items tr.item' ).each( function( i ) { | |
item_id = $( this ).data( 'order_item_id' ); | |
child_el = i + 1; | |
$( '<a href="#" class="dcwd-copyToClipboard dcwd-copyProductName dashicons dashicons-external" data-order_item_id="' + item_id + '" title="Copy product name to clipboard"></a>' ).insertAfter( $( '#order_line_items tr:nth-child('+child_el+') .wc-order-item-name') ); | |
} ); | |
// When the link is clicked copy the shipping name and address to the clipboard. | |
$( '.dcwd-copyAddress' ).click(function() { | |
event.preventDefault(); | |
formattedAddress = '<?php echo $formattedAddress; ?>'; | |
formattedAddress = formattedAddress.replace(/<br\/>/g, "\n"); | |
navigator.clipboard.writeText(formattedAddress).then(function() { | |
// Add the 'copied' class and remove it 4 seconds later. | |
$( '.dcwd-copyAddress' ).addClass( 'copied' ); | |
setTimeout(() => { $( '.dcwd-copyAddress' ).removeClass( 'copied' );; }, "4000") | |
}, function(err) { | |
// TODO: Consider some indication of the error. | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
// When the link is clicked copy the email address to the clipboard. | |
$( '.dcwd-copyEmail' ).click(function() { | |
event.preventDefault(); | |
emailAddress = '<?php echo $emailAddress; ?>'; | |
navigator.clipboard.writeText(emailAddress).then(function() { | |
// Add the 'copied' class and remove it 4 seconds later. | |
$( '.dcwd-copyEmail' ).addClass( 'copied' ); | |
setTimeout(() => { $( '.dcwd-copyEmail' ).removeClass( 'copied' );; }, "4000") | |
}, function(err) { | |
// TODO: Consider some indication of the error. | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
// When the link is clicked copy the phone number to the clipboard. | |
$( '.dcwd-copyPhone' ).click(function() { | |
event.preventDefault(); | |
phoneNumber = '<?php echo $phoneNumber; ?>'; | |
navigator.clipboard.writeText(phoneNumber).then(function() { | |
// Add the 'copied' class and remove it 4 seconds later. | |
$( '.dcwd-copyPhone' ).addClass( 'copied' ); | |
setTimeout(() => { $( '.dcwd-copyPhone' ).removeClass( 'copied' );; }, "4000") | |
}, function(err) { | |
// TODO: Consider some indication of the error. | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
// When the link is clicked copy the order number to the clipboard. | |
$( '.dcwd-copyOrderNum' ).click(function() { | |
event.preventDefault(); | |
orderNumber = '<?php echo $orderNumber; ?>'; | |
navigator.clipboard.writeText(orderNumber).then(function() { | |
// Add the 'copied' class and remove it 4 seconds later. | |
$( '.dcwd-copyOrderNum' ).addClass( 'copied' ); | |
setTimeout(() => { $( '.dcwd-copyOrderNum' ).removeClass( 'copied' );; }, "4000") | |
}, function(err) { | |
// TODO: Consider some indication of the error. | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
// When the link is clicked copy the full name to the clipboard. | |
$( '.dcwd-copyFullName' ).click(function() { | |
event.preventDefault(); | |
fullName = '<?php echo $fullName; ?>'; | |
navigator.clipboard.writeText(fullName).then(function() { | |
// Add the 'copied' class and remove it 4 seconds later. | |
$( '.dcwd-copyFullName' ).addClass( 'copied' ); | |
setTimeout(() => { $( '.dcwd-copyFullName' ).removeClass( 'copied' );; }, "4000") | |
}, function(err) { | |
// TODO: Consider some indication of the error. | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
const product_names = []; | |
<?php | |
foreach ( $order->get_items() as $item_id => $item ) { | |
printf( 'product_names[%d] = "%s";%s', $item_id, $item->get_name(), "\n" ); | |
} | |
?> | |
// When a product name is clicked then copy it to the clipboard. | |
$( '.dcwd-copyProductName' ).click(function() { | |
event.preventDefault(); | |
item_id = $( this ).data( 'order_item_id' ); | |
productName = product_names[ item_id ]; | |
this_link = $( this ); | |
navigator.clipboard.writeText(productName).then(function() { | |
// Add the 'copied' class and remove it 4 seconds later. | |
this_link.addClass( 'copied' ); | |
setTimeout(() => { this_link.removeClass( 'copied' );; }, "4000") | |
}, function(err) { | |
// TODO: Consider some indication of the error. | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
<?php | |
?> | |
}); | |
</script> | |
<?php | |
} | |
// Add extra data fields to the Backbone modal to eliminate front end extrating of order data. | |
public function add_backbone_data( $order_data, $order ) { | |
$order_data['billing_email'] = $order->get_billing_email(); | |
$order_data['billing_phone'] = $order->get_billing_phone(); | |
$formattedAddress = $order->get_formatted_shipping_address(); | |
// If shipping empty (e.g. only virtual products in the order) get billing address. | |
if ( empty( $formattedAddress ) ) { | |
$formattedAddress = $order->get_formatted_billing_address(); | |
} | |
// Change like breaks to pipe char so the data can be put in 'data' attribute. | |
$order_data['address_email'] = str_replace( '<br/>', '|', $formattedAddress ) . '|' . $order->get_billing_email(); | |
return $order_data; | |
} | |
// Add the Copy buttons to the bottom of the preview modal dialog. | |
public function add_copy_buttons_to_backbone_modal() { | |
ob_start(); | |
?> | |
<style> | |
.copyOrderData { display: flex; flex-wrap: wrap; gap: 1em; padding: 1em; } | |
</style> | |
<div class="copyOrderData"> | |
<a href="#" class="button dcwd-copyToClipboard dcwd-copyOrderNumber" data-ordernumber="{{ data.order_number }}">Copy order ID<span class="dashicons dashicons-external"></span></a> | |
<a href="#" class="button dcwd-copyToClipboard dcwd-copyEmail" data-orderemail="{{ data.billing_email }}">Copy email<span class="dashicons dashicons-external"></span></a> | |
<a href="#" class="button dcwd-copyToClipboard dcwd-copyPhone" data-orderphone="{{ data.billing_phone }}">Copy phone<span class="dashicons dashicons-external"></span></a> | |
<a href="#" class="button dcwd-copyToClipboard dcwd-copyAddrEmail" data-orderaddremail="{{ data.address_email }}">Copy address & email<span class="dashicons dashicons-external"></span></a> | |
</div> | |
<?php | |
echo ob_get_clean(); | |
} | |
// Load the JS on the Orders screen. | |
public function order_details_modal_script() { | |
// Limit the script to the Orders page (it will also be on the Edit Order page | |
// but get_current_screen() is identical for both). | |
if ( 'woocommerce_page_wc-orders' != get_current_screen()->id ) { | |
return; | |
} | |
?> | |
<script> | |
jQuery(document).ready(function( $ ) { | |
// Add a class and remove it a few seconds later. | |
classToAdd = 'button-primary'; | |
timeoutDelay = '3000'; | |
$( document.body ).on('wc_backbone_modal_loaded', function( event, target ) { | |
$( '.dcwd-copyOrderNumber' ).on( 'click', function() { | |
orderNumber = $(this).data('ordernumber'); | |
console.log( 'Order number:', orderNumber ); | |
navigator.clipboard.writeText(orderNumber).then(function() { | |
$( '.dcwd-copyOrderNumber' ).addClass( classToAdd ); | |
setTimeout(() => { $( '.dcwd-copyOrderNumber' ).removeClass( classToAdd ); }, timeoutDelay ) | |
}, function(err) { | |
// TODO: Consider some indication of the error. | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
$( '.dcwd-copyEmail' ).on( 'click', function() { | |
orderEmail = $(this).data('orderemail'); | |
console.log( 'Order email:', orderEmail ); | |
navigator.clipboard.writeText(orderEmail).then(function() { | |
$( '.dcwd-copyEmail' ).addClass( classToAdd ); | |
setTimeout(() => { $( '.dcwd-copyEmail' ).removeClass( classToAdd ); }, timeoutDelay ) | |
}, function(err) { | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
$( '.dcwd-copyPhone' ).on( 'click', function() { | |
orderPhone = $(this).data('orderphone'); | |
console.log( 'Order phone:', orderPhone ); | |
navigator.clipboard.writeText(orderPhone).then(function() { | |
$( '.dcwd-copyPhone' ).addClass( classToAdd ); | |
setTimeout(() => { $( '.dcwd-copyPhone' ).removeClass( classToAdd ); }, timeoutDelay ) | |
}, function(err) { | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
$( '.dcwd-copyAddrEmail' ).on( 'click', function() { | |
// Change pipe char back to line breaks. | |
orderOrderaddrEmail = $(this).data('orderaddremail').replace( /\|/g, "\n" ); | |
console.log( 'Order address/email:', orderOrderaddrEmail ); | |
navigator.clipboard.writeText(orderOrderaddrEmail).then(function() { | |
$( '.dcwd-copyAddrEmail' ).addClass( classToAdd ); | |
setTimeout(() => { $( '.dcwd-copyAddrEmail' ).removeClass( classToAdd ); }, timeoutDelay ) | |
}, function(err) { | |
console.error('Async: Could not copy text: ', err); | |
}); | |
}); | |
}); | |
}); | |
</script> | |
<?php | |
} | |
} | |
$CopyOrderAddressToClipboard = new CopyOrderAddressToClipboard(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment