Sessions 3-4 deliverables. Establishes the Asterion content pipeline :
EN pages generated programmatically from content-source/*-data.php, FR
translations rebuilt natively by Bricks's WPML integration from EN structure
+ WPML strings (no direct FR _bricks_page_content_2 writes).
## tools/_av-bricks-shared.php (the library)
Shared helpers + element atoms (eyebrow/heading/text/button/text-link) +
section builders (hero/prose/list/cards/cta/upsell/closer) + page write
pipeline that handles : write EN content via $wpdb (bypass Bricks's filter),
force Bricks::Elements::load_elements() so controls are populated, fire
wpml_page_builder_register_strings, push dict FR into icl_string_translations
(skip user CTE edits, auto-prefix /fr on internal URLs), trigger Bricks
native FR rebuild via wpml_page_builder_string_translated, and ensure a CTE
job exists (icl_translate_job.editor='wpml' + revision=NULL + populate
icl_translate rows with base64+gzip-encoded EN/FR strings).
## tools/av-generate-{industries,solutions}.php
Thin wrappers : URL trigger (template_redirect, not init — Bricks elements
load on 'wp' hook), add_filter('av_fr_translation_dict', ...) to register
FR strings, content-type-specific hub builder. ~150 FR strings per type.
## tools/av-translate-{industries,solutions}-fr.php
One-shot seeds for the initial FR linked posts via WPML trid mapping.
## tools/av-wpml-* operational mu-plugins
- set-cte : switch doc_translation_method to ICL_TM_TMETHOD_EDITOR (=1)
- create-jobs : reset CTE jobs (status=10 + needs_update=1 + editor=wpml +
revision=NULL). Integrated into pipeline via av_ensure_wpml_cte_job().
- fix-status / repair : align FR posts to correct trid + cleanup orphans.
- push-translations : push dict into icl_string_translations.
- plugins-check / debug / dump-strings / icl-dump / rescan : diagnostics.
## tools/av-fix-link-types.php
One-shot DB migration : convert legacy link.type='internal'+url to 'external'
across all _bricks_page_*_2 meta values (JSON and PHP-serialized). Bricks's
set_link_attributes() only renders href= for type='external' — older session-3
templates lost their href silently.
## Other tools/ mu-plugins
- av-bricks-rest-bridge : pivot — exposes Bricks CPTs over REST, dual-format
read filter (JSON for MCP / array for renderer), auto-mirror content→header/
footer for templates.
- av-apply-bricks-styles, av-cleanup-orphans, av-create-wp-menu, av-debug-meta,
av-dump-bricks, av-fix-template-meta : misc operational scripts.
## Bricks Config/
JSON snapshots of bricks_theme_styles, bricks_color_palette,
bricks_global_settings for diffable inspection.
## .gitignore
Add .mcp.json — holds per-developer WP App Password for the Bricks MCP server.
## wp-content/themes/asterion-bricks/assets/css/utilities.css
Note that all responsive overrides are now emitted natively by the page
generator via Bricks per-breakpoint settings, no custom CSS needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
150 lines
7.6 KiB
PHP
150 lines
7.6 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: Asterion — Generate industry pages (one-shot)
|
|
* Description: /?av_generate_industries=1 reads content-source/industries-data.php
|
|
* and creates/updates : hub /industries/ + 4 child pages (police, special-forces,
|
|
* military, firefighters). Idempotent.
|
|
*
|
|
* Heavy lifting (builders + FR pipeline) lives in _av-bricks-shared.php.
|
|
* This file only owns : URL trigger + hub builder + FR strings for Industries.
|
|
*/
|
|
defined('ABSPATH') || exit;
|
|
|
|
require_once __DIR__ . '/_av-bricks-shared.php';
|
|
|
|
/* -----------------------------------------------------------------
|
|
Register Industries-specific FR strings into the shared dict.
|
|
----------------------------------------------------------------- */
|
|
add_filter('av_fr_translation_dict', function ($dict) {
|
|
return array_merge($dict, [
|
|
// Hub
|
|
'Industries' => 'Secteurs',
|
|
"Built for those who carry the weight."
|
|
=> "Conçu pour celles et ceux qui en portent le poids.",
|
|
"Police, special forces, military, and emergency services share a hard truth: their craft is rehearsed in the worst conditions, and rarely enough. PROSERVE\u{2122} puts contemporary training within everyone's reach \u{2014} every shift, every budget, every operating context."
|
|
=> "Police, forces spéciales, armée et services d'urgence partagent une vérité dure : leur métier se répète dans les pires conditions, et trop rarement. PROSERVE\u{2122} met l'entraînement contemporain à la portée de tous \u{2014} chaque service, chaque budget, chaque contexte opérationnel.",
|
|
"Four operating contexts. One platform."
|
|
=> "Quatre contextes opérationnels. Une seule plateforme.",
|
|
|
|
// Card eyebrows + tile labels
|
|
'National & Municipal' => 'Nationale & Municipale',
|
|
'Police' => 'Police',
|
|
"Police training, when the schedule won't bend."
|
|
=> "L'entraînement police, quand l'agenda ne fléchit pas.",
|
|
'Tactical units' => 'Unités tactiques',
|
|
'Special Forces' => 'Forces spéciales',
|
|
"Special forces train differently. Their simulation should, too."
|
|
=> "Les forces spéciales s'entraînent autrement. Leur simulation devrait suivre.",
|
|
'Armed forces' => 'Forces armées',
|
|
'Military' => 'Armée',
|
|
"Military VR training. Sovereign. Modular. Field-deployable."
|
|
=> "Entraînement VR pour l'armée. Souverain. Modulaire. Déployable sur le terrain.",
|
|
'Emergency services' => "Services d'urgence",
|
|
'Firefighters' => 'Sapeurs-pompiers',
|
|
"Train the worst day. Safely. Repeatedly."
|
|
=> "Entraînez-vous au pire jour. En sécurité. Indéfiniment.",
|
|
|
|
// Card body concatenations from hub builder
|
|
"National & Municipal \u{2014} Police training, when the schedule won't bend."
|
|
=> "Nationale & Municipale \u{2014} L'entraînement police, quand l'agenda ne fléchit pas.",
|
|
"Tactical units \u{2014} Special forces train differently. Their simulation should, too."
|
|
=> "Unités tactiques \u{2014} Les forces spéciales s'entraînent autrement. Leur simulation devrait suivre.",
|
|
"Armed forces \u{2014} Military VR training. Sovereign. Modular. Field-deployable."
|
|
=> "Forces armées \u{2014} Entraînement VR pour l'armée. Souverain. Modulaire. Déployable sur le terrain.",
|
|
"Emergency services \u{2014} Train the worst day. Safely. Repeatedly."
|
|
=> "Services d'urgence \u{2014} Entraînez-vous au pire jour. En sécurité. Indéfiniment.",
|
|
|
|
// Hero / detail-page CTA labels specific to Industries
|
|
'Request a Demo for police use' => 'Demander une démo pour la police',
|
|
'Request a Demo for fire & emergency' => "Demander une démo pour pompiers & urgences",
|
|
]);
|
|
});
|
|
|
|
/* -----------------------------------------------------------------
|
|
URL trigger — fires on template_redirect (NOT init) so Bricks's
|
|
element controls are loaded (happens on 'wp' hook). Without loaded
|
|
controls, Bricks's wpml_page_builder_register_strings handler skips
|
|
every element silently and NO strings reach icl_strings → ATE empty.
|
|
----------------------------------------------------------------- */
|
|
add_action('template_redirect', function () {
|
|
if (empty($_GET['av_generate_industries'])) return;
|
|
if (! in_array($_SERVER['SERVER_NAME'] ?? '', ['localhost', '127.0.0.1'], true)) wp_die('localhost only');
|
|
|
|
$data_file = 'C:\\ASTERION\\GIT\\WP2026\\content-source\\industries-data.php';
|
|
if (! file_exists($data_file)) wp_die("data file not found: $data_file");
|
|
|
|
$industries = include $data_file;
|
|
if (! is_array($industries)) wp_die('industries data not array');
|
|
|
|
$parent_id = av_ensure_hub_page('industries', 'Industries');
|
|
$report = ["Rebuilding /industries/ hub + child detail pages..."];
|
|
|
|
// Hub
|
|
$hub_elements = av_build_industries_hub_elements($industries);
|
|
$hub_write = av_write_bricks_page_elements($parent_id, $hub_elements);
|
|
$report[] = " HUB → page #$parent_id (rebuilt) — " . count($hub_elements)
|
|
. " elements (json={$hub_write['json_len']}, stored={$hub_write['stored_len']})";
|
|
|
|
// Detail pages
|
|
foreach ($industries as $slug => $d) {
|
|
$page = av_upsert_detail_page($slug, 'industries', $d);
|
|
$report[] = " $slug → page #{$page['id']} ({$page['action']}) — {$page['element_count']} elements (json={$page['json_len']}, stored={$page['stored_len']})";
|
|
}
|
|
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
echo implode("\n", $report);
|
|
exit;
|
|
});
|
|
|
|
/* -----------------------------------------------------------------
|
|
Industries hub builder : hero + 4-card grid + closer CTA
|
|
----------------------------------------------------------------- */
|
|
function av_build_industries_hub_elements($industries) {
|
|
$els = [];
|
|
|
|
// HERO
|
|
$els = array_merge($els, av_build_hero('hub', [
|
|
'eyebrow' => 'Industries',
|
|
'tagline' => "Built for those who carry the weight.",
|
|
'hero_lead' => "Police, special forces, military, and emergency services share a hard truth: their craft is rehearsed in the worst conditions, and rarely enough. PROSERVE\u{2122} puts contemporary training within everyone's reach \u{2014} every shift, every budget, every operating context.",
|
|
'ctas' => [
|
|
['label' => 'Book a demo', 'href' => '/request-demo/', 'variant' => 'primary'],
|
|
['label' => 'Request a T&E Kit', 'href' => '/request-te-kit/', 'variant' => 'secondary'],
|
|
],
|
|
]));
|
|
|
|
// 4-card grid — one card per industry
|
|
$cards_section = [
|
|
'eyebrow' => '',
|
|
'title' => 'Four operating contexts. One platform.',
|
|
'items' => [],
|
|
];
|
|
foreach ($industries as $slug => $d) {
|
|
$cards_section['items'][] = [
|
|
'title' => $d['title'],
|
|
'body' => ($d['eyebrow'] ?? '') . " \u{2014} " . ($d['tagline'] ?? ''),
|
|
];
|
|
}
|
|
$els = array_merge($els, av_build_cards_section('hubgrid', 0, $cards_section, 'var(--color-bg-subtle)'));
|
|
|
|
// Append "Learn more →" link in each card (post-build patch)
|
|
$i = 0;
|
|
foreach ($industries as $slug => $d) {
|
|
$kid = "khubgridc0x{$i}";
|
|
$lid = "lnhubgridc0x{$i}";
|
|
$els[] = av_text_link($lid, $kid, "Learn more \u{2192}", "/industries/$slug/");
|
|
foreach ($els as &$el) {
|
|
if (($el['id'] ?? '') === $kid) {
|
|
$el['children'][] = $lid;
|
|
}
|
|
}
|
|
unset($el);
|
|
$i++;
|
|
}
|
|
|
|
// CLOSER CTA
|
|
$els = array_merge($els, av_build_closer_cta('hub'));
|
|
|
|
return $els;
|
|
}
|