This commit is contained in:
Javier Casares 2026-03-02 11:22:59 +00:00
commit 5cdf7f48e5
29 changed files with 6526 additions and 0 deletions

View file

@ -0,0 +1,583 @@
<?php
/**
* Network Admin page.
*
* Registers the plugin settings page under Network Admin > 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 );
?>
<div class="wrap">
<h1><?php esc_html_e( 'MLP2 to MLP5 Migrator', 'robotstxt-mlp2-mlp5' ); ?></h1>
<?php self::render_feedback_notices(); ?>
<?php if ( $ready ) : ?>
<?php self::render_wizard( $has_original, $has_backup, $mlp5_active, $mlp5 ); ?>
<?php else : ?>
<?php self::render_state_banner( $has_original, $has_backup, $mlp5_active ); ?>
<?php self::render_status_card( $has_original, $has_backup, $mlp5_active, $mlp5 ); ?>
<?php self::render_action_forms( $has_original, $has_backup, $mlp5_active ); ?>
<?php endif; ?>
</div>
<?php
}
/**
* Render the full wizard when the system is ready (State C).
*
* @param bool $has_original Whether original MLP2 tables exist.
* @param bool $has_backup Whether backup MLP2 tables exist.
* @param bool $mlp5_active Whether MLP5 is network-active.
* @param Robotstxt_Mlp2_Mlp5_Detector_Mlp5 $mlp5 MLP5 detector instance.
*/
private static function render_wizard(
bool $has_original,
bool $has_backup,
bool $mlp5_active,
Robotstxt_Mlp2_Mlp5_Detector_Mlp5 $mlp5
): void {
$current_step = Robotstxt_Mlp2_Mlp5_Wizard_State::get_current_step();
$step_labels = array(
1 => __( 'Preflight', 'robotstxt-mlp2-mlp5' ),
2 => __( 'Analysis', 'robotstxt-mlp2-mlp5' ),
3 => __( 'Site Relations', 'robotstxt-mlp2-mlp5' ),
4 => __( 'Content Relations', 'robotstxt-mlp2-mlp5' ),
5 => __( 'Verification', 'robotstxt-mlp2-mlp5' ),
6 => __( 'Cleanup', 'robotstxt-mlp2-mlp5' ),
);
?>
<div style="background:#fff;border:1px solid #c3c4c7;padding:1.5em;max-width:780px;">
<?php /* Step indicator */ ?>
<ol style="display:flex;list-style:none;margin:0 0 1.5em;padding:0;gap:0;">
<?php for ( $i = 1; $i <= Robotstxt_Mlp2_Mlp5_Wizard_State::TOTAL_STEPS; $i++ ) : ?>
<?php
$is_done = Robotstxt_Mlp2_Mlp5_Wizard_State::is_step_complete( $i );
$is_current = ( $i === $current_step );
if ( $is_current ) {
$bg = '#2271b1';
$color = '#fff';
} elseif ( $is_done ) {
$bg = '#00a32a';
$color = '#fff';
} else {
$bg = '#f0f0f1';
$color = '#50575e';
}
?>
<li style="flex:1;text-align:center;padding:.5em .25em;background:<?php echo esc_attr( $bg ); ?>;color:<?php echo esc_attr( $color ); ?>;font-size:.85em;font-weight:<?php echo $is_current ? '700' : '400'; ?>;border-right:2px solid #fff;">
<?php echo esc_html( $i . '. ' . $step_labels[ $i ] ); ?>
<?php echo $is_done ? ' ✓' : ''; ?>
</li>
<?php endfor; ?>
</ol>
<?php /* Step content */ ?>
<div style="min-height:200px;">
<?php
switch ( $current_step ) {
case 1:
Robotstxt_Mlp2_Mlp5_Step_Preflight::render();
break;
case 2:
Robotstxt_Mlp2_Mlp5_Step_Analysis::render();
break;
case 3:
Robotstxt_Mlp2_Mlp5_Step_Site_Relations::render();
break;
case 4:
Robotstxt_Mlp2_Mlp5_Step_Content_Relations::render();
break;
case 5:
Robotstxt_Mlp2_Mlp5_Step_Verification::render();
break;
case 6:
Robotstxt_Mlp2_Mlp5_Step_Cleanup::render();
break;
}
?>
</div>
<?php /* Reset disclosure */ ?>
<details style="margin-top:2em;border-top:1px solid #e2e2e2;padding-top:1em;">
<summary style="cursor:pointer;color:#646970;"><?php esc_html_e( 'Reset migration (start over)', 'robotstxt-mlp2-mlp5' ); ?></summary>
<div style="margin-top:.75em;padding:.75em;border:1px solid #dcdcde;background:#fff8f8;">
<p><?php esc_html_e( 'This will reset the wizard to Step 1 and clear the migration log. It does not modify MLP5 data.', 'robotstxt-mlp2-mlp5' ); ?></p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'mlp_migrator_reset', 'mlp_migrator_nonce' ); ?>
<input type="hidden" name="action" value="mlp_migrator_reset_wizard" />
<button type="submit" class="button button-secondary"><?php esc_html_e( 'Reset wizard', 'robotstxt-mlp2-mlp5' ); ?></button>
</form>
</div>
</details>
</div>
<?php /* Status sidebar (compact, always visible) */ ?>
<div style="margin-top:1em;max-width:780px;">
<details>
<summary style="cursor:pointer;color:#646970;"><?php esc_html_e( 'System status', 'robotstxt-mlp2-mlp5' ); ?></summary>
<div style="margin-top:.5em;">
<?php self::render_status_card( $has_original, $has_backup, $mlp5_active, $mlp5 ); ?>
</div>
</details>
</div>
<?php
}
// -------------------------------------------------------------------------
// Shared UI helpers (used in both preparation and wizard views)
// -------------------------------------------------------------------------
/**
* Show feedback notices after redirects.
*/
private static function render_feedback_notices(): void {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['renamed'] ) ) {
$count = (int) $_GET['renamed'];
/* translators: %d: number of renamed tables */
printf( '<div class="notice notice-success is-dismissible"><p>%s</p></div>', 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( '<div class="notice notice-error"><p>%s</p></div>', esc_html( sprintf( _n( '%d table could not be renamed. Check database permissions.', '%d tables could not be renamed. Check database permissions.', $count, 'robotstxt-mlp2-mlp5' ), $count ) ) ); // phpcs:ignore WordPress.WP.I18n.MismatchedPlaceholders
}
if ( isset( $_GET['restored'] ) ) {
$count = (int) $_GET['restored'];
/* translators: %d: number of restored tables */
printf( '<div class="notice notice-success is-dismissible"><p>%s</p></div>', esc_html( sprintf( _n( '%d table restored to original name.', '%d tables restored to original names.', $count, 'robotstxt-mlp2-mlp5' ), $count ) ) ); // phpcs:ignore WordPress.WP.I18n.MismatchedPlaceholders
}
if ( isset( $_GET['restore_errors'] ) ) {
$count = (int) $_GET['restore_errors'];
/* translators: %d: number of tables that could not be restored */
printf( '<div class="notice notice-error"><p>%s</p></div>', esc_html( sprintf( _n( '%d table could not be restored. Check database permissions.', '%d tables could not be restored. Check database permissions.', $count, 'robotstxt-mlp2-mlp5' ), $count ) ) ); // phpcs:ignore WordPress.WP.I18n.MismatchedPlaceholders
}
if ( isset( $_GET['reset'] ) ) {
printf( '<div class="notice notice-success is-dismissible"><p>%s</p></div>', esc_html__( 'Wizard reset. You can start the migration from Step 1.', 'robotstxt-mlp2-mlp5' ) );
}
if ( isset( $_GET['cleanup_done'] ) ) {
printf( '<div class="notice notice-success is-dismissible"><p>%s</p></div>', esc_html__( 'Cleanup completed.', 'robotstxt-mlp2-mlp5' ) );
}
if ( isset( $_GET['cleanup_error'] ) ) {
printf( '<div class="notice notice-error"><p>%s</p></div>', esc_html__( 'Cleanup confirmation invalid. Type DELETE exactly.', 'robotstxt-mlp2-mlp5' ) );
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
/**
* Show a banner communicating the overall system state (preparation view only).
*
* @param bool $has_original Whether original MLP2 tables exist.
* @param bool $has_backup Whether backup MLP2 tables exist.
* @param bool $mlp5_active Whether MLP5 is network-active.
*/
private static function render_state_banner( bool $has_original, bool $has_backup, bool $mlp5_active ): void {
if ( $has_original && ! $has_backup ) {
$color = '#d63638';
$msg = __( 'Action required: MLP2 tables must be renamed before MultilingualPress 5 can be activated.', 'robotstxt-mlp2-mlp5' );
} elseif ( $has_backup && ! $mlp5_active ) {
$color = '#dba617';
$msg = __( 'MLP2 tables renamed. Now go to Network Admin &gt; Plugins and activate MultilingualPress 5.', 'robotstxt-mlp2-mlp5' );
} elseif ( $has_original && $has_backup ) {
$color = '#dba617';
$msg = __( 'Warning: both original and backup MLP2 tables were found. Investigate before proceeding.', 'robotstxt-mlp2-mlp5' );
} else {
$color = '#d63638';
$msg = __( 'No MLP2 tables found. Migration cannot proceed. Restore your database from a backup.', 'robotstxt-mlp2-mlp5' );
}
printf(
'<div style="border-left:4px solid %s;padding:.75em 1em;margin:1em 0;background:#fff;max-width:640px;"><strong>%s</strong></div>',
esc_attr( $color ),
wp_kses_post( $msg )
);
}
/**
* Show the system status card.
*
* @param bool $has_original Whether original MLP2 tables exist.
* @param bool $has_backup Whether backup MLP2 tables exist.
* @param bool $mlp5_active Whether MLP5 is network-active.
* @param Robotstxt_Mlp2_Mlp5_Detector_Mlp5 $mlp5 MLP5 detector instance.
*/
private static function render_status_card(
bool $has_original,
bool $has_backup,
bool $mlp5_active,
Robotstxt_Mlp2_Mlp5_Detector_Mlp5 $mlp5
): void {
$orig_count = count( Robotstxt_Mlp2_Mlp5_Table_Preparer::get_original_tables() );
$backup_count = count( Robotstxt_Mlp2_Mlp5_Table_Preparer::get_backup_tables() );
?>
<div class="card" style="max-width:640px;margin-top:1em;">
<h2 style="margin-top:0;"><?php esc_html_e( 'System Status', 'robotstxt-mlp2-mlp5' ); ?></h2>
<table class="widefat striped" style="border:0;">
<tbody>
<tr>
<td><?php esc_html_e( 'MLP2 tables (original names)', 'robotstxt-mlp2-mlp5' ); ?></td>
<td>
<?php if ( $has_original ) : ?>
<span style="color:#d63638;">&#9679; <?php echo esc_html( sprintf( /* translators: %d: number of tables */ _n( '%d table — must be renamed', '%d tables — must be renamed', $orig_count, 'robotstxt-mlp2-mlp5' ), $orig_count ) ); // phpcs:ignore WordPress.WP.I18n.MismatchedPlaceholders ?></span>
<?php else : ?>
<span style="color:#46b450;">&#10003; <?php esc_html_e( 'None', 'robotstxt-mlp2-mlp5' ); ?></span>
<?php endif; ?>
</td>
</tr>
<tr>
<td><?php esc_html_e( 'MLP2 tables (backup copies)', 'robotstxt-mlp2-mlp5' ); ?></td>
<td>
<?php if ( $has_backup ) : ?>
<span style="color:#46b450;">&#10003; <?php echo esc_html( sprintf( /* translators: %d: number of backup tables */ _n( '%d backup table ready', '%d backup tables ready', $backup_count, 'robotstxt-mlp2-mlp5' ), $backup_count ) ); // phpcs:ignore WordPress.WP.I18n.MismatchedPlaceholders ?></span>
<?php else : ?>
<span style="color:#888;">&#8212; <?php esc_html_e( 'Not yet renamed', 'robotstxt-mlp2-mlp5' ); ?></span>
<?php endif; ?>
</td>
</tr>
<tr>
<td><?php esc_html_e( 'MultilingualPress 5 (network-active)', 'robotstxt-mlp2-mlp5' ); ?></td>
<td>
<?php if ( $mlp5_active ) : ?>
<span style="color:#46b450;">&#10003; <?php echo esc_html( sprintf( /* translators: %s: version */ __( 'Active (v%s)', 'robotstxt-mlp2-mlp5' ), $mlp5->get_version() ) ); ?></span>
<?php else : ?>
<span style="color:#888;">&#8212; <?php esc_html_e( 'Not active', 'robotstxt-mlp2-mlp5' ); ?></span>
<?php endif; ?>
</td>
</tr>
</tbody>
</table>
</div>
<?php
}
/**
* Render action forms for the preparation view.
*
* @param bool $has_original Whether original MLP2 tables exist.
* @param bool $has_backup Whether backup MLP2 tables exist.
* @param bool $mlp5_active Whether MLP5 is network-active.
*/
private static function render_action_forms( bool $has_original, bool $has_backup, bool $mlp5_active ): void {
if ( $has_original ) {
?>
<div style="max-width:640px;margin-top:1.5em;">
<h2><?php esc_html_e( 'Step 1 — Rename MLP2 Tables', 'robotstxt-mlp2-mlp5' ); ?></h2>
<p><?php esc_html_e( 'MultilingualPress 5 cannot activate while the original MLP2 table names are present. Click below to rename them to temporary backup copies ({prefix}mlp2bak_*). This is safe and fully reversible.', 'robotstxt-mlp2-mlp5' ); ?></p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'mlp_migrator_rename', 'mlp_migrator_nonce' ); ?>
<input type="hidden" name="action" value="mlp_migrator_rename_tables" />
<p><button type="submit" class="button button-primary"><?php esc_html_e( 'Rename MLP2 tables (prepare for MLP5 activation)', 'robotstxt-mlp2-mlp5' ); ?></button></p>
</form>
</div>
<?php
}
if ( $has_backup && ! $mlp5_active ) {
?>
<div style="max-width:640px;margin-top:1.5em;">
<h2><?php esc_html_e( 'Step 2 — Activate MultilingualPress 5', 'robotstxt-mlp2-mlp5' ); ?></h2>
<p><?php esc_html_e( 'MLP2 tables have been renamed. MultilingualPress 5 can now be activated without the old-version compatibility error.', 'robotstxt-mlp2-mlp5' ); ?></p>
<p><a href="<?php echo esc_url( network_admin_url( 'plugins.php' ) ); ?>" class="button button-primary"><?php esc_html_e( 'Go to Network Admin › Plugins', 'robotstxt-mlp2-mlp5' ); ?></a></p>
</div>
<?php
}
if ( $has_backup ) {
?>
<div style="max-width:640px;margin-top:2em;">
<details>
<summary style="cursor:pointer;color:#646970;"><?php esc_html_e( 'Emergency: restore original MLP2 table names', 'robotstxt-mlp2-mlp5' ); ?></summary>
<div style="margin-top:.75em;padding:.75em;border:1px solid #dcdcde;background:#fff8f8;">
<p><?php esc_html_e( 'This renames backup tables back to their original MLP2 names. Only use this before MLP5 has been activated.', 'robotstxt-mlp2-mlp5' ); ?></p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'mlp_migrator_restore', 'mlp_migrator_nonce' ); ?>
<input type="hidden" name="action" value="mlp_migrator_restore_tables" />
<button type="submit" class="button button-secondary"><?php esc_html_e( 'Restore original table names', 'robotstxt-mlp2-mlp5' ); ?></button>
</form>
</div>
</details>
</div>
<?php
}
}
}