Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails

WCEmailPostsCleanup::run │ public static │ WC 11.1.0

Entry point for the db-update callback.

Always returns false (one-shot), matching the contract \WC_Install::run_update_callback_end() expects.

Method of the class: WCEmailPostsCleanup{}

No Hooks.

Returns

bool. Always false.

Usage

$result = WCEmailPostsCleanup::run( ?Email_Editor_Logger_Interface $logger ): bool;
?Email_Editor_Logger_Interface $logger
.
Default: null

Changelog

Since 11.1.0 Introduced.

WCEmailPostsCleanup::run() code WC 11.1.2

public static function run( ?Email_Editor_Logger_Interface $logger = null ): bool {
	$logger = $logger ?? new Logger( wc_get_logger() );

	$posts_manager = WCTransactionalEmailPostsManager::get_instance();
	$emails_by_id  = $posts_manager->get_emails_by_id();

	$deleted = 0;
	$kept    = 0;

	foreach ( self::fetch_email_post_mappings() as $mapping ) {
		try {
			$option_name = (string) $mapping->option_name;
			$post_id     = (int) $mapping->option_value;
			$email_type  = self::email_type_from_option_name( $option_name );

			// The SQL LIKE match is loose (each `_` is a single-char
			// wildcard); never touch options that only resemble the
			// mapping shape — they belong to someone else.
			if ( null === $email_type ) {
				continue;
			}

			if ( $post_id <= 0 ) {
				delete_option( $option_name );
				continue;
			}

			$post = get_post( $post_id );
			if ( ! $post instanceof \WP_Post || Integration::EMAIL_POST_TYPE !== $post->post_type ) {
				// Orphaned mapping.
				delete_option( $option_name );
				continue;
			}

			// A trashed copy no longer affects rendering; treat it as "revert to
			// default". The email editor UI never trashes woo_email posts (its
			// delete action permanently deletes via the
			// `woocommerce_email_editor_trash_modal_should_permanently_delete`
			// filter), so a trashed post slipped past that guard out-of-band.
			if ( 'trash' === $post->post_status ) {
				if ( wp_delete_post( $post->ID, true ) ) {
					delete_option( $option_name );
					++$deleted;
				}
				continue;
			}

			$email = $emails_by_id[ $email_type ] ?? null;
			if ( ! $email instanceof \WC_Email ) {
				// Cannot compute the canonical content (e.g. extension deactivated);
				// keep the post — it keeps rendering from the DB as before.
				update_post_meta( $post->ID, WCTransactionalEmailPostsManager::EMAIL_TYPE_META_KEY, $email_type );
				++$kept;
				continue;
			}

			if ( self::was_never_customized( $post, $email ) ) {
				// When the deletion fails (or a `pre_delete_post` filter
				// short-circuits it with a falsy value), keep the mapping —
				// otherwise the post would linger published while the email
				// falls back to the file template.
				if ( ! wp_delete_post( $post->ID, true ) ) {
					continue;
				}
				// `before_delete_post` also removes the mapping when the email
				// editor feature is active; delete the option defensively for
				// the case it isn't.
				delete_option( $option_name );
				++$deleted;
			} else {
				update_post_meta( $post->ID, WCTransactionalEmailPostsManager::EMAIL_TYPE_META_KEY, $email_type );
				++$kept;
			}
		} catch ( \Throwable $e ) {
			$logger->error(
				sprintf(
					'Email posts cleanup failed for mapping %s: %s',
					(string) $mapping->option_name,
					$e->getMessage()
				),
				array(
					'option_name' => (string) $mapping->option_name,
					'context'     => 'email_posts_cleanup',
				)
			);
			continue;
		}
	}

	delete_transient( self::LEGACY_GENERATION_TRANSIENT );

	$posts_manager->clear_caches();

	$logger->info(
		sprintf( 'Email posts cleanup finished: %d never-customized post(s) deleted, %d customized post(s) kept.', $deleted, $kept ),
		array( 'context' => 'email_posts_cleanup' )
	);

	return false;
}