Commit 7ea8d089f5 for woocommerce

commit 7ea8d089f59631f45321e59ccd7e3ac03c70ef43
Author: Seun Olorunsola <30554163+triple0t@users.noreply.github.com>
Date:   Tue Nov 25 14:45:46 2025 +0100

    Fix settings notifications preview shows 404 not found (#62117)

    * Update transient expiration to one week and flush rewrite rules when new templates are generated.

    * Enhance email editor integration by adding support for block templates. Update WC_Email class to include a new property for block template paths and modify email template retrieval logic to prioritize block templates. Remove outdated notes and streamline documentation for custom email templates.

    * Add changelog file

    * Fix lint errors and update test case

    * Fix coderabbitai commets

diff --git a/docs/extensions/settings-and-config/email-editor-integration.md b/docs/extensions/settings-and-config/email-editor-integration.md
index 99447ec673..011a98d85c 100644
--- a/docs/extensions/settings-and-config/email-editor-integration.md
+++ b/docs/extensions/settings-and-config/email-editor-integration.md
@@ -126,12 +126,10 @@ add_filter( 'woocommerce_transactional_emails_for_block_editor', 'your_plugin_re

 **Development tip:** WooCommerce caches email post-generation with a transient. When testing or developing, delete the transient `wc_email_editor_initial_templates_generated` to force post-generation.

-## 4. Create block template
+## 4. Create the initial block template

 Create `templates/emails/block/your-custom-email.php`:

-**Note:** Block templates are the modern approach for email editor integration. However, WooCommerce maintains backward compatibility with traditional email templates. If you don't provide a block template, WooCommerce will fall back to your traditional `template_html` and `template_plain` files defined in your email class. These properties are intended to be used in the `get_content_html` and `get_content_plain` methods to load the corresponding template files. This ensures your emails continue to work even without block template support.
-
 **Template base property:** Make sure to set the `$template_base` property in your email class constructor to point to your plugin's template directory. This allows WooCommerce to properly locate and load your block template files. The block template filename is expected to match the plain template, but using the `block` directory instead of `plain`.

 ```php
@@ -153,6 +151,8 @@ defined( 'ABSPATH' ) || exit;
 <!-- /wp:woocommerce/email-content -->
 ```

+Pro tip: If you use a custom path for your email templates, set the block template path using the `template_block` property on the email class.
+
 **Email content placeholder:**

 The `BlockEmailRenderer::WOO_EMAIL_CONTENT_PLACEHOLDER` is a special placeholder that gets replaced with the main email content when the email is rendered. This placeholder is essential for integrating with WooCommerce's email system and allows the email editor to inject the core email content (like order details, customer information, etc.) into your custom template.
@@ -161,6 +161,8 @@ By default, WooCommerce uses the [general block email template](https://github.c

 If your email needs to use different content, you have two options:

+**Using custom block content template:**
+
 1. **Set a custom template**: Set the `$template_block_content` property in your email class constructor to point to a custom template for the block content:

     ```php
@@ -177,37 +179,8 @@ If your email needs to use different content, you have two options:
     }
     ```

-**Register the template:**
-
-You need to register your block template with the email editor so it can be used for editing. This connects your template file to your email class:
-
-```php
-function your_plugin_register_email_templates( $templates_registry ) {
-    $template = new \Automattic\WooCommerce\EmailEditor\Engine\Templates\Template(
-        // Template prefix (your plugin slug)
-        'your-plugin',
-        // Template slug (unique identifier)
-        'your-custom-email-template',
-        // Display title in editor
-        __( 'Custom Email Template', 'your-plugin' ),
-        // Description
-        __( 'Custom Email Template description', 'your-plugin' ),
-        // Template content
-        '<!-- wp:paragraph -->
-<p>Custom Email Template</p>
-<!-- /wp:paragraph -->
-
-<!-- wp:post-content {"lock":{"move":true,"remove":false},"layout":{"type":"default"}} /-->' ,
-        // Email post types this template can be used for
-        array( 'woo_email' )
-    );
-
-    $templates_registry->register( $template );
-
-    return $templates_registry;
-}
-add_filter( 'woocommerce_email_editor_register_templates', 'your_plugin_register_email_templates' );
-```
+**Using action hook:**
+You can use the action hook `woocommerce_email_general_block_email` to execute additional actions within the content template.

 ## 5. Set Up Triggers

@@ -350,6 +323,7 @@ class YourPlugin_Loyalty_Welcome_Email extends WC_Email {

         $this->template_html  = 'emails/loyalty-welcome.php';
         $this->template_plain = 'emails/plain/loyalty-welcome.php';
+        $this->template_block = 'emails/block/loyalty-welcome.php';
         $this->template_base  = plugin_dir_path( __FILE__ ) . 'templates/';

         parent::__construct();
@@ -432,21 +406,6 @@ add_filter( 'woocommerce_transactional_emails_for_block_editor', function( $emai
     return $emails;
 } );

-// Register block template for the email editor
-add_filter( 'woocommerce_email_editor_register_templates', function( $registry ) {
-    $template = new \Automattic\WooCommerce\EmailEditor\Engine\Templates\Template(
-        'your-plugin',
-        'loyalty-welcome',
-        __( 'Loyalty Welcome Email', 'your-plugin' ),
-        __( 'Welcome email for new loyalty program members', 'your-plugin' ),
-        file_get_contents( plugin_dir_path( __FILE__ ) . 'templates/emails/block/loyalty-welcome.html' ),
-        array( 'woo_email' )
-    );
-    $registry->register( $template );
-
-    return $registry;
-} );
-
 // Set up trigger - when to send the email
 add_action( 'your_plugin_customer_joined_loyalty', function( $customer_id, $points_earned ) {
     $emails = WC()->mailer()->get_emails();
diff --git a/plugins/woocommerce/changelog/stomail-7641-settings-notifications-preview-shows-404-not-found b/plugins/woocommerce/changelog/stomail-7641-settings-notifications-preview-shows-404-not-found
new file mode 100644
index 0000000000..f555bc3b84
--- /dev/null
+++ b/plugins/woocommerce/changelog/stomail-7641-settings-notifications-preview-shows-404-not-found
@@ -0,0 +1,4 @@
+Significance: patch
+Type: update
+
+Ensure rewrite rules are updated whenever new block email templates are generated.
diff --git a/plugins/woocommerce/includes/emails/class-wc-email.php b/plugins/woocommerce/includes/emails/class-wc-email.php
index 6baea583c7..7a93bab08c 100644
--- a/plugins/woocommerce/includes/emails/class-wc-email.php
+++ b/plugins/woocommerce/includes/emails/class-wc-email.php
@@ -94,6 +94,13 @@ class WC_Email extends WC_Settings_API {
 	 */
 	public $template_html;

+	/**
+	 * Initial email block template path.
+	 *
+	 * @var string
+	 */
+	public $template_block;
+
 	/**
 	 * Template path.
 	 *
@@ -1282,7 +1289,7 @@ class WC_Email extends WC_Settings_API {
 	/**
 	 * Get template.
 	 *
-	 * @param  string $type Template type. Can be either 'template_html' or 'template_plain'.
+	 * @param  string $type Template type. Can be either 'template_html', 'template_plain' or 'template_block'.
 	 * @return string
 	 */
 	public function get_template( $type ) {
@@ -1292,6 +1299,8 @@ class WC_Email extends WC_Settings_API {
 			return $this->template_html;
 		} elseif ( 'template_plain' === $type ) {
 			return $this->template_plain;
+		} elseif ( 'template_block' === $type ) {
+			return $this->template_block;
 		}
 		return '';
 	}
diff --git a/plugins/woocommerce/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsGenerator.php b/plugins/woocommerce/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsGenerator.php
index cdd303517c..19e7f4cddb 100644
--- a/plugins/woocommerce/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsGenerator.php
+++ b/plugins/woocommerce/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsGenerator.php
@@ -116,7 +116,7 @@ class WCTransactionalEmailPostsGenerator {
 	 * @return string The email template.
 	 */
 	public function get_email_template( $email ) {
-		$template_name = str_replace( 'plain', 'block', $email->template_plain );
+		$template_name = ! empty( $email->template_block ) ? $email->template_block : str_replace( 'plain', 'block', $email->template_plain );

 		try {
 			$template_html = wc_get_template_html(
@@ -126,6 +126,10 @@ class WCTransactionalEmailPostsGenerator {
 				$email->template_base ?? ''
 			);
 		} catch ( \Exception $e ) {
+			// wc_get_template_html() uses ob_start(), so we need to clean the output buffer if an exception is thrown.
+			if ( ob_get_level() > 0 ) {
+				ob_end_clean();
+			}
 			$template_html = '';
 		}

@@ -175,7 +179,11 @@ class WCTransactionalEmailPostsGenerator {
 			return false;
 		}

-		set_transient( $this->transient_name, Constants::get_constant( 'WC_VERSION' ), MONTH_IN_SECONDS );
+		set_transient( $this->transient_name, Constants::get_constant( 'WC_VERSION' ), WEEK_IN_SECONDS );
+
+		// Flush rewrite rules to ensure the new templates are loaded.
+		flush_rewrite_rules();
+
 		return true;
 	}

diff --git a/plugins/woocommerce/tests/php/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsGeneratorTest.php b/plugins/woocommerce/tests/php/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsGeneratorTest.php
index 7c5d87b5db..f03335f33d 100644
--- a/plugins/woocommerce/tests/php/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsGeneratorTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/EmailEditor/WCTransactionalEmails/WCTransactionalEmailPostsGeneratorTest.php
@@ -46,13 +46,27 @@ class WCTransactionalEmailPostsGeneratorTest extends \WC_Unit_Test_Case {
 	 * Test that init doesn't run if transient exists.
 	 */
 	public function testInitDoesNotRunIfTransientExists(): void {
-		set_transient( 'wc_email_editor_initial_templates_generated', WOOCOMMERCE_VERSION, MONTH_IN_SECONDS );
+		set_transient( 'wc_email_editor_initial_templates_generated', WOOCOMMERCE_VERSION, WEEK_IN_SECONDS );

 		$result = $this->email_generator->initialize();

 		$this->assertTrue( $result );
 	}

+	/**
+	 * Test that get_email_template prioritizes template_block property.
+	 */
+	public function testGetEmailTemplatePrioritizesTemplateBlockProperty(): void {
+		$email                 = $this->createMock( \WC_Email::class );
+		$email->template_plain = 'emails/plain/customer-note.php';
+		$email->template_block = 'emails/block/customer-processing-order.php';
+
+		$template = $this->email_generator->get_email_template( $email );
+
+		$this->assertStringContainsString( 'Thank you for your order', $template );
+		$this->assertStringNotContainsString( 'A note has been added to your order', $template );
+	}
+
 	/**
 	 * Test that get_email_template returns default template when custom template doesn't exist.
 	 */