feat(bricks+wpml): full programmatic page generation + native WPML CTE integration

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>
This commit is contained in:
2026-05-14 19:40:28 +02:00
parent af0ed66122
commit d2badf49d4
28 changed files with 4305 additions and 1 deletions

View File

@@ -0,0 +1,200 @@
<?php
/**
* Plugin Name: Asterion — Apply Bricks Theme Style (one-shot)
* Description: Visit /?av_apply_styles=1 (admin only) → writes the full Asterion Theme Style + fixes Bricks postTypes. Idempotent.
*
* Sources of truth :
* - tokens.css (wp-content/themes/asterion-bricks/assets/css/tokens.css)
* - Design Handoff PDF section 2 (typography scale, line heights)
*/
defined('ABSPATH') || exit;
add_action('init', function () {
if (empty($_GET['av_apply_styles'])) return;
if (! current_user_can('manage_options')) wp_die('admin only');
$report = [];
/* ---------------------------------------------------------------
1. Bricks Global Settings : add post + CPT to builder access
--------------------------------------------------------------- */
$global = get_option('bricks_global_settings', []);
if (! is_array($global)) $global = [];
$global['postTypes'] = ['page', 'post', 'case_study', 'scenario'];
update_option('bricks_global_settings', $global);
$report[] = "bricks_global_settings.postTypes = " . implode(',', $global['postTypes']);
/* ---------------------------------------------------------------
2. Bricks Theme Styles : full Asterion config
--------------------------------------------------------------- */
$existing = get_option('bricks_theme_styles', []);
$cond_id = $existing['asterion']['settings']['conditions']['conditions'][0]['id'] ?? 'skuksb';
// Helpers
$color = fn($var) => ['raw' => $var];
$sp = fn($t,$r,$b,$l) => ['top'=>$t,'right'=>$r,'bottom'=>$b,'left'=>$l];
$styles = [
'asterion' => [
'label' => 'Asterion',
'settings' => [
'_custom' => true,
'conditions' => [
'conditions' => [
['id' => $cond_id, 'main' => 'any'],
],
],
/* ---- GENERAL : site background ---- */
'general' => [
'siteBackground' => [
'color' => $color('var(--color-bg-default)'),
],
],
/* ---- COLORS : 11 semantic roles ---- */
'colors' => [
'colorPrimary' => $color('var(--color-brand-navy)'),
'colorSecondary' => $color('var(--color-brand-gold)'),
'colorLight' => $color('var(--color-bg-default)'),
'colorDark' => $color('var(--color-bg-dark)'),
'colorMuted' => $color('var(--color-text-muted)'),
'colorBorder' => $color('var(--color-border-default)'),
'colorInfo' => $color('var(--color-info)'),
'colorSuccess' => $color('var(--color-success)'),
'colorWarning' => $color('var(--color-warning)'),
'colorDanger' => $color('var(--color-alert-red)'),
],
/* ---- LINKS : gold + underline ---- */
'links' => [
'typography' => [
'color' => $color('var(--color-brand-gold)'),
'text-decoration' => 'underline',
],
],
/* ---- TYPOGRAPHY ---- */
'typography' => [
// HTML font-size: 100% so 1rem = 16px
'typographyHtml' => '100%',
'typographyBody' => [
'font-family' => 'Inter',
'font-weight' => '400',
'font-size' => '1rem',
'line-height' => '1.6',
'letter-spacing' => '0',
'color' => $color('var(--color-text-primary)'),
],
'typographyHeadings' => [
'font-family' => 'Inter',
'font-weight' => '700',
'letter-spacing' => '-0.01em',
'color' => $color('var(--color-text-primary)'),
],
// H1 : 36px / line 1.20
'typographyHeadingH1' => [
'font-size' => '2.25rem',
'line-height' => '1.20',
],
'h1Margin' => $sp('0', '0', '1.5rem', '0'),
// H2 : 28px / line 1.25
'typographyHeadingH2' => [
'font-size' => '1.75rem',
'line-height' => '1.25',
],
'h2Margin' => $sp('0', '0', '1.25rem', '0'),
// H3 : 22px / line 1.30
'typographyHeadingH3' => [
'font-size' => '1.375rem',
'line-height' => '1.30',
],
'h3Margin' => $sp('0', '0', '1rem', '0'),
// H4 : 18px / line 1.35
'typographyHeadingH4' => [
'font-size' => '1.125rem',
'line-height' => '1.35',
],
'h4Margin' => $sp('0', '0', '0.75rem', '0'),
// H5 : 16px / line 1.40
'typographyHeadingH5' => [
'font-size' => '1rem',
'line-height' => '1.40',
],
'h5Margin' => $sp('0', '0', '0.5rem', '0'),
// H6 : 14px / line 1.40
'typographyHeadingH6' => [
'font-size' => '0.875rem',
'line-height' => '1.40',
],
'h6Margin' => $sp('0', '0', '0.5rem', '0'),
// Focus outline (a11y, WCAG 2.1)
'focusOutline' => '2px solid var(--color-info)',
],
/* ---- BUTTON : default + primary + secondary defaults ---- */
'button' => [
'typography' => [
'font-family' => 'Inter',
'font-weight' => '600',
],
'sizeDefaultPadding' => $sp('0.75rem', '1.5rem', '0.75rem', '1.5rem'),
'sizeSmPadding' => $sp('0.5rem', '1rem', '0.5rem', '1rem'),
'sizeMdPadding' => $sp('0.75rem', '1.5rem', '0.75rem', '1.5rem'),
'sizeLgPadding' => $sp('1rem', '2rem', '1rem', '2rem'),
// Primary button = navy bg, white text, gold hover (managed via element classes)
'primaryBackground' => $color('var(--color-brand-navy)'),
'primaryTypography' => [
'color' => $color('var(--color-text-on-dark)'),
],
// Secondary button = gold bg, navy text
'secondaryBackground' => $color('var(--color-brand-gold)'),
'secondaryTypography' => [
'color' => $color('var(--color-brand-navy)'),
],
],
/* ---- SECTION : default padding (8-pt grid) ---- */
'section' => [
'padding' => $sp('5rem', '4rem', '5rem', '4rem'),
],
/* ---- CONTAINER : max-width 1280 ---- */
'container' => [
'widthMax' => '1280px',
],
],
],
];
update_option('bricks_theme_styles', $styles);
$report[] = "bricks_theme_styles[asterion] = full payload (" . strlen(wp_json_encode($styles)) . " bytes)";
/* ---------------------------------------------------------------
3. Done
--------------------------------------------------------------- */
$report[] = "";
$report[] = "Reload the Bricks builder (Ctrl+R) — you should see :";
$report[] = " - Body in Inter 16px navy";
$report[] = " - H1-H6 in Inter 700 navy with proper sizes";
$report[] = " - Links in gold underlined";
$report[] = " - Sections with 5rem top/bottom padding";
$report[] = " - postTypes : page, post, case_study, scenario";
wp_die('<pre style="font:12px/1.5 monospace;padding:2rem;background:#0B1F3A;color:#E0C892;">'
. esc_html(implode("\n", $report))
. '</pre>', 'Asterion theme style applied');
});