Skip to content
This repository was archived by the owner on May 21, 2025. It is now read-only.

Commit 5715001

Browse files
authored
Merge branch 'trunk' into fix/4678-query-args
2 parents 0d59867 + f8fa2f1 commit 5715001

File tree

5 files changed

+145
-104
lines changed

5 files changed

+145
-104
lines changed

changelog.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
* Update - Removed unnecessary get_time() calls to reduce redundant get_last_order() queries in the Subscriptions list table.
88
* Update - Improved performance on the Orders list table when rendering the Subscription Relationship column.
99
* Update - Increase the number of args accepted by wcs_get_subscriptions(), to bring about parity with wc_get_orders().
10+
* Fix - Updated subscription email item table template to align with WooCommerce 9.7 email improvements.
1011
* Fix - Prevent PHP warning on cart page shipping method updates by removing unused method: maybe_restore_shipping_methods.
12+
* Fix - Ensure the order_awaiting_payment session arg is restored when loading a renewal cart from the session to prevent duplicate orders.
1113
* Dev - Fix Node version mismatch between package.json and .nvmrc (both are now set to v16.17.1).
1214

1315
= 8.0.1 - 2025-02-13 =

includes/class-wc-subscriptions-email-notifications.php

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ public static function init() {
5353
// Add settings UI.
5454
add_filter( 'woocommerce_subscription_settings', [ __CLASS__, 'add_settings' ], 20 );
5555

56-
// Add admin notice.
57-
add_action( 'admin_notices', [ __CLASS__, 'maybe_add_admin_notice' ] );
58-
5956
// Bump settings update time whenever related options change.
6057
add_action( 'update_option_' . WC_Subscriptions_Admin::$option_prefix . self::$offset_setting_string, [ __CLASS__, 'set_notification_settings_update_time' ], 10, 3 );
6158
add_action( 'update_option_' . WC_Subscriptions_Admin::$option_prefix . self::$switch_setting_string, [ __CLASS__, 'set_notification_settings_update_time' ], 10, 3 );
@@ -307,58 +304,4 @@ public static function add_settings( $settings ) {
307304
WC_Subscriptions_Admin::insert_setting_after( $settings, WC_Subscriptions_Admin::$option_prefix . '_miscellaneous', $notification_settings, 'multiple_settings', 'sectionend' );
308305
return $settings;
309306
}
310-
311-
/**
312-
* Maybe add an admin notice to inform the store manager about the existance of the notifications feature.
313-
*/
314-
public static function maybe_add_admin_notice() {
315-
316-
// If the notifications feature is enabled, don't show the notice.
317-
if ( self::notifications_globally_enabled() ) {
318-
return;
319-
}
320-
321-
// Prevent showing the notice on the Subscriptions settings page.
322-
if ( isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && 'subscriptions' === $_GET['tab'] ) {
323-
return;
324-
}
325-
326-
$option_name = 'wcs_hide_customer_notifications_notice';
327-
$nonce = '_wcsnonce';
328-
$action = 'wcs_hide_customer_notifications_notice_action';
329-
330-
// First, check if the notice is being dismissed.
331-
$nonce_argument = sanitize_text_field( wp_unslash( $_GET[ $nonce ] ?? '' ) );
332-
if ( isset( $_GET[ $action ], $nonce_argument ) && wp_verify_nonce( $nonce_argument, $action ) ) {
333-
update_option( $option_name, 'yes' );
334-
wp_safe_redirect( remove_query_arg( [ $action, $nonce ] ) );
335-
return;
336-
}
337-
338-
if ( 'yes' === get_option( $option_name ) ) {
339-
return;
340-
}
341-
342-
$admin_notice = new WCS_Admin_Notice( 'notice', array(), wp_nonce_url( add_query_arg( $action, 'dismiss' ), $action, $nonce ) );
343-
$notice_title = __( 'WooCommerce Subscriptions: Introducing customer email notifications!', 'woocommerce-subscriptions' );
344-
$notice_content = __( 'You can now send email notifications for subscription renewals, expirations, and free trials. Go to the "Customer Notifications" settings section to configure when your customers receive these important updates.', 'woocommerce-subscriptions' );
345-
$html_content = sprintf( '<p class="main"><strong>%1$s</strong></p><p>%2$s</p>', $notice_title, $notice_content );
346-
$admin_notice->set_html_content( $html_content );
347-
$admin_notice->set_actions(
348-
array(
349-
array(
350-
'name' => __( 'Manage settings', 'woocommerce-subscriptions' ),
351-
'url' => admin_url( 'admin.php?page=wc-settings&tab=subscriptions' ),
352-
'class' => 'button button-primary',
353-
),
354-
array(
355-
'name' => __( 'Learn more', 'woocommerce-subscriptions' ),
356-
'url' => 'https://woocommerce.com/document/subscriptions/subscriptions-notifications/',
357-
'class' => 'button',
358-
),
359-
)
360-
);
361-
362-
$admin_notice->display();
363-
}
364307
}

