Corrections Pods (sous-types custom-simple, sync pick→taxonomie), agenda accueil et gras/italique single

This commit is contained in:
2026-07-06 17:12:01 +02:00
parent e6ab5124dd
commit e30b0baa50
7 changed files with 272 additions and 34 deletions

View File

@@ -0,0 +1,107 @@
<?php
/**
* Corrige la sauvegarde des champs pick « custom-simple » dont une option
* contient un caractère spécial HTML (', ", &, <, >).
*
* Symptôme : sélectionner « Journée d'étude » dans le menu `type_*` puis
* enregistrer ne sauvegarde rien — le champ reste vide.
*
* Cause : le front-end Pods (DFV/React) encode la *valeur* des options avec
* htmlspecialchars (ex. « Journée d'étude » → « Journée d&#039;étude »), alors
* que la liste des valeurs valides (`pick_custom`) contient l'apostrophe brute.
* À la sauvegarde, PodsAPI::save_pod_item compare la valeur reçue aux options
* autorisées (isset($custom_values[$v])) : « Journée d&#039;étude » ne matche
* aucune clé → la valeur est rejetée, et PodsMeta l'écrit vide.
*
* Correctif : on rejoue l'inverse de l'encodage (html_entity_decode) sur la
* valeur des champs pick custom-simple dans `pods_api_pre_save_pod_item_post`,
* qui s'exécute *avant* la validation des options. « Journée d&#039;étude »
* redevient « Journée d'étude » et matche l'option. Sans effet sur les valeurs
* sans caractère spécial (décodage no-op) ni sur les données existantes.
*/
add_filter( 'pods_api_pre_save_pod_item_post', 'thalim_decode_custom_simple_entities', 10, 3 );
function thalim_decode_custom_simple_entities( $pieces, $is_new_item, $id ) {
if ( empty( $pieces['fields'] ) ) {
return $pieces;
}
foreach ( $pieces['fields'] as $name => $field_data ) {
// Ne cibler que les pick « custom-simple » (options en dur via pick_custom).
if ( 'pick' !== pods_v( 'type', $field_data ) ) {
continue;
}
if ( 'custom-simple' !== pods_v( 'pick_object', $field_data ) ) {
continue;
}
$value = isset( $field_data['value'] ) ? $field_data['value'] : null;
if ( null === $value || '' === $value ) {
continue;
}
$decoded = thalim_html_entity_decode_deep( $value );
if ( $decoded !== $value ) {
$pieces['fields'][ $name ]['value'] = $decoded;
}
}
return $pieces;
}
/**
* Pendant côté AFFICHAGE du même bug d'encodage.
*
* Symptôme : après avoir enregistré « Journée d'étude », le menu de la page
* d'édition ré-affiche « -- Sélectionnez -- » alors que la valeur est bien
* stockée (la pastille s'affiche sur le post).
*
* Cause : Pods construit la *valeur* (`id`) de chaque option DFV via
* html_entity_decode( esc_html( $id ), ENT_COMPAT ) (PodsField_Pick::
* build_dfv_field_item_data_recurse_item). esc_html encode l'apostrophe en
* &#039;, mais ENT_COMPAT NE décode PAS les apostrophes → l'option garde la
* value « Journée d&#039;étude », qui ne correspond pas à la valeur stockée
* brute « Journée d'étude » → le <select> React retombe sur le placeholder.
*
* Correctif : on redécode les entités des `id` d'options des pick custom-simple
* via le filtre `pods_field_dfv_data` (qui reçoit toute la config DFV). La value
* de l'option redevient brute et matche la valeur stockée → l'option s'affiche
* sélectionnée. Bonus : le DFV soumettra alors la value brute au save.
*/
add_filter( 'pods_field_dfv_data', 'thalim_decode_custom_simple_dfv_ids', 10, 3 );
function thalim_decode_custom_simple_dfv_ids( $data, $args, $attributes ) {
// $args->options peut être un array OU un objet Field (ArrayAccess) ; pods_v gère les deux.
$type = isset( $args->type ) ? $args->type : '';
$options = isset( $args->options ) ? $args->options : [];
$pick_object = pods_v( 'pick_object', $options );
if ( 'pick' !== $type || 'custom-simple' !== $pick_object ) {
return $data;
}
if ( empty( $data['fieldItemData'] ) || ! is_array( $data['fieldItemData'] ) ) {
return $data;
}
foreach ( $data['fieldItemData'] as $i => $item ) {
if ( is_array( $item ) && isset( $item['id'] ) && is_string( $item['id'] ) ) {
$data['fieldItemData'][ $i ]['id'] = html_entity_decode( $item['id'], ENT_QUOTES | ENT_HTML5, 'UTF-8' );
}
}
return $data;
}
/**
* html_entity_decode récursif (gère string et tableau de strings — un pick
* multi-valeur soumet un tableau). ENT_QUOTES pour couvrir &#039; / &quot;.
*/
function thalim_html_entity_decode_deep( $value ) {
if ( is_array( $value ) ) {
return array_map( 'thalim_html_entity_decode_deep', $value );
}
if ( is_string( $value ) ) {
return html_entity_decode( $value, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
}
return $value;
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Synchronise les champs pick → taxonomie du pod `post` vers les vraies
* relations de termes WordPress (wp_term_relationships) après chaque save.
*
* Problème : `programmes_de_recherche`, `etiquettes` et `axes_thematiques` sont
* des champs pick `pick_object = taxonomy` configurés avec `pick_sync_taxonomy = 0`.
* Quand on les édite dans l'admin, Pods stocke la relation dans SES tables
* (postmeta + wp_podsrel) mais NE crée PAS l'assignation de terme WordPress.
* Or les pages d'archive de ces taxonomies (`taxonomy.php`, `tag.php`) et le
* nuage de mots-clés interrogent la vraie taxonomie via tax_query / get_terms :
* un post assigné uniquement via le champ Pods n'y apparaît pas.
* (Le champ `categorie` n'est PAS concerné : lui a `pick_sync_taxonomy = 1`.)
*
* L'importeur HAL, lui, écrit déjà le terme natif (wp_set_object_terms) ; le
* trou est côté sauvegarde admin. Ce hook comble ce trou pour toutes les
* origines de save.
*
* Stratégie : APPEND-ONLY. On n'ajoute que les termes présents dans le champ
* Pods mais absents de la taxonomie native. On ne retire jamais de terme —
* ainsi aucun risque de détruire des assignations posées par ailleurs (ex.
* post_tag issus du matching HAL). La contrepartie (un terme retiré du champ
* Pods reste en taxonomie native) est un moindre mal, très rare ici.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Champs pick→taxonomie du pod `post` à synchroniser (clé meta => taxonomy WP).
* `categorie` est volontairement absent (déjà synchronisé par Pods).
*/
function thalim_pod_tax_sync_fields(): array {
return [
'programmes_de_recherche' => 'programme_de_recherche',
'etiquettes' => 'post_tag',
'axes_thematiques' => 'axe_thematique',
];
}
add_action( 'save_post', 'thalim_sync_pods_taxonomy_relations', 99, 2 );
function thalim_sync_pods_taxonomy_relations( $post_id, $post ) {
if ( ! ( $post instanceof WP_Post ) || 'post' !== $post->post_type ) {
return;
}
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
return;
}
foreach ( thalim_pod_tax_sync_fields() as $field => $taxonomy ) {
if ( ! taxonomy_exists( $taxonomy ) ) {
continue;
}
// Valeurs du champ Pods (source de vérité) : une ligne postmeta par term_id.
$ids = array_values( array_unique( array_filter(
array_map( 'intval', (array) get_post_meta( $post_id, $field ) )
) ) );
if ( empty( $ids ) ) {
continue;
}
// Termes déjà assignés en taxonomie native.
$current = wp_get_object_terms( $post_id, $taxonomy, [ 'fields' => 'ids' ] );
$current = is_wp_error( $current ) ? [] : array_map( 'intval', $current );
// N'ajouter que les manquants (append), ne jamais retirer.
$missing = array_diff( $ids, $current );
if ( ! empty( $missing ) ) {
wp_set_object_terms( $post_id, array_values( $missing ), $taxonomy, true );
}
}
}