Commit 6a7859dccdb for woocommerce

commit 6a7859dccdbaa8bac28fe40b57a8a657d678db71
Author: James Kemp <me@jckemp.com>
Date:   Tue Feb 24 10:18:17 2026 +0000

    Fix template override detection in System Status to show actual runtime behavior (#62964)

    * Fix template override detection in System Status to show actual runtime behavior

    The previous implementation had issues:
    - Called wc_get_template filter with incorrect first parameter ($file instead of located path)
    - Showed theme overrides that would actually be replaced by plugin filters at runtime
    - Incorrectly showed WC core templates as overrides (e.g., block-notices)

    This change:
    - Uses wc_locate_template() to properly locate templates including plugin filter overrides
    - Applies wc_get_template filter with correct parameters matching runtime behavior
    - Removes redundant elseif chain (now handled by wc_locate_template)
    - Shows the template that will actually be used at runtime

    Adds unit test for filter override detection.

    * Add changefile(s) from automation for the following project(s): woocommerce

    * Normalize template override paths to be relative to ABSPATH

    ---------

    Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/62964-fix-template-override-detection b/plugins/woocommerce/changelog/62964-fix-template-override-detection
new file mode 100644
index 00000000000..a0035ca2b79
--- /dev/null
+++ b/plugins/woocommerce/changelog/62964-fix-template-override-detection
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix System Status template override detection to show actual runtime behavior, including plugin filter overrides.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php
index 986a90e9d4a..f5a1c3499d6 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php
@@ -1355,24 +1355,31 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
 			$scan_files[] = 'taxonomy-product_cat.php';
 			$scan_files[] = 'taxonomy-product_tag.php';

+			$wc_templates_dir = WC()->plugin_path() . '/templates/';
+
 			foreach ( $scan_files as $file ) {
-				$located = apply_filters( 'wc_get_template', $file, $file, array(), WC()->template_path(), WC()->plugin_path() . '/templates/' );
-
-				if ( file_exists( $located ) ) {
-					$theme_file = $located;
-				} elseif ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
-					$theme_file = get_stylesheet_directory() . '/' . $file;
-				} elseif ( file_exists( get_stylesheet_directory() . '/' . WC()->template_path() . $file ) ) {
-					$theme_file = get_stylesheet_directory() . '/' . WC()->template_path() . $file;
-				} elseif ( file_exists( get_template_directory() . '/' . $file ) ) {
-					$theme_file = get_template_directory() . '/' . $file;
-				} elseif ( file_exists( get_template_directory() . '/' . WC()->template_path() . $file ) ) {
-					$theme_file = get_template_directory() . '/' . WC()->template_path() . $file;
-				} else {
-					$theme_file = false;
+				$located = wc_locate_template( $file, WC()->template_path(), $wc_templates_dir );
+
+				/**
+				 * Allow 3rd party plugins to filter the template file location.
+				 *
+				 * @param string $located       The located template path.
+				 * @param string $file          The template file name.
+				 * @param array  $args          Template arguments.
+				 * @param string $template_path The template path.
+				 * @param string $default_path  The default path.
+				 *
+				 * @since 2.1.0
+				 */
+				$located = apply_filters( 'wc_get_template', $located, $file, array(), WC()->template_path(), $wc_templates_dir );
+
+				// Check if the template is overridden (located outside WC core templates dir).
+				$override_file = false;
+				if ( 0 !== strpos( $located, $wc_templates_dir ) && file_exists( $located ) ) {
+					$override_file = $located;
 				}

-				if ( ! empty( $theme_file ) ) {
+				if ( ! empty( $override_file ) ) {
 					$core_file = $file;

 					// Update *-product_<cat|tag> template name before searching in core.
@@ -1380,16 +1387,16 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
 						$core_file = str_replace( '_', '-', $core_file );
 					}

-					$core_version  = WC_Admin_Status::get_file_version( WC()->plugin_path() . '/templates/' . $core_file );
-					$theme_version = WC_Admin_Status::get_file_version( $theme_file );
-					if ( $core_version && ( empty( $theme_version ) || version_compare( $theme_version, $core_version, '<' ) ) ) {
+					$core_version     = WC_Admin_Status::get_file_version( WC()->plugin_path() . '/templates/' . $core_file );
+					$override_version = WC_Admin_Status::get_file_version( $override_file );
+					if ( $core_version && ( empty( $override_version ) || version_compare( $override_version, $core_version, '<' ) ) ) {
 						if ( ! $outdated_templates ) {
 							$outdated_templates = true;
 						}
 					}
 					$override_files[] = array(
-						'file'         => str_replace( WP_CONTENT_DIR . '/themes/', '', $theme_file ),
-						'version'      => $theme_version,
+						'file'         => str_replace( ABSPATH, '', $override_file ),
+						'version'      => $override_version,
 						'core_version' => $core_version,
 					);
 				}
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller-test.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller-test.php
new file mode 100644
index 00000000000..971e07c1be9
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller-test.php
@@ -0,0 +1,64 @@
+<?php
+declare( strict_types = 1 );
+
+/**
+ * Tests for WC_REST_System_Status_V2_Controller.
+ *
+ * @since 10.6.0
+ */
+class WC_REST_System_Status_V2_Controller_Test extends WC_REST_Unit_Test_Case {
+
+	/**
+	 * The System Under Test.
+	 *
+	 * @var WC_REST_System_Status_V2_Controller
+	 */
+	private $sut;
+
+	/**
+	 * Set up test fixtures.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+		$this->sut = new WC_REST_System_Status_V2_Controller();
+		delete_transient( 'wc_system_status_theme_info' );
+	}
+
+	/**
+	 * Tear down test fixtures.
+	 */
+	public function tearDown(): void {
+		parent::tearDown();
+		remove_all_filters( 'wc_get_template' );
+		delete_transient( 'wc_system_status_theme_info' );
+	}
+
+	/**
+	 * @testdox Should detect template override via wc_get_template filter.
+	 */
+	public function test_get_theme_info_detects_wc_get_template_filter_override(): void {
+		$template_to_override = 'cart/cart.php';
+		$override_path        = WC()->plugin_path() . '/includes/class-woocommerce.php';
+
+		add_filter(
+			'wc_get_template',
+			function ( $template, $template_name ) use ( $template_to_override, $override_path ) {
+				if ( $template_to_override === $template_name ) {
+					return $override_path;
+				}
+				return $template;
+			},
+			10,
+			2
+		);
+
+		$theme_info = $this->sut->get_theme_info();
+
+		$override_files = array_column( $theme_info['overrides'], 'file' );
+		$this->assertContains(
+			str_replace( ABSPATH, '', $override_path ),
+			$override_files,
+			'Template overridden via wc_get_template filter should appear in overrides'
+		);
+	}
+}