includes/class-wc-subscriptions-email.php

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -241,25 +241,23 @@ public static function email_order_items_table( $order, $args = array() ) {
241241
$order = wc_get_order( $order );
242242
}
243243

244-
if ( is_a( $order, 'WC_Abstract_Order' ) ) {
245-
$show_download_links_callback = ( isset( $args['show_download_links'] ) && $args['show_download_links'] ) ? '__return_true' : '__return_false';
246-
$show_purchase_note_callback = ( isset( $args['show_purchase_note'] ) && $args['show_purchase_note'] ) ? '__return_true' : '__return_false';
244+
if ( ! is_a( $order, 'WC_Abstract_Order' ) ) {
245+
return $items_table;
246+
}
247247

248-
unset( $args['show_download_links'] );
249-
unset( $args['show_purchase_note'] );
248+
$show_download_links_callback = ( isset( $args['show_download_links'] ) && $args['show_download_links'] ) ? '__return_true' : '__return_false';
249+
$show_purchase_note_callback = ( isset( $args['show_purchase_note'] ) && $args['show_purchase_note'] ) ? '__return_true' : '__return_false';
250250

251-
add_filter( 'woocommerce_order_is_download_permitted', $show_download_links_callback );
252-
add_filter( 'woocommerce_order_is_paid', $show_purchase_note_callback );
251+
unset( $args['show_download_links'] );
252+
unset( $args['show_purchase_note'] );
253253

254-
if ( function_exists( 'wc_get_email_order_items' ) ) { // WC 3.0+
255-
$items_table = wc_get_email_order_items( $order, $args );
256-
} else {
257-
$items_table = $order->email_order_items_table( $args );
258-
}
254+
add_filter( 'woocommerce_order_is_download_permitted', $show_download_links_callback );
255+
add_filter( 'woocommerce_order_is_paid', $show_purchase_note_callback );
259256

260-
remove_filter( 'woocommerce_order_is_download_permitted', $show_download_links_callback );
261-
remove_filter( 'woocommerce_order_is_paid', $show_purchase_note_callback );
262-
}
257+
$items_table = wc_get_email_order_items( $order, $args );
258+
259+
remove_filter( 'woocommerce_order_is_download_permitted', $show_download_links_callback );
260+
remove_filter( 'woocommerce_order_is_paid', $show_purchase_note_callback );
263261

