feat(en-pages): build remaining 14 pages (Editorial / Forms / Hubs / Legal)

Brings the EN site to 34 live pages, completing the original sitemap
from PDF Strategie & Contenu Partie C.1.

Editorial section (5 pages, page-editorial-detail.php template)
- /why-asterion/, /about/, /customers/, /trust/, /partners/
- inc/editorial-data.php centralizes the copy from D.18-D.21, D.28
- Reuses the industry-detail partial (same section schema)

Conversion section (4 pages, page-conversion-form.php template)
- /request-demo/, /request-te-kit/, /request-quote/, /contact/
- inc/forms-data.php declares fields per page (text/email/select/textarea)
- template-parts/conversion-form.php renders two-column layout
  (form left, promise right), eligibility list (T&E), FAQ (Demo)
- inc/form-handler.php: nonce-protected POST handler with honeypot,
  per-field sanitizer, wp_mail to AV_LEADS_RECIPIENT (defaults to
  jerome.foucher@asterionvr.com, override via wp-config.php define),
  redirect-back-with-status pattern.

Insights & Resources (page-insights-hub.php, page-resources.php)
- /insights/ queries `post`, falls back to placeholder when empty
- /resources/ lists 4 resource groups (datasheets, whitepapers,
  videos, brochures) per D.23

Legal (page-legal.php template, parent + 4 children)
- /legal/, /legal/privacy/, /legal/terms/, /legal/cookies/, /legal/notice/
- Renders page content if filled, falls back to slug-driven section
  skeleton (per D.29) for counsel to draft against.

All 16 new URLs verified HTTP 200, sizes 49-66 KB each. Mu-plugin
asterion-seed-rest.php in LocalWP self-deleted after seeding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-09 12:41:36 +02:00
parent 45b0b95232
commit 70864129ed
10 changed files with 1066 additions and 2 deletions

View File

@@ -0,0 +1,108 @@
<?php
/**
* Asterion Bricks — Conversion form handler
*
* Receives POSTs from /request-demo/, /request-te-kit/, /request-quote/,
* /contact/. Validates the WordPress nonce, screens the honeypot, sanitizes
* the payload, and emails the lead to the configured recipient.
*
* Hook : admin_post_av_form_submit (logged-out users use admin_post_nopriv).
*
* Lead destination defaults to AV_LEADS_RECIPIENT (override in wp-config.php),
* falling back to the WP admin email.
*
* @package AsterionBricks
*/
defined('ABSPATH') || exit;
if (! defined('AV_LEADS_RECIPIENT')) {
define('AV_LEADS_RECIPIENT', 'jerome.foucher@asterionvr.com');
}
function av_handle_form_submit() {
// 1. Nonce
if (! isset($_POST['av_nonce']) || ! wp_verify_nonce(wp_unslash($_POST['av_nonce']), 'av_form_submit')) {
wp_safe_redirect(home_url('/?av_status=error'));
exit;
}
// 2. Honeypot — bots fill anything
if (! empty($_POST['av_hp'])) {
// Pretend success so bots don't retry.
$redirect = isset($_POST['redirect_to']) ? esc_url_raw(wp_unslash($_POST['redirect_to'])) : home_url('/');
wp_safe_redirect(add_query_arg('av_status', 'success', $redirect));
exit;
}
// 3. Form data
$slug = isset($_POST['form_slug']) ? sanitize_key(wp_unslash($_POST['form_slug'])) : '';
$forms_data = include AV_THEME_DIR . '/inc/forms-data.php';
if (! isset($forms_data[$slug])) {
wp_safe_redirect(home_url('/?av_status=error'));
exit;
}
$form = $forms_data[$slug];
// 4. Sanitize fields
$payload = [];
foreach ($form['fields'] as $field) {
$name = $field['name'];
if (! isset($_POST[$name])) {
$payload[$name] = '';
continue;
}
$raw = wp_unslash($_POST[$name]);
if ($field['type'] === 'email') {
$payload[$name] = sanitize_email($raw);
} elseif ($field['type'] === 'textarea') {
$payload[$name] = sanitize_textarea_field($raw);
} else {
$payload[$name] = sanitize_text_field($raw);
}
}
// 5. Required-field validation
foreach ($form['fields'] as $field) {
if (! empty($field['required']) && empty($payload[$field['name']])) {
$redirect = isset($_POST['redirect_to']) ? esc_url_raw(wp_unslash($_POST['redirect_to'])) : home_url('/');
wp_safe_redirect(add_query_arg('av_status', 'error', $redirect));
exit;
}
}
// 6. Compose mail
$subject_prefix = '[Asterion VR · ' . $form['title'] . ']';
$subject = $subject_prefix . ' ' . ($payload['organization'] ?? $payload['full_name'] ?? '(unidentified lead)');
$body = "New lead from " . get_bloginfo('name') . " — form: " . $form['title'] . "\n";
$body .= "URL : " . (isset($_POST['redirect_to']) ? esc_url_raw(wp_unslash($_POST['redirect_to'])) : home_url('/')) . "\n";
$body .= str_repeat('-', 60) . "\n";
foreach ($form['fields'] as $field) {
$value = $payload[$field['name']] ?? '';
if ($value === '') continue;
$body .= str_pad($field['label'], 28) . ': ' . $value . "\n";
}
$body .= str_repeat('-', 60) . "\n";
$body .= "Submitted : " . current_time('mysql') . "\n";
$body .= "IP : " . (isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field($_SERVER['REMOTE_ADDR']) : '?') . "\n";
$body .= "User Agent: " . (isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '?') . "\n";
$headers = ['Content-Type: text/plain; charset=UTF-8'];
if (! empty($payload['email'])) {
$headers[] = 'Reply-To: ' . $payload['email'];
}
$recipient = apply_filters('av_form_recipient', AV_LEADS_RECIPIENT, $slug, $payload);
$sent = wp_mail($recipient, $subject, $body, $headers);
// 7. Redirect
$redirect = isset($_POST['redirect_to']) ? esc_url_raw(wp_unslash($_POST['redirect_to'])) : home_url('/');
$status = $sent ? 'success' : 'error';
wp_safe_redirect(add_query_arg('av_status', $status, $redirect));
exit;
}
add_action('admin_post_av_form_submit', 'av_handle_form_submit');
add_action('admin_post_nopriv_av_form_submit', 'av_handle_form_submit');