refactor(2meet-data-optimizer): handle admin form posts before the page renders
- the main page's form handlers move unchanged from render_page() to
handle_post(), hooked on load-{page}: inside render_page() the admin
header was already sent, so their wp_safe_redirect() calls could not work
- the setup wizard's step 4 snapshot post moves to handle_snapshot_post() on
its load hook; a failure redirects with snap_failed=1 for the notice
- Verify: unit 701, integration 577, phpcs, stan; dev36 load hook handles a
posted reset and redirects before output
- PR: PR-097
This commit is contained in:
@@ -89,13 +89,16 @@ class TMDO_Admin {
|
||||
* Register the admin menu page under Tools.
|
||||
*/
|
||||
public static function register_menu(): void {
|
||||
add_management_page(
|
||||
$hook_suffix = add_management_page(
|
||||
__( 'WP Data Optimizer', '2meet-data-optimizer' ),
|
||||
__( 'WP Data Optimizer', '2meet-data-optimizer' ),
|
||||
'manage_options',
|
||||
self::MENU_SLUG,
|
||||
array( __CLASS__, 'render_page' )
|
||||
);
|
||||
if ( $hook_suffix ) {
|
||||
add_action( 'load-' . $hook_suffix, array( __CLASS__, 'handle_post' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,9 +329,10 @@ class TMDO_Admin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the admin page.
|
||||
* Handle the page's form posts on load-{page}, before any output, so their
|
||||
* redirects work (inside render_page() the admin header was already sent).
|
||||
*/
|
||||
public static function render_page(): void {
|
||||
public static function handle_post(): void {
|
||||
if ( ! TMDO_Capability::current_user_can_admin() ) {
|
||||
return;
|
||||
}
|
||||
@@ -752,6 +756,15 @@ class TMDO_Admin {
|
||||
);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the admin page.
|
||||
*/
|
||||
public static function render_page(): void {
|
||||
if ( ! TMDO_Capability::current_user_can_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tabs = array(
|
||||
'dashboard' => __( '儀表板', '2meet-data-optimizer' ),
|
||||
@@ -774,7 +787,7 @@ class TMDO_Admin {
|
||||
'hivepress' => __( 'HivePress 整合', '2meet-data-optimizer' ),
|
||||
);
|
||||
|
||||
$tab = sanitize_key( wp_unslash( $_GET['tab'] ?? 'dashboard' ) );
|
||||
$tab = sanitize_key( wp_unslash( $_GET['tab'] ?? 'dashboard' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- tab navigation, no state change.
|
||||
// v2.11.8: legacy tabs removed — redirect bookmarks to current main maintenance entry.
|
||||
if ( in_array( $tab, array( 'sop', 'migration' ), true ) ) {
|
||||
$tab = 'entity-bridge';
|
||||
|
||||
@@ -106,7 +106,7 @@ class TMDO_Setup_Wizard {
|
||||
* @return void
|
||||
*/
|
||||
public static function register_menu_page(): void {
|
||||
add_submenu_page(
|
||||
$hook_suffix = add_submenu_page(
|
||||
'tools.php',
|
||||
__( 'WPDO 設定嚮導', '2meet-data-optimizer' ),
|
||||
__( 'WPDO 設定嚮導', '2meet-data-optimizer' ),
|
||||
@@ -114,6 +114,33 @@ class TMDO_Setup_Wizard {
|
||||
'wpdo-setup-wizard',
|
||||
array( __CLASS__, 'render_page' )
|
||||
);
|
||||
if ( $hook_suffix ) {
|
||||
add_action( 'load-' . $hook_suffix, array( __CLASS__, 'handle_snapshot_post' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step 4 baseline snapshot (POST + nonce, prevents Referer leak — v3.3.x
|
||||
* P1-6b hardening), handled on load-{page} so the redirect runs before any
|
||||
* output.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function handle_snapshot_post(): void {
|
||||
if ( ! isset( $_POST['wpdo_take_snapshot'] ) || ! TMDO_Capability::current_user_can_admin() || ! class_exists( 'TMDO_Snapshot_Manager' ) ) {
|
||||
return;
|
||||
}
|
||||
check_admin_referer( self::NONCE_NAME );
|
||||
$result = TMDO_Snapshot_Manager::create(
|
||||
'manual',
|
||||
array(),
|
||||
array(
|
||||
'notes' => 'wizard_baseline',
|
||||
'retention_days' => 365,
|
||||
)
|
||||
);
|
||||
wp_safe_redirect( admin_url( 'tools.php?page=wpdo-setup-wizard&step=4&' . ( empty( $result['ok'] ) ? 'snap_failed=1' : 'snap_done=1' ) ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,20 +383,8 @@ class TMDO_Setup_Wizard {
|
||||
</p>
|
||||
</form>
|
||||
<?php
|
||||
// Take snapshot if action requested (POST + nonce, prevents Referer leak — v3.3.x P1-6b hardening).
|
||||
if ( isset( $_POST['wpdo_take_snapshot'] ) && check_admin_referer( self::NONCE_NAME ) && class_exists( 'TMDO_Snapshot_Manager' ) ) {
|
||||
$result = TMDO_Snapshot_Manager::create(
|
||||
'manual',
|
||||
array(),
|
||||
array(
|
||||
'notes' => 'wizard_baseline',
|
||||
'retention_days' => 365,
|
||||
)
|
||||
);
|
||||
if ( ! empty( $result['ok'] ) ) {
|
||||
wp_safe_redirect( admin_url( 'tools.php?page=wpdo-setup-wizard&step=4&snap_done=1' ) );
|
||||
exit;
|
||||
}
|
||||
// The snapshot itself is taken in handle_snapshot_post(), before any output.
|
||||
if ( isset( $_GET['snap_failed'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
echo '<div class="notice notice-error"><p>' . esc_html__( 'Snapshot 建立失敗,請查日誌。', '2meet-data-optimizer' ) . '</p></div>';
|
||||
}
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user