feat(admin): Settings 分區即時儲存(backport A v3.0.2)
7 個設定分區各自加「儲存此區塊」按鈕,經 wp_ajax_wpdo_save_settings_section 以 AJAX 存檔,不再需要整頁 reload。 - 新增 ajax_save_settings_section() + 7 個 private save_section_*() - 新增 admin/assets/wpdo-settings.js(106 行)與 4 條 CSS 規則 - render_settings() 的 7 個 h3 各包上 .wpdo-settings-section[data-section] - 全頁送出按鈕改標「儲存全部設定」,原本的 POST handler 保持不變(漸進增強) hp-transient / wc-term-count 兩個分區的 toggle 仍由核心 admin 呈現(實作在 AddOn),只寫 wp_options,故不需 class_exists 守衛。
This commit is contained in:
@@ -513,3 +513,31 @@
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Settings per-section instant save (v3.0.2 backport) ────────────── */
|
||||
|
||||
.wpdo-settings-section {
|
||||
background: var(--color-surface-card, #fbf7f0);
|
||||
border: 1px solid var(--color-border-soft, #e5dccd);
|
||||
border-radius: var(--radius-md, 12px);
|
||||
padding: var(--space-4, 16px) var(--space-6, 24px);
|
||||
margin-bottom: var(--space-5, 20px);
|
||||
}
|
||||
|
||||
.wpdo-section-save-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3, 12px);
|
||||
padding-top: var(--space-3, 12px);
|
||||
}
|
||||
|
||||
.wpdo-section-save-status {
|
||||
font-size: 0.9rem;
|
||||
color: var(--color-success, #5a8c5a);
|
||||
opacity: 0;
|
||||
transition: opacity var(--duration-normal, 200ms) var(--ease-default, ease);
|
||||
}
|
||||
|
||||
.wpdo-section-save-status.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* WP Data Optimizer — Settings per-section instant save.
|
||||
*
|
||||
* Each .wpdo-settings-section card has a "儲存此區塊" button that posts only
|
||||
* that section's fields via AJAX, updating options without a full page reload.
|
||||
*
|
||||
* @since 3.0.2
|
||||
*/
|
||||
/* global wpdoSettings */
|
||||
( function () {
|
||||
'use strict';
|
||||
|
||||
if ( typeof wpdoSettings === 'undefined' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { ajaxUrl, nonce, i18n } = wpdoSettings;
|
||||
|
||||
/**
|
||||
* Collect form fields within a section into a FormData object.
|
||||
* Unchecked checkboxes are intentionally omitted (server does isset() ? '1' : '0').
|
||||
*
|
||||
* @param {HTMLElement} section
|
||||
* @returns {FormData}
|
||||
*/
|
||||
function collectFields( section ) {
|
||||
const fd = new FormData();
|
||||
section.querySelectorAll( 'input, select, textarea' ).forEach( ( el ) => {
|
||||
if ( ! el.name ) {
|
||||
return;
|
||||
}
|
||||
if ( el.type === 'checkbox' ) {
|
||||
if ( el.checked ) {
|
||||
fd.append( el.name, el.value );
|
||||
}
|
||||
} else if ( el.type === 'radio' ) {
|
||||
if ( el.checked ) {
|
||||
fd.append( el.name, el.value );
|
||||
}
|
||||
} else {
|
||||
fd.append( el.name, el.value );
|
||||
}
|
||||
} );
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a transient status message, then fade it out after 2.5s.
|
||||
*
|
||||
* @param {HTMLElement} statusEl
|
||||
* @param {string} message
|
||||
* @param {boolean} isError
|
||||
*/
|
||||
function showStatus( statusEl, message, isError ) {
|
||||
statusEl.textContent = message;
|
||||
statusEl.style.color = isError ? 'var(--color-error, #b03d3d)' : 'var(--color-success, #5a8c5a)';
|
||||
statusEl.classList.add( 'visible' );
|
||||
clearTimeout( statusEl._wpdo_timer );
|
||||
statusEl._wpdo_timer = setTimeout( () => {
|
||||
statusEl.classList.remove( 'visible' );
|
||||
}, 2500 );
|
||||
}
|
||||
|
||||
document.addEventListener( 'click', function ( e ) {
|
||||
const btn = e.target.closest( '.wpdo-section-save' );
|
||||
if ( ! btn ) {
|
||||
return;
|
||||
}
|
||||
const section = btn.closest( '.wpdo-settings-section' );
|
||||
const statusEl = btn.nextElementSibling;
|
||||
const sectionKey = section ? section.dataset.section : '';
|
||||
if ( ! sectionKey ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalLabel = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = i18n.saving;
|
||||
|
||||
const fd = collectFields( section );
|
||||
fd.append( 'action', 'wpdo_save_settings_section' );
|
||||
fd.append( '_ajax_nonce', nonce );
|
||||
fd.append( 'section', sectionKey );
|
||||
|
||||
fetch( ajaxUrl, {
|
||||
method : 'POST',
|
||||
credentials : 'same-origin',
|
||||
body : fd,
|
||||
} )
|
||||
.then( ( r ) => r.json() )
|
||||
.then( ( data ) => {
|
||||
if ( data.success ) {
|
||||
showStatus( statusEl, i18n.saved, false );
|
||||
} else {
|
||||
showStatus( statusEl, i18n.error, true );
|
||||
}
|
||||
} )
|
||||
.catch( () => {
|
||||
showStatus( statusEl, i18n.error, true );
|
||||
} )
|
||||
.finally( () => {
|
||||
btn.disabled = false;
|
||||
btn.textContent = originalLabel;
|
||||
} );
|
||||
} );
|
||||
} )();
|
||||
+185
-1
@@ -44,6 +44,7 @@ class TMDO_Admin {
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
|
||||
add_action( 'network_admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
|
||||
add_action( 'wp_ajax_wpdo_admin_action', array( __CLASS__, 'handle_ajax' ) );
|
||||
add_action( 'wp_ajax_wpdo_save_settings_section', array( __CLASS__, 'ajax_save_settings_section' ) );
|
||||
add_action( 'admin_notices', array( __CLASS__, 'maybe_nginx_backup_notice' ) );
|
||||
}
|
||||
|
||||
@@ -300,6 +301,28 @@ class TMDO_Admin {
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Settings per-section instant save.
|
||||
wp_enqueue_script(
|
||||
'wpdo-settings',
|
||||
TMDO_URL . 'admin/assets/wpdo-settings.js',
|
||||
array(),
|
||||
TMDO_VERSION,
|
||||
true
|
||||
);
|
||||
wp_localize_script(
|
||||
'wpdo-settings',
|
||||
'wpdoSettings',
|
||||
array(
|
||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'wpdo_save_settings' ),
|
||||
'i18n' => array(
|
||||
'saving' => __( '儲存中…', '2meet-data-optimizer' ),
|
||||
'saved' => __( '✓ 已儲存', '2meet-data-optimizer' ),
|
||||
'error' => __( '儲存失敗,請重試。', '2meet-data-optimizer' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3211,6 +3234,7 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
<?php wp_nonce_field( 'wpdo_save_settings' ); ?>
|
||||
<input type="hidden" name="wpdo_save_settings" value="1">
|
||||
|
||||
<div class="wpdo-settings-section" data-section="notifications">
|
||||
<h3><?php esc_html_e( '📧 Email 警報', '2meet-data-optimizer' ); ?></h3>
|
||||
<p class="description"><?php esc_html_e( '預設關閉。啟用後,每日健康檢查 cron 發現 critical 警告時會發信。同一個警告在 throttle 視窗內不重發。', '2meet-data-optimizer' ); ?></p>
|
||||
<table class="form-table" role="presentation">
|
||||
@@ -3311,6 +3335,12 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="wpdo-section-save-row">
|
||||
<button type="button" class="button wpdo-section-save"><?php esc_html_e( '儲存此區塊', '2meet-data-optimizer' ); ?></button>
|
||||
<span class="wpdo-section-save-status" aria-live="polite"></span>
|
||||
</div>
|
||||
</div><!-- /.wpdo-settings-section -->
|
||||
<div class="wpdo-settings-section" data-section="entity-bridge">
|
||||
<h3 style="margin-top: 2em;"><?php esc_html_e( '⚡ Entity Bridge(v2.5.4 / post v2.11.0)', '2meet-data-optimizer' ); ?></h3>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Hook Bus 攔截 WordPress 原生 metadata filter,將 user / post / term / comment meta 路由至扁平化資料表。', '2meet-data-optimizer' ); ?><br>
|
||||
@@ -3387,6 +3417,12 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
OR option_name LIKE '\\_transient\\_timeout\\_wpdo\\_hp\\_pm\\_%'"
|
||||
);
|
||||
?>
|
||||
<div class="wpdo-section-save-row">
|
||||
<button type="button" class="button wpdo-section-save"><?php esc_html_e( '儲存此區塊', '2meet-data-optimizer' ); ?></button>
|
||||
<span class="wpdo-section-save-status" aria-live="polite"></span>
|
||||
</div>
|
||||
</div><!-- /.wpdo-settings-section -->
|
||||
<div class="wpdo-settings-section" data-section="hp-transient">
|
||||
<h3 style="margin-top: 2em;"><?php esc_html_e( '🌿 HivePress Transient Filter(v2.11.5)', '2meet-data-optimizer' ); ?></h3>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'HivePress 內部用 update_post_meta($post_id, \'_transient_<name>\', $value) 把 TTL cache 寫進 wp_postmeta(每個 hp_listing publish 觸發 8-16 個 transient row)。本 filter 在 metadata 層攔截並重新路由到 wp_options(native transient API),HivePress 完全無感,wp_postmeta 保持乾淨。Filter 是 metadata 層運作,不依賴 mode promote。', '2meet-data-optimizer' ); ?>
|
||||
@@ -3477,6 +3513,12 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
OR meta_key IN ('_hp_price','_hp_status','_hp_featured','_hp_verified','_hp_view_count','_thumbnail_id','_edit_lock','_edit_last')"
|
||||
);
|
||||
?>
|
||||
<div class="wpdo-section-save-row">
|
||||
<button type="button" class="button wpdo-section-save"><?php esc_html_e( '儲存此區塊', '2meet-data-optimizer' ); ?></button>
|
||||
<span class="wpdo-section-save-status" aria-live="polite"></span>
|
||||
</div>
|
||||
</div><!-- /.wpdo-settings-section -->
|
||||
<div class="wpdo-settings-section" data-section="garbage-filter">
|
||||
<h3 style="margin-top: 2em;"><?php esc_html_e( '🗑 Term + Comment Garbage Filter(v2.12.1)', '2meet-data-optimizer' ); ?></h3>
|
||||
<p class="description">
|
||||
<?php esc_html_e( '攔截 wp_termmeta / wp_commentmeta 已知垃圾 keys 的寫入並 silent drop(_wxr_import_* WP 匯入殘留、_2meet_demo_* demo 標記、commentmeta 中 8 個誤寫的 post-domain orphan keys)。Phase 0 cleanup CLI 清歷史,本 filter 防再次累積。Read 路徑不攔截(向後相容)。', '2meet-data-optimizer' ); ?>
|
||||
@@ -3557,6 +3599,12 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
WHERE option_name LIKE '\\_transient\\_wpdo\\_wc\\_termcount\\_%'"
|
||||
);
|
||||
?>
|
||||
<div class="wpdo-section-save-row">
|
||||
<button type="button" class="button wpdo-section-save"><?php esc_html_e( '儲存此區塊', '2meet-data-optimizer' ); ?></button>
|
||||
<span class="wpdo-section-save-status" aria-live="polite"></span>
|
||||
</div>
|
||||
</div><!-- /.wpdo-settings-section -->
|
||||
<div class="wpdo-settings-section" data-section="wc-term-count">
|
||||
<h3 style="margin-top: 2em;"><?php esc_html_e( '🛒 WooCommerce Term Count Filter(v2.12.3)', '2meet-data-optimizer' ); ?></h3>
|
||||
<p class="description">
|
||||
<?php esc_html_e( '攔截 WooCommerce 寫入 wp_termmeta 的 product_count_<taxonomy> cache rows,重新路由到 wp_options(native transient 結構)。WC 自身 cache 失效邏輯不變(每次新增/刪除 product 時會重算寫入),僅儲存位置改變。Read 路徑亦會優先從 wp_options 讀回,cache miss 才 fall-through 至 wp_termmeta(向後相容)。', '2meet-data-optimizer' ); ?>
|
||||
@@ -3619,6 +3667,12 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
? TMDO_Term_Comment_Misc_Bucket::count_rows( 'comment' )
|
||||
: 0;
|
||||
?>
|
||||
<div class="wpdo-section-save-row">
|
||||
<button type="button" class="button wpdo-section-save"><?php esc_html_e( '儲存此區塊', '2meet-data-optimizer' ); ?></button>
|
||||
<span class="wpdo-section-save-status" aria-live="polite"></span>
|
||||
</div>
|
||||
</div><!-- /.wpdo-settings-section -->
|
||||
<div class="wpdo-settings-section" data-section="misc-bucket">
|
||||
<h3 style="margin-top: 2em;"><?php esc_html_e( '📦 Term + Comment Misc Bucket(v2.12.4)', '2meet-data-optimizer' ); ?></h3>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Catch-all flat 表,捕捉所有未被前面 filter 處理的 term/comment meta keys(priority 99,整條 filter chain 的最後一棒)。讓 wp_termmeta / wp_commentmeta 完全可避開(v3.0.0 DROP 前置條件)。寫入 wp_wpdo_term_misc / wp_wpdo_comment_misc 兩張 K/V 表,PRIMARY KEY (entity_id, meta_key)。讀取 cache miss 時 fall-through 至 wp_*meta 維持向後相容。', '2meet-data-optimizer' ); ?>
|
||||
@@ -3665,6 +3719,12 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="wpdo-section-save-row">
|
||||
<button type="button" class="button wpdo-section-save"><?php esc_html_e( '儲存此區塊', '2meet-data-optimizer' ); ?></button>
|
||||
<span class="wpdo-section-save-status" aria-live="polite"></span>
|
||||
</div>
|
||||
</div><!-- /.wpdo-settings-section -->
|
||||
<div class="wpdo-settings-section" data-section="automator">
|
||||
<h3 style="margin-top: 2em;"><?php esc_html_e( '🤖 FSM Automator(v2.5.0 M13)', '2meet-data-optimizer' ); ?></h3>
|
||||
<p class="description"><?php esc_html_e( '預設關閉。啟用後,每日 04:30 UTC 跑一次,自動執行 FSM Advisor 標記為 PROMOTE 的 module 推進。Destructive 轉態(verify→cutover、cutover→cleanup、cleanup→complete)即使 enabled 也永不自動。', '2meet-data-optimizer' ); ?></p>
|
||||
<table class="form-table" role="presentation">
|
||||
@@ -3703,10 +3763,134 @@ wpdo.getListings({ per_page: 3 }).then(r => console.log(r));'
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php submit_button( __( '儲存設定', '2meet-data-optimizer' ) ); ?>
|
||||
<div class="wpdo-section-save-row">
|
||||
<button type="button" class="button wpdo-section-save"><?php esc_html_e( '儲存此區塊', '2meet-data-optimizer' ); ?></button>
|
||||
<span class="wpdo-section-save-status" aria-live="polite"></span>
|
||||
</div>
|
||||
</div><!-- /.wpdo-settings-section -->
|
||||
<?php submit_button( __( '儲存全部設定', '2meet-data-optimizer' ) ); ?>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
|
||||
// ── Per-section save helpers ──────────────────────────────────────────
|
||||
// These private helpers are always invoked from ajax_save_settings_section()
|
||||
// which calls check_ajax_referer() first. PHPCS cannot trace the call chain.
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce checked by ajax_save_settings_section() caller
|
||||
// phpcs:disable Squiz.Commenting.FunctionComment.Missing,Squiz.Commenting.FunctionComment.WrongStyle -- internal helpers
|
||||
|
||||
private static function save_section_notifications(): void {
|
||||
update_option( 'wpdo_email_alerts_enabled', isset( $_POST['wpdo_email_alerts_enabled'] ) ? '1' : '0', false );
|
||||
$recipient = isset( $_POST['wpdo_alert_email'] ) ? sanitize_email( wp_unslash( (string) $_POST['wpdo_alert_email'] ) ) : '';
|
||||
update_option( 'wpdo_alert_email', $recipient, false );
|
||||
$throttle = isset( $_POST['wpdo_alert_throttle_hours'] ) ? max( 1, min( 168, (int) $_POST['wpdo_alert_throttle_hours'] ) ) : 24;
|
||||
update_option( 'wpdo_alert_throttle_hours', $throttle, false );
|
||||
foreach ( array( 'slack', 'discord', 'telegram' ) as $ch ) {
|
||||
update_option( "wpdo_{$ch}_enabled", isset( $_POST[ "wpdo_{$ch}_enabled" ] ) ? '1' : '0', false );
|
||||
$thr = isset( $_POST[ "wpdo_{$ch}_throttle_hours" ] ) ? max( 1, min( 168, (int) $_POST[ "wpdo_{$ch}_throttle_hours" ] ) ) : 24;
|
||||
update_option( "wpdo_{$ch}_throttle_hours", $thr, false );
|
||||
$sev = isset( $_POST[ "wpdo_{$ch}_severity" ] ) ? sanitize_key( wp_unslash( (string) $_POST[ "wpdo_{$ch}_severity" ] ) ) : 'critical_only';
|
||||
if ( ! in_array( $sev, array( 'critical_only', 'critical_and_recommended' ), true ) ) {
|
||||
$sev = 'critical_only';
|
||||
}
|
||||
update_option( "wpdo_{$ch}_severity", $sev, false );
|
||||
}
|
||||
foreach ( array( 'wpdo_slack_webhook', 'wpdo_discord_webhook' ) as $webhook_key ) {
|
||||
if ( ! isset( $_POST[ $webhook_key ] ) ) {
|
||||
continue;
|
||||
}
|
||||
$webhook = esc_url_raw( wp_unslash( (string) $_POST[ $webhook_key ] ) );
|
||||
if ( '' === $webhook ) {
|
||||
continue;
|
||||
}
|
||||
if ( class_exists( 'TMDO_Crypto' ) ) {
|
||||
TMDO_Crypto::set_option( $webhook_key, $webhook );
|
||||
} else {
|
||||
update_option( $webhook_key, $webhook, false );
|
||||
}
|
||||
}
|
||||
if ( isset( $_POST['wpdo_telegram_bot_token'] ) ) {
|
||||
$token = sanitize_text_field( wp_unslash( (string) $_POST['wpdo_telegram_bot_token'] ) );
|
||||
if ( '' !== $token ) {
|
||||
if ( class_exists( 'TMDO_Crypto' ) ) {
|
||||
TMDO_Crypto::set_option( 'wpdo_telegram_bot_token', $token );
|
||||
} else {
|
||||
update_option( 'wpdo_telegram_bot_token', $token, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( isset( $_POST['wpdo_telegram_chat_id'] ) ) {
|
||||
update_option( 'wpdo_telegram_chat_id', sanitize_text_field( wp_unslash( (string) $_POST['wpdo_telegram_chat_id'] ) ), false );
|
||||
}
|
||||
}
|
||||
|
||||
private static function save_section_entity_bridge(): void {
|
||||
update_option( 'wpdo_hook_bus_enabled', isset( $_POST['wpdo_hook_bus_enabled'] ) ? '1' : '0', false );
|
||||
if ( class_exists( 'TMDO_Mode_Manager' ) ) {
|
||||
foreach ( array( 'user', 'post', 'term', 'comment' ) as $entity_type ) {
|
||||
$new_mode = isset( $_POST[ 'wpdo_bridge_mode_' . $entity_type ] )
|
||||
? sanitize_key( wp_unslash( (string) $_POST[ 'wpdo_bridge_mode_' . $entity_type ] ) )
|
||||
: '';
|
||||
if ( TMDO_Mode_Manager::is_valid_mode( $new_mode ) ) {
|
||||
TMDO_Mode_Manager::set( $entity_type, $new_mode );
|
||||
}
|
||||
}
|
||||
TMDO_Mode_Manager::reset_cache();
|
||||
}
|
||||
if ( class_exists( 'TMDO_Hook_Bus_Bridge' ) ) {
|
||||
TMDO_Hook_Bus_Bridge::reset_cache();
|
||||
}
|
||||
}
|
||||
|
||||
private static function save_section_hp_transient(): void {
|
||||
update_option( 'wpdo_hp_transient_filter_enabled', isset( $_POST['wpdo_hp_transient_filter_enabled'] ) ? '1' : '0', false );
|
||||
}
|
||||
|
||||
private static function save_section_garbage_filter(): void {
|
||||
update_option( 'wpdo_term_comment_garbage_filter_enabled', isset( $_POST['wpdo_term_comment_garbage_filter_enabled'] ) ? '1' : '0', false );
|
||||
}
|
||||
|
||||
private static function save_section_wc_term_count(): void {
|
||||
update_option( 'wpdo_wc_term_count_filter_enabled', isset( $_POST['wpdo_wc_term_count_filter_enabled'] ) ? '1' : '0', false );
|
||||
}
|
||||
|
||||
private static function save_section_misc_bucket(): void {
|
||||
update_option( 'wpdo_term_comment_misc_bucket_enabled', isset( $_POST['wpdo_term_comment_misc_bucket_enabled'] ) ? '1' : '0', false );
|
||||
}
|
||||
|
||||
private static function save_section_automator(): void {
|
||||
update_option( 'wpdo_automator_enabled', isset( $_POST['wpdo_automator_enabled'] ) ? '1' : '0', false );
|
||||
$blacklist = isset( $_POST['wpdo_automator_blacklist'] ) && is_array( $_POST['wpdo_automator_blacklist'] )
|
||||
? array_values( array_filter( array_map( 'sanitize_key', wp_unslash( $_POST['wpdo_automator_blacklist'] ) ) ) )
|
||||
: array();
|
||||
update_option( 'wpdo_automator_blacklist', $blacklist, false );
|
||||
}
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing,Squiz.Commenting.FunctionComment.Missing,Squiz.Commenting.FunctionComment.WrongStyle
|
||||
|
||||
/**
|
||||
* AJAX: save a single settings section without a full page reload.
|
||||
*/
|
||||
public static function ajax_save_settings_section(): void {
|
||||
check_ajax_referer( 'wpdo_save_settings' );
|
||||
if ( ! TMDO_Capability::current_user_can_admin() ) {
|
||||
wp_send_json_error( 'unauthorized', 403 );
|
||||
}
|
||||
$section = isset( $_POST['section'] ) ? sanitize_key( wp_unslash( (string) $_POST['section'] ) ) : '';
|
||||
$handlers = array(
|
||||
'notifications' => 'save_section_notifications',
|
||||
'entity-bridge' => 'save_section_entity_bridge',
|
||||
'hp-transient' => 'save_section_hp_transient',
|
||||
'garbage-filter' => 'save_section_garbage_filter',
|
||||
'wc-term-count' => 'save_section_wc_term_count',
|
||||
'misc-bucket' => 'save_section_misc_bucket',
|
||||
'automator' => 'save_section_automator',
|
||||
);
|
||||
if ( ! isset( $handlers[ $section ] ) ) {
|
||||
wp_send_json_error( 'invalid_section', 400 );
|
||||
}
|
||||
self::{$handlers[ $section ]}();
|
||||
wp_send_json_success( array( 'message' => __( '設定已儲存。', '2meet-data-optimizer' ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Boot admin hooks.
|
||||
|
||||
Reference in New Issue
Block a user