array( 'menu_dashboard', Capabilities::MANAGE_SETTINGS, array( self::class, 'render_dashboard' ) ), self::SETUP_SLUG => array( 'menu_setup', Capabilities::MANAGE_SETTINGS, array( SetupWizardController::class, 'render' ) ), self::TOOLS_SLUG => array( 'menu_tools', Capabilities::RUN_DIAGNOSTICS, array( self::class, 'render_tools' ) ), ); foreach ( $pages as $slug => $page ) { if ( $visible !== $slug && 'admin.php' !== $pagenow ) { continue; } $title = SetupWizardController::text( $language, $page[0] ); $label = $visible === $slug ? __( 'Commerce Assistant', 'leanmerce-commerce-assistant' ) : $title; // Empty parent registers a hidden WordPress page with its real admin_page suffix. $hook = add_submenu_page( $visible === $slug ? SharedAdminMenu::PARENT_SLUG : '', $title, $label, $page[1], $slug, $page[2] ); AdminScreen::remember( $slug, $hook ); } SharedAdminMenu::remove_own_duplicate(); } public static function active_parent( $parent_file ) { return AdminScreen::is_leanmerce_page() ? SharedAdminMenu::PARENT_SLUG : $parent_file; } public static function page_title( string $admin_title ): string { if ( ! AdminScreen::is_leanmerce_page() ) { return $admin_title; } $key = AdminScreen::is_leanmerce_page( self::SETUP_SLUG ) ? 'menu_setup' : ( AdminScreen::is_leanmerce_page( self::TOOLS_SLUG ) ? 'menu_tools' : 'menu_dashboard' ); return SetupWizardController::text( SetupWizardController::current_language(), $key ) . ' ‹ ' . get_bloginfo( 'name' ) . ' — WordPress'; } public static function active_submenu( $submenu_file ) { if ( AdminScreen::is_leanmerce_page() ) { return current_user_can( Capabilities::MANAGE_SETTINGS ) ? self::MENU_SLUG : self::TOOLS_SLUG; } return $submenu_file; } public static function render_navigation( string $active, string $language ): void { $pages = array( self::MENU_SLUG => array( 'menu_dashboard', Capabilities::MANAGE_SETTINGS ), self::SETUP_SLUG => array( 'menu_setup', Capabilities::MANAGE_SETTINGS ), self::TOOLS_SLUG => array( 'menu_tools', Capabilities::RUN_DIAGNOSTICS ), ); echo ''; } public static function handle_save_setup_step(): void { SetupWizardController::handle_save(); } public static function handle_run_audit(): void { self::require_capability( Capabilities::RUN_DIAGNOSTICS ); RequestGuard::require_post(); if ( ! wp_verify_nonce( RequestGuard::nonce( 'lmcc_run_audit' ), 'lmcc_run_audit' ) ) { RequestGuard::reject( 403 ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Type/byte boundary before scalar unslash and sanitize_key below; malformed input must not be recursively processed. if ( isset( $_POST['redirect_page'] ) && ( ! is_string( $_POST['redirect_page'] ) || strlen( $_POST['redirect_page'] ) > 64 ) ) { RequestGuard::reject( 400 ); } $redirect_page = isset( $_POST['redirect_page'] ) ? sanitize_key( wp_unslash( $_POST['redirect_page'] ) ) : self::MENU_SLUG; if ( ! in_array( $redirect_page, array( self::MENU_SLUG, self::SETUP_SLUG, self::TOOLS_SLUG ), true ) ) { $redirect_page = self::MENU_SLUG; } AuditRuntime::run_audit( true ); $args = array( 'page' => $redirect_page, 'lmcc_audited' => 1, ); if ( self::SETUP_SLUG === $redirect_page ) { $args['step'] = 7; } wp_safe_redirect( add_query_arg( $args, admin_url( 'admin.php' ) ) ); exit; } public static function handle_export_diagnostics(): void { self::require_capability( Capabilities::RUN_DIAGNOSTICS ); if ( ! wp_verify_nonce( RequestGuard::nonce( 'lmcc_export_diagnostics' ), 'lmcc_export_diagnostics' ) ) { RequestGuard::reject( 403 ); } $data = AuditRuntime::diagnostics(); if ( function_exists( 'nocache_headers' ) ) { nocache_headers(); } header( 'Content-Type: application/json; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="leanmerce-commerce-compliance-diagnostics-' . gmdate( 'Ymd-His' ) . '.json"' ); echo wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- JSON download response. exit; } public static function render_dashboard(): void { self::require_capability( Capabilities::MANAGE_SETTINGS ); $state = AuditRuntime::setup()->get_state(); $report = AuditRuntime::last_report(); $facts = AuditRuntime::environment(); $language = SetupWizardController::current_language(); ?>


$settings @param array $facts */ private static function render_step( int $step, array $settings, array $facts ): void { switch ( $step ) { case 1: self::step_languages( $settings, $facts ); break; case 2: self::step_business( $settings ); break; case 3: self::step_sales( $settings ); break; case 4: self::step_terms( $settings, $facts ); break; case 5: self::step_delivery( $settings, $facts ); break; case 6: self::step_documents( $settings, $facts ); break; } } /** @param array $settings @param array $facts */ private static function step_languages( array $settings, array $facts ): void { $languages = (array) $settings['languages']; ?>

$label ) : ?>

$settings */ private static function step_business( array $settings ): void { $m = (array) $settings['merchant']; ?>

$settings */ private static function step_sales( array $settings ): void { $s = (array) $settings['sales']; $m = (array) $settings['merchant']; ?>

$settings @param array $facts */ private static function step_terms( array $settings, array $facts ): void { $t = (array) $settings['terms']; ?>

$settings @param array $facts */ private static function step_delivery( array $settings, array $facts ): void { $d = (array) $settings['delivery']; $s = (array) $settings['sales']; ?>

$settings @param array $facts */ private static function step_documents( array $settings, array $facts ): void { $d = (array) $settings['documents']; ?>

.

counts(); ?>

resolution_mode(), implode( ', ', $report->pack_ids() ) ) ); ?>

has_blockers() && 0 === $counts['warning'] ) : ?>

has_blockers() ) : ?>

results() as $result ) : ?>

' . esc_html( strtoupper( $result->get_status() ) ) . '' . esc_html( $result->get_code() ) . '' . esc_html( $result->get_message() ) . '' . esc_html( $result->get_remediation() ) . ''; } private static function render_step_navigation( int $current ): void { $labels = array( 1 => __( 'Language and store', 'leanmerce-commerce-assistant' ), 2 => __( 'Business details', 'leanmerce-commerce-assistant' ), 3 => __( 'Sales/legal', 'leanmerce-commerce-assistant' ), 4 => __( 'Terms/contract', 'leanmerce-commerce-assistant' ), 5 => __( 'Delivery/emails', 'leanmerce-commerce-assistant' ), 6 => __( 'Documents', 'leanmerce-commerce-assistant' ), 7 => __( 'Audit', 'leanmerce-commerce-assistant' ), ); echo '

'; foreach ( $labels as $step => $label ) { $link = '' . esc_html( $step . '. ' . $label ) . ''; echo ( $step === $current ? '' . $link . '' : $link ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- link components escaped above. if ( $step < 7 ) { echo '  |  '; } } echo '

'; } private static function render_notices(): void { $saved = isset( $_GET['lmcc_saved'] ) ? absint( wp_unslash( $_GET['lmcc_saved'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag; no state change. $audited = isset( $_GET['lmcc_audited'] ) ? absint( wp_unslash( $_GET['lmcc_audited'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag; no state change. $completed = isset( $_GET['lmcc_completed'] ) ? absint( wp_unslash( $_GET['lmcc_completed'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display flag; no state change. $language = SetupWizardController::current_language(); if ( $saved ) { echo '

' . esc_html( SetupWizardController::text( $language, 'step_saved' ) ) . '

'; } if ( $audited ) { echo '

' . esc_html( SetupWizardController::text( $language, 'audit_saved' ) ) . '

'; } if ( $completed ) { echo '

' . esc_html( SetupWizardController::text( $language, 'setup_completed' ) ) . '

'; } } private static function requested_step( int $fallback ): int { $requested = isset( $_GET['step'] ) ? absint( wp_unslash( $_GET['step'] ) ) : $fallback; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only navigation value; no state change. return max( 1, min( SetupService::TOTAL_STEPS, $requested ) ); } private static function setup_url( int $step ): string { return add_query_arg( array( 'page' => self::SETUP_SLUG, 'step' => $step, ), admin_url( 'admin.php' ) ); } private static function require_capability( string $capability ): void { if ( ! current_user_can( $capability ) ) { $language = SetupWizardController::current_language(); wp_die( esc_html( SetupWizardController::text( $language, 'error_permission' ) ), '', array( 'response' => 403 ) ); } } private static function text_field( string $name, string $label, $value, string $type = 'text' ): void { printf( '

', esc_html( $label ), esc_attr( $type ), esc_attr( $name ), esc_attr( (string) $value ) ); } private static function checkbox( string $name, string $label, $value ): void { printf( '

', esc_attr( $name ), checked( ! empty( $value ), true, false ), esc_html( $label ) ); } private static function language_select( string $name, string $label, string $current ): void { echo '

'; } private static function email_select( string $name, string $label, string $current, array $emails ): void { echo '

'; } private static function count_enabled( array $items ): int { $count = 0; foreach ( $items as $item ) { if ( is_array( $item ) && ! empty( $item['enabled'] ) ) { ++$count; } } return $count; } private function __construct() {} }