'connect_wizard_content', 2 => 'install_demo_content', ); /** * Page header steps labels * * @var array */ private array $page_header_steps; /** * Page header content labels * * @var array */ private array $page_header_content; /** * URL domain of hosted system * * @var string */ private string $url_domain_hosted_system; /** * Domain of hosted system * * @var string */ private string $domain_hosted_system; /** * Whether products have been created * * @var bool */ private bool $is_created_products; /** * Whether categories have been created * * @var bool */ private bool $is_created_categories; /** * Saved step * * @var int */ private int $saved_step; /** * Product statuses to ignore when counting * * @var array */ private array $ignore_statuses_product = array( 'auto-draft', 'draft' ); /** @var Prodigy_User */ private Prodigy_User $user; /** * Constructor * * Initializes the setup wizard and handles token processing. * * @throws \Exception */ public function __construct( Prodigy_User $user ) { $this->user = $user; $this->initialize_properties(); $this->handle_token_processing(); $this->handle_clear_data_request(); $this->handle_redirect_request(); $this->load_stored_data(); $this->setup_hooks(); $this->set_platform_settings(); } /** * @return void */ public function set_platform_settings() { $settings_response = Prodigy_Request_Maker::get_instance()->do_settings_request( 'apps,statistic-apps,external-apps,product-setting,store-customization-settings.customization-error-messages,customer-extra-fields' ); ( new Prodigy_Settings_Parser() )->set_platform_settings( $settings_response ); } /** * Initialize class properties */ private function initialize_properties(): void { $this->page_header_steps = array( 1 => __( 'Connection', 'prodigy-commerce' ), 2 => __( 'Demo Content', 'prodigy-commerce' ), ); $this->page_header_content = array( 1 => __( 'Data Synchronization', 'prodigy-commerce' ), 2 => __( 'Prodigy Demo Content', 'prodigy-commerce' ), ); } /** * * @return bool */ private function is_wizard_page_request(): bool { $page = Prodigy_Parameters::get_parameter( $_GET, self::PARAM_PAGE ); return is_string( $page ) && $page === self::WIZARD_PAGE_SLUG; } /** * * @throws \Exception */ private function handle_token_processing(): void { if ( ! $this->is_wizard_page_request() ) { return; } $is_post = isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST'; $new_token = $is_post ? Prodigy_Parameters::get_parameter( $_POST, self::PARAM_TOKEN ) : Prodigy_Parameters::get_parameter( $_GET, self::PARAM_TOKEN ); if ( empty( $new_token ) ) { return; } if ( ! $this->verify_nonce( $is_post ) ) { return; } if ( ! current_user_can( 'manage_options' ) ) { return; } if ( ! $is_post ) { $this->render_token_confirmation_form( $new_token ); exit; } $store_connector = new Prodigy_Connection( Prodigy_Request_Maker::get_instance() ); if ( $store_connector->is_store_already_connected( $new_token ) ) { $this->redirect_to_wizard(); exit; } if ( $store_connector->has_connected_store() ) { $store_connector->disconnect_store(); $this->clear_content_for_old_store(); $refresh_key = Prodigy_Cookies::get_cookie( Prodigy_User::REFRESH_COOKIE_NAME ); $this->user->remove_impersonation_user( $refresh_key ); $this->user->hosted_system_logout_process( $refresh_key ); $this->user->logout(); $this->user->delete_user_cookie(); $this->user->clear_external_checkout_cookie(); } $store_connector->connect_store( $new_token ); $this->clear_store_settings(); $this->redirect_to_wizard(); exit; } /** * Output minimal page with form that POSTs token back (user-initiated only). * this is a "confirm connection" intermediary page: GET with a token → display the form → the form is sent via POST → the wizard disconnects (if necessary) and then reconnects via POST. This is how we protect against uncontrolled disconnect calls. * * @param string $token Token from hosted system redirect. */ private function render_token_confirmation_form( string $token ): void { $action = admin_url( 'index.php?page=' . self::WIZARD_PAGE_SLUG ); $nonce = wp_create_nonce( self::WIZARD_NONCE_ACTION ); $title = __( 'Connecting your store…', 'prodigy-commerce' ); ?> <?php echo esc_html( $title ); ?>

is_wizard_page_request() ) { return; } if ( empty( $_GET[ self::PARAM_CLEAR ] ) ) { return; } $this->clear_data_before_connect(); } /** * Handle redirect to hosted system request */ private function handle_redirect_request(): void { if ( empty( $_GET[ self::PARAM_REDIRECT_HOSTED ] ) ) { return; } if ( ! $this->verify_nonce() ) { return; } $this->redirect_to_hosted_system(); } /** * Load stored data from WordPress options */ private function load_stored_data(): void { $this->is_connected = get_option( self::OPTION_STORE_KEY ); $this->saved_step = get_option( self::OPTION_STEP_WIZARD, self::DEFAULT_STEP ); $this->domain_hosted_system = get_option( self::OPTION_DOMAIN_HOSTED_SYSTEM, __( 'Store name domain hosted', 'prodigy-commerce' ) ); $this->url_domain_hosted_system = get_option( self::OPTION_URL_DOMAIN_HOSTED_SYSTEM, __( 'Store url domain hosted', 'prodigy-commerce' ) ); $this->is_created_products = $this->is_created_products(); $this->is_created_categories = $this->is_created_categories(); $this->set_step(); } /** * Setup WordPress hooks */ private function setup_hooks(): void { add_action( 'admin_menu', array( $this, 'admin_menus' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'wizard_enqueue_scripts' ) ); add_action( 'admin_init', array( $this, 'setup_wizard' ) ); add_action( 'admin_init', array( $this, 'get_terms_prodigy' ) ); } /** * Verify nonce for security * * @param bool $from_post Whether token came from POST (use POST for nonce); otherwise GET. * @return bool */ private function verify_nonce( bool $from_post = false ): bool { $nonce = $this->get_nonce_from_request( $from_post ); return ! empty( $nonce ) && wp_verify_nonce( sanitize_key( $nonce ), self::WIZARD_NONCE_ACTION ); } /** * Get nonce from request parameters (GET or POST depending on token source). * * @param bool $from_post Whether to read nonce from POST; otherwise from GET. * @return string|null */ private function get_nonce_from_request( bool $from_post = false ): ?string { $source = $from_post ? $_POST : $_GET; if ( ! is_array( $source ) ) { return null; } // Support both _wpnonce and amp;_wpnonce (HTML-escaped query in GET). $nonce = Prodigy_Parameters::get_parameter( $source, self::PARAM_NONCE ); if ( $nonce === null && isset( $source[ self::PARAM_TOKEN ] ) ) { $nonce = Prodigy_Parameters::get_parameter( $source, 'amp;_wpnonce' ); } return $nonce; } /** * Clear content for old store */ public function clear_content_for_old_store(): void { Prodigy_Uninstall_Plugin::delete_products(); Prodigy_Uninstall_Plugin::delete_categories(); Prodigy_Uninstall_Plugin::delete_attributes_terms(); } /** * Reset demo settings for new store */ public function clear_store_settings(): void { delete_option( self::OPTION_DEMO_CONTENT ); } /** * Add admin menus/screens */ public function admin_menus(): void { add_dashboard_page( '', '', 'manage_options', self::WIZARD_PAGE_SLUG, '' ); } /** * Check if categories exist * * @return bool */ public function is_created_categories(): bool { $count_cats = wp_count_terms( Prodigy::get_prodigy_category_type() ); // Ignore default category if ( $count_cats === '1' ) { $count_cats = 0; } return (bool) $count_cats; } /** * Check if products exist * * @return bool */ public function is_created_products(): bool { $count_posts = wp_count_posts( Prodigy::get_prodigy_product_type() ); if ( ! $count_posts ) { return false; } foreach ( $count_posts as $status => $count ) { if ( in_array( $status, $this->ignore_statuses_product, true ) ) { continue; } if ( $count > 0 ) { return true; } } return false; } /** * Clear products, categories and attributes and redirect to dashboard * * @return string */ public function clear_data_before_connect(): string { if ( ! $this->is_connected ) { $this->clear_pg_content(); } $nonce = wp_create_nonce( self::WIZARD_NONCE_ACTION ); return admin_url( 'index.php?page=' . self::WIZARD_PAGE_SLUG . '&_wpnonce=' . $nonce ); } /** * Clear products, categories and attributes * * @return string|void */ public function clear_pg_content() { if ( $this->is_connected ) { $nonce = wp_create_nonce( self::WIZARD_NONCE_ACTION ); return admin_url( 'index.php?page=' . self::WIZARD_PAGE_SLUG . '&_wpnonce=' . $nonce ); } $this->clear_content_for_old_store(); } /** * Show the setup wizard */ public function setup_wizard(): void { if ( ! $this->verify_nonce() ) { return; } if ( empty( $_GET[ self::PARAM_PAGE ] ) || self::WIZARD_PAGE_SLUG !== $_GET[ self::PARAM_PAGE ] ) { return; } $step_template = $this->get_template_content_wizard(); $this->setup_wizard_header(); $this->$step_template(); $this->setup_wizard_footer(); exit; } /** * Set step for wizard */ public function set_step(): void { if ( ! $this->verify_nonce() ) { return; } $step = Prodigy_Parameters::get_parameter( $_GET, self::PARAM_STEP ); if ( $step && $this->is_connected && isset( $this->steps[ $step ] ) ) { $this->step = (int) $step; } } /** * Get URL for login to hosted system * * @return string */ public function get_url_login_hosted_system(): string { return wp_nonce_url( admin_url( 'index.php?page=' . self::WIZARD_PAGE_SLUG . '&' . self::PARAM_REDIRECT_HOSTED . '=1' ), self::WIZARD_NONCE_ACTION ); } /** * Redirect to hosted system */ public function redirect_to_hosted_system(): void { $sync_content = get_option( self::OPTION_INDICATOR_SYNC_CONTENT ); if ( $sync_content === self::SYNC_CONTENT_FALSE ) { $this->clear_pg_content(); } $this->set_token_for_hosted_system(); $nonce = wp_create_nonce( self::WIZARD_NONCE_ACTION ); $params = array( 'redirect_url' => wp_nonce_url( admin_url( 'index.php?page=' . self::WIZARD_PAGE_SLUG . '&_wpnonce=' . $nonce ), self::WIZARD_NONCE_ACTION ), ); $url = PRODIGY_PROTOCOL_DOMAIN . PRODIGY_MAIN_DOMAIN . '/plugin_connection_stores?' . http_build_query( $params ); wp_redirect( $url ); exit; } /** * Set token for hosted system * * @return bool */ public function set_token_for_hosted_system(): bool { return update_option( self::NAME_TOKEN_HOSTED_SYSTEM, substr( sha1( wp_rand() ), 0, self::LENGTH_CHARACTERS_TOKEN ), false ); } /** * Get template content for wizard step * * @return string */ public function get_template_content_wizard(): string { $step = $this->steps[ self::DEFAULT_STEP ]; if ( $this->is_connected && isset( $this->steps[ $this->step ] ) ) { $step = $this->steps[ $this->step ]; } return $step; } /** * Setup Wizard Header */ public function setup_wizard_header(): void { set_current_screen(); require_once PRODIGY_PLUGIN_PATH . 'admin/partials/wizard/prodigy-wizard-header-page.php'; } /** * Setup Wizard Footer */ public function setup_wizard_footer(): void { require_once PRODIGY_PLUGIN_PATH . 'admin/partials/wizard/prodigy-wizard-footer-page.php'; } /** * Register/enqueue scripts and styles for the Setup Wizard */ public function wizard_enqueue_scripts(): void { if ( ! did_action( 'wp_enqueue_media' ) ) { wp_enqueue_media(); } wp_enqueue_script( 'wizard', Prodigy::plugin_url() . '/assets/admin/wizard/js/wizard.js', array( 'jquery' ), PRODIGY_VERSION, true ); wp_enqueue_style( 'wizard', Prodigy::plugin_url() . '/assets/admin/wizard/css/wizard.css', array(), PRODIGY_VERSION ); } /** * Get Prodigy terms * * @return int[]|string|string[]|\WP_Error|\WP_Term[] */ public function get_terms_prodigy() { return get_terms( array( 'taxonomy' => Prodigy::get_prodigy_category_type(), 'hide_empty' => false, 'parent' => 0, ) ); } /** * STEP 1: Connect wizard content */ public function connect_wizard_content(): void { update_option( self::OPTION_STEP_WIZARD, 1 ); require_once PRODIGY_PLUGIN_PATH . 'admin/partials/wizard/prodigy-wizard-step1-page.php'; } /** * STEP 2: Install demo content */ public function install_demo_content(): void { update_option( self::OPTION_STEP_WIZARD, 2 ); update_option( self::OPTION_INSTALL_DEMO_CONTENT_WIZARD, 0 ); require_once PRODIGY_PLUGIN_PATH . 'admin/partials/wizard/prodigy-wizard-step2-page.php'; } }