feat: Page Editor Phase 3 - SSR integration and navigation
- Implement serve_ssr_content with full PageSSR rendering - SEO meta tags (title, description, og:*) - Minimal CSS for bot-friendly presentation - Yoast/Rank Math SEO data integration - Add maybe_serve_ssr_for_bots hook (priority 2 on template_redirect) - Serves SSR for structural pages with WooNooW structure - Serves SSR for CPT items with templates - Add use statements for PageSSR and PlaceholderRenderer - Add Pages link to Appearance submenu in NavigationRegistry - Bump NAV_VERSION to 1.1.0
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
<?php
|
||||
namespace WooNooW\Frontend;
|
||||
|
||||
use WooNooW\Frontend\PageSSR;
|
||||
use WooNooW\Frontend\PlaceholderRenderer;
|
||||
|
||||
/**
|
||||
* Template Override
|
||||
* Overrides WooCommerce templates to use WooNooW SPA
|
||||
@@ -38,6 +41,9 @@ class TemplateOverride
|
||||
// Serve SPA directly for frontpage routes (priority 1 = very early, before WC)
|
||||
add_action('template_redirect', [__CLASS__, 'serve_spa_for_frontpage_routes'], 1);
|
||||
|
||||
// Serve SSR for bots on pages/CPT with WooNooW structure (priority 2 = after frontpage check)
|
||||
add_action('template_redirect', [__CLASS__, 'maybe_serve_ssr_for_bots'], 2);
|
||||
|
||||
// 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);
|
||||
@@ -723,15 +729,15 @@ class TemplateOverride
|
||||
*
|
||||
* @param int $page_id Page ID to render
|
||||
* @param string $type 'page' or 'template'
|
||||
* @param array|null $post_data Post data for template rendering (CPT items)
|
||||
* @param \WP_Post|null $post_obj Post object for template rendering (CPT items)
|
||||
*/
|
||||
public static function serve_ssr_content($page_id, $type = 'page', $post_data = null)
|
||||
public static function serve_ssr_content($page_id, $type = 'page', $post_obj = null)
|
||||
{
|
||||
// Get page structure
|
||||
if ($type === 'page') {
|
||||
$structure = get_post_meta($page_id, '_wn_page_structure', true);
|
||||
} else {
|
||||
// CPT template
|
||||
// CPT template - type is the post_type like 'post', 'portfolio', etc.
|
||||
$structure = get_option("wn_template_{$type}", null);
|
||||
}
|
||||
|
||||
@@ -739,9 +745,120 @@ class TemplateOverride
|
||||
return false; // No structure, let normal WP handle it
|
||||
}
|
||||
|
||||
// Will be implemented in PageSSR class
|
||||
// For now, return false to let normal WP handle
|
||||
// TODO: Implement PageSSR::render($structure, $post_data)
|
||||
return false;
|
||||
// Render using PageSSR
|
||||
$post_data = null;
|
||||
if ($post_obj && $type !== 'page') {
|
||||
$placeholder_renderer = new PlaceholderRenderer();
|
||||
$post_data = $placeholder_renderer->build_post_data($post_obj);
|
||||
}
|
||||
|
||||
$ssr = new PageSSR();
|
||||
$html = $ssr->render($structure['sections'], $post_data);
|
||||
|
||||
if (empty($html)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get page title
|
||||
$title = $type === 'page' ? get_the_title($page_id) : '';
|
||||
if ($post_obj) {
|
||||
$title = get_the_title($post_obj);
|
||||
}
|
||||
|
||||
// SEO data
|
||||
$seo_title = $title . ' - ' . get_bloginfo('name');
|
||||
$seo_description = '';
|
||||
|
||||
// Try to get Yoast/Rank Math SEO data
|
||||
if ($type === 'page') {
|
||||
$seo_title = get_post_meta($page_id, '_yoast_wpseo_title', true) ?:
|
||||
get_post_meta($page_id, 'rank_math_title', true) ?: $seo_title;
|
||||
$seo_description = get_post_meta($page_id, '_yoast_wpseo_metadesc', true) ?:
|
||||
get_post_meta($page_id, 'rank_math_description', true) ?: '';
|
||||
} elseif ($post_obj) {
|
||||
$seo_title = get_post_meta($post_obj->ID, '_yoast_wpseo_title', true) ?:
|
||||
get_post_meta($post_obj->ID, 'rank_math_title', true) ?: $seo_title;
|
||||
$seo_description = get_post_meta($post_obj->ID, '_yoast_wpseo_metadesc', true) ?:
|
||||
get_post_meta($post_obj->ID, 'rank_math_description', true) ?:
|
||||
wp_trim_words(wp_strip_all_tags($post_obj->post_content), 30);
|
||||
}
|
||||
|
||||
// Output SSR HTML
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html <?php language_attributes(); ?>>
|
||||
<head>
|
||||
<meta charset="<?php bloginfo('charset'); ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?php echo esc_html($seo_title); ?></title>
|
||||
<?php if ($seo_description): ?>
|
||||
<meta name="description" content="<?php echo esc_attr($seo_description); ?>">
|
||||
<?php endif; ?>
|
||||
<link rel="canonical" href="<?php echo esc_url(get_permalink($post_obj ?: $page_id)); ?>">
|
||||
<meta property="og:title" content="<?php echo esc_attr($seo_title); ?>">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="<?php echo esc_url(get_permalink($post_obj ?: $page_id)); ?>">
|
||||
<?php if ($seo_description): ?>
|
||||
<meta property="og:description" content="<?php echo esc_attr($seo_description); ?>">
|
||||
<?php endif; ?>
|
||||
<style>
|
||||
/* Minimal SSR styles for bots */
|
||||
body { font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; margin: 0; padding: 0; }
|
||||
.wn-ssr { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
||||
.wn-section { padding: 40px 0; }
|
||||
.wn-section h1, .wn-section h2 { margin-bottom: 16px; }
|
||||
.wn-section p { margin-bottom: 12px; }
|
||||
.wn-section img { max-width: 100%; height: auto; }
|
||||
.wn-hero { background: #f5f5f5; padding: 60px 20px; text-align: center; }
|
||||
.wn-cta-banner { background: #4f46e5; color: white; padding: 40px 20px; text-align: center; }
|
||||
.wn-cta-banner a { color: white; text-decoration: underline; }
|
||||
.wn-feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 24px; }
|
||||
.wn-feature-item { padding: 20px; border: 1px solid #e5e5e5; border-radius: 8px; }
|
||||
</style>
|
||||
<?php wp_head(); ?>
|
||||
</head>
|
||||
<body <?php body_class('wn-ssr-page'); ?>>
|
||||
<div class="wn-ssr">
|
||||
<?php echo $html; ?>
|
||||
</div>
|
||||
<?php wp_footer(); ?>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle SSR for structural pages and CPT items when bot detected
|
||||
* Should be called from template_redirect hook
|
||||
*/
|
||||
public static function maybe_serve_ssr_for_bots()
|
||||
{
|
||||
// Only serve SSR for bots
|
||||
if (!self::is_bot()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a page with WooNooW structure
|
||||
if (is_singular('page')) {
|
||||
$page_id = get_queried_object_id();
|
||||
$structure = get_post_meta($page_id, '_wn_page_structure', true);
|
||||
|
||||
if (!empty($structure) && !empty($structure['sections'])) {
|
||||
self::serve_ssr_content($page_id, 'page');
|
||||
}
|
||||
}
|
||||
|
||||
// Check for CPT items with templates
|
||||
$post_type = get_post_type();
|
||||
if ($post_type && is_singular() && $post_type !== 'page') {
|
||||
$template = get_option("wn_template_{$post_type}", null);
|
||||
|
||||
if (!empty($template) && !empty($template['sections'])) {
|
||||
$post_obj = get_queried_object();
|
||||
self::serve_ssr_content($post_obj->ID, $post_type, $post_obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user