Files
wpdev d52d604d7a
Anti-EAV Lint + Quality Gate / anti-eav-lint (push) Successful in 8s
Tests / Unit Tests (push) Successful in 10s
Tests / Integration Tests (push) Successful in 35s
Tests / PHP Lint (push) Successful in 7s
Tests / PHPCS (push) Successful in 19s
Tests / PHPStan (push) Successful in 24s
style(admin): inline style 全面改用 CSS class(PR-H)
admin/ 的 inline style= 由 348 處降到 9 處,剩下的 9 處全是 CSS custom
property 載體(--wpdo-bar-width / --wpdo-cov-pct 等動態數值),與來源外掛的
設計一致。

- wpdo-admin.css 543 → 911 行:補上 148 個 class(來源檔在 selector 與
  rule-block 層級都是既有內容的超集,逐條核對過),另加 .wpdo-form-inline
- 5 個變數改為 class 版本:$mode_cls / $badge_mode_cls / $diffs_cls /
  $ap_cls / $e_mode_cls;3 個 stress-test template 補 $mode_text_cls /
  $mode_card_cls
- 刪掉因此變成孤兒的 $mode_badge / $style / $mode_style / $badge_style /
  $e_mode_bg / $badge_bg
- entity card 改 .wpdo-eb-card、pipeline dot 改 .wpdo-stage-pill、demote 按鈕
  改 .wpdo-eb-btn-dim;.wpdo-native-counts / .wpdo-recommendation /
  .wpdo-setup-banner-btn 的重複 inline style 移除
- dashboard 的 top-views 查詢一併改用上一個 commit 的 TMDO_Zone_Warm::VIEW_KEY

PHPStan 抓出自動轉換造成的 11 個未定義變數,已全數補回定義並複驗。
2026-07-31 10:34:57 +08:00

