Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails

WCEmailTemplateSelectiveApplier::apply_selectivelypublic staticWC 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_email post ID.
$choices(массив) (обязательный)
.

Список изменений

С версии 10.9.0 Введена.

Код 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,
	);
}