feat: Dynamic SPA slug, field label storage, and SPA frontpage support (WIP)
This commit is contained in:
@@ -72,7 +72,7 @@ class Assets
|
||||
'wpAdminUrl' => admin_url('admin.php?page=woonoow'),
|
||||
'isAuthenticated' => is_user_logged_in(),
|
||||
'pluginUrl' => trailingslashit(plugins_url('/', dirname(__DIR__))),
|
||||
'storeUrl' => home_url('/store/'),
|
||||
'storeUrl' => self::get_spa_url(),
|
||||
'customerSpaEnabled' => get_option('woonoow_customer_spa_enabled', false),
|
||||
]);
|
||||
wp_add_inline_script($handle, 'window.WNW_CONFIG = window.WNW_CONFIG || WNW_CONFIG;', 'after');
|
||||
@@ -197,7 +197,7 @@ class Assets
|
||||
'wpAdminUrl' => admin_url('admin.php?page=woonoow'),
|
||||
'isAuthenticated' => is_user_logged_in(),
|
||||
'pluginUrl' => trailingslashit(plugins_url('/', dirname(__DIR__))),
|
||||
'storeUrl' => home_url('/store/'),
|
||||
'storeUrl' => self::get_spa_url(),
|
||||
'customerSpaEnabled' => get_option('woonoow_customer_spa_enabled', false),
|
||||
]);
|
||||
|
||||
@@ -312,4 +312,21 @@ class Assets
|
||||
// Bump when releasing; in dev we don't cache-bust
|
||||
return defined('WOONOOW_VERSION') ? WOONOOW_VERSION : '0.1.0';
|
||||
}
|
||||
|
||||
/** Get the SPA page URL from appearance settings (dynamic slug) */
|
||||
private static function get_spa_url(): string
|
||||
{
|
||||
$appearance_settings = get_option('woonoow_appearance_settings', []);
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
|
||||
if ($spa_page_id) {
|
||||
$spa_url = get_permalink($spa_page_id);
|
||||
if ($spa_url) {
|
||||
return trailingslashit($spa_url);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to /store/ if no SPA page configured
|
||||
return home_url('/store/');
|
||||
}
|
||||
}
|
||||
@@ -113,9 +113,14 @@ class Menu {
|
||||
] );
|
||||
|
||||
// Add Store link if customer SPA is not disabled
|
||||
$appearance_settings = get_option('woonoow_appearance_settings', []);
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
$spa_page = get_post($spa_page_id);
|
||||
$customer_spa_enabled = get_option( 'woonoow_customer_spa_enabled', true );
|
||||
if ( $customer_spa_enabled ) {
|
||||
$store_url = home_url( '/store/' );
|
||||
if ( $customer_spa_enabled && $spa_page) {
|
||||
|
||||
$spa_slug = $spa_page->post_name;
|
||||
$store_url = home_url( '/' . $spa_slug );
|
||||
$wp_admin_bar->add_node( [
|
||||
'id' => 'woonoow-store',
|
||||
'title' => '<span class="ab-icon dashicons-cart"></span><span class="ab-label">' . __( 'Store', 'woonoow' ) . '</span>',
|
||||
|
||||
@@ -133,7 +133,7 @@ class StandaloneAdmin {
|
||||
locale: <?php echo wp_json_encode( get_locale() ); ?>,
|
||||
siteUrl: <?php echo wp_json_encode( home_url() ); ?>,
|
||||
siteName: <?php echo wp_json_encode( get_bloginfo( 'name' ) ); ?>,
|
||||
storeUrl: <?php echo wp_json_encode( home_url( '/store/' ) ); ?>,
|
||||
storeUrl: <?php echo wp_json_encode( self::get_spa_url() ); ?>,
|
||||
customerSpaEnabled: <?php echo get_option( 'woonoow_customer_spa_enabled', false ) ? 'true' : 'false'; ?>
|
||||
};
|
||||
|
||||
@@ -196,4 +196,21 @@ class StandaloneAdmin {
|
||||
'currency_pos' => (string) $currency_pos,
|
||||
];
|
||||
}
|
||||
|
||||
/** Get the SPA page URL from appearance settings (dynamic slug) */
|
||||
private static function get_spa_url(): string
|
||||
{
|
||||
$appearance_settings = get_option( 'woonoow_appearance_settings', [] );
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
|
||||
if ( $spa_page_id ) {
|
||||
$spa_url = get_permalink( $spa_page_id );
|
||||
if ( $spa_url ) {
|
||||
return trailingslashit( $spa_url );
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to /store/ if no SPA page configured
|
||||
return home_url( '/store/' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -858,25 +858,49 @@ class OrdersController {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Billing information is required for a healthy order
|
||||
$required_billing_fields = [
|
||||
'first_name' => __( 'Billing first name', 'woonoow' ),
|
||||
'last_name' => __( 'Billing last name', 'woonoow' ),
|
||||
'email' => __( 'Billing email', 'woonoow' ),
|
||||
];
|
||||
// 3. Billing information required based on checkout fields configuration
|
||||
// Get checkout field settings to respect hidden/required status from PHP snippets
|
||||
$checkout_fields = apply_filters( 'woonoow/checkout/fields', [], $items );
|
||||
|
||||
// Address fields only required for physical products
|
||||
if ( $has_physical_product ) {
|
||||
$required_billing_fields['address_1'] = __( 'Billing address', 'woonoow' );
|
||||
$required_billing_fields['city'] = __( 'Billing city', 'woonoow' );
|
||||
$required_billing_fields['postcode'] = __( 'Billing postcode', 'woonoow' );
|
||||
$required_billing_fields['country'] = __( 'Billing country', 'woonoow' );
|
||||
// Helper to check if a billing field is required
|
||||
$is_field_required = function( $field_key ) use ( $checkout_fields ) {
|
||||
foreach ( $checkout_fields as $field ) {
|
||||
if ( isset( $field['key'] ) && $field['key'] === $field_key ) {
|
||||
// Field is not required if hidden or explicitly not required
|
||||
if ( ! empty( $field['hidden'] ) || $field['type'] === 'hidden' ) {
|
||||
return false;
|
||||
}
|
||||
return ! empty( $field['required'] );
|
||||
}
|
||||
}
|
||||
// Default: core fields are required if not found in API
|
||||
return true;
|
||||
};
|
||||
|
||||
// Core billing fields - check against API configuration
|
||||
if ( $is_field_required( 'billing_first_name' ) && empty( $billing['first_name'] ) ) {
|
||||
$validation_errors[] = __( 'Billing first name is required', 'woonoow' );
|
||||
}
|
||||
|
||||
foreach ( $required_billing_fields as $field => $label ) {
|
||||
if ( empty( $billing[ $field ] ) ) {
|
||||
/* translators: %s: field label */
|
||||
$validation_errors[] = sprintf( __( '%s is required', 'woonoow' ), $label );
|
||||
if ( $is_field_required( 'billing_last_name' ) && empty( $billing['last_name'] ) ) {
|
||||
$validation_errors[] = __( 'Billing last name is required', 'woonoow' );
|
||||
}
|
||||
if ( $is_field_required( 'billing_email' ) && empty( $billing['email'] ) ) {
|
||||
$validation_errors[] = __( 'Billing email is required', 'woonoow' );
|
||||
}
|
||||
|
||||
// Address fields only required for physical products AND if not hidden
|
||||
if ( $has_physical_product ) {
|
||||
if ( $is_field_required( 'billing_address_1' ) && empty( $billing['address_1'] ) ) {
|
||||
$validation_errors[] = __( 'Billing address is required', 'woonoow' );
|
||||
}
|
||||
if ( $is_field_required( 'billing_city' ) && empty( $billing['city'] ) ) {
|
||||
$validation_errors[] = __( 'Billing city is required', 'woonoow' );
|
||||
}
|
||||
if ( $is_field_required( 'billing_postcode' ) && empty( $billing['postcode'] ) ) {
|
||||
$validation_errors[] = __( 'Billing postcode is required', 'woonoow' );
|
||||
}
|
||||
if ( $is_field_required( 'billing_country' ) && empty( $billing['country'] ) ) {
|
||||
$validation_errors[] = __( 'Billing country is required', 'woonoow' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1244,10 +1268,18 @@ class OrdersController {
|
||||
$s = sanitize_text_field( $req->get_param('search') ?? '' );
|
||||
$limit = max( 1, min( 20, absint( $req->get_param('limit') ?? 10 ) ) );
|
||||
|
||||
$args = [ 'limit' => $limit, 'status' => 'publish' ];
|
||||
if ( $s ) { $args['s'] = $s; }
|
||||
|
||||
$prods = wc_get_products( $args );
|
||||
// Use WP_Query for proper search support (wc_get_products doesn't support 's' parameter)
|
||||
$args = [
|
||||
'post_type' => [ 'product' ],
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => $limit,
|
||||
];
|
||||
if ( $s ) {
|
||||
$args['s'] = $s;
|
||||
}
|
||||
|
||||
$query = new \WP_Query( $args );
|
||||
$prods = array_filter( array_map( 'wc_get_product', $query->posts ) );
|
||||
$rows = array_map( function( $p ) {
|
||||
$data = [
|
||||
'id' => $p->get_id(),
|
||||
|
||||
@@ -86,25 +86,39 @@ class AddressController {
|
||||
// Generate new ID
|
||||
$new_id = empty($addresses) ? 1 : max(array_column($addresses, 'id')) + 1;
|
||||
|
||||
// Prepare address data
|
||||
// Standard address fields
|
||||
$standard_fields = ['first_name', 'last_name', 'company', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'email', 'phone'];
|
||||
$reserved_fields = ['id', 'label', 'type', 'is_default'];
|
||||
|
||||
// Prepare address data with standard fields
|
||||
$address = [
|
||||
'id' => $new_id,
|
||||
'label' => sanitize_text_field($request->get_param('label')),
|
||||
'type' => sanitize_text_field($request->get_param('type')), // 'billing', 'shipping', or 'both'
|
||||
'first_name' => sanitize_text_field($request->get_param('first_name')),
|
||||
'last_name' => sanitize_text_field($request->get_param('last_name')),
|
||||
'company' => sanitize_text_field($request->get_param('company')),
|
||||
'address_1' => sanitize_text_field($request->get_param('address_1')),
|
||||
'address_2' => sanitize_text_field($request->get_param('address_2')),
|
||||
'city' => sanitize_text_field($request->get_param('city')),
|
||||
'state' => sanitize_text_field($request->get_param('state')),
|
||||
'postcode' => sanitize_text_field($request->get_param('postcode')),
|
||||
'country' => sanitize_text_field($request->get_param('country')),
|
||||
'email' => sanitize_email($request->get_param('email')),
|
||||
'phone' => sanitize_text_field($request->get_param('phone')),
|
||||
'is_default' => (bool) $request->get_param('is_default'),
|
||||
];
|
||||
|
||||
// Add standard fields
|
||||
foreach ($standard_fields as $field) {
|
||||
$value = $request->get_param($field);
|
||||
if ($field === 'email') {
|
||||
$address[$field] = sanitize_email($value);
|
||||
} else {
|
||||
$address[$field] = sanitize_text_field($value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add any custom fields (like destination_id from Rajaongkir)
|
||||
$all_params = $request->get_json_params();
|
||||
if (is_array($all_params)) {
|
||||
foreach ($all_params as $key => $value) {
|
||||
if (!in_array($key, $standard_fields) && !in_array($key, $reserved_fields)) {
|
||||
// Store custom field
|
||||
$address[$key] = is_string($value) ? sanitize_text_field($value) : $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this is set as default, unset other defaults of the same type
|
||||
if ($address['is_default']) {
|
||||
foreach ($addresses as &$addr) {
|
||||
@@ -138,22 +152,36 @@ class AddressController {
|
||||
if ($addr['id'] === $address_id) {
|
||||
$found = true;
|
||||
|
||||
// Update fields
|
||||
$addr['label'] = sanitize_text_field($request->get_param('label'));
|
||||
$addr['type'] = sanitize_text_field($request->get_param('type'));
|
||||
$addr['first_name'] = sanitize_text_field($request->get_param('first_name'));
|
||||
$addr['last_name'] = sanitize_text_field($request->get_param('last_name'));
|
||||
$addr['company'] = sanitize_text_field($request->get_param('company'));
|
||||
$addr['address_1'] = sanitize_text_field($request->get_param('address_1'));
|
||||
$addr['address_2'] = sanitize_text_field($request->get_param('address_2'));
|
||||
$addr['city'] = sanitize_text_field($request->get_param('city'));
|
||||
$addr['state'] = sanitize_text_field($request->get_param('state'));
|
||||
$addr['postcode'] = sanitize_text_field($request->get_param('postcode'));
|
||||
$addr['country'] = sanitize_text_field($request->get_param('country'));
|
||||
$addr['email'] = sanitize_email($request->get_param('email'));
|
||||
$addr['phone'] = sanitize_text_field($request->get_param('phone'));
|
||||
// Standard address fields
|
||||
$standard_fields = ['first_name', 'last_name', 'company', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'email', 'phone'];
|
||||
$reserved_fields = ['id', 'label', 'type', 'is_default'];
|
||||
|
||||
// Update standard meta fields
|
||||
$addr['label'] = sanitize_text_field($request->get_param('label'));
|
||||
$addr['type'] = sanitize_text_field($request->get_param('type'));
|
||||
$addr['is_default'] = (bool) $request->get_param('is_default');
|
||||
|
||||
// Update standard fields
|
||||
foreach ($standard_fields as $field) {
|
||||
$value = $request->get_param($field);
|
||||
if ($field === 'email') {
|
||||
$addr[$field] = sanitize_email($value);
|
||||
} else {
|
||||
$addr[$field] = sanitize_text_field($value);
|
||||
}
|
||||
}
|
||||
|
||||
// Update any custom fields (like destination_id from Rajaongkir)
|
||||
$all_params = $request->get_json_params();
|
||||
if (is_array($all_params)) {
|
||||
foreach ($all_params as $key => $value) {
|
||||
if (!in_array($key, $standard_fields) && !in_array($key, $reserved_fields)) {
|
||||
// Store/update custom field
|
||||
$addr[$key] = is_string($value) ? sanitize_text_field($value) : $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this is set as default, unset other defaults of the same type
|
||||
if ($addr['is_default']) {
|
||||
foreach ($addresses as &$other_addr) {
|
||||
|
||||
@@ -197,7 +197,13 @@ class Assets {
|
||||
// Determine SPA base path for BrowserRouter
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
$spa_page = $spa_page_id ? get_post($spa_page_id) : null;
|
||||
$base_path = $spa_page ? '/' . $spa_page->post_name : '/store';
|
||||
|
||||
// Check if SPA page is set as WordPress frontpage
|
||||
$frontpage_id = (int) get_option('page_on_front');
|
||||
$is_spa_frontpage = $frontpage_id && $spa_page_id && $frontpage_id === (int) $spa_page_id;
|
||||
|
||||
// If SPA is frontpage, base path is /, otherwise use page slug
|
||||
$base_path = $is_spa_frontpage ? '' : ($spa_page ? '/' . $spa_page->post_name : '/store');
|
||||
|
||||
// Check if BrowserRouter is enabled (default: true for SEO)
|
||||
$use_browser_router = $appearance_settings['general']['use_browser_router'] ?? true;
|
||||
@@ -249,6 +255,16 @@ class Assets {
|
||||
private static function should_load_assets() {
|
||||
global $post;
|
||||
|
||||
// Check if we're serving SPA directly (set by serve_spa_for_frontpage_routes)
|
||||
if (defined('WOONOOW_SERVE_SPA') && WOONOOW_SERVE_SPA) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if we're on a frontpage SPA route (by URL detection)
|
||||
if (self::is_frontpage_spa_route()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// First check: Is this a designated SPA page?
|
||||
if (self::is_spa_page()) {
|
||||
return true;
|
||||
@@ -366,6 +382,51 @@ class Assets {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current request is a frontpage SPA route
|
||||
* Used to detect SPA routes by URL when SPA page is set as frontpage
|
||||
*/
|
||||
private static function is_frontpage_spa_route() {
|
||||
// Get SPA settings
|
||||
$appearance_settings = get_option('woonoow_appearance_settings', []);
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
$spa_mode = $appearance_settings['general']['spa_mode'] ?? 'full';
|
||||
|
||||
// Only run in full SPA mode
|
||||
if ($spa_mode !== 'full' || !$spa_page_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if SPA page is set as WordPress frontpage
|
||||
$frontpage_id = (int) get_option('page_on_front');
|
||||
if (!$frontpage_id || $frontpage_id !== (int) $spa_page_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the current request path
|
||||
$request_uri = $_SERVER['REQUEST_URI'] ?? '/';
|
||||
$path = parse_url($request_uri, PHP_URL_PATH);
|
||||
$path = '/' . trim($path, '/');
|
||||
|
||||
// Define SPA routes
|
||||
$spa_routes = ['/', '/shop', '/cart', '/checkout', '/my-account', '/login', '/register', '/reset-password'];
|
||||
|
||||
// Check exact matches
|
||||
if (in_array($path, $spa_routes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check path prefixes
|
||||
$prefix_routes = ['/shop/', '/my-account/', '/product/'];
|
||||
foreach ($prefix_routes as $prefix) {
|
||||
if (strpos($path, $prefix) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue conflicting scripts when SPA is active
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,9 @@ class TemplateOverride
|
||||
// Redirect WooCommerce pages to SPA routes early (before template loads)
|
||||
add_action('template_redirect', [__CLASS__, 'redirect_wc_pages_to_spa'], 5);
|
||||
|
||||
// Serve SPA directly for frontpage routes (priority 1 = very early, before WC)
|
||||
add_action('template_redirect', [__CLASS__, 'serve_spa_for_frontpage_routes'], 1);
|
||||
|
||||
// Hook to wp_loaded with priority 10 (BEFORE WooCommerce's priority 20)
|
||||
// This ensures we process add-to-cart before WooCommerce does
|
||||
add_action('wp_loaded', [__CLASS__, 'intercept_add_to_cart'], 10);
|
||||
@@ -68,7 +71,7 @@ class TemplateOverride
|
||||
|
||||
/**
|
||||
* Register rewrite rules for BrowserRouter SEO
|
||||
* Catches all /store/* routes and serves the SPA page
|
||||
* Catches all SPA routes and serves the SPA page
|
||||
*/
|
||||
public static function register_spa_rewrite_rules()
|
||||
{
|
||||
@@ -89,13 +92,82 @@ class TemplateOverride
|
||||
|
||||
$spa_slug = $spa_page->post_name;
|
||||
|
||||
// Rewrite /store/anything to serve the SPA page
|
||||
// React Router handles the path after that
|
||||
add_rewrite_rule(
|
||||
'^' . preg_quote($spa_slug, '/') . '/(.*)$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=$matches[1]',
|
||||
'top'
|
||||
);
|
||||
// Check if SPA page is set as WordPress frontpage
|
||||
$frontpage_id = (int) get_option('page_on_front');
|
||||
$is_spa_frontpage = $frontpage_id && $frontpage_id === (int) $spa_page_id;
|
||||
|
||||
if ($is_spa_frontpage) {
|
||||
// When SPA is frontpage, add root-level routes
|
||||
// /shop, /shop/* → SPA page
|
||||
add_rewrite_rule(
|
||||
'^shop/?$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=shop',
|
||||
'top'
|
||||
);
|
||||
add_rewrite_rule(
|
||||
'^shop/(.*)$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=shop/$matches[1]',
|
||||
'top'
|
||||
);
|
||||
|
||||
// /product/* → SPA page
|
||||
add_rewrite_rule(
|
||||
'^product/(.*)$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=product/$matches[1]',
|
||||
'top'
|
||||
);
|
||||
|
||||
// /cart → SPA page
|
||||
add_rewrite_rule(
|
||||
'^cart/?$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=cart',
|
||||
'top'
|
||||
);
|
||||
|
||||
// /checkout → SPA page
|
||||
add_rewrite_rule(
|
||||
'^checkout/?$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=checkout',
|
||||
'top'
|
||||
);
|
||||
|
||||
// /my-account, /my-account/* → SPA page
|
||||
add_rewrite_rule(
|
||||
'^my-account/?$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=my-account',
|
||||
'top'
|
||||
);
|
||||
add_rewrite_rule(
|
||||
'^my-account/(.*)$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=my-account/$matches[1]',
|
||||
'top'
|
||||
);
|
||||
|
||||
// /login, /register, /reset-password → SPA page
|
||||
add_rewrite_rule(
|
||||
'^login/?$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=login',
|
||||
'top'
|
||||
);
|
||||
add_rewrite_rule(
|
||||
'^register/?$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=register',
|
||||
'top'
|
||||
);
|
||||
add_rewrite_rule(
|
||||
'^reset-password/?$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=reset-password',
|
||||
'top'
|
||||
);
|
||||
} else {
|
||||
// Rewrite /slug/anything to serve the SPA page
|
||||
// React Router handles the path after that
|
||||
add_rewrite_rule(
|
||||
'^' . preg_quote($spa_slug, '/') . '/(.*)$',
|
||||
'index.php?page_id=' . $spa_page_id . '&woonoow_spa_path=$matches[1]',
|
||||
'top'
|
||||
);
|
||||
}
|
||||
|
||||
// Register query var for the SPA path
|
||||
add_filter('query_vars', function($vars) {
|
||||
@@ -174,6 +246,12 @@ class TemplateOverride
|
||||
return; // No SPA page configured
|
||||
}
|
||||
|
||||
// Skip if SPA is set as frontpage (serve_spa_for_frontpage_routes handles it)
|
||||
$frontpage_id = (int) get_option('page_on_front');
|
||||
if ($frontpage_id && $frontpage_id === (int) $spa_page_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Already on SPA page, don't redirect
|
||||
global $post;
|
||||
if ($post && $post->ID == $spa_page_id) {
|
||||
@@ -224,6 +302,88 @@ class TemplateOverride
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve SPA template directly for frontpage SPA routes
|
||||
* When SPA page is set as WordPress frontpage, intercept known routes
|
||||
* and serve the SPA template directly (bypasses WooCommerce templates)
|
||||
*/
|
||||
public static function serve_spa_for_frontpage_routes()
|
||||
{
|
||||
// Get SPA settings
|
||||
$appearance_settings = get_option('woonoow_appearance_settings', []);
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
$spa_mode = $appearance_settings['general']['spa_mode'] ?? 'full';
|
||||
|
||||
// Only run in full SPA mode
|
||||
if ($spa_mode !== 'full' || !$spa_page_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if SPA page is set as WordPress frontpage
|
||||
$frontpage_id = (int) get_option('page_on_front');
|
||||
if (!$frontpage_id || $frontpage_id !== (int) $spa_page_id) {
|
||||
return; // SPA is not frontpage, let normal routing handle it
|
||||
}
|
||||
|
||||
// Get the current request path
|
||||
$request_uri = $_SERVER['REQUEST_URI'] ?? '/';
|
||||
$path = parse_url($request_uri, PHP_URL_PATH);
|
||||
$path = '/' . trim($path, '/');
|
||||
|
||||
// Define SPA routes that should be intercepted when SPA is frontpage
|
||||
$spa_routes = [
|
||||
'/', // Frontpage itself
|
||||
'/shop', // Shop page
|
||||
'/cart', // Cart page
|
||||
'/checkout', // Checkout page
|
||||
'/my-account', // Account page
|
||||
'/login', // Login page
|
||||
'/register', // Register page
|
||||
'/reset-password', // Password reset
|
||||
];
|
||||
|
||||
// Check for exact matches or path prefixes
|
||||
$should_serve_spa = false;
|
||||
|
||||
// Check exact matches
|
||||
if (in_array($path, $spa_routes)) {
|
||||
$should_serve_spa = true;
|
||||
}
|
||||
|
||||
// Check path prefixes (for sub-routes)
|
||||
$prefix_routes = ['/shop/', '/my-account/', '/product/'];
|
||||
foreach ($prefix_routes as $prefix) {
|
||||
if (strpos($path, $prefix) === 0) {
|
||||
$should_serve_spa = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Not a SPA route
|
||||
if (!$should_serve_spa) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent caching for dynamic SPA content
|
||||
nocache_headers();
|
||||
|
||||
// Load the SPA template directly and exit
|
||||
$spa_template = plugin_dir_path(dirname(dirname(__FILE__))) . 'templates/spa-full-page.php';
|
||||
if (file_exists($spa_template)) {
|
||||
// Set up minimal WordPress environment for the template
|
||||
status_header(200);
|
||||
|
||||
// Define constant to tell Assets to load unconditionally
|
||||
if (!defined('WOONOOW_SERVE_SPA')) {
|
||||
define('WOONOOW_SERVE_SPA', true);
|
||||
}
|
||||
|
||||
// Include the SPA template
|
||||
include $spa_template;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable canonical redirects for SPA routes
|
||||
* This prevents WordPress from redirecting /product/slug URLs
|
||||
@@ -406,17 +566,25 @@ class TemplateOverride
|
||||
private static function is_spa_page()
|
||||
{
|
||||
global $post;
|
||||
if (!$post) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Get SPA settings from appearance
|
||||
$appearance_settings = get_option('woonoow_appearance_settings', []);
|
||||
$spa_page_id = $appearance_settings['general']['spa_page'] ?? 0;
|
||||
$spa_mode = $appearance_settings['general']['spa_mode'] ?? 'full';
|
||||
|
||||
// Only return true if spa_mode is 'full' AND we're on the SPA page
|
||||
if ($spa_mode === 'full' && $spa_page_id && $post->ID == $spa_page_id) {
|
||||
// Only check if spa_mode is 'full' and SPA page is configured
|
||||
if ($spa_mode !== 'full' || !$spa_page_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if current page is the SPA page
|
||||
if ($post && $post->ID == $spa_page_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if SPA page is set as WordPress frontpage and we're on frontpage
|
||||
$frontpage_id = (int) get_option('page_on_front');
|
||||
if ($frontpage_id && $frontpage_id === (int) $spa_page_id && is_front_page()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user