hook = (string) add_submenu_page( '', // Hidden: reachable via admin.php?page=arwc-setup, absent from the menu. __( 'Welcome to AgenticTrack', 'agentictrack-agent-readiness-for-woocommerce' ), __( 'Welcome to AgenticTrack', 'agentictrack-agent-readiness-for-woocommerce' ), self::CAPABILITY, self::PAGE_SLUG, array( $this, 'render' ) ); } /** * Redirect to the wizard once, right after first activation. * * Gated by BOTH the one-shot activation transient AND the durable "done" * option, so it fires at most once and never for a merchant who has already * completed or skipped it. * * @return void */ public function maybe_redirect() { if ( ! get_transient( self::TRANSIENT_REDIRECT ) ) { return; } // One shot: clear the trigger no matter what happens next. delete_transient( self::TRANSIENT_REDIRECT ); // Never override a bulk/network activation or an AJAX/cron context. if ( wp_doing_ajax() || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) { return; } if ( isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- core-provided flag, read-only. return; } if ( ! current_user_can( self::CAPABILITY ) ) { return; } if ( $this->is_done() ) { return; } wp_safe_redirect( admin_url( 'admin.php?page=' . self::PAGE_SLUG ) ); exit; } /** * Handle "Skip setup": mark onboarding complete and return to the audit. * * @return void */ public function handle_skip() { if ( ! isset( $_GET['arwc_setup'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- presence check; nonce verified below before any change. return; } if ( 'skip' !== sanitize_key( wp_unslash( $_GET['arwc_setup'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- value compared to a literal; nonce verified below before any change. return; } if ( ! current_user_can( self::CAPABILITY ) ) { return; } check_admin_referer( 'arwc_setup_skip' ); $this->mark_done(); wp_safe_redirect( admin_url( 'admin.php?page=' . ARWC_Admin::PAGE_SLUG ) ); exit; } /** * Enqueue the wizard's styles/scripts on its own screen only. * * @param string $hook_suffix Current admin page hook. * @return void */ public function enqueue_assets( $hook_suffix ) { if ( '' === $this->hook || $hook_suffix !== $this->hook ) { return; } wp_enqueue_style( 'arwc-fonts', 'https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,500;12..96,600;12..96,700;12..96,800&family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap', array(), ARWC_VERSION ); // The shared brand stylesheet supplies the design tokens the wizard reuses. wp_enqueue_style( 'arwc-admin', ARWC_PLUGIN_URL . 'admin/css/admin.css', array( 'arwc-fonts' ), ARWC_VERSION ); wp_enqueue_script( 'arwc-onboarding', ARWC_PLUGIN_URL . 'admin/js/onboarding.js', array( 'jquery' ), ARWC_VERSION, true ); $arwc_plugin = ARWC_Plugin::instance(); wp_localize_script( 'arwc-onboarding', 'ARWC_SETUP', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( ARWC_Admin::NONCE_ACTION ), 'batchSize' => $arwc_plugin->scanner()->get_batch_size(), 'doneUrl' => admin_url( 'admin.php?page=' . self::PAGE_SLUG . '&step=results' ), 'i18n' => array( 'scanning' => __( 'Scanning your catalog…', 'agentictrack-agent-readiness-for-woocommerce' ), 'error' => __( 'Something went wrong while scanning. You can retry, or skip and run the audit later.', 'agentictrack-agent-readiness-for-woocommerce' ), /* translators: 1: processed count, 2: total count. */ 'progress' => __( 'Scored %1$s of %2$s products…', 'agentictrack-agent-readiness-for-woocommerce' ), ), ) ); } /** * Render the wizard (delegates to the step view). * * @return void */ public function render() { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', 'agentictrack-agent-readiness-for-woocommerce' ) ); } $arwc_plugin = ARWC_Plugin::instance(); $arwc_scanner = $arwc_plugin->scanner(); $arwc_license = $arwc_plugin->license(); // Which step to show. Scanning is client-driven; the results step is // server-rendered from the freshly stored scan summary. $arwc_step = isset( $_GET['step'] ) ? sanitize_key( wp_unslash( $_GET['step'] ) ) : 'welcome'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only step router, no state change. if ( ! in_array( $arwc_step, array( 'welcome', 'scan', 'results' ), true ) ) { $arwc_step = 'welcome'; } // Reaching the results step means the merchant has seen value — this is the // natural "completed" point, so the wizard never auto-launches again. if ( 'results' === $arwc_step ) { $this->mark_done(); } $arwc_total = $arwc_scanner->get_total_products(); $arwc_summary = $arwc_scanner->get_summary(); $arwc_skip_url = wp_nonce_url( add_query_arg( array( 'page' => self::PAGE_SLUG, 'arwc_setup' => 'skip', ), admin_url( 'admin.php' ) ), 'arwc_setup_skip' ); require ARWC_PLUGIN_DIR . 'admin/views/setup.php'; } /** * Whether onboarding has been completed or skipped. * * @return bool */ public function is_done() { return '' !== (string) get_option( self::OPTION_DONE, '' ); } /** * Mark onboarding complete (idempotent). * * @return void */ protected function mark_done() { if ( ! $this->is_done() ) { update_option( self::OPTION_DONE, '1' ); } } }