Files
AsterionWP2026/wp-content/themes/asterion-bricks/inc/render-bricks.php
j.foucher 026be81373 chore: session 2 snapshot — archive before Bricks-first restart
Snapshots the project at the end of session 2, just before resetting to a
Bricks-first orthodox architecture. RESTART-PLAN.md at the root explains
why this approach is being abandoned and what the next session will look like.

Why we restart
- Three architectures got stacked: Custom PHP Templates → Gutenberg blocks
  in post_content → Bricks data in post meta. Each layer fights the next.
- Bricks Builder UI does not load because our header.php / footer.php /
  page.php emit chrome that conflicts with Bricks' own template-parts.
- CSS overrides multiplied (Gutenberg + Bricks variants) and still don't
  pixel-match the original PHP-rendered visual.
- 8+ one-shot mu-plugins live in LocalWP to patch over inconsistencies.

What survives the restart (preserved here on main)
- BRIEF, both PDFs, .txt extracts (sources of truth)
- assets/css/tokens.css (full design system, untouched)
- assets/fonts/Inter-Variable*.woff2 (RGPD self-hosted)
- inc/<section>-data.php files (text copy from PDF strategie, will become
  /content-source/ on next session for reuse as translation dictionary
  and as input to programmatic page generation)
- The Git repo, remote, branch main
- LocalWP site, Bricks v2.3.4, WPML 4.9.3 with active licenses
- WPML EN + FR setup with directory URL strategy

What gets wiped at the start of next session
- wp-content/themes/asterion-bricks/header.php / footer.php / page.php /
  index.php / front-page.php / template-parts/ / templates/
- inc/render-blocks.php, inc/render-blocks-native.php, inc/render-bricks.php
- inc/shortcodes.php, inc/i18n.php, inc/form-handler.php
- 35+ WP pages created over the session
- All one-shot mu-plugins in LocalWP/wp-content/mu-plugins/

Restart approach (detailed in RESTART-PLAN.md)
- Minimalist child theme: style.css, functions.php, theme.json, tokens.css,
  trimmed utilities.css, fonts, inc/cpt.php — that's it.
- No header.php / footer.php / page.php in child theme — Bricks Templates
  (Header / Footer / Single — Page) take over via Bricks' Conditions.
