Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails
WCEmailTemplateSelectiveApplier::apply_selectively │ public static │ WC 10.9.0
Apply the selected set of core template changes to a woo_email post.
Метод класса: WCEmailTemplateSelectiveApplier{}
Хуков нет.
Возвращает
Массив<Строку,. mixed>|\WP_Error On success, an array with keys merged_content, revision_id, version_to, status ('applied'), structural_skipped, and aliases_migrated (a list of deprecated block-name aliases rewritten to canonical form during the apply, e.g. ['woo/email-content']).
Использование
$result = WCEmailTemplateSelectiveApplier::apply_selectively( $post_id, $choices );
- $post_id(int) (обязательный)
- The
woo_emailpost ID. - $choices(массив) (обязательный)
- .
Список изменений
| С версии 10.9.0 | Введена. |
Код WCEmailTemplateSelectiveApplier::apply_selectively() WCEmailTemplateSelectiveApplier::apply selectively WC 10.9.1
public static function apply_selectively( int $post_id, array $choices ) {
$post = get_post( $post_id );
if ( ! $post instanceof \WP_Post || Integration::EMAIL_POST_TYPE !== $post->post_type ) {
return new \WP_Error(
'post_not_found',
sprintf(
/* translators: %d: post ID */
__( 'No woo_email post found for ID %d.', 'woocommerce' ),
$post_id
),
array( 'status' => 404 )
);
}
$posts_manager = WCTransactionalEmailPostsManager::get_instance();
$email_id = $posts_manager->get_email_type_from_post_id( $post_id );
if ( ! is_string( $email_id ) || '' === $email_id ) {
return new \WP_Error(
'email_not_found',
__( 'No email type associated with the given post ID.', 'woocommerce' ),
array( 'status' => 404 )
);
}
$sync_config = WCEmailTemplateSyncRegistry::get_email_sync_config( $email_id );
if ( null === $sync_config ) {
return new \WP_Error(
'not_sync_enabled',
sprintf(
/* translators: %s: email ID */
__( 'Email "%s" is not registered for template sync; selective apply is unavailable.', 'woocommerce' ),
$email_id
),
array( 'status' => 422 )
);
}
$emails = $posts_manager->get_emails_by_id();
$email = $emails[ $email_id ] ?? null;
if ( ! $email instanceof \WC_Email ) {
return new \WP_Error(
'email_not_found',
sprintf(
/* translators: %s: email ID */
__( 'Email instance for "%s" is unavailable.', 'woocommerce' ),
$email_id
),
array( 'status' => 404 )
);
}
$summary = WCEmailTemplateChangeSummary::summarize( $post_id );
if ( ! empty( $summary['is_fallback'] ) ) {
return new \WP_Error(
'no_actionable_summary',
__( 'No actionable diff is available for this post; refusing to apply.', 'woocommerce' ),
array( 'status' => 422 )
);
}
$post_content = (string) $post->post_content;
try {
$core_content = WCTransactionalEmailPostsGenerator::compute_canonical_post_content( $email );
} catch ( \Throwable $e ) {
self::get_logger()->error(
sprintf(
'Selective apply failed to compute canonical content for email "%s": %s',
$email_id,
$e->getMessage()
),
array(
'email_id' => $email_id,
'post_id' => $post_id,
'context' => 'email_template_selective_applier',
)
);
return new \WP_Error(
'canonical_render_failed',
__( 'Failed to compute the canonical core render.', 'woocommerce' ),
array( 'status' => 500 )
);
}//end try
// When the post has `last_core_render` meta, the change-summary already classified
// each block via three-way attribution (yours-vs-base, core-vs-base) and the merge
// can consume that payload directly — gating use_core decisions to "real" conflicts
// only and rejecting LCS pairs that the summary classified as separate add+remove.
$base_render_for_merge = (string) get_post_meta( $post_id, WCEmailTemplateDivergenceDetector::LAST_CORE_RENDER_META_KEY, true );
$summary_for_merge = '' !== $base_render_for_merge ? $summary : null;
$merged_result = self::merge( $post_content, $core_content, $choices, $summary_for_merge );
$merged_content = $merged_result['content'];
$structural_skipped = $merged_result['structural_skipped'];
$aliases_migrated = $merged_result['aliases_migrated'];
// Alignment is decided on whitespace-normalized content: serialize_blocks()
// can't reproduce the PHP template's literal whitespace byte-for-byte, so a
// strict === would never match even when the trees are semantically equal.
// When aligned, persist canonical verbatim so source_hash, classify_post,
// and downstream byte comparisons hold without further normalization.
$is_aligned_with_canonical = (
self::normalize_for_comparison( $merged_content )
=== self::normalize_for_comparison( $core_content )
);
if ( $is_aligned_with_canonical ) {
$merged_content = $core_content;
}
// Snapshot every meta apply is about to overwrite. Restoring only a
// subset would leave the banner / email-list gates reading stale
// post-apply `_wc_email_template_version`, hiding the pending update.
$prior_last_core_render = (string) get_post_meta( $post_id, WCEmailTemplateDivergenceDetector::LAST_CORE_RENDER_META_KEY, true );
$prior_version = (string) get_post_meta( $post_id, WCEmailTemplateDivergenceDetector::VERSION_META_KEY, true );
$prior_source_hash = (string) get_post_meta( $post_id, WCEmailTemplateDivergenceDetector::SOURCE_HASH_META_KEY, true );
$prior_last_synced_at = (string) get_post_meta( $post_id, WCEmailTemplateDivergenceDetector::LAST_SYNCED_AT_META_KEY, true );
$revision_id = wp_generate_uuid4();
$snapshot = array(
'revision_id' => $revision_id,
'content' => $post_content,
'last_core_render' => $prior_last_core_render,
'version' => $prior_version,
'source_hash' => $prior_source_hash,
'last_synced_at' => $prior_last_synced_at,
'snapshot_at' => gmdate( 'Y-m-d H:i:s' ),
);
update_post_meta( $post_id, self::SNAPSHOT_META_KEY, $snapshot );
self::$is_applying = true;
try {
$updated = wp_update_post(
array(
'ID' => $post_id,
'post_content' => $merged_content,
),
true
);
if ( is_wp_error( $updated ) ) {
delete_post_meta( $post_id, self::SNAPSHOT_META_KEY );
return $updated;
}
$saved_post = get_post( $post_id );
$saved_body = $saved_post instanceof \WP_Post ? (string) $saved_post->post_content : $merged_content;
$source_hash = $is_aligned_with_canonical
? sha1( $saved_body )
: sha1( $core_content );
$synced_at = gmdate( 'Y-m-d H:i:s' );
$version_to = (string) $sync_config['version'];
update_post_meta( $post_id, WCEmailTemplateDivergenceDetector::VERSION_META_KEY, $version_to );
update_post_meta( $post_id, WCEmailTemplateDivergenceDetector::SOURCE_HASH_META_KEY, $source_hash );
update_post_meta( $post_id, WCEmailTemplateDivergenceDetector::LAST_SYNCED_AT_META_KEY, $synced_at );
// Three-way diff base reference: "what core looked like the last time we synced."
// Always stamps the current canonical (NOT the merged content) — selective apply IS
// a sync against the new canonical even if the merchant kept some yours-blocks.
update_post_meta( $post_id, WCEmailTemplateDivergenceDetector::LAST_CORE_RENDER_META_KEY, $core_content );
if ( $is_aligned_with_canonical ) {
WCEmailTemplateDivergenceDetector::reclassify( $post_id );
} else {
// reclassify() returns null in this branch (current_core ===
// stored, current_post !== stored) and would leave prior status
// untouched, so stamp directly.
update_post_meta(
$post_id,
WCEmailTemplateDivergenceDetector::STATUS_META_KEY,
WCEmailTemplateDivergenceDetector::STATUS_CORE_UPDATED_CUSTOMIZED
);
}
} finally {
self::$is_applying = false;
}//end try
// Invalidate the change-summary cache so the next read reflects the merged state.
WCEmailTemplateChangeSummary::reset_cache();
// Fire `_update_applied` for the selective-applier path. Static extensions:
// the selective applier only acts on `core_updated_customized` posts, so
// `had_customizations` is always true and `auto_resolved` is always false.
WCEmailTemplateSyncTracker::record_selective_applied( $post_id );
return array(
'merged_content' => $merged_content,
'revision_id' => $revision_id,
'version_to' => $version_to,
'status' => 'applied',
'structural_skipped' => $structural_skipped,
'aliases_migrated' => $aliases_migrated,
);
}