Files
AsterionWP2026/wp-content/themes/asterion-bricks/template-parts/conversion-form.php
j.foucher f18a57e83f feat(wpml): refactor to post_content + duplicate site in French via WPML
This commit makes the site editable from WP admin (Gutenberg or Bricks
"Edit with Bricks") and translatable via WPML Translation Editor by
shifting page rendering away from Custom Templates and into post_content.

Architecture change
-------------------
Before: Custom Templates assigned to each page (page-solution-detail.php
etc.) generated the page HTML at runtime by reading from inc/*-data.php
arrays. WPML had nothing to translate, Bricks Builder had nothing to
edit, the client could only modify content by editing PHP.

After:
- Pages now hold their full Gutenberg HTML inside post_content
- The default page.php template renders the_content() — no Custom
  Templates assigned anymore
- Bricks Builder picks up post_content and converts it to its element
  tree when "Edit with Bricks" is clicked
- WPML Translation Editor exposes every block for translation
- The client can edit pages directly in WP admin

What this commit ships
----------------------
- inc/render-blocks.php: data array → Gutenberg HTML payload generator
  with helpers per section type (prose / cards / list / cta) and per
  page archetype (solution / industry / technology / editorial / form
  / overview hub).
- inc/shortcodes.php: [av_form slug="request-demo"] for dynamic form
  blocks (forms can't live as static HTML — they need nonces and
  per-request state).
- inc/i18n.php: av_lang() / av_t() helpers for inline string translation
  in header.php and footer.php (covers ~80 UI strings); switches based
  on WPML's wpml_current_language filter.
- page.php: minimal default template — get_header() + the_content() +
  get_footer().
- header.php: real WPML language switcher driven by
  wpml_active_languages filter (replaces the placeholder EN · FR
  toggle); UI strings now route through av_t().
- footer.php: same UI string treatment.
- Custom Templates retained but no longer assigned (pages were stripped
  by the migration mu-plugin); they remain available if a page wants
  to opt back into the data-file pattern.

Operational migrations (one-shot mu-plugins, not versioned)
-----------------------------------------------------------
1. asterion-migrate-to-blocks.php walked all 26 PHP-templated pages,
   pre-rendered them via the new render-blocks helpers, wrote the
   result to post_content, and stripped _wp_page_template.
2. asterion-duplicate-fr.php cloned all 35 pages into French via WPML
   API, preserved parent/child hierarchy, linked translations through
   the trid table, marked them as duplicates pending refinement.
3. asterion-fix-fr-slugs.php aligned French slugs with English ones
   (WP's wp_unique_post_slug() had appended -2 suffixes; we bypass
   via $wpdb->update so /fr/solutions/ resolves correctly).

Verified
--------
- All 35 EN URLs return HTTP 200
- All 35 FR URLs return HTTP 200 under /fr/<slug>/
- Language switcher in header outputs correct hreflang links
- WPML emits hreflang en / fr / x-default in <head>
- Body class confirms wp-child-theme-asterion-bricks active
- Bricks frontend CSS still enqueued
- Inter Variable still preloaded

Next steps
----------
- French content is currently the duplicated EN copy. Translate via
  WP admin → WPML → Translation Editor (per-block UI), or edit pages
  directly in /fr/<slug>/ via Gutenberg / Bricks
- WPML String Translation can scan the theme to surface av_t() / __()
  strings for UI-level translation overrides

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 13:11:42 +02:00

210 lines
10 KiB
PHP

<?php
/**
* Asterion Bricks — Conversion form partial
*
* Renders one conversion page (Demo / T&E / Quote / Contact). Two-column
* hero (form left, promise right) + optional eligibility list + FAQ.
*
* Data passed via `set_query_var('av_form', $form_data)`.
*
* Form posts to admin-post.php with action=av_form_submit; the handler in
* inc/form-handler.php validates the nonce, sanitizes, and emails the lead
* to jerome.foucher@asterionvr.com.
*
* @package AsterionBricks
*/
defined('ABSPATH') || exit;
$form = get_query_var('av_form');
if (! is_array($form)) {
return;
}
$home_url = home_url('/');
$slug_override = get_query_var('av_form_slug_override');
$slug = $slug_override ?: get_post_field('post_name', get_queried_object_id());
// Surface success/error from query string (set by the handler on redirect).
$av_status = isset($_GET['av_status']) ? sanitize_key($_GET['av_status']) : '';
?>
<!-- HERO -->
<section class="av-hero" aria-labelledby="form-headline">
<div class="av-hero__media" aria-hidden="true">
<div style="width:100%;height:100%;background:
radial-gradient(ellipse at 50% 30%, rgba(201,164,90,0.12), transparent 60%),
linear-gradient(135deg, #081427 0%, #0B1F3A 50%, #13294B 100%);"></div>
</div>
<div class="av-container">
<div class="av-hero__inner">
<p class="av-hero__eyebrow"><?php echo esc_html($form['eyebrow']); ?></p>
<h1 id="form-headline" class="av-hero__headline" style="font-size:clamp(2.25rem,5vw,var(--text-display-lg));line-height:var(--leading-display-lg);"><?php echo esc_html($form['tagline']); ?></h1>
<p class="av-hero__sub"><?php echo esc_html($form['hero_lead']); ?></p>
<?php if (! empty($form['shortcuts'])) : ?>
<div class="av-hero__ctas">
<?php foreach ($form['shortcuts'] as $sc) :
$variant = ($sc['variant'] === 'primary') ? 'av-btn--primary' : 'av-btn--on-dark av-btn--secondary';
?>
<a href="<?php echo esc_url($home_url . ltrim($sc['href'], '/')); ?>" class="av-btn <?php echo esc_attr($variant); ?> av-btn--md">
<?php echo esc_html($sc['label']); ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</section>
<!-- TWO-COLUMN: form (left) + promise (right) -->
<section class="av-section" aria-labelledby="form-title">
<div class="av-container">
<?php if ($av_status === 'success') : ?>
<div role="status" aria-live="polite" style="padding:var(--space-6);background:rgba(31,139,76,0.08);border-left:3px solid var(--color-success);border-radius:var(--radius-sm);margin-bottom:var(--space-8);">
<strong><?php esc_html_e('Thank you — your request was sent.', 'asterion-bricks'); ?></strong><br>
<?php esc_html_e('We will reply within the timeframe stated above. Check your inbox (and spam) for confirmation.', 'asterion-bricks'); ?>
</div>
<?php elseif ($av_status === 'error') : ?>
<div role="alert" style="padding:var(--space-6);background:rgba(200,16,46,0.06);border-left:3px solid var(--color-error);border-radius:var(--radius-sm);margin-bottom:var(--space-8);">
<strong><?php esc_html_e('Something went wrong.', 'asterion-bricks'); ?></strong>
<?php esc_html_e(' Please try again, or email us directly at hello@asterionvr.com.', 'asterion-bricks'); ?>
</div>
<?php endif; ?>
<div style="display:grid;grid-template-columns:1fr;gap:var(--space-12);" class="av-form-grid">
<!-- FORM -->
<div>
<h2 id="form-title" class="av-section__title" style="font-size:var(--text-h2);"><?php esc_html_e('Tell us about your unit.', 'asterion-bricks'); ?></h2>
<p class="av-section__lead" style="margin-bottom:var(--space-6);"><?php echo esc_html($form['reassurance']); ?></p>
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="av-form">
<?php wp_nonce_field('av_form_submit', 'av_nonce'); ?>
<input type="hidden" name="action" value="av_form_submit">
<input type="hidden" name="form_slug" value="<?php echo esc_attr($slug); ?>">
<input type="hidden" name="redirect_to" value="<?php echo esc_attr(get_permalink()); ?>">
<input type="text" name="av_hp" value="" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px;" aria-hidden="true">
<div class="av-stack av-stack--lg">
<?php foreach ($form['fields'] as $field) :
$field_id = 'av-' . esc_attr($field['name']);
$required = ! empty($field['required']);
?>
<div>
<label for="<?php echo esc_attr($field_id); ?>" class="av-label">
<?php echo esc_html($field['label']); ?>
<?php if ($required) : ?><span class="av-label__required" aria-hidden="true">*</span><?php endif; ?>
</label>
<?php if ($field['type'] === 'textarea') : ?>
<textarea
id="<?php echo esc_attr($field_id); ?>"
name="<?php echo esc_attr($field['name']); ?>"
class="av-textarea"
rows="5"
<?php echo $required ? 'required aria-required="true"' : ''; ?>></textarea>
<?php elseif ($field['type'] === 'select') : ?>
<select
id="<?php echo esc_attr($field_id); ?>"
name="<?php echo esc_attr($field['name']); ?>"
class="av-select"
<?php echo $required ? 'required aria-required="true"' : ''; ?>>
<?php foreach ($field['options'] as $opt) : ?>
<option value="<?php echo esc_attr($opt); ?>"<?php echo $opt === '' ? ' disabled selected' : ''; ?>>
<?php echo $opt === '' ? esc_html__('— select —', 'asterion-bricks') : esc_html($opt); ?>
</option>
<?php endforeach; ?>
</select>
<?php else : ?>
<input
type="<?php echo esc_attr($field['type']); ?>"
id="<?php echo esc_attr($field_id); ?>"
name="<?php echo esc_attr($field['name']); ?>"
class="av-input"
<?php echo $required ? 'required aria-required="true"' : ''; ?>
autocomplete="<?php echo $field['type'] === 'email' ? 'email' : 'on'; ?>">
<?php endif; ?>
</div>
<?php endforeach; ?>
<button type="submit" class="av-btn av-btn--primary av-btn--lg" style="width:auto;align-self:flex-start;">
<?php echo esc_html($form['submit_label'] ?? __('Send', 'asterion-bricks')); ?>
</button>
</div>
</form>
</div>
<!-- PROMISE -->
<aside style="background:var(--color-bg-subtle);padding:var(--space-8);border-radius:var(--radius-md);align-self:start;">
<h2 style="font-family:var(--font-display);font-size:var(--text-h3);font-weight:var(--font-weight-semibold);margin-bottom:var(--space-4);">
<?php echo esc_html($form['promise_title']); ?>
</h2>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-3);font-size:var(--text-body-md);line-height:var(--leading-body-md);color:var(--color-text-secondary);">
<?php foreach ($form['promise_items'] as $item) : ?>
<li style="padding-left:var(--space-6);position:relative;">
<span aria-hidden="true" style="position:absolute;left:0;top:0.55em;width:8px;height:8px;background:var(--color-brand-gold);border-radius:50%;"></span>
<?php echo esc_html($item); ?>
</li>
<?php endforeach; ?>
</ul>
</aside>
</div>
<style>
@media (min-width: 1024px) {
.av-form-grid { grid-template-columns: 1.4fr 1fr !important; }
}
</style>
</div>
</section>
<!-- ELIGIBILITY (T&E only) -->
<?php if (! empty($form['eligibility'])) : ?>
<section class="av-section av-section--dark" data-av-reveal>
<div class="av-container">
<div class="av-section__head">
<span class="av-eyebrow av-eyebrow--gold"><?php esc_html_e('Eligibility', 'asterion-bricks'); ?></span>
<h2 class="av-section__title"><?php echo esc_html($form['eligibility']['title']); ?></h2>
<p class="av-section__lead"><?php echo esc_html($form['eligibility']['lead']); ?></p>
</div>
<ul style="list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-3);max-width:72ch;color:var(--color-text-on-dark-muted);">
<?php foreach ($form['eligibility']['items'] as $item) : ?>
<li style="padding-left:var(--space-6);position:relative;">
<span aria-hidden="true" style="position:absolute;left:0;top:0.6em;width:8px;height:8px;background:var(--color-brand-gold);border-radius:50%;"></span>
<?php echo esc_html($item); ?>
</li>
<?php endforeach; ?>
</ul>
</div>
</section>
<?php endif; ?>
<!-- FAQ (Demo only) -->
<?php if (! empty($form['faq'])) : ?>
<section class="av-section" data-av-reveal>
<div class="av-container av-container--narrow">
<div class="av-section__head">
<span class="av-eyebrow"><?php esc_html_e('Frequently asked', 'asterion-bricks'); ?></span>
<h2 class="av-section__title"><?php esc_html_e('Quick answers.', 'asterion-bricks'); ?></h2>
</div>
<div class="av-stack av-stack--lg">
<?php foreach ($form['faq'] as $i => $qa) : ?>
<details style="border-bottom:1px solid var(--color-border-subtle);padding-block:var(--space-4);">
<summary style="font-family:var(--font-display);font-size:var(--text-h4);font-weight:var(--font-weight-semibold);cursor:pointer;list-style:none;display:flex;justify-content:space-between;align-items:center;">
<?php echo esc_html($qa['q']); ?>
<span aria-hidden="true">+</span>
</summary>
<p style="margin-top:var(--space-3);color:var(--color-text-secondary);font-size:var(--text-body-md);line-height:var(--leading-body-md);">
<?php echo esc_html($qa['a']); ?>
</p>
</details>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>