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>
186 lines
8.2 KiB
PHP
186 lines
8.2 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: Asterion — Create ATE-flagged translate_job rows for our FR pages
|
|
* Description:
|
|
* /?av_wpml_create_jobs=1 → for each FR linked page, insert a row in
|
|
* wp_icl_translate_job with editor='ate' + translated=1, and adjust the
|
|
* translation_status row to mirror Bricks #14's pattern (status=2 IN_PROGRESS
|
|
* with uuid + batch_id). This should switch the page-list icon from pencil
|
|
* ✏️ to gear ⚙️ and route the click to ATE instead of WP editor.
|
|
*
|
|
* /?av_wpml_remove_jobs=1 → revert if it breaks something (delete jobs, set
|
|
* status=10 again).
|
|
*
|
|
* This is exploratory : we mimic the structure WPML's internal send_jobs()
|
|
* creates, without going through TranslationProxy (cloud).
|
|
*/
|
|
defined('ABSPATH') || exit;
|
|
|
|
add_action('template_redirect', function () {
|
|
if (! in_array($_SERVER['SERVER_NAME'] ?? '', ['localhost', '127.0.0.1'], true)) return;
|
|
if (! empty($_GET['av_wpml_create_jobs'])) av_wpml_create_jobs();
|
|
if (! empty($_GET['av_wpml_remove_jobs'])) av_wpml_remove_jobs();
|
|
});
|
|
|
|
function av_target_fr_translations() {
|
|
global $wpdb;
|
|
$slugs = ['industries', 'police', 'special-forces', 'military', 'firefighters',
|
|
'solutions', 'my-proserve', 'proserve-flex', 'proserve-academy', 'customization'];
|
|
$placeholders = implode(',', array_fill(0, count($slugs), '%s'));
|
|
return $wpdb->get_results($wpdb->prepare(
|
|
"SELECT p.ID AS post_id, p.post_title AS title, t.trid, t.translation_id, ts.rid, ts.status
|
|
FROM {$wpdb->posts} p
|
|
JOIN {$wpdb->prefix}icl_translations t ON t.element_id = p.ID AND t.element_type='post_page' AND t.language_code='fr'
|
|
LEFT JOIN {$wpdb->prefix}icl_translation_status ts ON ts.translation_id = t.translation_id
|
|
WHERE p.post_name IN ($placeholders)",
|
|
...$slugs
|
|
));
|
|
}
|
|
|
|
function av_wpml_create_jobs() {
|
|
global $wpdb;
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
|
|
$rows = av_target_fr_translations();
|
|
if (! $rows) { echo "No FR translations found\n"; exit; }
|
|
|
|
$batch_id = 1; // reuse Bricks #14's batch (or create new one — using 1 for simplicity)
|
|
|
|
foreach ($rows as $r) {
|
|
if (! $r->rid) {
|
|
echo "FR #{$r->post_id} '{$r->title}' : no translation_status row, skipping\n";
|
|
continue;
|
|
}
|
|
|
|
// 1. Set translation_status to status=2 (ICL_TM_IN_PROGRESS) to match
|
|
// Bricks #14's pattern. This is what makes the FR column icon = gear ⚙️
|
|
// and routes the click to the translation editor (CTE).
|
|
// Note: editing the EN page directly will show a "Translation in progress,
|
|
// wait before editing" warning dialog — informative only, click "I understand"
|
|
// to dismiss.
|
|
$uuid = wp_generate_uuid4();
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'icl_translation_status',
|
|
[
|
|
'status' => 2, // ICL_TM_IN_PROGRESS (= gear icon)
|
|
'needs_update' => 0,
|
|
'translator_id' => 1,
|
|
'translation_service' => 'local',
|
|
'batch_id' => $batch_id,
|
|
'uuid' => $uuid,
|
|
'tp_revision' => 1,
|
|
'timestamp' => current_time('mysql'),
|
|
'links_fixed' => 1,
|
|
],
|
|
['rid' => $r->rid]
|
|
);
|
|
|
|
// 2. Insert/update translate_job row with editor='ate'
|
|
$existing_job = $wpdb->get_var($wpdb->prepare(
|
|
"SELECT job_id FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d ORDER BY job_id DESC LIMIT 1",
|
|
$r->rid
|
|
));
|
|
|
|
// editor='wpml' = CTE (Classic Translation Editor), works locally without
|
|
// cloud sync. translated=1 + completed_date set = the FR is "ready",
|
|
// matches Bricks #14's pattern (which shows gear icon and opens CTE).
|
|
// CRITICAL : revision MUST be NULL. WPML's filter_status_css_class() in
|
|
// class-wpml-tm-translation-status-display.php joins icl_translate_job
|
|
// with the explicit condition `translate_job.revision IS NULL`. Jobs
|
|
// with non-null revision are silently ignored → pencil icon, no CTE.
|
|
$job_data = [
|
|
'rid' => $r->rid,
|
|
'translator_id' => 1,
|
|
'translated' => 1, // FR considered done (we pre-filled via dict)
|
|
'manager_id' => 1,
|
|
// 'revision' deliberately omitted → DB default = NULL
|
|
'title' => $r->title,
|
|
'deadline_date' => null,
|
|
'completed_date' => current_time('mysql'),
|
|
'editor' => 'wpml', // CTE (matches doc_translation_method=1)
|
|
'editor_job_id' => null,
|
|
'automatic' => 0,
|
|
];
|
|
|
|
if ($existing_job) {
|
|
$wpdb->update($wpdb->prefix . 'icl_translate_job', $job_data, ['job_id' => $existing_job]);
|
|
// $wpdb->update can't write SQL NULL (it coerces to empty string).
|
|
// Force revision NULL via raw query for the icon's JOIN to match.
|
|
$wpdb->query($wpdb->prepare(
|
|
"UPDATE {$wpdb->prefix}icl_translate_job SET revision = NULL WHERE job_id = %d",
|
|
$existing_job
|
|
));
|
|
echo "FR #{$r->post_id} '{$r->title}' (rid={$r->rid}) : job UPDATED #{$existing_job} (editor={$job_data['editor']}, revision=NULL)\n";
|
|
} else {
|
|
$wpdb->insert($wpdb->prefix . 'icl_translate_job', $job_data);
|
|
$new_job_id = $wpdb->insert_id;
|
|
// Belt-and-suspenders : ensure revision is NULL even on fresh insert.
|
|
$wpdb->query($wpdb->prepare(
|
|
"UPDATE {$wpdb->prefix}icl_translate_job SET revision = NULL WHERE job_id = %d",
|
|
$new_job_id
|
|
));
|
|
echo "FR #{$r->post_id} '{$r->title}' (rid={$r->rid}) : job CREATED #{$new_job_id} (editor={$job_data['editor']}, revision=NULL)\n";
|
|
}
|
|
}
|
|
|
|
echo "\nDONE. Refresh WP Admin → Pages. The FR column icon should switch from pencil ✏️ to gear ⚙️.\n";
|
|
echo "Click the gear to open ATE for that page.\n";
|
|
exit;
|
|
}
|
|
|
|
function av_wpml_remove_jobs() {
|
|
global $wpdb;
|
|
header('Content-Type: text/plain; charset=utf-8');
|
|
|
|
$rows = av_target_fr_translations();
|
|
foreach ($rows as $r) {
|
|
if (! $r->rid) continue;
|
|
|
|
// 1. Remove translate_job rows
|
|
$wpdb->query($wpdb->prepare(
|
|
"DELETE FROM {$wpdb->prefix}icl_translate_job WHERE rid=%d",
|
|
$r->rid
|
|
));
|
|
|
|
// 2. Reset status row : ALL the cloud-sync artifacts cleared
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'icl_translation_status',
|
|
[
|
|
'status' => 10,
|
|
'needs_update' => 0,
|
|
'batch_id' => 0,
|
|
'uuid' => '',
|
|
'tp_id' => '',
|
|
'tp_revision' => 0,
|
|
'ts_status' => null,
|
|
'review_status' => null,
|
|
'ate_comm_retry_count' => 0,
|
|
'_prevstate' => '',
|
|
'translation_service' => 'local',
|
|
'translator_id' => 0,
|
|
],
|
|
['rid' => $r->rid]
|
|
);
|
|
|
|
echo "FR #{$r->post_id} : job removed, status fully reset\n";
|
|
}
|
|
|
|
// 3. Cleanup orphan translation_batches rows that may trigger cloud-fetch
|
|
$orphans = $wpdb->query(
|
|
"DELETE FROM {$wpdb->prefix}icl_translation_batches
|
|
WHERE batch_name LIKE 'asterion%' OR id NOT IN
|
|
(SELECT DISTINCT batch_id FROM {$wpdb->prefix}icl_translation_status WHERE batch_id > 0)"
|
|
);
|
|
echo "\nOrphan translation_batches rows cleaned : $orphans\n";
|
|
|
|
// 4. Cleanup orphan translation_downloads rows (background ATE sync queue)
|
|
$tbl_downloads = $wpdb->prefix . 'icl_translation_downloads';
|
|
if ($wpdb->get_var("SHOW TABLES LIKE '$tbl_downloads'")) {
|
|
$cleared = $wpdb->query("DELETE FROM $tbl_downloads");
|
|
echo "Cleared $cleared rows from icl_translation_downloads (pending cloud sync queue)\n";
|
|
}
|
|
|
|
echo "\nReverted to clean state. WPML queue + page list should be back to normal.\n";
|
|
exit;
|
|
}
|