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.
|
* Register the admin menu page under Tools.
|
||||||
*/
|
*/
|
||||||
public static function register_menu(): void {
|
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' ),
|
||||||
__( 'WP Data Optimizer', '2meet-data-optimizer' ),
|
__( 'WP Data Optimizer', '2meet-data-optimizer' ),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
self::MENU_SLUG,
|
self::MENU_SLUG,
|
||||||
array( __CLASS__, 'render_page' )
|
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() ) {
|
if ( ! TMDO_Capability::current_user_can_admin() ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -752,6 +756,15 @@ class TMDO_Admin {
|
|||||||
);
|
);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the admin page.
|
||||||
|
*/
|
||||||
|
public static function render_page(): void {
|
||||||
|
if ( ! TMDO_Capability::current_user_can_admin() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$tabs = array(
|
$tabs = array(
|
||||||
'dashboard' => __( '儀表板', '2meet-data-optimizer' ),
|
'dashboard' => __( '儀表板', '2meet-data-optimizer' ),
|
||||||
@@ -774,7 +787,7 @@ class TMDO_Admin {
|
|||||||
'hivepress' => __( 'HivePress 整合', '2meet-data-optimizer' ),
|
'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.
|
// v2.11.8: legacy tabs removed — redirect bookmarks to current main maintenance entry.
|
||||||
if ( in_array( $tab, array( 'sop', 'migration' ), true ) ) {
|
if ( in_array( $tab, array( 'sop', 'migration' ), true ) ) {
|
||||||
$tab = 'entity-bridge';
|
$tab = 'entity-bridge';
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ class TMDO_Setup_Wizard {
|
|||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function register_menu_page(): void {
|
public static function register_menu_page(): void {
|
||||||
add_submenu_page(
|
$hook_suffix = add_submenu_page(
|
||||||
'tools.php',
|
'tools.php',
|
||||||
__( 'WPDO 設定嚮導', '2meet-data-optimizer' ),
|
__( 'WPDO 設定嚮導', '2meet-data-optimizer' ),
|
||||||
__( 'WPDO 設定嚮導', '2meet-data-optimizer' ),
|
__( 'WPDO 設定嚮導', '2meet-data-optimizer' ),
|
||||||
@@ -114,6 +114,33 @@ class TMDO_Setup_Wizard {
|
|||||||
'wpdo-setup-wizard',
|
'wpdo-setup-wizard',
|
||||||
array( __CLASS__, 'render_page' )
|
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>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
<?php
|
<?php
|
||||||
// Take snapshot if action requested (POST + nonce, prevents Referer leak — v3.3.x P1-6b hardening).
|
// The snapshot itself is taken in handle_snapshot_post(), before any output.
|
||||||
if ( isset( $_POST['wpdo_take_snapshot'] ) && check_admin_referer( self::NONCE_NAME ) && class_exists( 'TMDO_Snapshot_Manager' ) ) {
|
if ( isset( $_GET['snap_failed'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||||
$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;
|
|
||||||
}
|
|
||||||
echo '<div class="notice notice-error"><p>' . esc_html__( 'Snapshot 建立失敗,請查日誌。', '2meet-data-optimizer' ) . '</p></div>';
|
echo '<div class="notice notice-error"><p>' . esc_html__( 'Snapshot 建立失敗,請查日誌。', '2meet-data-optimizer' ) . '</p></div>';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|||||||
Reference in New Issue
Block a user