Commit 036a162ab30 for woocommerce
commit 036a162ab30009ec5edfeeb3d78089bdc45fd9d8
Author: Tom Cafferkey <tjcafferkey@gmail.com>
Date: Tue Sep 8 08:16:42 2026 +0100
Move order withdrawal hooks to init (#68419)
* Fix order withdrawal feature hook registration
* Add changelog for order withdrawal hook fix
* Fix order withdrawal cleanup hooks when disabled
* Update plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php
Co-authored-by: Oleksandr Aratovskyi <79862886+oaratovskyi@users.noreply.github.com>
* Preserve order withdrawal setting keys
* Maintain is_enabled check in render_view
---------
Co-authored-by: Oleksandr Aratovskyi <79862886+oaratovskyi@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/fix-order-withdrawal-feature-hooks b/plugins/woocommerce/changelog/fix-order-withdrawal-feature-hooks
new file mode 100644
index 00000000000..7870f894ec2
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-order-withdrawal-feature-hooks
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Register order withdrawal endpoint hooks only when the feature is enabled.
diff --git a/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php b/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php
index 26e5e738400..74144d58f2b 100644
--- a/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php
+++ b/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php
@@ -63,14 +63,28 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
*/
public function register(): void {
add_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $this, 'maybe_flush_rewrite_rules' ), 10, 1 );
+ add_action( 'init', array( $this, 'register_feature_hooks' ), 0, 0 );
+ }
+
+ /**
+ * Register hooks for the current feature state.
+ *
+ * @since 11.1.0
+ */
+ public function register_feature_hooks(): void {
+ add_action( 'woocommerce_before_delete_order', array( $this->form_processor, 'delete_order_withdrawal_inbox_note_for_order' ), 10, 1 );
+ add_action( 'before_delete_post', array( $this->form_processor, 'delete_order_withdrawal_inbox_note_for_order' ), 10, 1 );
+ add_action( 'woocommerce_privacy_remove_order_personal_data', array( $this->form_processor, 'delete_order_withdrawal_inbox_note_for_order' ), 10, 1 );
+
+ if ( ! $this->is_enabled() ) {
+ $this->feature_highlight_notification->register();
+ return;
+ }
+
add_filter( 'woocommerce_get_query_vars', array( $this, 'add_query_var' ), 10, 1 );
add_filter( 'woocommerce_endpoint_' . self::ENDPOINT_KEY . '_title', array( $this, 'get_endpoint_title' ), 10, 1 );
add_filter( 'woocommerce_settings_pages', array( $this, 'add_endpoint_setting' ), 10, 1 );
add_action( 'woocommerce_account_' . self::ENDPOINT_KEY . '_endpoint', array( $this, 'render_view' ) );
- add_action( 'woocommerce_before_delete_order', array( $this->form_processor, 'delete_order_withdrawal_inbox_note_for_order' ), 10, 1 );
- add_action( 'before_delete_post', array( $this->form_processor, 'delete_order_withdrawal_inbox_note_for_order' ), 10, 1 );
- add_action( 'woocommerce_privacy_remove_order_personal_data', array( $this->form_processor, 'delete_order_withdrawal_inbox_note_for_order' ), 10, 1 );
- add_action( 'init', array( $this, 'maybe_register_feature_highlight_notification' ), 10, 0 );
}
/**
@@ -108,17 +122,6 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
}
}
- /**
- * Register the order withdrawal feature highlight notification if the feature is not enabled.
- *
- * @since 11.1.0
- */
- public function maybe_register_feature_highlight_notification(): void {
- if ( ! $this->is_enabled() ) {
- $this->feature_highlight_notification->register();
- }
- }
-
/**
* Register the order withdrawal query var.
*
@@ -132,8 +135,10 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
return array();
}
- if ( $this->is_enabled() ) {
- $query_vars[ self::ENDPOINT_KEY ] = (string) get_option( self::ENDPOINT_OPTION, self::ENDPOINT_SLUG );
+ $endpoint = (string) get_option( self::ENDPOINT_OPTION, self::ENDPOINT_SLUG );
+
+ if ( ! empty( $endpoint ) ) {
+ $query_vars[ self::ENDPOINT_KEY ] = $endpoint;
}
return $query_vars;
@@ -164,10 +169,6 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
return array();
}
- if ( ! $this->is_enabled() ) {
- return $settings;
- }
-
$endpoint_setting = array(
'title' => __( 'Order withdrawal', 'woocommerce' ),
'desc' => __( 'Endpoint for the order withdrawal page.', 'woocommerce' ),
@@ -180,7 +181,7 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
$new_settings = array();
$added = false;
- foreach ( $settings as $setting ) {
+ foreach ( $settings as $key => $setting ) {
if ( is_array( $setting ) && self::ENDPOINT_OPTION === ( $setting['id'] ?? '' ) ) {
return $settings;
}
@@ -191,15 +192,15 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
'sectionend' === ( $setting['type'] ?? '' ) &&
'account_endpoint_options' === ( $setting['id'] ?? '' )
) {
- $new_settings[] = $endpoint_setting;
- $added = true;
+ $new_settings[ self::ENDPOINT_OPTION ] = $endpoint_setting;
+ $added = true;
}
- $new_settings[] = $setting;
+ $new_settings[ $key ] = $setting;
}
if ( ! $added ) {
- $new_settings[] = $endpoint_setting;
+ $new_settings[ self::ENDPOINT_OPTION ] = $endpoint_setting;
}
return $new_settings;
@@ -214,7 +215,6 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
if ( ! $this->is_enabled() ) {
return;
}
-
wc_get_template( 'myaccount/form-order-withdrawal.php', $this->get_template_args() );
}
diff --git a/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalTest.php b/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalTest.php
index abc7f02af99..d73ec1f2822 100644
--- a/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalTest.php
@@ -671,16 +671,24 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
* @testdox Should register cleanup hooks for HPOS and legacy order deletion.
*/
public function test_controller_registers_order_deletion_cleanup_hooks(): void {
+ $this->enable_feature();
+
$controller = new OrderWithdrawalController();
- $controller->init( $this->sut, new OrderWithdrawalFormView(), new OrderWithdrawalFeatureHighlightNotification() );
+ $controller->init(
+ $this->sut,
+ new OrderWithdrawalFormView(),
+ new OrderWithdrawalFeatureHighlightNotification()
+ );
try {
$controller->register();
+ $controller->register_feature_hooks();
$this->assertNotFalse( has_action( 'woocommerce_before_delete_order', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ) ) );
$this->assertNotFalse( has_action( 'before_delete_post', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ) ) );
} finally {
remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $controller, 'maybe_flush_rewrite_rules' ), 10 );
+ remove_action( 'init', array( $controller, 'register_feature_hooks' ), 0 );
remove_filter( 'woocommerce_get_query_vars', array( $controller, 'add_query_var' ), 10 );
remove_filter( 'woocommerce_endpoint_order-withdrawal_title', array( $controller, 'get_endpoint_title' ), 10 );
remove_filter( 'woocommerce_settings_pages', array( $controller, 'add_endpoint_setting' ), 10 );
@@ -700,19 +708,23 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
$controller = new OrderWithdrawalController();
$notification = new OrderWithdrawalFeatureHighlightNotification();
- $controller->init( $this->sut, new OrderWithdrawalFormView(), $notification );
+ $controller->init(
+ $this->sut,
+ new OrderWithdrawalFormView(),
+ $notification
+ );
try {
$controller->register();
- $this->assertNotFalse( has_action( 'init', array( $controller, 'maybe_register_feature_highlight_notification' ) ), 'The controller should defer feature highlight notification registration until init.' );
+ $this->assertNotFalse( has_action( 'init', array( $controller, 'register_feature_hooks' ) ), 'The controller should defer feature-specific hook registration until init.' );
- $controller->maybe_register_feature_highlight_notification();
+ $controller->register_feature_hooks();
$this->assertFalse( has_action( 'update_option_woocommerce_coming_soon', array( $notification, 'maybe_add_note_when_store_goes_live' ) ), 'The feature highlight notification should not listen for coming-soon changes when the feature is enabled.' );
$this->assertFalse( has_action( 'wc_admin_daily', array( $notification, 'possibly_add_note' ) ), 'The feature highlight notification should not run daily when the feature is enabled.' );
} finally {
- remove_action( 'init', array( $controller, 'maybe_register_feature_highlight_notification' ), 10 );
+ remove_action( 'init', array( $controller, 'register_feature_hooks' ), 0 );
remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $controller, 'maybe_flush_rewrite_rules' ), 10 );
remove_filter( 'woocommerce_get_query_vars', array( $controller, 'add_query_var' ), 10 );
remove_filter( 'woocommerce_endpoint_order-withdrawal_title', array( $controller, 'get_endpoint_title' ), 10 );
@@ -724,6 +736,45 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
}
}
+ /**
+ * @testdox Should keep cleanup hooks but skip endpoint hooks after order withdrawal is disabled.
+ */
+ public function test_controller_keeps_cleanup_hooks_after_feature_is_disabled(): void {
+ $this->enable_feature();
+ $this->disable_feature();
+
+ $controller = new OrderWithdrawalController();
+ $notification = new OrderWithdrawalFeatureHighlightNotification();
+
+ $controller->init(
+ $this->sut,
+ new OrderWithdrawalFormView(),
+ $notification
+ );
+
+ try {
+ $controller->register();
+ $controller->register_feature_hooks();
+
+ $this->assertFalse( has_filter( 'woocommerce_get_query_vars', array( $controller, 'add_query_var' ) ), 'The endpoint query var should not be registered while the feature is disabled.' );
+ $this->assertFalse( has_filter( 'woocommerce_endpoint_order-withdrawal_title', array( $controller, 'get_endpoint_title' ) ), 'The endpoint title should not be registered while the feature is disabled.' );
+ $this->assertFalse( has_filter( 'woocommerce_settings_pages', array( $controller, 'add_endpoint_setting' ) ), 'The endpoint setting should not be registered while the feature is disabled.' );
+ $this->assertFalse( has_action( 'woocommerce_account_order-withdrawal_endpoint', array( $controller, 'render_view' ) ), 'The endpoint renderer should not be registered while the feature is disabled.' );
+ $this->assertNotFalse( has_action( 'woocommerce_before_delete_order', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ) ), 'HPOS order cleanup should stay registered while the feature is disabled.' );
+ $this->assertNotFalse( has_action( 'before_delete_post', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ) ), 'Legacy order cleanup should stay registered while the feature is disabled.' );
+ $this->assertNotFalse( has_action( 'woocommerce_privacy_remove_order_personal_data', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ) ), 'Privacy erasure cleanup should stay registered while the feature is disabled.' );
+ $this->assertNotFalse( has_action( 'wc_admin_daily', array( $notification, 'possibly_add_note' ) ), 'The feature highlight notification should run while the feature is disabled.' );
+ } finally {
+ remove_action( 'init', array( $controller, 'register_feature_hooks' ), 0 );
+ remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $controller, 'maybe_flush_rewrite_rules' ), 10 );
+ remove_action( 'update_option_woocommerce_coming_soon', array( $notification, 'maybe_add_note_when_store_goes_live' ), 10 );
+ remove_action( 'wc_admin_daily', array( $notification, 'possibly_add_note' ), 10 );
+ remove_action( 'woocommerce_before_delete_order', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ), 10 );
+ remove_action( 'before_delete_post', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ), 10 );
+ remove_action( 'woocommerce_privacy_remove_order_personal_data', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ), 10 );
+ }
+ }
+
/**
* @testdox Should keep the user on review with an error notice when notification emails fail.
*/