'environment', 2 => 'source', 3 => 'coverage', 4 => 'acknowledge', 5 => 'complete', ); /** * Register handlers (menu owned by AdminApp tabs). * * @return void */ public function register() { add_action( 'admin_init', array( $this, 'handle_post' ) ); add_action( 'admin_notices', array( $this, 'prompt_incomplete_wizard' ) ); } /** * Persist wizard step submissions. * * @return void */ public function handle_post() { if ( ! isset( $_POST['aksmb_wizard_nonce'] ) ) { return; } if ( ! Capabilities::current_user_can_manage() ) { return; } if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['aksmb_wizard_nonce'] ) ), 'aksmb_cogs_wizard' ) ) { return; } $step = isset( $_POST['wizard_step'] ) ? absint( $_POST['wizard_step'] ) : 1; $action = isset( $_POST['wizard_action'] ) ? sanitize_key( wp_unslash( $_POST['wizard_action'] ) ) : 'next'; $state = $this->get_state(); if ( 'back' === $action ) { $state['step'] = max( 1, $step - 1 ); $this->save_state( $state ); $this->redirect_step( (int) $state['step'] ); } if ( 2 === $step ) { $cost = get_option( Constants::OPTION_COST_SETTINGS, array() ); if ( ! is_array( $cost ) ) { $cost = array(); } $prev_cost = $cost; $source = isset( $_POST['canonical_cost_source'] ) ? sanitize_key( wp_unslash( $_POST['canonical_cost_source'] ) ) : ''; if ( ! ( new CanonicalCostSource() )->is_valid( $source ) ) { $env_detect = ( new CogsEnvironment() )->detect(); $source = ! empty( $env_detect['native_cogs_enabled'] ) ? CanonicalCostSource::WOOCOMMERCE : CanonicalCostSource::MARGINBEACON; } $cost['canonical_cost_source'] = $source; $cost['prefer_native_cogs'] = ( CanonicalCostSource::WOOCOMMERCE === $source ); $cost['fallback_enabled'] = ! empty( $_POST['fallback_enabled'] ); $percent = isset( $_POST['global_assumed_cost_percent'] ) ? sanitize_text_field( wp_unslash( $_POST['global_assumed_cost_percent'] ) ) : ''; if ( '' !== $percent ) { if ( ! preg_match( '/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/', trim( $percent ) ) ) { add_settings_error( 'aksmb_wizard', 'percent', __( 'Assumed cost percentage must be a number between 0 and 100.', 'marginbeacon' ), 'error' ); $state['step'] = 2; $this->save_state( $state ); return; } $percent_str = trim( (string) $percent ); $in_range = bccomp( $percent_str, '0', 6 ) >= 0 && bccomp( $percent_str, '100', 6 ) <= 0; if ( ! $in_range ) { add_settings_error( 'aksmb_wizard', 'percent', __( 'Assumed cost percentage must be between 0 and 100.', 'marginbeacon' ), 'error' ); $state['step'] = 2; $this->save_state( $state ); return; } $cost['global_assumed_cost_percent'] = $percent; } else { $cost['global_assumed_cost_percent'] = null; } $cost['custom_meta_key'] = isset( $_POST['custom_meta_key'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_meta_key'] ) ) : ''; $cost_changed = $prev_cost !== $cost; $updated = update_option( Constants::OPTION_COST_SETTINGS, $cost, true ); ( new CanonicalCostSource() )->set( $source ); if ( $cost_changed && $updated ) { ( new \Kayvandra\MarginBeacon\Application\Freshness\FreshnessService() )->on_global_threshold_change(); } $state['source_configured'] = true; } if ( 4 === $step ) { if ( empty( $_POST['ack_incomplete'] ) || empty( $_POST['ack_tax_exclusive'] ) || empty( $_POST['ack_scope'] ) ) { add_settings_error( 'aksmb_wizard', 'ack', __( 'You must acknowledge all required statements before continuing. Cost completion can be incomplete, but limitation acknowledgement cannot be skipped.', 'marginbeacon' ), 'error' ); $state['step'] = 4; $this->save_state( $state ); return; } $tax_basis = isset( $_POST['cogs_tax_basis'] ) ? sanitize_key( wp_unslash( $_POST['cogs_tax_basis'] ) ) : 'exclusive'; $cost = get_option( Constants::OPTION_COST_SETTINGS, array() ); if ( ! is_array( $cost ) ) { $cost = array(); } $cost['cogs_tax_basis'] = ( 'inclusive' === $tax_basis ) ? 'inclusive' : 'exclusive'; if ( 'inclusive' === $cost['cogs_tax_basis'] ) { $cost['needs_cogs_correction'] = true; } else { $cost['needs_cogs_correction'] = false; } update_option( Constants::OPTION_COST_SETTINGS, $cost, true ); $settings = get_option( Constants::OPTION_SETTINGS, array() ); if ( ! is_array( $settings ) ) { $settings = array(); } $settings['disclaimer_accepted'] = true; update_option( Constants::OPTION_SETTINGS, $settings, true ); $state['acknowledged'] = true; $state['ack_incomplete'] = true; $state['ack_tax_exclusive'] = ( 'exclusive' === $cost['cogs_tax_basis'] ); $state['completed'] = true; $state['completed_at_gmt'] = gmdate( 'Y-m-d H:i:s' ); } if ( 'next' === $action ) { $state['step'] = min( 5, $step + 1 ); } $this->save_state( $state ); $this->redirect_step( (int) $state['step'] ); } /** * Soft prompt when wizard never completed. * * @return void */ public function prompt_incomplete_wizard() { if ( ! Capabilities::current_user_can_manage() ) { return; } $state = $this->get_state(); if ( ! empty( $state['completed'] ) ) { return; } $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || false === strpos( (string) $screen->id, 'marginbeacon' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['tab'] ) && Tabs::COST_SETUP === sanitize_key( wp_unslash( $_GET['tab'] ) ) ) { return; } printf( '
%s %s
|
value="" /> |
|
%
%