'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' );
?>
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';
}
}