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; }