- User builds 5-7 archetype Bricks templates visually (~1-2h each) then
  programmatic generation fills the variants from inc/*-data.php sources.
- WPML duplication after EN is validated.

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

259 lines
9.1 KiB
PHP

<?php
/**
* Asterion Bricks — Bricks Builder native data renderer (POC: Industries)
*
* Generates the array of element descriptors that Bricks Builder expects in
* the post meta `_bricks_page_content_2` (constant `BRICKS_DB_PAGE_CONTENT`).
*
* Schema, confirmed by reading Bricks parent theme source
* (`includes/password-protection.php` and `includes/elements/base.php`):
*
* [
* [
* 'id' => string, // unique, via Bricks\Helpers::generate_random_id(false)
* 'name' => 'section' | 'container' | 'block' | 'heading' | 'text-basic' | 'button' | …,
* 'parent' => 0 | string, // 0 = page root, else parent's id
* 'children' => [string, …], // optional, for nestable elements
* 'settings' => [
* 'text' => 'Hello',
* 'tag' => 'h1' | 'h2' | …,
* 'link' => ['type' => 'external', 'url' => 'https://…'],
* '_cssClasses' => 'av-hero',
* …
* ],
* ],
* …
* ]
*
* Once stored, opening the page in WP admin and clicking "Edit with Bricks"
* surfaces every element in the Bricks builder canvas, fully editable.
*
* Frontend rendering is handled by Bricks itself — our `page.php` simply
* lets Bricks take over via its `template_include` filter.
*
* @package AsterionBricks
*/
defined('ABSPATH') || exit;
/**
* Generate a Bricks-compatible random element ID.
* Falls back to a local md5 prefix if the Bricks namespace isn't loaded yet.
*/
function av_brx_id() {
if (class_exists('\\Bricks\\Helpers') && method_exists('\\Bricks\\Helpers', 'generate_random_id')) {
return \Bricks\Helpers::generate_random_id(false);
}
return substr(md5(uniqid((string) mt_rand(), true)), 0, 6);
}
/**
* Tiny element factory.
*/
function av_brx_el($name, $parent, $settings = [], $children = null) {
$el = [
'id' => av_brx_id(),
'name' => $name,
'parent' => $parent,
'settings' => $settings,
];
if ($children !== null) {
$el['children'] = $children;
}
return $el;
}
/* =================================================================== */
/* Industries overview — proof of concept */
/* =================================================================== */
function av_brx_render_industries_overview() {
$home_url = home_url('/');
/* ---- pre-allocate IDs so we can hand them to parents/children ---- */
$hero_section_id = av_brx_id();
$hero_container_id = av_brx_id();
$hero_eyebrow_id = av_brx_id();
$hero_h1_id = av_brx_id();
$hero_sub_id = av_brx_id();
$cards_section_id = av_brx_id();
$cards_container_id = av_brx_id();
$cards_grid_id = av_brx_id();
$industries = [
[
'eyebrow' => 'National & Municipal',
'title' => 'Police',
'excerpt' => 'Vehicle stops, identity checks, urban CQB, de-escalation, riot containment. Built for the realities of contemporary policing.',
'href' => '/industries/police/',
'cta' => 'Police training',
],
[
'eyebrow' => 'Tactical units',
'title' => 'Special Forces',
'excerpt' => 'Hostage rescue, dynamic entries, hostile crowd extraction, sniper-spotter coordination. Built for the units where every error has a name.',
'href' => '/industries/special-forces/',
'cta' => 'Special forces training',
],
[
'eyebrow' => 'Armed forces',
'title' => 'Military',
'excerpt' => 'MOUT, convoy escort, FOB defense, IED awareness, joint operation drills. Built for armed forces that deploy fast and adapt faster.',
'href' => '/industries/military/',
'cta' => 'Military training',
],
[
'eyebrow' => 'Emergency services',
'title' => 'Firefighters',
'excerpt' => "Structure fires, hazmat, multi-casualty triage, confined-space rescue, industrial accidents. Built for first responders who can't rehearse the worst day at full scale.",
'href' => '/industries/firefighters/',
'cta' => 'Firefighter training',
],
];
$card_ids = [];
foreach ($industries as $i => $_) {
$card_ids[$i] = [
'card' => av_brx_id(),
'eyebrow' => av_brx_id(),
'title' => av_brx_id(),
'excerpt' => av_brx_id(),
'btn' => av_brx_id(),
];
}
$cards_children = array_map(fn($ids) => $ids['card'], $card_ids);
/* ---- assemble flat element array ---- */
$elements = [];
// 1. Hero section ----------------------------------------------------
$elements[] = [
'id' => $hero_section_id,
'name' => 'section',
'parent' => 0,
'children' => [$hero_container_id],
'settings' => ['_cssClasses' => 'av-hero'],
'label' => 'Hero',
];
$elements[] = [
'id' => $hero_container_id,
'name' => 'container',
'parent' => $hero_section_id,
'children' => [$hero_eyebrow_id, $hero_h1_id, $hero_sub_id],
'settings' => ['_cssClasses' => 'av-container av-hero__inner'],
];
$elements[] = [
'id' => $hero_eyebrow_id,
'name' => 'text-basic',
'parent' => $hero_container_id,
'settings' => [
'text' => 'Industries',
'tag' => 'p',
'_cssClasses' => 'av-hero__eyebrow',
],
];
$elements[] = [
'id' => $hero_h1_id,
'name' => 'heading',
'parent' => $hero_container_id,
'settings' => [
'tag' => 'h1',
'text' => 'Built with operators, for operators.',
'_cssClasses' => 'av-hero__headline',
],
];
$elements[] = [
'id' => $hero_sub_id,
'name' => 'text-basic',
'parent' => $hero_container_id,
'settings' => [
'text' => 'PROSERVE™ is not a generic VR platform repackaged for the public sector. Every scenario, every weapon profile, every instructor metric was specified by serving and former operators in close partnership with our engineering team.',
'tag' => 'p',
'_cssClasses' => 'av-hero__sub',
],
];
// 2. Cards section ---------------------------------------------------
$elements[] = [
'id' => $cards_section_id,
'name' => 'section',
'parent' => 0,
'children' => [$cards_container_id],
'settings' => ['_cssClasses' => 'av-section'],
'label' => 'Industries grid',
];
$elements[] = [
'id' => $cards_container_id,
'name' => 'container',
'parent' => $cards_section_id,
'children' => [$cards_grid_id],
'settings' => ['_cssClasses' => 'av-container'],
];
$elements[] = [
'id' => $cards_grid_id,
'name' => 'block',
'parent' => $cards_container_id,
'children' => $cards_children,
'settings' => ['_cssClasses' => 'av-grid-4'],
];
foreach ($industries as $i => $ind) {
$ids = $card_ids[$i];
$elements[] = [
'id' => $ids['card'],
'name' => 'block',
'parent' => $cards_grid_id,
'children' => [$ids['eyebrow'], $ids['title'], $ids['excerpt'], $ids['btn']],
'settings' => ['_cssClasses' => 'av-card'],
'label' => $ind['title'],
];
$elements[] = [
'id' => $ids['eyebrow'],
'name' => 'text-basic',
'parent' => $ids['card'],
'settings' => [
'text' => $ind['eyebrow'],
'tag' => 'p',
'_cssClasses' => 'av-card__eyebrow',
],
];
$elements[] = [
'id' => $ids['title'],
'name' => 'heading',
'parent' => $ids['card'],
'settings' => [
'tag' => 'h2',
'text' => $ind['title'],
'_cssClasses' => 'av-card__title',
],
];
$elements[] = [
'id' => $ids['excerpt'],
'name' => 'text-basic',
'parent' => $ids['card'],
'settings' => [
'text' => $ind['excerpt'],
'tag' => 'p',
'_cssClasses' => 'av-card__excerpt',
],
];
$elements[] = [
'id' => $ids['btn'],
'name' => 'button',
'parent' => $ids['card'],
'settings' => [
'text' => $ind['cta'],
'tag' => 'a',
'link' => [
'type' => 'external',
'url' => $home_url . ltrim($ind['href'], '/'),
],
'_cssClasses' => 'av-btn av-btn--secondary av-btn--md',
],
];
}
return $elements;
}