220 lines
9.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* One-click User Entity Migration Wizard.
*
* Variables in scope (from class-tmdo-admin.php::render_migration_wizard):
* $preflight array TMDO_Migration_Orchestrator::preflight() snapshot.
* $status array TMDO_Migration_Orchestrator::get_status() snapshot.
*
* @package WP_Data_Optimizer
* @since 2.8.0
*/
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$ratio = $preflight['ratio'] ?? 0;
$users_count = $preflight['users'] ?? 0;
$usermeta_count = $preflight['usermeta'] ?? 0;
$eav_residue = $preflight['eav_rows'] ?? 0;
$current_mode = $preflight['mode'] ?? 'unknown';
$strategy = $preflight['estimated_strategy'] ?? 'sync';
$est_sec = $preflight['estimated_sec'] ?? 0;
$groups = $preflight['groups'] ?? array();
$state = $status['state'] ?? 'idle';
$active = in_array( $state, array( 'running', 'paused' ), true );
$completed = 'completed' === $state;
$failed = 'failed' === $state;
?>
<div class="wpdo-migration-wizard" data-state="<?php echo esc_attr( $state ); ?>">
<header class="wpdo-mw-header">
<h2><?php esc_html_e( 'User Entity 遷移精靈', '2meet-data-optimizer' ); ?></h2>
<p class="description">
<?php esc_html_e( '把 wp_usermeta 中已註冊為 entity field 的 legacy keys 一次遷移到 flat tables。流程包含自動備份、demote、bulk SQL backfill、shadow_read 驗證、aeav_only cutover、以及最終的 EAV 殘留清除。', '2meet-data-optimizer' ); ?>
</p>
</header>
<!-- Pre-flight diagnostic panel ─────────────────────────────────────── -->
<section class="wpdo-mw-panel wpdo-mw-preflight" id="wpdo-mw-preflight">
<h3><?php esc_html_e( '當前狀態', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-mw-metrics">
<div class="wpdo-mw-metric">
<span class="wpdo-mw-metric-label"><?php esc_html_e( 'wp_users', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value" id="wpdo-mw-users"><?php echo esc_html( number_format_i18n( $users_count ) ); ?></span>
</div>
<div class="wpdo-mw-metric">
<span class="wpdo-mw-metric-label"><?php esc_html_e( 'wp_usermeta', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value" id="wpdo-mw-usermeta"><?php echo esc_html( number_format_i18n( $usermeta_count ) ); ?></span>
</div>
<div class="wpdo-mw-metric wpdo-mw-metric-primary">
<span class="wpdo-mw-metric-label"><?php esc_html_e( '當前 ratio', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value" id="wpdo-mw-ratio">1:<?php echo esc_html( (string) $ratio ); ?></span>
</div>
<div class="wpdo-mw-metric">
<span class="wpdo-mw-metric-label"><?php esc_html_e( 'EAV 殘留', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value" id="wpdo-mw-residue"><?php echo esc_html( number_format_i18n( $eav_residue ) ); ?></span>
</div>
<div class="wpdo-mw-metric">
<span class="wpdo-mw-metric-label"><?php esc_html_e( 'user mode', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value wpdo-mw-mode wpdo-mw-mode-<?php echo esc_attr( $current_mode ); ?>"><?php echo esc_html( $current_mode ); ?></span>
</div>
</div>
<details class="wpdo-mw-groups">
<summary><?php esc_html_e( '各 entity group 詳細狀態', '2meet-data-optimizer' ); ?></summary>
<table class="widefat striped">
<thead>
<tr>
<th><?php esc_html_e( 'Group', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'Keys', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'Flat rows', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'EAV 殘留', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'Backfill 策略', '2meet-data-optimizer' ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $groups as $name => $g ) : ?>
<tr>
<td><code><?php echo esc_html( $name ); ?></code></td>
<td><?php echo esc_html( (string) count( $g['keys'] ) ); ?></td>
<td><?php echo esc_html( (string) $g['flat_rows'] ); ?></td>
<td>
<?php if ( $g['residue'] > 0 ) : ?>
<strong><?php echo esc_html( (string) $g['residue'] ); ?></strong>
<?php else : ?>
<span class="wpdo-mw-zero">0</span>
<?php endif; ?>
</td>
<td><code><?php echo esc_html( $g['strategy'] ); ?></code></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</details>
</section>
<!-- Run-migration panel (idle state) ──────────────────────────────── -->
<section class="wpdo-mw-panel wpdo-mw-run" id="wpdo-mw-run-panel"
<?php echo $active || $completed ? 'hidden' : ''; ?>>
<?php if ( 0 === $eav_residue && 'aeav_only' === $current_mode ) : ?>
<div class="wpdo-mw-nothing-todo">
<h3>✅ <?php esc_html_e( '無事可做', '2meet-data-optimizer' ); ?></h3>
<p><?php esc_html_e( '所有 entity group 已 aeav_only / 0 EAV 殘留。', '2meet-data-optimizer' ); ?></p>
<p>
<?php
echo esc_html(
sprintf(
/* translators: %s: ratio */
__( '當前 ratio1:%s', '2meet-data-optimizer' ),
(string) $ratio
)
);
?>
</p>
</div>
<?php else : ?>
<h3><?php esc_html_e( '執行一鍵遷移', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-mw-estimate">
<?php
printf(
/* translators: 1: rows count, 2: strategy, 3: estimated seconds */
esc_html__( '將遷移 %1$s 行 EAV 殘留,預估策略:%2$s(約 %3$s 秒)。', '2meet-data-optimizer' ),
'<strong>' . esc_html( number_format_i18n( $eav_residue ) ) . '</strong>',
'<code>' . esc_html( $strategy ) . '</code>',
'<strong>' . esc_html( (string) $est_sec ) . '</strong>'
);
?>
</div>
<fieldset class="wpdo-mw-options">
<legend><?php esc_html_e( '進階選項', '2meet-data-optimizer' ); ?></legend>
<label>
<input type="checkbox" id="wpdo-mw-opt-backup" checked>
<?php esc_html_e( '自動備份 wp_usermeta 至 uploads/wpdo-backups/(推薦)', '2meet-data-optimizer' ); ?>
</label>
<label>
<input type="checkbox" id="wpdo-mw-opt-strict" checked>
<?php esc_html_e( '嚴格驗證(500 users / 10% 抽樣)', '2meet-data-optimizer' ); ?>
</label>
<label>
<input type="checkbox" id="wpdo-mw-opt-24h">
<?php esc_html_e( '24 小時 shadow_read 觀察視窗(生產環境用,會暫停 24h 才繼續)', '2meet-data-optimizer' ); ?>
</label>
<label>
<input type="checkbox" id="wpdo-mw-opt-async">
<?php esc_html_e( '強制 asynccron 推進,不阻塞 HTTP 請求)', '2meet-data-optimizer' ); ?>
</label>
<label>
<input type="checkbox" id="wpdo-mw-opt-dryrun">
<?php esc_html_e( 'Dry-run(不寫入、不刪除,僅模擬流程)', '2meet-data-optimizer' ); ?>
</label>
</fieldset>
<div class="wpdo-mw-confirm">
<label>
<input type="checkbox" id="wpdo-mw-confirm-backup" required>
<?php esc_html_e( '我已了解:流程將從 wp_usermeta 刪除已註冊欄位的 EAV 行,過程不可逆(自動備份提供救援路徑)。', '2meet-data-optimizer' ); ?>
</label>
</div>
<button type="button" class="button button-primary button-hero" id="wpdo-mw-start" disabled>
🚀 <?php esc_html_e( '開始一鍵遷移', '2meet-data-optimizer' ); ?>
</button>
<?php endif; ?>
</section>
<!-- Live progress panel (running state) ───────────────────────────── -->
<section class="wpdo-mw-panel wpdo-mw-progress" id="wpdo-mw-progress-panel"
<?php echo $active || $failed ? '' : 'hidden'; ?>>
<h3 id="wpdo-mw-progress-title"><?php esc_html_e( '執行中…', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-mw-progress-bar">
<div class="wpdo-mw-progress-fill" id="wpdo-mw-progress-fill"
style="--wpdo-bar-width:<?php echo esc_attr( (string) ( $status['overall_progress'] ?? 0 ) ); ?>%"></div>
<span class="wpdo-mw-progress-pct" id="wpdo-mw-progress-pct"><?php echo esc_html( (string) ( $status['overall_progress'] ?? 0 ) ); ?>%</span>
</div>
<div class="wpdo-mw-current">
<span class="wpdo-mw-current-phase">
<?php esc_html_e( '當前階段:', '2meet-data-optimizer' ); ?>
<code id="wpdo-mw-current-phase-name"><?php echo esc_html( $status['phase'] ?? 'idle' ); ?></code>
</span>
<span class="wpdo-mw-elapsed" id="wpdo-mw-elapsed">0.0s</span>
</div>
<div class="wpdo-mw-live-ratio">
<span><?php esc_html_e( 'Live ratio', '2meet-data-optimizer' ); ?></span>
<span id="wpdo-mw-live-ratio-text">
1:<?php echo esc_html( (string) ( $status['metrics']['ratio_start'] ?? $ratio ) ); ?>
1:<?php echo esc_html( (string) ( $status['metrics']['ratio_now'] ?? $ratio ) ); ?>
</span>
</div>
<div class="wpdo-mw-log-wrap">
<h4><?php esc_html_e( 'Log', '2meet-data-optimizer' ); ?></h4>
<pre class="wpdo-mw-log" id="wpdo-mw-log"><?php echo esc_html( implode( "\n", (array) ( $status['log'] ?? array() ) ) ); ?></pre>
</div>
<div class="wpdo-mw-actions">
<button type="button" class="button" id="wpdo-mw-cancel"><?php esc_html_e( '取消', '2meet-data-optimizer' ); ?></button>
<button type="button" class="button button-primary" id="wpdo-mw-resume" hidden><?php esc_html_e( 'Resume', '2meet-data-optimizer' ); ?></button>
</div>
</section>
<!-- Completed state ───────────────────────────────────────────────── -->
<section class="wpdo-mw-panel wpdo-mw-done" id="wpdo-mw-done-panel"
<?php echo $completed ? '' : 'hidden'; ?>>
<h3>✅ <?php esc_html_e( '遷移完成', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-mw-done-summary" id="wpdo-mw-done-summary"></div>
<button type="button" class="button" id="wpdo-mw-reset"><?php esc_html_e( '回到診斷頁', '2meet-data-optimizer' ); ?></button>
</section>
</div>