Fix button roundtrip in editor, alignment persistence, and test email rendering

This commit is contained in:
Dwindi Ramadhana
2026-01-17 13:10:50 +07:00
parent 0e9ace902d
commit 6d2136d3b5
61 changed files with 8287 additions and 866 deletions

View File

@@ -42,6 +42,13 @@ class AppearanceController {
'callback' => [__CLASS__, 'save_footer'],
'permission_callback' => [__CLASS__, 'check_permission'],
]);
// Save menu settings
register_rest_route(self::API_NAMESPACE, '/appearance/menus', [
'methods' => 'POST',
'callback' => [__CLASS__, 'save_menus'],
'permission_callback' => [__CLASS__, 'check_permission'],
]);
// Save page-specific settings
register_rest_route(self::API_NAMESPACE, '/appearance/pages/(?P<page>[a-zA-Z0-9_-]+)', [
@@ -73,7 +80,11 @@ class AppearanceController {
* Get all appearance settings
*/
public static function get_settings(WP_REST_Request $request) {
$settings = get_option(self::OPTION_KEY, self::get_default_settings());
$stored = get_option(self::OPTION_KEY, []);
$defaults = self::get_default_settings();
// Merge stored with defaults to ensure all fields exist (recursive)
$settings = array_replace_recursive($defaults, $stored);
return new WP_REST_Response([
'success' => true,
@@ -85,8 +96,12 @@ class AppearanceController {
* Save general settings
*/
public static function save_general(WP_REST_Request $request) {
$settings = get_option(self::OPTION_KEY, self::get_default_settings());
$settings = get_option(self::OPTION_KEY, []);
$defaults = self::get_default_settings();
$settings = array_replace_recursive($defaults, $settings);
$colors = $request->get_param('colors') ?? [];
$general_data = [
'spa_mode' => sanitize_text_field($request->get_param('spaMode')),
'spa_page' => absint($request->get_param('spaPage') ?? 0),
@@ -101,11 +116,13 @@ class AppearanceController {
'scale' => floatval($request->get_param('typography')['scale'] ?? 1.0),
],
'colors' => [
'primary' => sanitize_hex_color($request->get_param('colors')['primary'] ?? '#1a1a1a'),
'secondary' => sanitize_hex_color($request->get_param('colors')['secondary'] ?? '#6b7280'),
'accent' => sanitize_hex_color($request->get_param('colors')['accent'] ?? '#3b82f6'),
'text' => sanitize_hex_color($request->get_param('colors')['text'] ?? '#111827'),
'background' => sanitize_hex_color($request->get_param('colors')['background'] ?? '#ffffff'),
'primary' => sanitize_hex_color($colors['primary'] ?? '#1a1a1a'),
'secondary' => sanitize_hex_color($colors['secondary'] ?? '#6b7280'),
'accent' => sanitize_hex_color($colors['accent'] ?? '#3b82f6'),
'text' => sanitize_hex_color($colors['text'] ?? '#111827'),
'background' => sanitize_hex_color($colors['background'] ?? '#ffffff'),
'gradientStart' => sanitize_hex_color($colors['gradientStart'] ?? '#9333ea'),
'gradientEnd' => sanitize_hex_color($colors['gradientEnd'] ?? '#3b82f6'),
],
];
@@ -230,6 +247,44 @@ class AppearanceController {
'data' => $footer_data,
], 200);
}
/**
* Save menu settings
*/
public static function save_menus(WP_REST_Request $request) {
$settings = get_option(self::OPTION_KEY, self::get_default_settings());
$menus = $request->get_param('menus') ?? [];
// Sanitize menus
$sanitized_menus = [
'primary' => [],
'mobile' => [], // Optional separate mobile menu
];
foreach (['primary', 'mobile'] as $location) {
if (isset($menus[$location]) && is_array($menus[$location])) {
foreach ($menus[$location] as $item) {
$sanitized_menus[$location][] = [
'id' => sanitize_text_field($item['id'] ?? uniqid()),
'label' => sanitize_text_field($item['label'] ?? ''),
'type' => sanitize_text_field($item['type'] ?? 'page'), // page, custom
'value' => sanitize_text_field($item['value'] ?? ''), // slug or url
'target' => sanitize_text_field($item['target'] ?? '_self'),
];
}
}
}
$settings['menus'] = $sanitized_menus;
update_option(self::OPTION_KEY, $settings);
return new WP_REST_Response([
'success' => true,
'message' => 'Menu settings saved successfully',
'data' => $sanitized_menus,
], 200);
}
/**
* Save page-specific settings
@@ -389,11 +444,23 @@ class AppearanceController {
'sort_order' => 'ASC',
]);
$pages_list = array_map(function($page) {
$store_pages = [
(int) get_option('woocommerce_shop_page_id'),
(int) get_option('woocommerce_cart_page_id'),
(int) get_option('woocommerce_checkout_page_id'),
(int) get_option('woocommerce_myaccount_page_id'),
];
$pages_list = array_map(function($page) use ($store_pages) {
$is_woonoow = !empty(get_post_meta($page->ID, '_wn_page_structure', true));
$is_store = in_array((int)$page->ID, $store_pages, true);
return [
'id' => $page->ID,
'title' => $page->post_title,
'slug' => $page->post_name,
'is_woonoow_page' => $is_woonoow,
'is_store_page' => $is_store,
];
}, $pages);
@@ -427,6 +494,8 @@ class AppearanceController {
'accent' => '#3b82f6',
'text' => '#111827',
'background' => '#ffffff',
'gradientStart' => '#9333ea',
'gradientEnd' => '#3b82f6',
],
],
'header' => [
@@ -458,6 +527,14 @@ class AppearanceController {
],
'social_links' => [],
],
'menus' => [
'primary' => [
['id' => 'home', 'label' => 'Home', 'type' => 'page', 'value' => '/', 'target' => '_self'],
['id' => 'shop', 'label' => 'Shop', 'type' => 'page', 'value' => 'shop', 'target' => '_self'],
],
// Fallback for mobile if empty is to use primary
'mobile' => [],
],
'pages' => [
'shop' => [
'layout' => [