Skip to content

Instantly share code, notes, and snippets.

@damiencarbery
Last active April 14, 2024 11:12
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save damiencarbery/d8d5d066ecb2d78ee2fa4557b3e7de46 to your computer and use it in GitHub Desktop.
Save damiencarbery/d8d5d066ecb2d78ee2fa4557b3e7de46 to your computer and use it in GitHub Desktop.
Tracking Info to WooCommerce order - Use CMB2 to add a custom metabox to add tracking information to WooCommerce orders. The information is then added to the "Completed Order" email. https://www.damiencarbery.com/2020/01/add-tracking-info-to-woocommerce-order/
<?php
/*
Plugin Name: Add Tracking Provider
Plugin URI: https://www.damiencarbery.com/
Description: Add new An Post (Irish postal service) as a tracking provider.
Author: Damien Carbery
Author URI: https://www.damiencarbery.com
Version: 0.1.20240414
*/
add_filter( 'dcwd_set_tracking_providers', 'add_tracking_provider' );
function add_tracking_provider( $providers ) {
$providers[ 'An Post' ] = 'anpost.com';
return $providers;
}
<?php
/*
Plugin Name: Tracking Info to WooCommerce order
Plugin URI: https://www.damiencarbery.com/2020/01/add-tracking-info-to-woocommerce-order/
Description: Use custom metabox to add tracking information to WooCommerce orders. The information is then added to the "Completed Order" email.
Author: Damien Carbery
Author URI: https://www.damiencarbery.com
Version: 0.6.20240414
WC tested to: 8.7.0
Requires Plugins: woocommerce
*/
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Utilities\OrderUtil;
// 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 );
}
} );
add_action( 'woocommerce_loaded', 'dcwd_load_meta_box_hooks' );
function dcwd_load_meta_box_hooks() {
add_action( 'add_meta_boxes', 'dcwd_add_tracking_info_meta_box' );
// Use different hook to save meta data when HPOS active.
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
add_action( 'woocommerce_process_shop_order_meta', 'dcwd_save_tracking_info_meta_box_data' );
}
else {
add_action( 'save_post', 'dcwd_save_tracking_info_meta_box_data' );
}
}
function dcwd_add_tracking_info_meta_box() {
// Use different screen paaramater to show meta box when HPOS active.
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
add_meta_box( 'dcwd_tracking_info_meta_box', 'Tracking Information', 'dcwd_display_tracking_info_meta_box', 'woocommerce_page_wc-orders', 'side', 'high' );
}
else {
add_meta_box( 'dcwd_tracking_info_meta_box', 'Tracking Information', 'dcwd_display_tracking_info_meta_box', 'shop_order', 'side', 'high' );
}
}
// Render custom meta box.
function dcwd_display_tracking_info_meta_box( $post ) {
wp_nonce_field( plugin_basename(__FILE__), 'dcwd_tracking_info_nonce' );
// Get the order object to retrieve the meta.
if ( $post instanceof WC_Order ) {
$order_id = $post->get_id();
} else {
$order_id = $post->ID;
}
$order = wc_get_order( $order_id );
$tracking_number = $order->get_meta( 'tracking_number', true );
$tracking_url = $order->get_meta( 'tracking_url', true );
?>
<style>
.dcwd-tracking-info label { font-weight: bold; padding-bottom: 0.5em; }
.dcwd-tracking-info input { width: 100%; margin-bottom: 1em; }
</style>
<div class="dcwd-tracking-info">
<label for="tracking_number">Tracking number</label>
<input type="text" class="dcwd-tracking" name="tracking_number" value="<?php esc_attr_e( $tracking_number ); ?>" />
<label for="tracking_url">Tracking URL</label>
<input type="text" name="tracking_url" value="<?php esc_attr_e( $tracking_url ); ?>" />
<?php /* Changing order status can now happen at the same time as adding tracking info (because I am using 'save_post' action which is earlier than when CMB2 saved data). */ ?>
<!--<p class="dcwd-tracking-info-description">Be sure to add tracking data and click 'Update' before setting the order status to 'Completed', and clicking 'Update' again. If not done in this order the email sent to the customer will not contain the tracking data.</p>-->
</div>
<?php
}
// Sanitize and store the updated tracking info.
function dcwd_save_tracking_info_meta_box_data( $order_id ) {
if ( dcwd_user_can_save( $order_id, 'dcwd_tracking_info_nonce' ) ) {
$order = wc_get_order( $order_id );
if ( $order ) {
$save_needed = false;
if ( isset( $_POST['tracking_number'] ) && 0 < strlen( trim( $_POST['tracking_number'] ) ) ) {
$tracking_number = sanitize_text_field( trim( $_POST['tracking_number'] ) );
$order->update_meta_data( 'tracking_number', $tracking_number );
$save_needed = true;
}
if ( isset( $_POST['tracking_url'] ) && 0 < strlen( trim( $_POST['tracking_url'] ) ) ) {
$tracking_url = sanitize_url( $_POST['tracking_url'] );
$order->update_meta_data( 'tracking_url', $tracking_url );
$save_needed = true;
}
if ( $save_needed ) {
$order->save_meta_data();
}
}
}
}
// Verify the nonce and that this is not a post revision or autosave.
function dcwd_user_can_save( $post_id, $nonce ) {
$is_autosave = wp_is_post_autosave( $post_id );
$is_revision = wp_is_post_revision( $post_id );
$is_valid_nonce = ( isset( $_POST[ $nonce ] ) && wp_verify_nonce( $_POST[ $nonce ], plugin_basename( __FILE__ ) ) );
return ! ( $is_autosave || $is_revision ) && $is_valid_nonce;
}
// If using 'Email Template Customizer for WooCommerce' plugin then use a different hook
// to add the tracking information to the email.
add_action( 'plugins_loaded', 'dcwd_check_for_email_template_customizer' );
function dcwd_check_for_email_template_customizer() {
if ( class_exists( 'Woo_Email_Template_Customizer' ) ) {
// Email Template Customizer for WooCommerce plugin does not use the 'woocommerce_email_order_details'
// hook so use 'woocommerce_email_after_order_table' instead (it is one of the 3 available ones in the
// plugin's 'WC Hook' field.
add_action( 'woocommerce_email_after_order_table', 'dcwd_add_tracking_info_to_order_completed_email', 5, 4 );
}
}
// Examine the tracking url and return a provider name.
function dcwd_get_tracking_provider_from_url( $url ) {
$providers = apply_filters( 'dcwd_set_tracking_providers', array(
'USPS' => 'usps.com',
'FedEx' => 'fedex.com',
'UPS' => 'ups.com',
'DHL' => 'dhl.',
'Singapore Post' => 'www.singpost.com',
'Royal Mail' => 'royalmail.com',
) );
foreach ( $providers as $provider => $provider_url ) {
if ( strpos( $url, $provider_url ) !== false ) {
return $provider;
}
}
// Unknown provider.
return null;
}
// Determine whether an order has only virtual items (and therefore does not have shipping).
function dcwd_is_order_virtual_only( $order_id ) {
$order = wc_get_order( $order_id );
$only_virtual = true;
if ( $order ) {
foreach ( $order->get_items() as $order_item ) {
$item = wc_get_product( $order_item->get_product_id() );
if ( !$item->is_virtual() && !$item->is_downloadable() && 'pw-gift-card' != $item->get_type() ) {
// This order contains a physical product so stop looking.
return false;
}
}
if ( $only_virtual ) {
//error_log( sprintf( '<p>Order: %d - Virtual.</p>', $order->get_id() ) );
return true;
}
else {
//error_log( sprintf( '<p>Order: %d - Contains physical products.</p>', $order->get_id() ) );
}
}
return false;
}
// If available, include the tracking information in the Completed Order email.
add_action( 'woocommerce_email_order_details', 'dcwd_add_tracking_info_to_order_completed_email', 5, 4 );
function dcwd_add_tracking_info_to_order_completed_email( $order, $sent_to_admin, $plain_text, $email ) {
/* // Only customers need to know about the tracking information.
if ( ! $sent_to_admin ) {
return;
}
*/
if ( 'customer_completed_order' == $email->id ) {
$order_id = $order->get_id();
// Do not add tracking info if the order only has virtual items and does not have shipping.
if ( dcwd_is_order_virtual_only( $order_id ) ) { return; }
$tracking_number = $order->get_meta( 'tracking_number', true );
$tracking_url = $order->get_meta( 'tracking_url', true );
// Quit if either tracking field is empty.
if ( empty( $tracking_number ) || empty( $tracking_url ) ) {
// Debugging code.
//error_log( sprintf( 'Order %d does not have both tracking number (%s) and url (%s)', $order_id, $tracking_number, $tracking_url ) );
echo '<h2>Tracking information</h2><p>Sorry, tracking information is not available at this time.</p>';
return;
}
$tracking_provider = dcwd_get_tracking_provider_from_url( $tracking_url );
if ( $plain_text ) {
if ( ! empty( $tracking_provider ) ) {
printf( "\nYour order has been shipped with %s. The tracking number is %s and you can track it at %s.\n", $tracking_provider, esc_html( $tracking_number ), esc_url( $tracking_url, array( 'http', 'https' ) ) );
}
else {
printf( "\nYour order has been shipped. The tracking number is %s and you can track it at %s.\n", esc_html( $tracking_number ), esc_url( $tracking_url, array( 'http', 'https' ) ) );
}
}
else {
if ( ! empty( $tracking_provider ) ) {
printf( '<h2>Tracking information</h2><p>Your %s tracking number is <a href="%s" style="color: #a7bf49">%s</a>.</p>', $tracking_provider, esc_url( $tracking_url, array( 'http', 'https' ) ), esc_html( $tracking_number ) );
}
else {
printf( '<h2>Tracking information</h2><p>Your tracking number is <strong><a href="%s" style="color: #a7bf49">%s</a></strong>.</p>', esc_url( $tracking_url, array( 'http', 'https' ) ), esc_html( $tracking_number ) );
}
}
}
}
// Display tracking information in My Account area.
add_action( 'woocommerce_view_order', 'dcwd_add_tracking_info_to_view_order_page', 5 );
function dcwd_add_tracking_info_to_view_order_page( $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) { return; }
// Do not add tracking info if the order only has virtual items and does not have shipping.
if ( dcwd_is_order_virtual_only( $order_id ) ) { return; }
$tracking_number = $order->get_meta( 'tracking_number');
$tracking_url = $order->get_meta( 'tracking_url' );
// Quit if either tracking field is empty.
if ( empty( $tracking_number ) || empty( $tracking_url ) ) {
// Debugging code.
//error_log( sprintf( 'Order %d does not have both tracking number (%s) and url (%s)', $order_id, $tracking_number, $tracking_url ) );
echo '<p>Sorry, tracking information is not available at this time.</p>';
return;
}
$tracking_provider = dcwd_get_tracking_provider_from_url( $tracking_url );
if ( ! empty( $tracking_provider ) ) {
printf( '<p>Your order has been shipped with <strong>%s</strong>. The tracking number is <strong><a href="%s">%s</a></strong>.</p>', $tracking_provider, esc_url( $tracking_url, array( 'http', 'https' ) ), esc_html( $tracking_number ) );
}
else {
printf( '<p>Your order has been shipped. The tracking number is <strong><a href="%s">%s</a></strong>.</p>', esc_url( $tracking_url, array( 'http', 'https' ) ), esc_html( $tracking_number ) );
}
}
@mPanasiewicz
Copy link

I'm new in wordpress world. Is it possible to attach this code as separate file in the child theme files or i need to put it in the functions.php?

@damiencarbery
Copy link
Author

I'm new in wordpress world. Is it possible to attach this code as separate file in the child theme files or i need to put it in the functions.php?

It's designed as a separate file to upload to wp-content/plugins and then activate it. More info: https://www.damiencarbery.com/2018/10/how-to-use-my-code-snippets/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment