Uname:Linux webm009.cluster131.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64

403WebShell
403Webshell
Server IP : 146.59.209.152  /  Your IP : 216.73.216.152
Web Server : Apache
System : Linux webm009.cluster131.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User : monpetu ( 144298)
PHP Version : 7.4.33
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/monpetu/www/ipprint/wp-content/plugins/cartflows/admin-core/ajax/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/monpetu/www/ipprint/wp-content/plugins/cartflows/admin-core/ajax/importer.php
<?php
/**
 * Importer
 *
 * @package CartFlows
 */

namespace CartflowsAdmin\AdminCore\Ajax;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use CartflowsAdmin\AdminCore\Inc\AdminMenu;
use CartflowsAdmin\AdminCore\Ajax\AjaxBase;
use CartflowsAdmin\AdminCore\Inc\AdminHelper;

/**
 * Importer.
 */
class Importer extends AjaxBase {

	/**
	 * Instance
	 *
	 * @access private
	 * @var object Class object.
	 * @since 1.0.0
	 */
	private static $instance;

	/**
	 * Initiator
	 *
	 * @since 1.0.0
	 * @return object initialized object of class.
	 */
	public static function get_instance() {
		if ( ! isset( self::$instance ) ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Register AJAX Events.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function register_ajax_events() {

		$ajax_events = array(
			'create_flow',
			'import_flow',

			'create_step',

			'import_step',

			'activate_plugin',
			'activate_theme',

			'sync_library',
			'request_count',
			'import_sites',
			'update_library_complete',
			'export_flow',

			'get_flows_list',

			'import_json_flow',
			'export_all_flows',
			'update_step',
		);

		$this->init_ajax_events( $ajax_events );

		add_action( 'admin_footer', array( $this, 'json_importer_popup_wrapper' ) );
		add_action( 'wp_ajax_cartflows_install_plugin', 'wp_ajax_install_plugin' );
	}

	/**
	 * Export Flows.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function export_all_flows() {

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		if ( ! check_ajax_referer( 'cartflows_export_all_flows', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		$export = \CartFlows_Importer::get_instance();
		$flows  = $export->get_all_flow_export_data();

		if ( ! empty( $flows ) && is_array( $flows ) && count( $flows ) > 0 ) {

			$response_data = array(
				'message' => __( 'Funnel exported successfully', 'cartflows' ),
				'flows'   => wp_json_encode( $flows ),
				'export'  => true,
			);

		} else {
			$response_data = array(
				'message' => __( 'No Funnels to export', 'cartflows' ),
				'flows'   => $flows,
				'export'  => false,
			);
		}

		wp_send_json_success( $response_data );
	}

	/**
	 * Import the Flow.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function import_json_flow() {

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_import_json_flow', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		// $_POST['flow_data'] is the JSON, There is nothing to sanitize JSON as it is data format not data type.
		$flow_data            = ( isset( $_POST['flow_data'] ) ) ? json_decode( stripslashes( $_POST['flow_data'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$check_store_checkout = isset( $_POST['check_store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['check_store_checkout'] ) ) : 'no'; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$force_import         = isset( $_POST['force_import'] ) ? sanitize_text_field( wp_unslash( $_POST['force_import'] ) ) : 'no'; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

		$response_data = array(
			'message'      => 'Error occured. Funnel not imported.',
			'flow_data'    => $flow_data,
			'redirect_url' => admin_url( 'admin.php?page=' . CARTFLOWS_SLUG ),
		);

		// Check if this is a Store Checkout flow and if one already exists.
		if ( 'yes' === $check_store_checkout && 'yes' !== $force_import ) {
			$existing_store_checkout = \Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' );
			
			if ( $existing_store_checkout ) {
				// Send confirmation prompt to the user.
				wp_send_json_success(
					array(
						'requires_confirmation' => true,
						'message'               => __( 'A Store Checkout funnel already exists. Importing this funnel will replace the current Store Checkout funnel.', 'cartflows' ),
						'flow_data'             => $flow_data,
					)
				);
			}
		}

		if ( is_array( $flow_data ) ) {
			// Set the flag as true to check for the import process is started for the CartFlows. So as to import/upload the required files.
			\CartFlows_Batch_Process::set_is_wcf_template_import( true );

			$imported_flow = \CartFlows_Importer::get_instance()->import_from_json_data( $flow_data );

			// Set the flag as false once the template import is complete.
			\CartFlows_Batch_Process::set_is_wcf_template_import( false );

			$response_data['message']      = 'Funnel Imported successfully';
			$response_data['redirect_url'] = admin_url( 'admin.php?page=' . CARTFLOWS_SLUG . '&path=flows' );

		}

		wp_send_json_success( $response_data );
	}

	/**
	 * Import Wrapper.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function json_importer_popup_wrapper() {
		echo '<div id="wcf-json-importer"></div>';
	}

	/**
	 * Export Step
	 */
	public function export_flow() {

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_export_flow', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}
		$flow_id = ( isset( $_POST['flow_id'] ) ) ? absint( $_POST['flow_id'] ) : '';

		if ( ! $flow_id ) {
			$response_data = array( 'message' => __( 'Invalid flow ID.', 'cartflows' ) );
			wp_send_json_error( $response_data );
		}

		$flows[] = \CartFlows_Importer::get_instance()->get_flow_export_data( $flow_id );

		$response_data = array(
			'message'   => __( 'Funnel exported successfully', 'cartflows' ),
			'flow_name' => sanitize_title( get_the_title( $flow_id ) ),
			'flows'     => wp_json_encode( $flows ),
		);
		wp_send_json_success( $response_data );
	}

	/**
	 * Update library complete
	 */
	public function update_library_complete() {
		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_update_library_complete', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		$templates = ! empty( $_POST['template'] ) ? sanitize_text_field( wp_unslash( $_POST['template'] ) ) : '';

		\CartFlows_Batch_Process::get_instance()->update_latest_checksums( $templates );

		update_site_option( 'cartflows-batch-is-complete', 'no' );
		update_site_option( 'cartflows-manual-sync-complete', 'yes' );

		$response_data = array( 'message' => 'SUCCESS: cartflows_import_sites' );
		wp_send_json_success( $response_data );
	}

	/**
	 * Import Sites
	 */
	public function import_sites() {
		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_import_sites', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		$page_no  = isset( $_POST['page_no'] ) ? absint( $_POST['page_no'] ) : '';
		$template = isset( $_POST['template'] ) ? sanitize_text_field( wp_unslash( $_POST['template'] ) ) : '';
		if ( $page_no ) {
			$sites_and_pages = \Cartflows_Batch_Processing_Sync_Library::get_instance()->import_sites( $page_no, $template );
			wp_send_json_success(
				array(
					'message'         => 'SUCCESS: cartflows_import_sites',
					'sites_and_pages' => $sites_and_pages,
				)
			);
		}

		wp_send_json_error(
			array(
				'message' => 'SUCCESS: cartflows_import_sites',
			)
		);
	}

	/**
	 * Sync Library
	 */
	public function sync_library() {
		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_sync_library', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		$templates = ! empty( $_POST['template'] ) ? sanitize_text_field( wp_unslash( $_POST['template'] ) ) : '';

		/**
		 * LOGIC
		 */
		if ( 'no' === \CartFlows_Batch_Process::get_instance()->get_last_export_checksums( $templates ) ) {
			wp_send_json_success( 'updated' );
		}

		$status = \CartFlows_Batch_Process::get_instance()->test_cron();
		if ( is_wp_error( $status ) ) {
			$import_with = 'ajax';
		} else {
			$import_with = 'batch';
			// Process import.
			\CartFlows_Batch_Process::get_instance()->process_batch( $templates );
		}

		$response_data = array(
			'message' => 'SUCCESS: cartflows_sync_library',
			'status'  => $import_with,
		);

		wp_send_json_success( $response_data );
	}

	/**
	 * Request Count
	 */
	public function request_count() {
		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_request_count', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		$templates = ! empty( $_POST['template'] ) ? sanitize_text_field( wp_unslash( $_POST['template'] ) ) : '';

		$total_requests = \CartFlows_Batch_Process::get_instance()->get_total_requests( '', $templates );
		if ( $total_requests ) {
			wp_send_json_success(
				array(
					'message' => 'SUCCESS: cartflows_request_count',
					'count'   => $total_requests,
				)
			);
		}

		wp_send_json_error(
			array(
				'message' => 'ERROR: cartflows_request_count',
				'count'   => $total_requests,
			)
		);
	}

	/**
	 * Create Step
	 */
	public function create_step() {

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_create_step', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		wcf()->logger->import_log( 'STARTED! Importing Step' );

		$flow_id = ( isset( $_POST['flow_id'] ) ) ? absint( $_POST['flow_id'] ) : 0;

		if ( CARTFLOWS_FLOW_POST_TYPE !== get_post_type( $flow_id ) ) {
			wp_send_json_error(
				array(
					array(
						'status'  => false,
						'message' => __( 'Invalid Funnel Id has been provided.', 'cartflows' ),
					),
				)
			);
		}

		$step_type  = ( isset( $_POST['step_type'] ) ) ? sanitize_text_field( $_POST['step_type'] ) : '';
		$step_title = ( isset( $_POST['step_title'] ) ) ? sanitize_text_field( $_POST['step_title'] ) : '';
		$step_title = isset( $_POST['step_name'] ) && ! empty( $_POST['step_name'] ) ? sanitize_text_field( wp_unslash( $_POST['step_name'] ) ) : $step_title;

		// Create new step.
		$new_step_id = \CartFlows_Importer::get_instance()->create_step( $flow_id, $step_type, $step_title );

		if ( empty( $new_step_id ) ) {
			/* translators: %s: step ID */
			wp_send_json_error( sprintf( __( 'Invalid step id %1$s.', 'cartflows' ), $new_step_id ) );
		}

		// Insert the new step at the clicked edge position when invoked from a canvas connector.
		$this->handle_edge_insertion_reorder( $flow_id, $new_step_id );
		$this->update_offer_step_source_redirects( $flow_id, $new_step_id );

		/**
		 * Redirect to the new flow edit screen
		 */
		$response_data = array(
			'message'      => __( 'Successfully created the step!', 'cartflows' ),
			'redirect_url' => admin_url( 'post.php?action=edit&post=' . $new_step_id ),
		);
		wp_send_json_success( $response_data );
	}

	/**
	 * Active Plugin
	 */
	public function activate_plugin() {
		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) || ! current_user_can( 'activate_plugins' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_activate_plugin', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		\wp_clean_plugins_cache();

		$plugin_init = ( isset( $_POST['init'] ) ) ? sanitize_text_field( $_POST['init'] ) : '';

		$do_sliently = true;

		$exclude_do_silently = array(
			'woo-cart-abandonment-recovery/woo-cart-abandonment-recovery.php',
		);

		if ( in_array( $plugin_init, $exclude_do_silently, true ) ) {
			$do_sliently = false;
		}

		$activate = \activate_plugin( $plugin_init, '', false, $do_sliently );

		if ( is_wp_error( $activate ) ) {
			wp_send_json_error(
				array(
					'success' => false,
					'message' => $activate->get_error_message(),
				)
			);
		}


		if ( class_exists( '\BSF_UTM_Analytics' ) && is_callable( '\BSF_UTM_Analytics::update_referer' ) ) {
			$plugin_slug = pathinfo( $plugin_init, PATHINFO_FILENAME ); // Retrives the plugin slug from the init.
			\BSF_UTM_Analytics::update_referer( 'cartflows', $plugin_slug );
		}

		wp_send_json_success(
			array(
				'success' => true,
				'message' => 'Plugin activated successfully.',
			)
		);
	}

	/**
	 * Activate theme
	 *
	 * @since 2.0.12
	 * @return void
	 */
	public function activate_theme() {

		// Verify Nonce.
		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) || ! current_user_can( 'switch_themes' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_activate_theme', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		// Check the theme slug is available or not.
		$theme_slug = ( isset( $_POST['theme_slug'] ) ) ? sanitize_text_field( $_POST['theme_slug'] ) : '';

		// If the theme slug is not available then bail.
		if ( empty( $theme_slug ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'parameter' ) );
			wp_send_json_error( $response_data );
		}

		// Pass the theme slug and switch the theme and activate it.
		switch_theme( $theme_slug );

		wp_send_json_success(
			array(
				'success' => true,
				'message' => __( 'Theme Activated', 'cartflows' ),
			)
		);
	}

	/**
	 * Create the Flow.
	 */
	public function create_flow() {

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_create_flow', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		// Capture creation method before insert so the meta is in place
		// when transition_post_status fires on publish.
		$creation_method = isset( $_POST['creation_method'] ) ? sanitize_text_field( wp_unslash( $_POST['creation_method'] ) ) : 'scratch';

		// Create post object.
		$new_flow_post = array(
			'post_title'   => isset( $_POST['flow_name'] ) ? sanitize_text_field( wp_unslash( $_POST['flow_name'] ) ) : '',
			'post_content' => '',
			'post_status'  => 'publish',
			'post_type'    => CARTFLOWS_FLOW_POST_TYPE,
			'meta_input'   => array(
				'wcf-flow-creation-method' => $creation_method,
			),
		);

		// Insert the post into the database.
		$flow_id = wp_insert_post( $new_flow_post );

		if ( is_wp_error( $flow_id ) ) {
			wp_send_json_error( $flow_id->get_error_message() );
		}

		$store_checkout = isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '';
		$instant_layout = isset( $_POST['instant_layout'] ) ? sanitize_text_field( wp_unslash( $_POST['instant_layout'] ) ) : '';

		// If is store checkout update store_checkout options.
		if ( 'true' === $store_checkout ) {
			update_option( '_cartflows_store_checkout', $flow_id );

			// reset global checkout on store checkout creation.
			$common_settings                             = \Cartflows_Helper::get_common_settings();
			$common_settings['global_checkout']          = '';
			$common_settings['override_global_checkout'] = 'disable';

			update_option( '_cartflows_common', $common_settings );
		}

		$flow_steps = array();

		if ( wcf()->is_woo_active ) {
			if ( 'true' === $store_checkout ) {
				$steps_data = array(
					'order-form'         => array(
						'title' => __( 'Checkout', 'cartflows' ),
						'type'  => 'checkout',
					),
					'order-confirmation' => array(
						'title' => __( 'Thank You', 'cartflows' ),
						'type'  => 'thankyou',
					),
				);
			} else {
				$steps_data = array(
					'sales'              => array(
						'title' => __( 'Sales Landing', 'cartflows' ),
						'type'  => 'landing',
					),
					'order-form'         => array(
						'title' => __( 'Checkout', 'cartflows' ),
						'type'  => 'checkout',
					),
					'order-confirmation' => array(
						'title' => __( 'Thank You', 'cartflows' ),
						'type'  => 'thankyou',
					),
				);
			}
		} else {
			$steps_data = array(
				'landing'  => array(
					'title' => __( 'Landing', 'cartflows' ),
					'type'  => 'landing',
				),
				'thankyou' => array(
					'title' => __( 'Thank You', 'cartflows' ),
					'type'  => 'landing',
				),
			);
		}

		foreach ( $steps_data as $slug => $data ) {

			$post_content = '';
			$step_type    = trim( $data['type'] );

			// Create new step.
			$step_id = wp_insert_post(
				array(
					'post_type'    => CARTFLOWS_STEP_POST_TYPE,
					'post_title'   => $data['title'],
					'post_content' => $post_content,
					'post_status'  => 'publish',
				)
			);

			// Return the error.
			if ( is_wp_error( $step_id ) ) {
				wp_send_json_error( $step_id->get_error_message() );
			}

			if ( $step_id ) {

				$flow_steps[] = array(
					'id'    => $step_id,
					'title' => $data['title'],
					'type'  => $step_type,
				);

				// Insert post meta.
				update_post_meta( $step_id, 'wcf-flow-id', $flow_id );
				update_post_meta( $step_id, 'wcf-step-type', $step_type );

				// Set taxonomies.
				wp_set_object_terms( $step_id, $step_type, CARTFLOWS_TAXONOMY_STEP_TYPE );
				wp_set_object_terms( $step_id, 'flow-' . $flow_id, CARTFLOWS_TAXONOMY_STEP_FLOW );

				update_post_meta( $step_id, '_wp_page_template', 'cartflows-default' );
			}
		}

		update_post_meta( $flow_id, 'wcf-steps', $flow_steps );

		// Enable the Instant Layout for the flow for all page builders if the funnel is created from scratch.
		update_post_meta( $flow_id, 'instant-layout-style', 'yes' );
		\Cartflows_Helper::set_analytics_flag( 'first_instant_layout_enabled' );

		// Track funnel creation method for analytics.
		// $creation_method was captured and persisted as post meta before wp_insert_post above.
		AdminHelper::track_funnel_creation_method( $creation_method );

		/**
		 * Redirect to the new flow edit screen
		 */
		$response_data = array(
			'message'      => __( 'Successfully created the Funnel!', 'cartflows' ),
			'redirect_url' => admin_url( 'post.php?action=edit&post=' . $flow_id ),
			'flow_id'      => $flow_id,
		);
		wp_send_json_success( $response_data );
	}

	/**
	 * Create the Flow.
	 */
	public function import_flow() {

		wcf()->logger->import_log( 'STARTED! Importing Flow' );

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_import_flow', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}
		// $_POST['flow'] is the JSON, There is nothing to sanitize JSON as it is data format not data type.
		$flow = isset( $_POST['flow'] ) ? json_decode( stripslashes( $_POST['flow'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

		// Get single step Rest API response.
		$response = \CartFlows_API::get_instance()->get_flow( $flow['ID'] );

		$is_error = AdminHelper::has_api_error( $response['data'] );

		if ( $is_error['error'] ) {

			do_action( 'cartflows_generate_analytics_lead', $flow, false );

			wp_send_json_error(
				array(
					'error_code'     => $is_error['error_code'],
					'call_to_action' => $is_error['call_to_action'],
					'message'        => $is_error['error_message'],
					'data'           => $response,
				)
			);
		}

		$license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';

		// If license is invalid then.
		if ( 'valid' !== $license_status ) {

			$cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );

			$cta = '';
			$btn = '';
			if ( 'not-installed' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$btn = sprintf( __( 'CartFlows Pro Required! %1$sUpgrade to CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="' . esc_url( \Cartflows_Helper::get_upgrade_to_pro_link( '', 'https://cartflows.com/' ) ) . '">', '</a>' );
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( 'To import the premium flow %1$supgrade to CartFlows Pro%2$s.', 'cartflows' ), '<a target="_blank" href="' . esc_url( \Cartflows_Helper::get_upgrade_to_pro_link( '', 'https://cartflows.com/' ) ) . '">', '</a>' );
			} elseif ( 'inactive' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$btn = sprintf( __( 'Activate the CartFlows Pro to import the flow! %1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?plugin_status=search&paged=1&s=CartFlows+Pro' ) . '">', '</a>' );
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( 'To import the premium flow %1$sactivate Cartflows Pro%2$s and validate the license key.', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?plugin_status=search&paged=1&s=CartFlows+Pro' ) . '">', '</a>' );
			} elseif ( 'active' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$btn = sprintf( __( 'Invalid License Key! %1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( 'To import the premium flow %1$sactivate CartFlows Pro%2$s.', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
			}

			wp_send_json_error(
				array(
					'message'        => \ucfirst( $license_status ) . ' license key!',
					'call_to_action' => $btn,
					'data'           => $response,
				)
			);
		}

		if ( empty( $flow ) ) {
			$response_data = array( 'message' => __( 'Funnel data not found.', 'cartflows' ) );
			wp_send_json_error( $response_data );
		}

		// Capture creation method before insert so the meta is in place
		// when transition_post_status fires on publish.
		$creation_method = isset( $_POST['creation_method'] ) ? sanitize_text_field( wp_unslash( $_POST['creation_method'] ) ) : 'ready_made_template';

		/**
		 * Create Flow
		 */
		$new_flow_post = array(
			'post_title'   => isset( $_POST['flow_name'] ) ? sanitize_text_field( wp_unslash( $_POST['flow_name'] ) ) : '',
			'post_content' => '',
			'post_status'  => 'publish',
			'post_type'    => CARTFLOWS_FLOW_POST_TYPE,
			'meta_input'   => array(
				'wcf-flow-creation-method' => $creation_method,
			),
		);

		// Insert the post into the database.
		$new_flow_id = wp_insert_post( $new_flow_post );

		if ( is_wp_error( $new_flow_id ) ) {
			do_action( 'cartflows_generate_analytics_lead', $flow, false );
			wp_send_json_error( $new_flow_id->get_error_message() );
		}

		$store_checkout = isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '';

		// If is global checkout update store_checkout options.
		if ( 'true' === $store_checkout ) {
			update_option( '_cartflows_store_checkout', $new_flow_id );

			// reset global checkout on store checkout creation.
			$common_settings                             = \Cartflows_Helper::get_common_settings();
			$common_settings['global_checkout']          = '';
			$common_settings['override_global_checkout'] = 'disable';

			update_option( '_cartflows_common', $common_settings );
		}

		// Import the Global Colors Data if exists.
		$this->import_funnel_gcp_vars_data( $response, $new_flow_id );

		wcf()->logger->import_log( '✓ Flow Created! Flow ID: ' . $new_flow_id . ' - Remote Flow ID - ' . $flow['ID'] );

		/**
		 * All Import Steps
		 */
		$steps = isset( $flow['steps'] ) ? $flow['steps'] : array();

		// Return of no steps are found in the imported flow.
		if ( empty( $steps ) ) {
			do_action( 'cartflows_generate_analytics_lead', $flow, false );
			$response_data = array( 'message' => __( 'Steps not found.', 'cartflows' ) );
			wp_send_json_error( $response_data );
		}

		// Set the flag as true to check for the import process is started for the CartFlows. So as to import/upload the required files.
		\CartFlows_Batch_Process::set_is_wcf_template_import( true );

		foreach ( $steps as $key => $step ) {

			if ( in_array( $step['type'], array( 'upsell', 'downsell' ), true ) && ( ! _is_cartflows_pro() || is_wcf_starter_plan() ) ) {
				continue;
			}

			$this->import_single_step(
				array(
					'step'              => array(
						'id'    => $step['ID'],
						'title' => $step['title'],
						'type'  => $step['type'],
					),
					'flow'              => array(
						'id' => $new_flow_id,
					),
					'is_store_checkout' => isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '',
				),
				'cartflows_import_flow'
			);
		}

		/**
		 * Redirect to the new flow edit screen
		 */
		$response_data = array(
			'message'      => __( 'Successfully imported the Flow!', 'cartflows' ),
			'items'        => $flow,
			'redirect_url' => admin_url( 'post.php?action=edit&post=' . $new_flow_id ),
			'new_flow_id'  => $new_flow_id,
		);

		// Set the flag as false once the template import is complete.
		\CartFlows_Batch_Process::set_is_wcf_template_import( false );

		// Check if the user has imported their first flow.
		$first_flow_imported = get_option( 'wcf_first_flow_imported', false );

		if ( ! $first_flow_imported ) {
			update_option( 'wcf_first_flow_imported', true );
		}

		// Track first template imported event.
		\Cartflows_Helper::set_analytics_flag( 'first_template_imported' );

		// Track funnel creation method for analytics.
		// $creation_method was captured and persisted as post meta before wp_insert_post above.
		AdminHelper::track_funnel_creation_method( $creation_method );

		wcf()->logger->import_log( 'COMPLETE! Importing Flow' );

		// Report the completed template import to the CartFlows server.
		do_action( 'cartflows_generate_analytics_lead', $flow, true );

		wp_send_json_success( $response_data );
	}

	/**
	 * Import Step
	 *
	 * @return void
	 */
	public function import_step() {

		wcf()->logger->import_log( 'STARTED! Importing Step' );

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_import_step', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		// $_POST['step'] is the JSON, There is nothing to sanitize JSON as it is data format not data type. Hence sanitizing decoded array below.
		$step = isset( $_POST['step'] ) ? json_decode( stripslashes( $_POST['step'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

		// Return if step data not found in the import request.
		if ( empty( $step ) ) {
			wp_send_json_error( array( 'message' => __( 'Step data ID not found for import.', 'cartflows' ) ) );
		}

		// Sanitizing decoded array.
		$step = array_map( 'sanitize_text_field', $step );

		$flow_id = isset( $_POST['flow_id'] ) ? absint( $_POST['flow_id'] ) : 0;

		$remote_flow_id = isset( $_POST['remote_flow_id'] ) ? absint( $_POST['remote_flow_id'] ) : 0;

		// Return if the remote flow ID is blank or not found in the request.
		if ( empty( $remote_flow_id ) || empty( $flow_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Funnel ID not found in the request.', 'cartflows' ) ) );
		}

		// Get single step Rest API response.
		$response = \CartFlows_API::get_instance()->get_flow( $remote_flow_id );

		if ( is_wp_error( $response['data'] ) ) {
			/* translators: %1$s: html tag, %2$s: link html start %3$s: link html end */
			$btn = sprintf( __( 'Request timeout error. Please check if the firewall or any security plugin is blocking the outgoing HTTP/HTTPS requests to templates.cartflows.com or not. %1$sTo resolve this issue, please check this %2$sarticle%3$s.', 'cartflows' ), '<br><br>', '<a target="_blank" href="' . esc_url( \Cartflows_Helper::get_kb_doc_link( 'https://cartflows.com/docs/request-timeout-error-while-importing-the-flow-step-templates/' ) ) . '">', '</a>' );

			wp_send_json_error(
				array(
					'message'        => $response['data']->get_error_message(),
					'call_to_action' => $btn,
					'data'           => $response,
				)
			);
		}

		$license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';

		// If license is invalid then.
		if ( 'valid' !== $license_status ) {

			$cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );

			$msg = '';
			$cta = '';
			if ( 'not-installed' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( '%1$sUpgrade to CartFlows Pro.%2$s', 'cartflows' ), '<a target="_blanks" class="wcf-button wcf-primary-button" href="' . esc_url( \Cartflows_Helper::get_upgrade_to_pro_link( '', 'https://cartflows.com/' ) ) . '">', '</a>' );
				$msg = __( 'To import the premium step, please upgrade to CartFlows Pro', 'cartflows' );
			} elseif ( 'inactive' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( '%1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" class="wcf-button wcf-primary-button" href="' . admin_url( 'plugins.php?plugin_status=search&paged=1&s=CartFlows+Pro' ) . '">', '</a>' );
				$msg = __( 'To import the premium step activate Cartflows Pro and validate the license key.', 'cartflows' );
			} elseif ( 'active' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( '%1$sActivate CartFlows Pro License %2$s', 'cartflows' ), '<a target="_blank" class="wcf-button wcf-primary-button" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
				$msg = __( 'To import the premium step activate the CartFlows Pro.', 'cartflows' );
			}

			wp_send_json_error(
				array(
					'message'        => \ucfirst( $license_status ) . ' license key! ' . $msg,
					'call_to_action' => $cta,
					'data'           => $response,
				)
			);
		}

		$step['title'] = isset( $_POST['step_name'] ) && ! empty( $_POST['step_name'] ) ? sanitize_text_field( wp_unslash( $_POST['step_name'] ) ) : $step['title'];

		// Set the flag as true to check for the import process is started for the CartFlows. So as to import/upload the required files.
		\CartFlows_Batch_Process::set_is_wcf_template_import( true );

		// Create steps.
		$this->import_single_step(
			array(
				'step' => array(
					'id'    => $step['ID'],
					'title' => $step['title'],
					'type'  => $step['type'],
				),
				'flow' => array(
					'id' => $flow_id,
				),
			),
			'cartflows_import_step'
		);

		wcf()->logger->import_log( 'COMPLETE! Importing Step' );

		if ( empty( $step ) ) {
			$response_data = array( 'message' => __( 'Step data not found.', 'cartflows' ) );
			wp_send_json_error( $response_data );
		}

		/**
		 * Redirect to the new step edit screen
		 */
		$response_data = array(
			'message' => __( 'Successfully imported the Step!', 'cartflows' ),
		);

		// Set the flag as false once the template import is complete.
		\CartFlows_Batch_Process::set_is_wcf_template_import( false );

		// Track first template imported event.
		\Cartflows_Helper::set_analytics_flag( 'first_template_imported' );

		wcf()->logger->import_log( 'COMPLETE! Importing Step' );

		wp_send_json_success( $response_data );
	}

	/**
	 * Updates post content of chosen template.
	 * working only for Store Checkout.
	 *
	 * @return void
	 * @since X.X.X
	 */
	public function update_step() {

		wcf()->logger->import_log( 'STARTED! Updating Step' );

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_update_step', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		// $_POST['step'] is the JSON, There is nothing to sanitize JSON as it is data format not data type. Hence sanitizing decoded array below.
		$step = isset( $_POST['step'] ) ? json_decode( stripslashes( $_POST['step'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		// Sanitizing decoded array.
		$step    = array_map( 'sanitize_text_field', $step );
		$flow_id = isset( $_POST['flow_id'] ) ? absint( $_POST['flow_id'] ) : 0;
		$step_id = isset( $_POST['step_id'] ) ? absint( $_POST['step_id'] ) : 0;

		$remote_flow_id = isset( $_POST['remote_flow_id'] ) ? absint( $_POST['remote_flow_id'] ) : 0;

		// Get single step Rest API response.
		$response = \CartFlows_API::get_instance()->get_flow( $remote_flow_id );
		if ( is_wp_error( $response['data'] ) ) {
			/* translators: %1$s: html tag, %2$s: link html start %3$s: link html end */
			$btn = sprintf( __( 'Request timeout error. Please check if the firewall or any security plugin is blocking the outgoing HTTP/HTTPS requests to templates.cartflows.com or not. %1$sTo resolve this issue, please check this %2$s article%3$s.', 'cartflows' ), '<br><br>', '<a target="_blank" href="' . esc_url( \Cartflows_Helper::get_kb_doc_link( 'https://cartflows.com/docs/request-timeout-error-while-importing-the-flow-step-templates/' ) ) . '">', '</a>' );

			wp_send_json_error(
				array(
					'message'        => $response['data']->get_error_message(),
					'call_to_action' => $btn,
					'data'           => $response,
				)
			);
		}

		$license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';

		// If license is invalid then.
		if ( 'valid' !== $license_status ) {

			$cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );

			$cta = '';
			if ( 'not-installed' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( 'Upgrade to %1$sCartFlows Pro.%2$s', 'cartflows' ), '<a target="_blanks" href="' . esc_url( \Cartflows_Helper::get_upgrade_to_pro_link( '', 'https://cartflows.com/' ) ) . '">', '</a>' );
			} elseif ( 'inactive' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( '%1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?plugin_status=search&paged=1&s=CartFlows+Pro' ) . '">', '</a>' );
			} elseif ( 'active' === $cf_pro_status ) {
				/* translators: %1$s: link html start, %2$s: link html end*/
				$cta = sprintf( __( 'CartFlows Pro license is not active. Activate %1$sCartFlows Pro License %2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
			}

			wp_send_json_error(
				array(
					'message' => \ucfirst( $license_status ) . ' license key! ' . $cta,
					'data'    => $response,
				)
			);
		}

		if ( empty( $remote_flow_id ) ) {
			$response_data = array( 'message' => __( 'Funnel data not found.', 'cartflows' ) );
			wp_send_json_error( $response_data );
		}
		$step['title'] = isset( $_POST['step_name'] ) && ! empty( $_POST['step_name'] ) ? sanitize_text_field( wp_unslash( $_POST['step_name'] ) ) : $step['title'];
		// Create steps.
		$this->update_single_step(
			array(
				'step'         => array(
					'id'    => $step['ID'],
					'title' => $step['title'],
					'type'  => $step['type'],
				),
				'flow'         => array(
					'id' => $flow_id,
				),
				'current_step' => array(
					'id' => $step_id,
				),
			)
		);

		wcf()->logger->import_log( 'COMPLETE! Importing Step' );

		if ( empty( $step ) ) {
			$response_data = array( 'message' => __( 'Step data not found.', 'cartflows' ) );
			wp_send_json_error( $response_data );
		}

		/**
		 * Redirect to the new step edit screen
		 */
		$response_data = array(
			'message' => __( 'Successfully imported the Step!', 'cartflows' ),
		);

		wcf()->logger->import_log( 'COMPLETE! Importing Step' );

		wp_send_json_success( $response_data );
	}

	/**
	 * Update Sinple Step
	 *
	 * @param array $args Rest API Arguments.
	 * @return void
	 */
	public function update_single_step( $args = array() ) {

		wcf()->logger->import_log( 'STARTED! Updating Step' );

		$step_id     = isset( $args['step']['id'] ) ? absint( $args['step']['id'] ) : 0;
		$new_step_id = isset( $args['current_step']['id'] ) ? absint( $args['current_step']['id'] ) : '';

		if ( empty( $step_id ) || empty( $new_step_id ) ) {
			/* translators: %s: step ID */
			wp_send_json_error( sprintf( __( 'Invalid step id %1$s or post id %2$s.', 'cartflows' ), $step_id, $new_step_id ) );
		}

		wcf()->logger->import_log( 'Remote Step ' . $step_id . ' for local flow "' . get_the_title( $new_step_id ) . '" [' . $new_step_id . ']' );

		// Get single step Rest API response.
		$response = \CartFlows_API::get_instance()->get_template( $step_id );
		wcf()->logger->import_log( wp_json_encode( $response ) );

		if ( 'divi' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
			if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {

				update_post_meta( $new_step_id, 'divi_content', $response['data']['divi_content'] );

				wp_update_post(
					array(
						'ID'           => $new_step_id,
						'post_content' => $response['data']['divi_content'],
					)
				);
			}
		}

		if ( 'gutenberg' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
			if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
				wp_update_post(
					array(
						'ID'           => $new_step_id,
						'post_content' => $response['data']['divi_content'],
					)
				);
			}
		}

		// Import Post Meta.
		$this->import_post_meta( $new_step_id, $response );

		/* Imported Step */
		update_post_meta( $new_step_id, 'cartflows_imported_step', 'yes' );

		do_action( 'cartflows_import_complete' );

		// Batch Process.
		do_action( 'cartflows_after_template_import', $new_step_id, $response );

		wcf()->logger->import_log( 'COMPLETE! Importing Step' );
	}

	/**
	 * Create Simple Step
	 *
	 * @param array  $args Rest API Arguments.
	 * @param string $action action name to check nonce.
	 *
	 * @return void
	 */
	public function import_single_step( $args, $action ) {

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( $action, 'security', false ) ) {
			$response_data = array( 'message' => __( 'Nonce verification failed.', 'cartflows' ) );
			wp_send_json_error( $response_data );
		}

		wcf()->logger->import_log( 'STARTED! Importing Step' );

		$step_id           = isset( $args['step']['id'] ) ? absint( $args['step']['id'] ) : 0;
		$step_title        = isset( $args['step']['title'] ) ? $args['step']['title'] : '';
		$step_type         = isset( $args['step']['type'] ) ? $args['step']['type'] : '';
		$flow_id           = isset( $args['flow']['id'] ) ? absint( $args['flow']['id'] ) : 0;
		$is_store_checkout = isset( $args['is_store_checkout'] ) ? $args['is_store_checkout'] : '';

		// create steps only for checkout and thankyou if store checkout.
		// This logic will be removed once we have store checkout templates on server.
		if ( 'true' === $is_store_checkout && ! in_array( $step_type, array( 'checkout', 'thankyou' ), true ) ) {
			return;
		}

		// Create new step.
		$new_step_id = \CartFlows_Importer::get_instance()->create_step( $flow_id, $step_type, $step_title );

		if ( empty( $step_id ) || empty( $new_step_id ) ) {
			/* translators: %s: step ID */
			wp_send_json_error( sprintf( __( 'Invalid step id %1$s or post id %2$s.', 'cartflows' ), $step_id, $new_step_id ) );
		}

		// Handle edge insertion - reorder flow steps when inserting on an edge.
		$this->handle_edge_insertion_reorder( $flow_id, $new_step_id );

		// Handle offer edge source update - update source offer step's redirect to point to new step.
		$this->update_offer_step_source_redirects( $flow_id, $new_step_id );

		wcf()->logger->import_log( 'Remote Step ' . $step_id . ' for local flow "' . get_the_title( $new_step_id ) . '" [' . $new_step_id . ']' );

		// Get single step Rest API response.
		$response = \CartFlows_API::get_instance()->get_template( $step_id );

		wcf()->logger->import_log( wp_json_encode( $response ) );

		// Return if there is an error while importing the step template.
		if ( ! $response['success'] ) {
			$response_data = $response['data'];
			$error_code    = wp_remote_retrieve_response_code( $response_data );
			$error_msge    = json_decode( wp_remote_retrieve_body( $response_data ) );

			wp_send_json_error(
				array(
					'error_code' => $error_code,
					'message'    => ! empty( $error_msge->message ) ? $error_msge->message : '',
					'data'       => $response['data'],
					'success'    => $response['success'],
				)
			);
		}

		if ( 'divi' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
			if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {

				update_post_meta( $new_step_id, 'divi_content', $response['data']['divi_content'] );

				wp_update_post(
					array(
						'ID'           => $new_step_id,
						'post_content' => $response['data']['divi_content'],
					)
				);
			}
		}

		if ( 'gutenberg' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
			if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
				wp_update_post(
					array(
						'ID'           => $new_step_id,
						'post_content' => $response['data']['divi_content'],
					)
				);
			}
		}

		// Import Post Meta.
		$this->import_post_meta( $new_step_id, $response );

		// Handle user-selected offer redirect settings for upsell/downsell steps.
		$this->update_user_selected_offer_redirects( $flow_id, $new_step_id, $step_type );

		if ( 'checkout' === $step_type ) {

			$posted_data = array(
				'primary_color' => isset( $_POST['primary_color'] ) ? sanitize_text_field( wp_unslash( $_POST['primary_color'] ) ) : '',
				'site_logo'     => isset( $_POST['site_logo'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['site_logo'] ) ) : '',
			);

			$this->update_store_checkout_template_data( $new_step_id, $response, $posted_data );
		}

		/* Imported Step */
		update_post_meta( $new_step_id, 'cartflows_imported_step', 'yes' );

		do_action( 'cartflows_import_complete' );

		// Batch Process.
		do_action( 'cartflows_after_template_import', $new_step_id, $response );

		wcf()->logger->import_log( 'COMPLETE! Importing Step' );
	}

	/**
	 * Import Post Meta
	 *
	 * @since 1.0.0
	 *
	 * @param  integer $post_id  Post ID.
	 * @param  array   $response  Post meta.
	 * @return void
	 */
	public function import_post_meta( $post_id, $response ) {

		$metadata = apply_filters( 'cartflows_template_import_meta_data', (array) $response['post_meta'] );

		$exclude_meta_keys = \Cartflows_Helper::get_instance()->get_meta_keys_to_exclude_from_import( $post_id );

		foreach ( $metadata as $meta_key => $meta_value ) {

			if ( in_array( $meta_key, $exclude_meta_keys, true ) ) {
				continue;
			}

			// Security: Only allow meta keys matching known prefixes to prevent arbitrary DB writes.
			if ( ! \Cartflows_Helper::get_instance()->is_meta_key_allowed_for_import( $meta_key ) ) {
				continue;
			}

			$meta_value = isset( $meta_value[0] ) ? $meta_value[0] : '';

			if ( $meta_value ) {

				// Beaver Builder layout keys are handled separately from the generic security
				// path below. BB stores rows/columns/modules as serialized arrays of stdClass
				// objects. We allow only stdClass — no other classes are expected or permitted.
				// Passing the raw serialized string directly to update_post_meta would cause
				// WordPress to double-serialize it, breaking BB's get_post_meta readback.
				$bb_meta_keys = array( '_fl_builder_data', '_fl_builder_draft', '_fl_builder_data_settings' );
				if ( in_array( $meta_key, $bb_meta_keys, true ) ) {
					$raw_data = $this->unserialize_bb_meta( $meta_key, $meta_value );
					if ( false === $raw_data ) {
						continue;
					}
				} elseif ( is_serialized( $meta_value, true ) ) {
					// Security: Using unserialize with allowed_classes=>false to prevent object injection.
					$raw_data = unserialize( stripslashes( $meta_value ), array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound
					// Drop malicious payloads completely to prevent fatal errors.
					// Security: Block serialized meta that contains nested PHP objects.
					// Even with allowed_classes => false, unserialized payloads may contain
					// __PHP_Incomplete_Class instances which WordPress attempts to mutate
					// during wp_unslash(), causing fatal errors and violating object safety.
					if (
						false === $raw_data ||
						$this->has_nested_object_payload( $raw_data )
					) {
						$raw_data = '';
						continue;
					}
				} elseif ( is_array( $meta_value ) ) {
					$raw_data = json_decode( stripslashes( $meta_value ), true );
				} else {
					$raw_data = $meta_value;
				}

				if ( '_elementor_data' === $meta_key ) {

					if ( is_array( $raw_data ) ) {
						$raw_data = wp_slash( wp_json_encode( $raw_data ) );
					} else {
						$raw_data = wp_slash( $raw_data );
					}
				}

				if ( '_elementor_data' !== $meta_key && '_elementor_draft' !== $meta_key && '_fl_builder_data' !== $meta_key && '_fl_builder_draft' !== $meta_key ) {
					if ( is_array( $raw_data ) ) {
						wcf()->logger->import_log( '✓ Added post meta ' . $meta_key /* . ' | ' . wp_json_encode( $raw_data ) */ );
					} elseif ( ! is_object( $raw_data ) ) {
							wcf()->logger->import_log( '✓ Added post meta ' . $meta_key /* . ' | ' . $raw_data */ );
					}
				}
				update_post_meta( $post_id, $meta_key, $raw_data );
			}
		}
	}

	/**
	 * Find the Checkout block and set the Primary color and site logo provided by the user.
	 *
	 * @since 1.10.0
	 *
	 * @param int   $post_id newly created steps ID.
	 * @param array $response data received from from the imported step.
	 * @param array $posted_data post data.
	 *
	 * @return void
	 */
	public function update_store_checkout_template_data( $post_id, $response, $posted_data ) {

		$store_checkout_id   = get_option( '_cartflows_store_checkout', false );
		$current_flow_id     = (int) wcf()->utils->get_flow_id_from_step_id( $post_id );
		$default_page_bulder = \Cartflows_Helper::get_common_setting( 'default_page_builder' );

		if ( empty( $posted_data['primary_color'] ) && empty( $posted_data['site_logo'] ) ) {
			return;
		}

		if ( $store_checkout_id !== $current_flow_id ) {
			return;
		}

		if ( 'elementor' === $default_page_bulder ) {

			$metadata = (array) $response['post_meta'];

			foreach ( $metadata as $meta_key => $meta_value ) {

				$meta_value = isset( $meta_value[0] ) ? $meta_value[0] : '';

				if ( $meta_value ) {

					if ( is_serialized( $meta_value, true ) ) {
						// Security: Using unserialize with allowed_classes=>false to prevent object injection.
						$raw_data = unserialize( stripslashes( $meta_value ), array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound
						if ( false === $raw_data || is_object( $raw_data ) ) {
							continue;
						}
					} elseif ( is_array( $meta_value ) ) {
						$raw_data = json_decode( stripslashes( $meta_value ), true );
					} else {
						$raw_data = $meta_value;
					}

					if ( '_elementor_data' === $meta_key ) {

						$raw_data = json_decode( $raw_data, true );

						// Find the checkout-form and update the primary color.
						$this->elementor_find_and_replace_template_data( $raw_data, $posted_data );

						if ( is_array( $raw_data ) ) {
							$raw_data = wp_slash( wp_json_encode( $raw_data ) );
						} else {
							$raw_data = wp_slash( $raw_data );
						}

						update_post_meta( $post_id, $meta_key, $raw_data );

					}
				}
			}
		} elseif ( 'gutenberg' === $default_page_bulder ) {

				$post   = get_post( $post_id );
				$blocks = parse_blocks( $post->post_content );

			if ( is_array( $blocks ) && ! empty( $blocks ) ) {

				$this->gutenberg_find_and_replace_template_data( $blocks, $posted_data );

				if ( ! empty( $blocks ) ) {
					$serialized_blocks = serialize_blocks( $blocks );

					wp_update_post(
						array(
							'ID'           => $post_id,
							'post_content' => $serialized_blocks,
						)
					);
				}
			}
		} elseif ( 'beaver-builder' === $default_page_bulder ) {
			$data = \FLBuilderModel::get_layout_data( 'published', $post_id );

			if ( ! empty( $data ) ) {

				$this->beaver_builder_find_and_replace_template_data( $data, $posted_data );

				// Update page builder data.
				update_post_meta( $post_id, '_fl_builder_data', $data );
				update_post_meta( $post_id, '_fl_builder_draft', $data );
			}
		}
	}


	/**
	 * Get flows list for preview
	 *
	 * @return void
	 */
	public function get_flows_list() {

		$response_data = array( 'message' => $this->get_error_msg( 'permission' ) );

		if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
			wp_send_json_error( $response_data );
		}

		/**
		 * Nonce verification
		 */
		if ( ! check_ajax_referer( 'cartflows_get_flows_list', 'security', false ) ) {
			$response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
			wp_send_json_error( $response_data );
		}

		$flows_list = \Cartflows_Helper::get_instance()->get_flows_and_steps();

		/**
		 * Redirect to the new step edit screen
		 */
		$response_data = array(
			'message' => __( 'Successful!', 'cartflows' ),
			'flows'   => $flows_list,
		);

		wp_send_json_success( $response_data );
	}

	/**
	 * Get the elementor widget data.
	 *
	 * @param array $elements elements data.
	 * @param array $posted_data posted data.
	 */
	public function elementor_find_and_replace_template_data( &$elements, $posted_data ) {

		foreach ( $elements as &$element ) {

			if ( 'widget' === $element['elType'] && 'checkout-form' === $element['widgetType'] ) {
				$element['settings']['global_primary_color'] = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element['settings']['global_primary_color'];
			}

			if ( 'widget' === $element['elType'] && 'image' === $element['widgetType'] && isset( $element['settings']['_css_classes'] ) ) {

				if ( str_contains( $element['settings']['_css_classes'], 'cartflows-store-checkout-logo-field' ) ) {
					$element['settings']['image']['url'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['settings']['image']['url'];
					$element['settings']['image']['id']  = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['id'] : $element['settings']['image']['id'];
				}
			}

			if ( ! empty( $element['elements'] ) ) {
				$this->elementor_find_and_replace_template_data( $element['elements'], $posted_data );
			}
		}
	}

	/**
	 * Get the block data.
	 *
	 * @param array $elements elements data.
	 * @param array $posted_data posted data.
	 */
	public function gutenberg_find_and_replace_template_data( &$elements, $posted_data ) {

		foreach ( $elements as &$element ) {
			if ( 'wcfb/checkout-form' === $element['blockName'] ) {
				// Update the element with the data.
				$element['attrs']['globalbgColor'] = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element['attrs']['globalbgColor'];
			}

			if ( 'uagb/info-box' === $element['blockName'] && isset( $element['attrs']['className'] ) ) {

				if ( str_contains( $element['attrs']['className'], 'cartflows-store-checkout-logo-field' ) ) {
					$element['attrs']['iconImage']['id']                   = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['id'] : $element['attrs']['iconImage']['id'];
					$element['attrs']['iconImage']['url']                  = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['url'];
					$element['attrs']['iconImage']['link']                 = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['link'];
					$element['attrs']['iconImage']['sizes']['full']['url'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['link'];
				}
			}

			if ( ! empty( $element['innerBlocks'] ) ) {
				$this->gutenberg_find_and_replace_template_data( $element['innerBlocks'], $posted_data );
			}
		}
	}

	/**
	 * Replace the logo and color in the BB template while importing.
	 *
	 * @param array $elements elements data.
	 * @param array $posted_data posted data.
	 */
	public function beaver_builder_find_and_replace_template_data( &$elements, $posted_data ) {

		foreach ( $elements as $node => &$element ) {
			if ( ! empty( $element->type ) && 'module' === $element->type ) {

				if ( ! empty( $element->settings->type ) && 'cartflows-bb-checkout-form' === $element->settings->type ) {
					// Update the logo in the template.
					$element->settings->global_primary_color = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element->settings->global_primary_color;
				}

				if ( ! empty( $element->settings->type ) && 'photo' === $element->settings->type && ! empty( $element->settings->class ) && 'cartflows-store-checkout-logo-field' === $element->settings->class ) {
					$module_setting = $element->settings;
					// Update the logo in the template.
					$element->settings->photo_src = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element->settings->photo_src;

				}
			}
		}
	}

	/**
	 * Imports the Global Colors and Patterns (GCP) variables data for a given flow.
	 *
	 * This function updates the post meta for a flow with the GCP variables data received in the response.
	 * It logs the import process and updates the post meta with the GCP data.
	 *
	 * @param array $response The response data containing the GCP variables.
	 * @param int   $flow_id The ID of the flow for which the GCP data is being imported.
	 *
	 * @return void
	 */
	public function import_funnel_gcp_vars_data( $response, $flow_id ) {

		wcf()->logger->import_log( 'Start: ' . __CLASS__ . ' :: ' . __FUNCTION__ );

		wcf()->logger->import_log( 'Newly Imported Flow ID: ' . $flow_id . PHP_EOL . ' Response ' . print_r( $response, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r

		if ( isset( $response['data']['flow_gcp_meta'] ) && ! empty( $response['data']['flow_gcp_meta'] ) && is_object( $response['data']['flow_gcp_meta'] ) ) {

			wcf()->logger->import_log( 'Before Importing:' . print_r( $response['data']['flow_gcp_meta'], true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r

			$gcp_data = (object) array_map( 'sanitize_text_field', (array) $response['data']['flow_gcp_meta'] );

			wcf()->logger->import_log( 'After Importing: ' . print_r( $gcp_data, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r

			$gcp_meta_keys = array(
				'wcf-enable-gcp-styling'  => 'yes',
				'wcf-gcp-primary-color'   => ! empty( $gcp_data->gcp_primary_color ) ? $gcp_data->gcp_primary_color : '',
				'wcf-gcp-secondary-color' => ! empty( $gcp_data->gcp_secondary_color ) ? $gcp_data->gcp_secondary_color : '',
				'wcf-gcp-text-color'      => ! empty( $gcp_data->gcp_text_color ) ? $gcp_data->gcp_text_color : '',
				'wcf-gcp-accent-color'    => ! empty( $gcp_data->gcp_accent_color ) ? $gcp_data->gcp_accent_color : '',
			);

			foreach ( $gcp_meta_keys as $key => $value ) {
				update_post_meta( $flow_id, $key, $value );
			}
		}

		wcf()->logger->import_log( 'End: ' . __CLASS__ . ' :: ' . __FUNCTION__ );
	}

	/**
	 * Deserialize a Beaver Builder layout meta value.
	 *
	 * BB layout keys (_fl_builder_data, _fl_builder_draft, _fl_builder_data_settings)
	 * store arrays of stdClass nodes. Only stdClass is allowed during unserialize —
	 * no other classes are expected in BB layout data and none should be permitted.
	 *
	 * Returns false if the value is serialized but cannot be unserialized, so the
	 * caller can skip the key rather than storing corrupt data silently.
	 *
	 * @since 3.0.2
	 *
	 * @param string $meta_key   The meta key being processed (used for error logging).
	 * @param mixed  $meta_value The raw meta value from the template API response.
	 * @return mixed Unserialized value, original value if not serialized, or false on failure.
	 */
	private function unserialize_bb_meta( $meta_key, $meta_value ) {
		if ( ! is_serialized( $meta_value ) ) {
			return $meta_value;
		}

		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound -- BB layout data uses only stdClass; no arbitrary classes are permitted.
		$result = unserialize( $meta_value, array( 'allowed_classes' => array( 'stdClass' ) ) );

		if ( false === $result ) {
			wcf()->logger->import_log( 'Failed to unserialize BB meta key: ' . $meta_key );
			return false;
		}

		return $result;
	}

	/**
	 * Detect whether the given value contains objects at any depth.
	 *
	 * This is used as a hard security guard to prevent storing
	 * unserialized payloads that contain PHP objects (including
	 * __PHP_Incomplete_Class), which can cause fatal errors and
	 * violate object injection protections.
	 *
	 * @since 2.2.1
	 *
	 * @param mixed $value The value to inspect recursively.
	 *
	 * @return bool True if an object is found anywhere in the payload.
	 */
	private function has_nested_object_payload( $value ) {

		if ( is_object( $value ) ) {
			return true;
		}

		if ( is_array( $value ) ) {
			foreach ( $value as $v ) {
				if ( $this->has_nested_object_payload( $v ) ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Handle edge insertion reorder.
	 *
	 * Reorders flow steps when a new step is inserted on an edge between two existing steps.
	 * Handles both START node edges and regular step edges.
	 *
	 * @since 3.1.0
	 * @param int $flow_id     The flow ID.
	 * @param int $new_step_id The newly created step ID.
	 * @return void
	 */
	private function handle_edge_insertion_reorder( $flow_id, $new_step_id ) {

		// Per-call IDOR guard — re-verify the current user can edit this flow.
		if ( ! $this->user_can_edit_flow( $flow_id ) ) {
			return;
		}

		$is_start_edge       = isset( $_POST['is_start_edge'] ) && 'true' === $_POST['is_start_edge']; // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$edge_source_step_id = isset( $_POST['edge_source_step_id'] ) ? absint( $_POST['edge_source_step_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$edge_target_step_id = isset( $_POST['edge_target_step_id'] ) ? absint( $_POST['edge_target_step_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing

		// Handle edge insertion if we have a target step ID (either from START node or regular step).
		if ( ! $edge_target_step_id || ( ! $is_start_edge && ! $edge_source_step_id ) ) {
			return;
		}

		// IDOR guard — both edge endpoints must belong to this flow.
		// The START node is virtual and has no step ID, so skip its check.
		if ( ! $is_start_edge && ! $this->is_step_in_flow( $edge_source_step_id, $flow_id ) ) {
			return;
		}
		if ( ! $this->is_step_in_flow( $edge_target_step_id, $flow_id ) ) {
			return;
		}

		// Get current flow steps.
		$flow_steps = get_post_meta( (int) $flow_id, 'wcf-steps', true );

		if ( ! is_array( $flow_steps ) || empty( $flow_steps ) ) {
			return;
		}

		// Find indices of source and target steps.
		$source_index   = -1; // -1 for START node means insert at position 0.
		$target_index   = -1;
		$new_step_index = -1;

		foreach ( $flow_steps as $index => $step_data ) {
			if ( isset( $step_data['id'] ) ) {
				if ( ! $is_start_edge && $step_data['id'] === $edge_source_step_id ) {
					$source_index = $index;
				}
				if ( $step_data['id'] === $edge_target_step_id ) {
					$target_index = $index;
				}
				if ( $step_data['id'] === $new_step_id ) {
					$new_step_index = $index;
				}
			}
		}

		// Determine if we can proceed with reordering.
		$can_reorder = -1 !== $target_index && -1 !== $new_step_index;
		if ( ! $is_start_edge ) {
			$can_reorder = $can_reorder && -1 !== $source_index;
		}

		if ( ! $can_reorder ) {
			return;
		}

		// Remove the new step from its current position (at the end).
		$new_step_data = $flow_steps[ $new_step_index ];
		array_splice( $flow_steps, $new_step_index, 1 );

		// Recalculate indices after removal (if new step was before them).
		if ( ! $is_start_edge && $new_step_index < $source_index ) {
			--$source_index;
		}
		if ( $new_step_index < $target_index ) {
			--$target_index;
		}

		// Insert the new step at the correct position.
		if ( $is_start_edge ) {
			// START node: insert at position 0 (beginning of flow).
			$insert_position = 0;
		} else {
			// Regular step: insert right after the source step.
			$insert_position = $source_index + 1;
		}

		array_splice( $flow_steps, (int) $insert_position, 0, array( $new_step_data ) );

		// Update the flow steps meta.
		update_post_meta( (int) $flow_id, 'wcf-steps', $flow_steps );
	}

	/**
	 * Update offer step source redirects.
	 *
	 * When inserting on an Accept/Reject edge, updates the source offer step's
	 * redirect to point to the new step.
	 *
	 * @since 3.1.0
	 * @param int $flow_id     The flow ID.
	 * @param int $new_step_id The newly created step ID.
	 * @return void
	 */
	private function update_offer_step_source_redirects( $flow_id, $new_step_id ) {

		// Per-call IDOR guard — re-verify the current user can edit this flow.
		if ( ! $this->user_can_edit_flow( $flow_id ) ) {
			return;
		}

		$edge_source_step_id = isset( $_POST['edge_source_step_id'] ) ? absint( $_POST['edge_source_step_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$edge_source_handle  = isset( $_POST['edge_source_handle'] ) ? sanitize_text_field( wp_unslash( $_POST['edge_source_handle'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing

		if ( $edge_source_step_id <= 0 || empty( $edge_source_handle ) ) {
			return;
		}

		// IDOR guard — both source and target steps must belong to this flow.
		if ( ! $this->is_step_in_flow( $edge_source_step_id, $flow_id ) ) {
			return;
		}
		if ( ! $this->is_step_in_flow( $new_step_id, $flow_id ) ) {
			return;
		}

		$source_step_type = get_post_meta( $edge_source_step_id, 'wcf-step-type', true );

		// Only update if source is an offer step (upsell/downsell).
		if ( ! in_array( $source_step_type, array( 'upsell', 'downsell' ), true ) ) {
			return;
		}

		$source_meta_updated = false;

		// Update the appropriate post meta based on handle.
		if ( 'a' === $edge_source_handle ) {
			// Accept edge - update wcf-yes-next-step.
			update_post_meta( $edge_source_step_id, 'wcf-yes-next-step', $new_step_id );
			$source_meta_updated = true;
		} elseif ( 'b' === $edge_source_handle ) {
			// Reject edge - update wcf-no-next-step.
			update_post_meta( $edge_source_step_id, 'wcf-no-next-step', $new_step_id );
			$source_meta_updated = true;
		}

		// Also update the flow_steps array to keep it in sync.
		if ( $source_meta_updated ) {
			$this->sync_offer_redirects_in_flow_steps( $flow_id, $edge_source_step_id, $edge_source_handle, $new_step_id );
		}
	}

	/**
	 * Sync offer redirects in flow_steps array.
	 *
	 * Updates the flow_steps array to keep it in sync with post meta.
	 * Handles both control steps and A/B test variations.
	 *
	 * @since 3.1.0
	 * @param int    $flow_id        The flow ID.
	 * @param int    $source_step_id The source offer step ID.
	 * @param string $handle         The edge handle ('a' for accept, 'b' for reject).
	 * @param int    $target_step_id The target step ID to redirect to.
	 * @return void
	 */
	private function sync_offer_redirects_in_flow_steps( $flow_id, $source_step_id, $handle, $target_step_id ) {
		$updated_flow_steps = get_post_meta( (int) $flow_id, 'wcf-steps', true );

		if ( ! is_array( $updated_flow_steps ) ) {
			return;
		}

		$flow_steps_updated = false;

		foreach ( $updated_flow_steps as $idx => $step_data ) {
			// Check if this is the control step (direct match).
			if ( isset( $step_data['id'] ) && (int) $step_data['id'] === (int) $source_step_id ) {
				if ( 'a' === $handle ) {
					$updated_flow_steps[ $idx ]['offer_yes_step_id'] = $target_step_id;
				} elseif ( 'b' === $handle ) {
					$updated_flow_steps[ $idx ]['offer_no_step_id'] = $target_step_id;
				}
				$flow_steps_updated = true;
				break;
			}

			// Check if the source is an A/B test variation within this step.
			if ( isset( $step_data['ab-test-variations'] ) && is_array( $step_data['ab-test-variations'] ) ) {
				foreach ( $step_data['ab-test-variations'] as $var_idx => $variation ) {
					if ( isset( $variation['id'] ) && (int) $variation['id'] === (int) $source_step_id ) {
						if ( 'a' === $handle ) {
							$updated_flow_steps[ $idx ]['ab-test-variations'][ $var_idx ]['offer_yes_step_id'] = $target_step_id;
						} elseif ( 'b' === $handle ) {
							$updated_flow_steps[ $idx ]['ab-test-variations'][ $var_idx ]['offer_no_step_id'] = $target_step_id;
						}
						$flow_steps_updated = true;
						break 2; // Break out of both loops.
					}
				}
			}
		}

		if ( $flow_steps_updated ) {
			update_post_meta( (int) $flow_id, 'wcf-steps', $updated_flow_steps );
		}
	}

	/**
	 * Update user-selected offer redirects.
	 *
	 * Handles user-selected offer redirect settings for upsell/downsell steps
	 * when the user explicitly selects Accept/Reject targets via the UI.
	 *
	 * @since 3.1.0
	 * @param int    $flow_id     The flow ID.
	 * @param int    $new_step_id The newly created step ID.
	 * @param string $step_type   The step type.
	 * @return void
	 */
	private function update_user_selected_offer_redirects( $flow_id, $new_step_id, $step_type ) {

		// Per-call IDOR guard — re-verify the current user can edit this flow.
		if ( ! $this->user_can_edit_flow( $flow_id ) ) {
			return;
		}

		// Only process for offer steps.
		if ( ! in_array( $step_type, array( 'upsell', 'downsell' ), true ) ) {
			return;
		}

		// IDOR guard — the new step itself must belong to this flow.
		if ( ! $this->is_step_in_flow( $new_step_id, $flow_id ) ) {
			return;
		}

		$user_offer_yes_step_id = isset( $_POST['offer_yes_step_id'] ) ? absint( $_POST['offer_yes_step_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$user_offer_no_step_id  = isset( $_POST['offer_no_step_id'] ) ? absint( $_POST['offer_no_step_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing

		// IDOR guard — redirect targets must belong to this flow if provided.
		if ( $user_offer_yes_step_id && ! $this->is_step_in_flow( $user_offer_yes_step_id, $flow_id ) ) {
			$user_offer_yes_step_id = 0;
		}
		if ( $user_offer_no_step_id && ! $this->is_step_in_flow( $user_offer_no_step_id, $flow_id ) ) {
			$user_offer_no_step_id = 0;
		}

		// Return if user didn't select any custom redirects.
		if ( ! $user_offer_yes_step_id && ! $user_offer_no_step_id ) {
			return;
		}

		$current_flow_steps = get_post_meta( (int) $flow_id, 'wcf-steps', true );

		if ( ! is_array( $current_flow_steps ) ) {
			return;
		}

		foreach ( $current_flow_steps as $idx => $step_data ) {
			if ( isset( $step_data['id'] ) && $step_data['id'] === $new_step_id ) {
				// Update offer_yes_step_id if user selected one.
				if ( $user_offer_yes_step_id ) {
					$current_flow_steps[ $idx ]['offer_yes_step_id'] = $user_offer_yes_step_id;
					update_post_meta( $new_step_id, 'wcf-yes-next-step', $user_offer_yes_step_id );
				}

				// Update offer_no_step_id if user selected one.
				if ( $user_offer_no_step_id ) {
					$current_flow_steps[ $idx ]['offer_no_step_id'] = $user_offer_no_step_id;
					update_post_meta( $new_step_id, 'wcf-no-next-step', $user_offer_no_step_id );
				}
				break;
			}
		}

		// Save the updated flow steps.
		update_post_meta( (int) $flow_id, 'wcf-steps', $current_flow_steps );
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit