Settings and * orchestrates the full migration wizard. Handles all POST actions for: * - Table rename / restore (preparation) * - Wizard step navigation (steps 1–6) * - Wizard reset * - CSV log export * * Network Admin only. Uses exclusively: * network_admin_menu, network_admin_init. * * @package Robotstxt_Mlp2_Mlp5 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Class Robotstxt_Mlp2_Mlp5_Admin_Page */ class Robotstxt_Mlp2_Mlp5_Admin_Page { /** * Menu slug for the plugin settings page. * * @var string */ const MENU_SLUG = 'robotstxt-mlp2-mlp5'; /** * Required capability to access the plugin page. * * The manage_network capability is the standard for Network Admin settings. * * @var string */ const CAPABILITY = 'manage_network'; /** * Register the Network Admin page and all POST/GET handlers. */ public static function init(): void { add_action( 'network_admin_menu', array( static::class, 'register_menu' ) ); add_action( 'admin_post_mlp_migrator_rename_tables', array( static::class, 'handle_rename_action' ) ); add_action( 'admin_post_mlp_migrator_restore_tables', array( static::class, 'handle_restore_action' ) ); add_action( 'admin_post_mlp_migrator_step', array( static::class, 'handle_step_actions' ) ); add_action( 'admin_post_mlp_migrator_reset_wizard', array( static::class, 'handle_reset_action' ) ); add_action( 'admin_post_mlp_migrator_export_log', array( 'Robotstxt_Mlp2_Mlp5_Step_Verification', 'maybe_export_csv' ) ); } /** * Add the settings page under Network Admin > Settings. */ public static function register_menu(): void { add_submenu_page( 'settings.php', __( 'MLP2 to MLP5 Migrator', 'robotstxt-mlp2-mlp5' ), __( 'MLP Migrator', 'robotstxt-mlp2-mlp5' ), self::CAPABILITY, self::MENU_SLUG, array( static::class, 'render' ) ); } /** * Return the URL to this plugin's Network Admin page. * * @param array $extra Optional extra query args. * @return string Absolute URL. */ public static function page_url( array $extra = array() ): string { $args = array_merge( array( 'page' => self::MENU_SLUG ), $extra ); return network_admin_url( 'settings.php?' . http_build_query( $args ) ); } // ------------------------------------------------------------------------- // POST handlers (preparation) // ------------------------------------------------------------------------- /** * Handle the "Rename MLP2 tables" POST action. */ public static function handle_rename_action(): void { check_admin_referer( 'mlp_migrator_rename', 'mlp_migrator_nonce' ); if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to perform this action.', 'robotstxt-mlp2-mlp5' ) ); } $result = Robotstxt_Mlp2_Mlp5_Table_Preparer::rename_to_backup(); $args = array( 'renamed' => count( $result['renamed'] ) ); if ( ! empty( $result['errors'] ) ) { $args['rename_errors'] = count( $result['errors'] ); } wp_safe_redirect( self::page_url( $args ) ); exit; } /** * Handle the "Restore original table names" POST action. */ public static function handle_restore_action(): void { check_admin_referer( 'mlp_migrator_restore', 'mlp_migrator_nonce' ); if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to perform this action.', 'robotstxt-mlp2-mlp5' ) ); } $result = Robotstxt_Mlp2_Mlp5_Table_Preparer::restore_from_backup(); $args = array( 'restored' => count( $result['restored'] ) ); if ( ! empty( $result['errors'] ) ) { $args['restore_errors'] = count( $result['errors'] ); } wp_safe_redirect( self::page_url( $args ) ); exit; } // ------------------------------------------------------------------------- // POST handlers (wizard steps) // ------------------------------------------------------------------------- /** * Route all wizard step POST actions. */ public static function handle_step_actions(): void { // phpcs:ignore WordPress.Security.NonceVerification.Missing if ( ! isset( $_POST['mlp_migrator_step_action'] ) ) { return; } if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to perform this action.', 'robotstxt-mlp2-mlp5' ) ); } // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $action = sanitize_key( wp_unslash( $_POST['mlp_migrator_step_action'] ) ); switch ( $action ) { case '1': self::handle_step_1_next(); break; case '2_run': self::handle_step_2_run(); break; case '2': self::handle_step_next( 2, 3, 'mlp_migrator_step_2' ); break; case '3_run': self::handle_step_3_run(); break; case '3': self::handle_step_next( 3, 4, 'mlp_migrator_step_3' ); break; case '4': self::handle_step_next( 4, 5, 'mlp_migrator_step_4' ); break; case '5': self::handle_step_next( 5, 6, 'mlp_migrator_step_5' ); break; case '6_cleanup': self::handle_step_6_cleanup(); break; } } /** * Handle "Next" from Step 1 (Preflight). */ private static function handle_step_1_next(): void { check_admin_referer( 'mlp_migrator_step_1', 'mlp_migrator_nonce' ); $checks = Robotstxt_Mlp2_Mlp5_Step_Preflight::run(); if ( $checks['can_proceed'] ) { Robotstxt_Mlp2_Mlp5_Wizard_State::mark_step_complete( 1 ); Robotstxt_Mlp2_Mlp5_Wizard_State::set_step( 2 ); } wp_safe_redirect( self::page_url() ); exit; } /** * Handle "Run Analysis" from Step 2. */ private static function handle_step_2_run(): void { check_admin_referer( 'mlp_migrator_analysis', 'mlp_migrator_nonce' ); $result = Robotstxt_Mlp2_Mlp5_Step_Analysis::run(); Robotstxt_Mlp2_Mlp5_Wizard_State::set_step_data( 2, $result ); wp_safe_redirect( self::page_url() ); exit; } /** * Handle "Start" from Step 3 (Site Relations). */ private static function handle_step_3_run(): void { check_admin_referer( 'mlp_migrator_site_relations', 'mlp_migrator_nonce' ); $result = Robotstxt_Mlp2_Mlp5_Step_Site_Relations::run(); Robotstxt_Mlp2_Mlp5_Wizard_State::set_step_data( 3, $result ); wp_safe_redirect( self::page_url() ); exit; } /** * Handle "Next" for a standard step (advance to next, mark complete). * * @param int $current_step Current step number. * @param int $next_step Next step number. * @param string $nonce_action Nonce action string. */ private static function handle_step_next( int $current_step, int $next_step, string $nonce_action ): void { check_admin_referer( $nonce_action, 'mlp_migrator_nonce' ); Robotstxt_Mlp2_Mlp5_Wizard_State::mark_step_complete( $current_step ); Robotstxt_Mlp2_Mlp5_Wizard_State::set_step( $next_step ); wp_safe_redirect( self::page_url() ); exit; } /** * Handle the cleanup POST from Step 6. */ private static function handle_step_6_cleanup(): void { check_admin_referer( 'mlp_migrator_cleanup', 'mlp_migrator_nonce' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $confirm = isset( $_POST['cleanup_confirm'] ) ? wp_unslash( $_POST['cleanup_confirm'] ) : ''; if ( 'DELETE' !== $confirm ) { wp_safe_redirect( self::page_url( array( 'cleanup_error' => '1' ) ) ); exit; } $deactivate = ! empty( $_POST['cleanup_deactivate'] ); $drop_tables = ! empty( $_POST['cleanup_drop_tables'] ) && $deactivate; $sitemeta = ! empty( $_POST['cleanup_sitemeta'] ); if ( $deactivate ) { Robotstxt_Mlp2_Mlp5_Step_Cleanup::deactivate_mlp2(); } if ( $drop_tables ) { Robotstxt_Mlp2_Mlp5_Step_Cleanup::drop_backup_tables(); } if ( $sitemeta ) { Robotstxt_Mlp2_Mlp5_Step_Cleanup::remove_mlp2_sitemeta(); } Robotstxt_Mlp2_Mlp5_Wizard_State::mark_step_complete( 6 ); wp_safe_redirect( self::page_url( array( 'cleanup_done' => '1' ) ) ); exit; } /** * Handle the wizard reset POST action. */ public static function handle_reset_action(): void { check_admin_referer( 'mlp_migrator_reset', 'mlp_migrator_nonce' ); if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to perform this action.', 'robotstxt-mlp2-mlp5' ) ); } Robotstxt_Mlp2_Mlp5_Wizard_State::reset(); Robotstxt_Mlp2_Mlp5_Logger::clear(); wp_safe_redirect( self::page_url( array( 'reset' => '1' ) ) ); exit; } // ------------------------------------------------------------------------- // Rendering // ------------------------------------------------------------------------- /** * Render the full page. */ public static function render(): void { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', 'robotstxt-mlp2-mlp5' ) ); } $has_original = Robotstxt_Mlp2_Mlp5_Table_Preparer::has_original_tables(); $has_backup = Robotstxt_Mlp2_Mlp5_Table_Preparer::has_backup_tables(); $mlp5 = new Robotstxt_Mlp2_Mlp5_Detector_Mlp5(); $mlp5_active = $mlp5->is_active(); // Once MLP5 is active its own tables match the mlp_* pattern. // They are not leftover MLP2 tables, so suppress the "must be renamed" signal. if ( $mlp5_active ) { $has_original = false; } // Migration is complete when wizard step 6 is marked done. $wizard_done = Robotstxt_Mlp2_Mlp5_Wizard_State::is_step_complete( Robotstxt_Mlp2_Mlp5_Wizard_State::TOTAL_STEPS ); $ready = ( $has_backup && $mlp5_active ) || ( $wizard_done && $mlp5_active ); ?>
%s
', esc_html( sprintf( _n( '%d table renamed successfully.', '%d tables renamed successfully.', $count, 'robotstxt-mlp2-mlp5' ), $count ) ) ); // phpcs:ignore WordPress.WP.I18n.MismatchedPlaceholders } if ( isset( $_GET['rename_errors'] ) ) { $count = (int) $_GET['rename_errors']; /* translators: %d: number of tables that could not be renamed */ printf( '%s
%s
%s
%s
%s
%s
| ● ✓ | |
| ✓ — | |
| ✓ get_version() ) ); ?> — |