PluginProbe
WooCommerce / 11.1.0-beta.2
WooCommerce v11.1.0-beta.2
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 All 648 releases
woocommerce / src / Internal / EmailEditor / WCTransactionalEmails / WCEmailScratchpadRefresher.php
WCEmailScratchpadRefresher.php
113 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 declare( strict_types=1 );
3
4 namespace Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails;
5
6 /**
7 * Refreshes never-edited editing scratchpads (unpublished `woo_email` posts)
8 * from the current file template, so the editor always opens on the content
9 * customers would receive. Edited scratchpads are never touched.
10 *
11 * @package Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails
12 * @since 11.1.0
13 *
14 * @internal
15 */
16 class WCEmailScratchpadRefresher {
17
18 /**
19 * Refresh the scratchpad from the current file template when it was never
20 * edited by the user.
21 *
22 * @param \WP_Post $scratchpad The unpublished scratchpad post.
23 * @param \WC_Email $email The email instance.
24 */
25 public function maybe_refresh( \WP_Post $scratchpad, \WC_Email $email ): void {
26 if ( ! $this->was_never_edited( $scratchpad ) ) {
27 return;
28 }
29
30 $this->refresh_content( $scratchpad, $email );
31 }
32
33 /**
34 * Check whether an unpublished email post was never edited by the user.
35 *
36 * Prefers the source hash stamped at creation and refresh (content
37 * untouched when it still matches); the timestamp fallback only applies to
38 * posts without a valid hash, e.g. stray unpublished posts created outside
39 * the lazy-creation flow.
40 *
41 * @param \WP_Post $post The post to check.
42 * @return bool True when the post content was never edited.
43 */
44 private function was_never_edited( \WP_Post $post ): bool {
45 $stored_hash = get_post_meta( $post->ID, WCEmailTemplateDivergenceDetector::SOURCE_HASH_META_KEY, true );
46 if ( is_string( $stored_hash ) && 1 === preg_match( '/^[0-9a-f]{40}$/', $stored_hash ) ) {
47 return sha1( (string) $post->post_content ) === $stored_hash;
48 }
49
50 return $post->post_date_gmt === $post->post_modified_gmt;
51 }
52
53 /**
54 * Refresh the scratchpad's content and title to the current file template.
55 *
56 * The title is system-owned, so it moves with the content. Keeps the
57 * sync meta baseline in step with the new content so a later
58 * `was_never_edited()` check still recognizes the post as untouched.
59 *
60 * @param \WP_Post $scratchpad The unpublished scratchpad post.
61 * @param \WC_Email $email The email instance.
62 */
63 private function refresh_content( \WP_Post $scratchpad, \WC_Email $email ): void {
64 $post_data = WCTransactionalEmailPostsGenerator::build_filtered_post_data( (string) $email->id, $email );
65 $canonical = (string) ( $post_data['post_content'] ?? '' );
66 $canonical_title = (string) ( $post_data['post_title'] ?? '' );
67
68 if ( '' === $canonical || ( $canonical === $scratchpad->post_content && $canonical_title === $scratchpad->post_title ) ) {
69 return;
70 }
71
72 $updated = wp_update_post(
73 array(
74 'ID' => $scratchpad->ID,
75 'post_content' => $canonical,
76 'post_title' => $canonical_title,
77 // The explicit empty value makes core skip template handling
78 // entirely, leaving the stored `_wp_page_template` meta as is.
79 // With the key omitted, wp_update_post() would re-inject the
80 // stored template (WP_Post::to_array()) and fail with "Invalid
81 // page template." whenever the email template is not
82 // registered — it only is while the editor package is
83 // bootstrapped.
84 'page_template' => '',
85 ),
86 true
87 );
88
89 if ( is_wp_error( $updated ) ) {
90 return;
91 }
92
93 $saved_post = get_post( $scratchpad->ID );
94 $saved_body = $saved_post instanceof \WP_Post ? (string) $saved_post->post_content : $canonical;
95
96 // Restamped for every email, not only sync-registry ones: the update
97 // above bumped `post_modified`, so without a matching hash the
98 // timestamp fallback in `was_never_edited()` would treat this
99 // scratchpad as edited forever and this refresh would never run again.
100 update_post_meta( $scratchpad->ID, WCEmailTemplateDivergenceDetector::SOURCE_HASH_META_KEY, sha1( $saved_body ) );
101
102 $sync_config = WCEmailTemplateSyncRegistry::get_email_sync_config( (string) $email->id );
103 if ( null !== $sync_config ) {
104 update_post_meta( $scratchpad->ID, WCEmailTemplateDivergenceDetector::VERSION_META_KEY, (string) $sync_config['version'] );
105 update_post_meta( $scratchpad->ID, WCEmailTemplateDivergenceDetector::LAST_SYNCED_AT_META_KEY, gmdate( 'Y-m-d H:i:s' ) );
106 update_post_meta( $scratchpad->ID, WCEmailTemplateDivergenceDetector::LAST_CORE_RENDER_META_KEY, $canonical );
107 }
108
109 $scratchpad->post_content = $saved_body;
110 $scratchpad->post_title = $canonical_title;
111 }
112 }
113