fix: resolve container width issues, spa redirects, and appearance settings overwrite. feat: enhance order/sub details and newsletter layout

This commit is contained in:
Dwindi Ramadhana
2026-02-05 00:09:40 +07:00
parent a0b5f8496d
commit 5f08c18ec7
77 changed files with 7027 additions and 4546 deletions

View File

@@ -1,41 +1,45 @@
<?php
namespace WooNooW\Admin;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
class AppearanceController {
class AppearanceController
{
const OPTION_KEY = 'woonoow_appearance_settings';
const API_NAMESPACE = 'woonoow/v1';
public static function init() {
public static function init()
{
add_action('rest_api_init', [__CLASS__, 'register_routes']);
}
public static function register_routes() {
public static function register_routes()
{
// Get all settings (public access for frontend)
register_rest_route(self::API_NAMESPACE, '/appearance/settings', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_settings'],
'permission_callback' => '__return_true',
]);
// Save general settings
register_rest_route(self::API_NAMESPACE, '/appearance/general', [
'methods' => 'POST',
'callback' => [__CLASS__, 'save_general'],
'permission_callback' => [__CLASS__, 'check_permission'],
]);
// Save header settings
register_rest_route(self::API_NAMESPACE, '/appearance/header', [
'methods' => 'POST',
'callback' => [__CLASS__, 'save_header'],
'permission_callback' => [__CLASS__, 'check_permission'],
]);
// Save footer settings
register_rest_route(self::API_NAMESPACE, '/appearance/footer', [
'methods' => 'POST',
@@ -49,7 +53,7 @@ class AppearanceController {
'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_-]+)', [
'methods' => 'POST',
@@ -63,7 +67,7 @@ class AppearanceController {
],
],
]);
// Get all WordPress pages for page selector
register_rest_route(self::API_NAMESPACE, '/pages/list', [
'methods' => 'GET',
@@ -71,40 +75,45 @@ class AppearanceController {
'permission_callback' => [__CLASS__, 'check_permission'],
]);
}
public static function check_permission() {
public static function check_permission()
{
return current_user_can('manage_woocommerce');
}
/**
* Get all appearance settings
*/
public static function get_settings(WP_REST_Request $request) {
public static function get_settings(WP_REST_Request $request)
{
$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,
'data' => $settings,
], 200);
}
/**
* Save general settings
*/
public static function save_general(WP_REST_Request $request) {
public static function save_general(WP_REST_Request $request)
{
$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),
'container_width' => sanitize_text_field($request->get_param('containerWidth') ?? 'boxed'),
'toast_position' => sanitize_text_field($request->get_param('toastPosition') ?? 'top-right'),
'typography' => [
'mode' => sanitize_text_field($request->get_param('typography')['mode'] ?? 'predefined'),
@@ -125,23 +134,25 @@ class AppearanceController {
'gradientEnd' => sanitize_hex_color($colors['gradientEnd'] ?? '#3b82f6'),
],
];
$settings['general'] = $general_data;
// Merge with existing general settings to preserve other keys (like spa_frontpage)
$settings['general'] = array_merge($settings['general'] ?? [], $general_data);
update_option(self::OPTION_KEY, $settings);
return new WP_REST_Response([
'success' => true,
'message' => 'General settings saved successfully',
'data' => $general_data,
], 200);
}
/**
* Save header settings
*/
public static function save_header(WP_REST_Request $request) {
public static function save_header(WP_REST_Request $request)
{
$settings = get_option(self::OPTION_KEY, self::get_default_settings());
$header_data = [
'style' => sanitize_text_field($request->get_param('style')),
'sticky' => (bool) $request->get_param('sticky'),
@@ -159,23 +170,24 @@ class AppearanceController {
'wishlist' => (bool) ($request->get_param('elements')['wishlist'] ?? false),
],
];
$settings['header'] = $header_data;
update_option(self::OPTION_KEY, $settings);
return new WP_REST_Response([
'success' => true,
'message' => 'Header settings saved successfully',
'data' => $header_data,
], 200);
}
/**
* Save footer settings
*/
public static function save_footer(WP_REST_Request $request) {
public static function save_footer(WP_REST_Request $request)
{
$settings = get_option(self::OPTION_KEY, self::get_default_settings());
$social_links = $request->get_param('socialLinks') ?? [];
$sanitized_links = [];
foreach ($social_links as $link) {
@@ -185,7 +197,7 @@ class AppearanceController {
'url' => esc_url_raw($link['url'] ?? ''),
];
}
// Sanitize contact data
$contact_data = $request->get_param('contactData');
$sanitized_contact = [
@@ -196,7 +208,7 @@ class AppearanceController {
'show_phone' => (bool) ($contact_data['show_phone'] ?? true),
'show_address' => (bool) ($contact_data['show_address'] ?? true),
];
// Sanitize labels
$labels = $request->get_param('labels');
$sanitized_labels = [
@@ -206,7 +218,7 @@ class AppearanceController {
'newsletter_title' => sanitize_text_field($labels['newsletter_title'] ?? 'Newsletter'),
'newsletter_description' => sanitize_text_field($labels['newsletter_description'] ?? 'Subscribe to get updates'),
];
// Sanitize custom sections
$sections = $request->get_param('sections') ?? [];
$sanitized_sections = [];
@@ -219,28 +231,44 @@ class AppearanceController {
'visible' => (bool) ($section['visible'] ?? true),
];
}
$footer_data = [
'payment' => [
'enabled' => (bool) ($request->get_param('payment')['enabled'] ?? true),
'title' => sanitize_text_field($request->get_param('payment')['title'] ?? 'We accept'),
'methods' => array_map(function ($method) {
return [
'id' => sanitize_text_field($method['id'] ?? uniqid()),
'url' => esc_url_raw($method['url'] ?? ''),
'label' => sanitize_text_field($method['label'] ?? ''),
'width' => sanitize_text_field($method['width'] ?? ''),
];
}, $request->get_param('payment')['methods'] ?? []),
],
'copyright' => [
'enabled' => (bool) ($request->get_param('copyright')['enabled'] ?? true),
'text' => wp_kses_post($request->get_param('copyright')['text'] ?? ''),
],
'columns' => sanitize_text_field($request->get_param('columns')),
'style' => sanitize_text_field($request->get_param('style')),
'copyright_text' => wp_kses_post($request->get_param('copyrightText')),
// 'copyright_text' => Deprecated, moved to copyright.text
// 'elements' => Deprecated, moved to individual sections (except menu/contact/social flags if needed)
'elements' => [
'newsletter' => (bool) ($request->get_param('elements')['newsletter'] ?? true),
'social' => (bool) ($request->get_param('elements')['social'] ?? true),
'payment' => (bool) ($request->get_param('elements')['payment'] ?? true),
'copyright' => (bool) ($request->get_param('elements')['copyright'] ?? true),
'menu' => (bool) ($request->get_param('elements')['menu'] ?? true),
'contact' => (bool) ($request->get_param('elements')['contact'] ?? true),
// Payment and Copyright moved
],
'social_links' => $sanitized_links,
'contact_data' => $sanitized_contact,
'labels' => $sanitized_labels,
'sections' => $sanitized_sections,
];
$settings['footer'] = $footer_data;
update_option(self::OPTION_KEY, $settings);
return new WP_REST_Response([
'success' => true,
'message' => 'Footer settings saved successfully',
@@ -251,11 +279,12 @@ class AppearanceController {
/**
* Save menu settings
*/
public static function save_menus(WP_REST_Request $request) {
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' => [],
@@ -265,7 +294,7 @@ class AppearanceController {
foreach (['primary', 'mobile'] as $location) {
if (isset($menus[$location]) && is_array($menus[$location])) {
foreach ($menus[$location] as $item) {
$sanitized_menus[$location][] = [
$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
@@ -275,46 +304,48 @@ class AppearanceController {
}
}
}
$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
*/
public static function save_page_settings(WP_REST_Request $request) {
public static function save_page_settings(WP_REST_Request $request)
{
$settings = get_option(self::OPTION_KEY, self::get_default_settings());
$page = $request->get_param('page');
// Get all parameters from request
$page_data = $request->get_json_params();
// Sanitize based on page type
$sanitized_data = self::sanitize_page_data($page, $page_data);
$settings['pages'][$page] = $sanitized_data;
update_option(self::OPTION_KEY, $settings);
return new WP_REST_Response([
'success' => true,
'message' => ucfirst($page) . ' page settings saved successfully',
'data' => $sanitized_data,
], 200);
}
/**
* Sanitize page-specific data
*/
private static function sanitize_page_data($page, $data) {
private static function sanitize_page_data($page, $data)
{
$sanitized = [];
switch ($page) {
case 'shop':
$sanitized = [
@@ -342,7 +373,7 @@ class AppearanceController {
],
];
break;
case 'product':
$sanitized = [
'layout' => [
@@ -366,7 +397,7 @@ class AppearanceController {
],
];
break;
case 'cart':
$sanitized = [
'layout' => [
@@ -381,7 +412,7 @@ class AppearanceController {
],
];
break;
case 'checkout':
$sanitized = [
'layout' => [
@@ -399,7 +430,7 @@ class AppearanceController {
],
];
break;
case 'thankyou':
$sanitized = [
'template' => sanitize_text_field($data['template'] ?? 'basic'),
@@ -414,7 +445,7 @@ class AppearanceController {
],
];
break;
case 'account':
$sanitized = [
'layout' => [
@@ -430,31 +461,32 @@ class AppearanceController {
];
break;
}
return $sanitized;
}
/**
* Get list of WordPress pages for page selector
*/
public static function get_pages_list(WP_REST_Request $request) {
public static function get_pages_list(WP_REST_Request $request)
{
$pages = get_pages([
'post_status' => 'publish',
'sort_column' => 'post_title',
'sort_order' => 'ASC',
]);
$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) {
$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,
@@ -463,21 +495,24 @@ class AppearanceController {
'is_store_page' => $is_store,
];
}, $pages);
return new WP_REST_Response([
'success' => true,
'data' => $pages_list,
], 200);
}
/**
* Get default settings structure
*/
public static function get_default_settings() {
public static function get_default_settings()
{
return [
'general' => [
'spa_mode' => 'full',
'spa_page' => 0,
'container_width' => 'boxed',
'toast_position' => 'top-right',
'typography' => [
'mode' => 'predefined',
@@ -516,12 +551,22 @@ class AppearanceController {
'footer' => [
'columns' => '4',
'style' => 'detailed',
'copyright_text' => '© 2024 WooNooW. All rights reserved.',
'payment' => [
'enabled' => true,
'title' => 'We accept',
'methods' => [
['id' => 'visa', 'url' => '', 'label' => 'Visa', 'default_icon' => 'visa'], // Placeholder logic for defaults
['id' => 'mastercard', 'url' => '', 'label' => 'Mastercard', 'default_icon' => 'mastercard'],
['id' => 'paypal', 'url' => '', 'label' => 'PayPal', 'default_icon' => 'paypal'],
],
],
'copyright' => [
'enabled' => true,
'text' => '© 2024 WooNooW. All rights reserved.',
],
'elements' => [
'newsletter' => true,
'social' => true,
'payment' => true,
'copyright' => true,
'menu' => true,
'contact' => true,
],