264262
return $items_table;
265263
}
@@ -274,13 +272,14 @@ public static function email_order_items_table( $order, $args = array() ) {
274272
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1
275273
*/
276274
public static function order_details( $order, $sent_to_admin = false, $plain_text = false, $email = '' ) {
277-
278-
$order_items_table_args = array(
275+
$email_improvements_enabled = wcs_is_wc_feature_enabled( 'email_improvements' );
276+
$image_size = $email_improvements_enabled ? 48 : 32; // These image sizes are defaults for WC core emails. @see wc_get_email_order_items().
277+
$order_items_table_args = array(
279278
'show_download_links' => ( $sent_to_admin ) ? false : $order->is_download_permitted(),
280279
'show_sku' => $sent_to_admin,
281280
'show_purchase_note' => ( $sent_to_admin ) ? false : $order->has_status( apply_filters( 'woocommerce_order_is_paid_statuses', array( 'processing', 'completed' ) ) ),
282-
'show_image' => '',
283-
'image_size' => '',
281+
'show_image' => $email_improvements_enabled,
282+
'image_size' => array( $image_size, $image_size ),
284283
'plain_text' => $plain_text,
285284
);
286285

includes/class-wcs-cart-renewal.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public function setup_hooks() {
113113
// Make sure renewal meta data persists between sessions
114114
add_filter( 'woocommerce_get_cart_item_from_session', array( &$this, 'get_cart_item_from_session' ), 10, 3 );
115115
add_action( 'woocommerce_cart_loaded_from_session', array( &$this, 'cart_items_loaded_from_session' ), 10 );
116+
add_action( 'woocommerce_cart_loaded_from_session', array( $this, 'restore_order_awaiting_payment' ), 10 );
116117

117118
// Make sure fees are added to the cart
118119
add_action( 'woocommerce_cart_calculate_fees', array( &$this, 'maybe_add_fees' ), 10, 1 );
@@ -248,14 +249,22 @@ public function maybe_setup_cart() {
248249
* @internal Core checkout uses order_awaiting_payment, Blocks checkout uses store_api_draft_order. Both validate the
249250
* cart hash to ensure the order matches the cart.
250251
*
251-
* @param int $order_id The order ID that is awaiting payment, or 0 to unset it.
252+
* @param int|WC_Order $order_id The order that is awaiting payment, or 0 to unset it.
252253
*/
253254
protected function set_order_awaiting_payment( $order_id ) {
255+
$order = null;
256+
257+
if ( is_a( $order_id, 'WC_Abstract_Order' ) ) {
258+
$order = $order_id;
259+
$order_id = $order->get_id();
260+
}
261+
254262
WC()->session->set( 'order_awaiting_payment', $order_id );
255263
WC()->session->set( 'store_api_draft_order', $order_id );
256264

257265
if ( $order_id ) {
258-
$this->set_cart_hash( $order_id );
266+
// To avoid needing to load the order object, pass it if available, otherwise pass the order ID.
267+
$this->set_cart_hash( $order ?? $order_id );
259268
}
260269
}
261270

@@ -1654,6 +1663,40 @@ public function set_renewal_order_cart_hash_on_block_checkout( $has_status, $ord
16541663
return $has_status;
16551664
}
16561665

1666+
/**
1667+
* Restores the order awaiting payment session args if the cart contains a subscription-related order.
1668+
*
1669+
* It's possible the that order_awaiting_payment and store_api_draft_order session args are not set if those session args are lost due
1670+
* to session destruction.
1671+
*
1672+
* This function checks the cart that is being loaded from the session and if the cart contains a subscription-related order and if the
1673+
* current user has permission to pay for it. If so, it restores the order awaiting payment session args.
1674+
*
1675+
* @param WC_Cart $cart The cart object.
1676+
*/
1677+
public function restore_order_awaiting_payment( $cart ) {
1678+
if ( ! is_a( $cart, WC_Cart::class ) ) {
1679+
return;
1680+
}
1681+
1682+
foreach ( $cart->get_cart() as $cart_item ) {
1683+
$order = $this->get_order( $cart_item );
1684+
1685+
if ( ! $order ) {
1686+
continue;
1687+
}
1688+
1689+
// If the current user has permission to pay for the order, restore the order awaiting payment session arg.
1690+
if ( $this->validate_current_user( $order ) ) {
1691+
$this->set_order_awaiting_payment( $order );
1692+
}
1693+
1694+
// Once we found an order, exit even if the user doesn't have permission to pay for it.
1695+
return;
1696+
1697+
}
1698+
}
1699+
16571700
/* Deprecated */
16581701

16591702
/**

templates/emails/email-order-details.php

Lines changed: 80 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,66 +2,120 @@
22
/**
33
* Order/Subscription details table shown in emails.
44
*
5-
* @author Prospress
5+
* Based on the WooCommerce core email-order-details.php template.
6+
*
67
* @package WooCommerce_Subscriptions/Templates/Emails
7-
* @version 1.0.0 - Migrated from WooCommerce Subscriptions v3.0.0
8+
* @version 7.3.0
89
*/
9-
if ( ! defined( 'ABSPATH' ) ) {
10-
exit; // Exit if accessed directly
11-
}
10+
11+
defined( 'ABSPATH' ) || exit;
1212

1313
$text_align = is_rtl() ? 'right' : 'left';
1414

15+
$email_improvements_enabled = wcs_is_wc_feature_enabled( 'email_improvements' );
16+
$heading_class = $email_improvements_enabled ? 'email-order-detail-heading' : '';
17+
$order_table_class = $email_improvements_enabled ? 'email-order-details' : '';
18+
$order_total_text_align = $email_improvements_enabled ? 'right' : 'left';
19+
20+
if ( $email_improvements_enabled ) {
21+
add_filter( 'woocommerce_order_shipping_to_display_shipped_via', '__return_false' );
22+
}
23+
1524
do_action( 'woocommerce_email_before_' . $order_type . '_table', $order, $sent_to_admin, $plain_text, $email );
1625

17-
if ( 'cancelled_subscription' != $email->id ) {
18-
echo '<h2>';
26+
if ( 'cancelled_subscription' !== $email->id ) {
27+
echo '<h2 class="' . esc_attr( $heading_class ) . '">';
1928

20-
$link_element_url = ( $sent_to_admin ) ? wcs_get_edit_post_link( wcs_get_objects_property( $order, 'id' ) ) : $order->get_view_order_url();
29+
$id_heading = sprintf(
30+
/* translators: %s: Order or subscription ID. */
31+
( 'order' === $order_type ) ? __( 'Order #%s', 'woocommerce-subscriptions' ) : __( 'Subscription #%s', 'woocommerce-subscriptions' ),
32+
$order->get_order_number()
33+
);
2134

22-
if ( 'order' == $order_type ) {
23-
// translators: $1-$2: opening and closing <a> tags $3: order's order number $4: date of order in <time> element
24-
printf( esc_html_x( '%1$sOrder #%3$s%2$s (%4$s)', 'Used in email notification', 'woocommerce-subscriptions' ), '<a href="' . esc_url( $link_element_url ) . '">', '</a>', esc_html( $order->get_order_number() ), sprintf( '<time datetime="%s">%s</time>', esc_attr( wcs_get_objects_property( $order, 'date_created' )->format( 'c' ) ), esc_html( wcs_format_datetime( wcs_get_objects_property( $order, 'date_created' ) ) ) ) );
35+
if ( $email_improvements_enabled ) {
36+
$heading = ( 'order' === $order_type ) ? __( 'Order summary', 'woocommerce-subscriptions' ) : __( 'Subscription summary', 'woocommerce-subscriptions' );
37+
echo wp_kses_post( $heading );
38+
echo '<span>';
2539
} else {
26-
// translators: $1-$3: opening and closing <a> tags $2: subscription's order number
27-
printf( esc_html_x( 'Subscription %1$s#%2$s%3$s', 'Used in email notification', 'woocommerce-subscriptions' ), '<a href="' . esc_url( $link_element_url ) . '">', esc_html( $order->get_order_number() ), '</a>' );
40+
// Prior to the email improvements, the sub_heading was wrapped in square brackets.
41+
$id_heading = '[' . $id_heading . ']';
2842
}
43+
44+
echo wp_kses_post(
45+
sprintf(
46+
'%s%s%s (<time datetime="%s">%s</time>)',
47+
'<a class="link" href="' . esc_url( ( $sent_to_admin ) ? wcs_get_edit_post_link( $order->get_id() ) : $order->get_view_order_url() ) . '">',
48+
$id_heading,
49+
'</a>',
50+
$order->get_date_created()->format( 'c' ),
51+
wcs_format_datetime( $order->get_date_created() )
52+
)
53+
);
54+
55+
if ( $email_improvements_enabled ) {
56+
echo '</span>';
57+
}
58+
2959
echo '</h2>';
3060
}
3161
?>
32-
<div style="margin-bottom: 40px;">
33-
<table class="td" cellspacing="0" cellpadding="6" style="width: 100%; font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;" border="1">
62+
<div style="margin-bottom: <?php echo $email_improvements_enabled ? '24px' : '40px'; ?>;">
63+
<table class="td font-family <?php echo esc_attr( $order_table_class ); ?>" cellspacing="0" cellpadding="6" style="width: 100%;" border="1">
64+
<?php if ( ! $email_improvements_enabled ) { ?>
3465
<thead>
3566
<tr>
3667
<th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php echo esc_html_x( 'Product', 'table headings in notification email', 'woocommerce-subscriptions' ); ?></th>
3768
<th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php echo esc_html_x( 'Quantity', 'table headings in notification email', 'woocommerce-subscriptions' ); ?></th>
3869
<th class="td" scope="col" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php echo esc_html_x( 'Price', 'table headings in notification email', 'woocommerce-subscriptions' ); ?></th>
3970
</tr>
4071
</thead>
72+
<?php } ?>
4173
<tbody>
4274
<?php echo wp_kses_post( WC_Subscriptions_Email::email_order_items_table( $order, $order_items_table_args ) ); ?>
4375
</tbody>
4476
<tfoot>
4577
<?php
46-
if ( $totals = $order->get_order_item_totals() ) {
78+
$item_totals = $order->get_order_item_totals();
79+
$item_totals_count = count( $item_totals );
80+
81+
if ( $item_totals ) {
4782
$i = 0;
48-
foreach ( $totals as $total ) {
83+
foreach ( $item_totals as $total ) {
4984
$i++;
85+
$last_class = ( $i === $item_totals_count ) ? ' order-totals-last' : '';
5086
?>
51-
<tr>
52-
<th class="td" scope="row" colspan="2" style="text-align:<?php echo esc_attr( $text_align ); ?>; <?php if ( 1 == $i ) { echo 'border-top-width: 4px;'; } ?>"><?php echo esc_html( $total['label'] ); ?></th>
53-
<td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>; <?php if ( 1 == $i ) { echo 'border-top-width: 4px;'; } ?>"><?php echo wp_kses_post( $total['value'] ); ?></td>
87+
<tr class="order-totals order-totals-<?php echo esc_attr( $total['type'] ?? 'unknown' ); ?><?php echo esc_attr( $last_class ); ?>">
88+
<th class="td text-align-left" scope="row" colspan="2" style="<?php echo ( 1 === $i ) ? 'border-top-width: 4px;' : ''; ?>">
89+
<?php
90+
echo wp_kses_post( $total['label'] ) . ' ';
91+
if ( $email_improvements_enabled ) {
92+
echo isset( $total['meta'] ) ? wp_kses_post( $total['meta'] ) : '';
93+
}
94+
?>
95+
</th>
96+
<td class="td text-align-<?php echo esc_attr( $order_total_text_align ); ?>" style="<?php echo ( 1 === $i ) ? 'border-top-width: 4px;' : ''; ?>"><?php echo wp_kses_post( $total['value'] ); ?></td>
5497
</tr>
5598
<?php
5699
}
57100
}
58101
if ( $order->get_customer_note() ) {
59-
?>
60-
<tr>
61-
<th class="td" scope="row" colspan="2" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php esc_html_e( 'Note:', 'woocommerce-subscriptions' ); ?></th>
62-
<td class="td" style="text-align:<?php echo esc_attr( $text_align ); ?>;"><?php echo wp_kses_post( wptexturize( $order->get_customer_note() ) ); ?></td>
63-
</tr>
64-
<?php
102+
if ( $email_improvements_enabled ) {
103+
?>
104+
<tr class="order-customer-note">
105+
<td class="td text-align-left" colspan="3">
106+
<b><?php esc_html_e( 'Customer note', 'woocommerce-subscriptions' ); ?></b><br>
107+
<?php echo wp_kses( nl2br( wptexturize( $order->get_customer_note() ) ), array( 'br' => array() ) ); ?>
108+
</td>
109+
</tr>
110+
<?php
111+
} else {
112+
?>
113+
<tr>
114+
<th class="td text-align-left" scope="row" colspan="2"><?php esc_html_e( 'Note:', 'woocommerce-subscriptions' ); ?></th>
115+
<td class="td text-align-left"><?php echo wp_kses( nl2br( wptexturize( $order->get_customer_note() ) ), array() ); ?></td>
116+
</tr>
117+
<?php
118+
}
65119
}
66120
?>
67121
</tfoot>

0 commit comments

Comments
 (0)