diff -ruN a/Nolan-make-it-real-vo/assets/mir.css b/Nolan-make-it-real-vo/assets/mir.css --- a/Nolan-make-it-real-vo/assets/mir.css 2026-09-25 10:24:22 +++ b/Nolan-make-it-real-vo/assets/mir.css 2026-09-25 10:24:22 @@ -122,5 +122,9 @@ .ni-mir-commander:hover,.ni-mir-commander:focus-visible{background:#161616;outline:none} .ni-mir-commander:disabled{opacity:.7;cursor:wait} .ni-mir-commande-prix{margin:0} +.ni-mir-commande-texte:not([hidden]){display:grid;gap:6px} +.ni-mir-commande-texte .ni-mir-mention{margin:0} +.ni-mir-commande-description{font:inherit;font-size:1rem;line-height:1.5;padding:12px;border:1px solid var(--c-bordure,#ddd);border-radius:6px;color:#161616;background:#fff;width:100%;box-sizing:border-box;resize:vertical;min-height:110px} +.ni-mir-commande-description:focus{outline:2px solid #161616;outline-offset:1px;border-color:#161616} .ni-mir-commande-retour{margin:0;font-size:var(--t-corps,1rem);color:#8C1D16} diff -ruN a/Nolan-make-it-real-vo/assets/mir.js b/Nolan-make-it-real-vo/assets/mir.js --- a/Nolan-make-it-real-vo/assets/mir.js 2026-09-25 10:24:22 +++ b/Nolan-make-it-real-vo/assets/mir.js 2026-09-25 10:24:22 @@ -275,12 +275,21 @@ var btnCommander = modaleCreer.querySelector('.ni-mir-commander'); if (btnCommander) { var retourCommande = modaleCreer.querySelector('.ni-mir-commande-retour'); + var panneauTexte = modaleCreer.querySelector('.ni-mir-commande-texte'); + var champTexte = modaleCreer.querySelector('.ni-mir-commande-description'); btnCommander.addEventListener('click', function () { if (btnCommander.disabled || nolanisation) { return; } + /* 1er clic : champ texte (description du poster) ; 2e clic : création */ + if (panneauTexte && panneauTexte.hidden) { + panneauTexte.hidden = false; + btnCommander.textContent = btnCommander.getAttribute('data-valider') || btnCommander.textContent; + if (champTexte) { champTexte.focus(); } + return; + } var libelle = btnCommander.textContent; btnCommander.disabled = true; btnCommander.textContent = CFG.txt.commande_prep; retourCommande.hidden = true; - api('commander', 'POST', { jeton: etat.jeton, phrase: saisie.value, composition: composition, langue: CFG.langue }) + api('commander', 'POST', { jeton: etat.jeton, phrase: saisie.value, composition: composition, langue: CFG.langue, description: champTexte ? champTexte.value : '' }) .then(function (r) { if (r && r.url) { window.location.href = r.url; return; } throw new Error(CFG.txt.commande_erreur); }) .catch(function (err) { retourCommande.textContent = err.message || CFG.txt.commande_erreur; retourCommande.hidden = false; diff -ruN a/Nolan-make-it-real-vo/nolanizreal-make-it-real-commande.php b/Nolan-make-it-real-vo/nolanizreal-make-it-real-commande.php --- a/Nolan-make-it-real-vo/nolanizreal-make-it-real-commande.php 2026-09-25 10:24:22 +++ b/Nolan-make-it-real-vo/nolanizreal-make-it-real-commande.php 2026-09-25 10:24:22 @@ -196,16 +196,34 @@ 'erreur' => array( 'fr' => 'La création du poster n’a pas abouti. Réessayez dans un instant.', 'en' => 'The poster could not be created. Please try again in a moment.' ), 'indispo' => array( 'fr' => 'La commande de posters n’est pas encore ouverte.', 'en' => 'Poster orders are not open yet.' ), 'desc' => array( 'fr' => 'Poster composé avec Make it real sur nolanizreal.art, imprimé d’après votre composition.', 'en' => 'Poster composed with Make it real on nolanizreal.art, printed from your composition.' ), + 'texte_label' => array( 'fr' => 'Votre texte (facultatif)', 'en' => 'Your text (optional)' ), + 'texte_aide' => array( 'fr' => 'Il deviendra la description de votre poster, sur sa page.', 'en' => 'It will become your poster’s description, on its page.' ), + 'texte_placeholder' => array( 'fr' => 'Ce que cette phrase veut dire pour vous…', 'en' => 'What this sentence means to you…' ), + 'valider' => array( 'fr' => 'Créer mon poster', 'en' => 'Create my poster' ), ); return $t[ $cle ][ 'en' === $lang ? 'en' : 'fr' ]; } +define( 'NI_MIR_DESCRIPTION_MAX', 800 ); + +/** Texte libre saisi dans la modale : texte brut, 800 caractères, pas plus d'une ligne vide d'affilée. */ +function ni_mir_nettoyer_description( $brut ) { + $t = trim( sanitize_textarea_field( (string) $brut ) ); + $t = preg_replace( "/\n{3,}/", "\n\n", str_replace( "\r", '', $t ) ); + return trim( mb_substr( $t, 0, NI_MIR_DESCRIPTION_MAX ) ); +} + function ni_mir_bloc_commande( $lang ) { $offres = ni_mir_offres(); if ( ! $offres ) { return ''; } $min = min( wp_list_pluck( $offres, 'prix' ) ); $prix = function_exists( 'wc_price' ) ? wp_strip_all_tags( wc_price( $min ) ) : number_format_i18n( $min, 2 ) . ' €'; - return '
' + $uid = 'ni-mir-texte-' . wp_rand( 1000, 9999 ); + return '
' + . '' + . '' . '

' . esc_html( sprintf( ni_mir_txt_commande( 'a_partir', $lang ), $prix ) ) . '

' . '
'; } @@ -225,15 +243,15 @@ if ( in_array( $type, array( 'oeuvre', 'affiche' ), true ) ) { return new WP_Error( 'ni_mir_doublon', ni_mir_txt( 'doublon_' . $type, $lang ), array( 'status' => 409 ) ); } if ( ! ni_mir_offres() ) { return new WP_Error( 'ni_mir_indispo', ni_mir_txt_commande( 'indispo', $lang ), array( 'status' => 400 ) ); } $comp = ni_mir_nettoyer_composition( $req->get_param( 'composition' ), count( ni_mir_mots( $phrase ) ) ); - $id = ni_mir_creer_produit( $phrase, $comp, $lang, $p ); + $id = ni_mir_creer_produit( $phrase, $comp, $lang, $p, ni_mir_nettoyer_description( $req->get_param( 'description' ) ) ); if ( is_wp_error( $id ) ) { return new WP_Error( 'ni_mir_creation', ni_mir_txt_commande( 'erreur', $lang ), array( 'status' => 500 ) ); } return rest_ensure_response( array( 'ok' => true, 'url' => get_permalink( $id ) ) ); } /** Crée (ou retrouve) le produit variable correspondant à une phrase + composition. */ -function ni_mir_creer_produit( $phrase, $comp, $lang, $participant = null ) { +function ni_mir_creer_produit( $phrase, $comp, $lang, $participant = null, $description = '' ) { $offres = ni_mir_offres(); - $cle = md5( ni_mir_normaliser( $phrase ) . '|' . implode( ',', $comp['ordre'] ) . '|' . $comp['decalage'] . '|' . $lang . '|' . md5( wp_json_encode( $offres ) ) ); + $cle = md5( ni_mir_normaliser( $phrase ) . '|' . implode( ',', $comp['ordre'] ) . '|' . $comp['decalage'] . '|' . $lang . '|' . md5( wp_json_encode( $offres ) ) . '|' . md5( $description ) ); $existant = get_posts( array( 'post_type' => 'product', 'post_status' => 'publish', 'meta_key' => '_ni_mir_poster_cle', 'meta_value' => $cle, 'fields' => 'ids', 'posts_per_page' => 1, 'suppress_filters' => true, 'lang' => '', 'ni_mir_inclure' => 1 ) ); if ( $existant ) { return (int) $existant[0]; } @@ -257,6 +275,8 @@ $produit->set_status( 'publish' ); $produit->set_catalog_visibility( 'hidden' ); $produit->set_short_description( ni_mir_txt_commande( 'desc', $lang ) ); + // Description (affichée sur la page produit) : le texte du participant, sinon la mention générique + $produit->set_description( wpautop( esc_html( '' !== $description ? $description : ni_mir_txt_commande( 'desc', $lang ) ) ) ); $produit->set_image_id( $att ); $pid = $produit->save(); if ( ! $pid ) { return new WP_Error( 'produit', 'Produit impossible' ); } @@ -266,7 +286,7 @@ $r = ni_appliquer_variations( $pid, $att ); if ( is_wp_error( $r ) ) { return $r; } update_post_meta( $pid, '_ni_mir_poster_cle', $cle ); - update_post_meta( $pid, '_ni_mir_poster', wp_slash( wp_json_encode( array( 'phrase' => $phrase, 'composition' => $comp, 'langue' => $lang, 'participant' => $participant ? (int) $participant->id : 0 ), JSON_UNESCAPED_UNICODE ) ) ); + update_post_meta( $pid, '_ni_mir_poster', wp_slash( wp_json_encode( array( 'phrase' => $phrase, 'composition' => $comp, 'langue' => $lang, 'participant' => $participant ? (int) $participant->id : 0, 'description' => $description ), JSON_UNESCAPED_UNICODE ) ) ); if ( function_exists( 'pll_set_post_language' ) ) { pll_set_post_language( $pid, $lang ); } wc_delete_product_transients( $pid ); return $pid; diff -ruN a/ProduitVO/includes/admin-page.php b/ProduitVO/includes/admin-page.php --- a/ProduitVO/includes/admin-page.php 2026-09-25 10:24:22 +++ b/ProduitVO/includes/admin-page.php 2026-09-25 10:24:22 @@ -94,6 +94,7 @@ 'fields' => 'ids', 'meta_key' => '_produitvo_images', 'no_found_rows' => true, + 'lang' => '', // Polylang : toutes les langues (sinon seule la langue de l'admin est traitée) ))); } ?> diff -ruN a/ProduitVO/includes/class-produitvo-product.php b/ProduitVO/includes/class-produitvo-product.php --- a/ProduitVO/includes/class-produitvo-product.php 2026-09-25 10:24:22 +++ b/ProduitVO/includes/class-produitvo-product.php 2026-09-25 10:24:22 @@ -428,6 +428,7 @@ 'fields' => 'ids', 'meta_key' => self::META_PARENT, 'no_found_rows' => true, + 'lang' => '', // Polylang : toutes les langues (sinon seule la langue de l'admin est traitée) )); foreach ($attachments as $attachment_id) { $product_id = (int) get_post_meta($attachment_id, self::META_PARENT, true); @@ -451,6 +452,7 @@ 'fields' => 'ids', 'meta_key' => self::META_PARENT, 'no_found_rows' => true, + 'lang' => '', // Polylang : toutes les langues (sinon seule la langue de l'admin est traitée) )); foreach ($remaining as $attachment_id) { $file = get_attached_file($attachment_id); @@ -699,6 +701,7 @@ 'orderby' => 'ID', 'order' => 'ASC', 'no_found_rows' => true, + 'lang' => '', // Polylang : toutes les langues (sinon seule la langue de l'admin est traitée) )); $total = count($ids); diff -ruN a/ProduitVO/produitvo.php b/ProduitVO/produitvo.php --- a/ProduitVO/produitvo.php 2026-09-25 10:24:22 +++ b/ProduitVO/produitvo.php 2026-09-25 10:24:22 @@ -3,7 +3,7 @@ * Plugin Name: ProduitVO * Plugin URI: https://geraldmorales.eu * Description: Génère pour chaque produit WooCommerce des visuels mis en situation (60×90, A3, A4, carte postale) à partir de l'image à la une, et les présente dans un slider automatique sur la fiche produit. - * Version: 1.0.7 + * Version: 1.0.8 * Author: Gérald Morales * Author URI: https://geraldmorales.eu * Text Domain: produitvo diff -ruN a/ProduitVO/uninstall.php b/ProduitVO/uninstall.php --- a/ProduitVO/uninstall.php 2026-09-25 10:24:22 +++ b/ProduitVO/uninstall.php 2026-09-25 10:24:22 @@ -30,6 +30,7 @@ 'fields' => 'ids', 'meta_key' => '_produitvo_product', 'no_found_rows' => true, + 'lang' => '', // Polylang : toutes les langues (sinon seule la langue de l'admin est traitée) )); foreach ($produitvo_attachments as $produitvo_attachment_id) { wp_delete_attachment($produitvo_attachment_id, true); diff -ruN a/nolanizreal-site/nolanizreal-site.php b/nolanizreal-site/nolanizreal-site.php --- a/nolanizreal-site/nolanizreal-site.php 2026-09-25 10:24:22 +++ b/nolanizreal-site/nolanizreal-site.php 2026-09-25 10:24:22 @@ -2,7 +2,7 @@ /** * Plugin Name: Nolan Izreal — Site * Description: Briques dynamiques du site nolanizreal.art que le builder ne fournit pas en natif : sélecteur de langue accessible et lien panier traduit. Aucune mise en forme ici — le design reste dans Oxygen. - * Version: 1.0.0 + * Version: 1.0.2 * Author: Gérald Morales */ @@ -671,15 +671,18 @@ * Fiche œuvre : briques que le builder ne fournit pas. */ add_action( 'after_setup_theme', function () { - add_theme_support( 'wc-product-gallery-zoom' ); - add_theme_support( 'wc-product-gallery-lightbox' ); + // Pas de zoom au survol ni de visionneuse (loupe) : l'affiche se montre telle quelle. + remove_theme_support( 'wc-product-gallery-zoom' ); + remove_theme_support( 'wc-product-gallery-lightbox' ); add_theme_support( 'wc-product-gallery-slider' ); -}, 20 ); +}, 99 ); +add_filter( 'woocommerce_single_product_zoom_enabled', '__return_false' ); +add_filter( 'woocommerce_single_product_photoswipe_enabled', '__return_false' ); // Vignettes non recadrées : une affiche ne se rogne pas, même en miniature. add_filter( 'woocommerce_gallery_thumbnail_size', function () { return 'medium'; } ); add_filter( 'woocommerce_gallery_image_size', function () { return 'large'; } ); -/** Galerie native WooCommerce : image principale + vues rattachées, zoom et visionneuse. */ +/** Galerie native WooCommerce : image principale + vues rattachées (sans zoom ni visionneuse). */ add_shortcode( 'ni_galerie_oeuvre', function () { if ( ! function_exists( 'wc_get_product' ) ) { return ''; } global $product; @@ -766,7 +769,7 @@ $liv = (int) get_page_by_path( 'shipping-and-returns' )->ID; if ( $liv && function_exists( 'pll_get_post' ) ) { $t = pll_get_post( $liv ); if ( $t ) { $liv = $t; } } if ( $liv && 'publish' === get_post_status( $liv ) ) { - $blocs .= sprintf( '

%s

%s

', esc_html( ni_mot( 'livraison' ) ), esc_url( get_permalink( $liv ) ), esc_html( get_the_title( $liv ) ) ); + $blocs .= sprintf( '

%s

', esc_url( get_permalink( $liv ) ), esc_html( get_the_title( $liv ) ) ); } return $blocs ? '
' . $blocs . '
' : ''; } ); @@ -795,7 +798,7 @@ } ); /** - * Galerie produit : styles des vignettes et du bouton zoom. + * Galerie produit : styles des vignettes, puis formulaire d'achat (sélecteurs, quantité, bouton). * Le CSS natif WooCommerce ne s'applique qu'à l'intérieur de `.woocommerce div.product` ; * notre modèle Oxygen rend la galerie dans `.ni-fiche-galerie`, on reproduit donc l'essentiel. */ @@ -810,10 +813,36 @@ . '.ni-fiche-galerie .flex-control-thumbs li{flex:0 0 calc(25% - 6px);margin:0;cursor:pointer}' . '.ni-fiche-galerie .flex-control-thumbs img{display:block;width:100%;height:auto;opacity:.55;transition:opacity .2s}' . '.ni-fiche-galerie .flex-control-thumbs img.flex-active,.ni-fiche-galerie .flex-control-thumbs img:hover,.ni-fiche-galerie .flex-control-thumbs img:focus-visible{opacity:1}' - . '.ni-fiche-galerie .woocommerce-product-gallery__trigger{position:absolute;top:12px;right:12px;z-index:9;width:36px;height:36px;background:#fff;border-radius:50%;text-indent:-9999px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,.2)}' - . '.ni-fiche-galerie .woocommerce-product-gallery__trigger::before{content:"";position:absolute;top:9px;left:9px;width:10px;height:10px;border:2px solid #161616;border-radius:50%;box-sizing:content-box}' - . '.ni-fiche-galerie .woocommerce-product-gallery__trigger::after{content:"";position:absolute;top:20px;left:22px;width:2px;height:9px;background:#161616;border-radius:2px;transform:rotate(-45deg)}' - . '.ni-fiche-galerie .woocommerce-product-gallery__trigger:focus-visible{outline:2px solid #161616;outline-offset:2px}' + . '.ni-fiche-galerie .woocommerce-product-gallery__trigger,.ni-fiche-galerie .zoomImg{display:none!important}' + . '.ni-fiche-galerie .woocommerce-product-gallery__image a{pointer-events:none;cursor:default}' + // Formulaire d'achat : le gabarit Breakdance (bde-woo-select, bde-quantity-button) arrive sans sa feuille de style + . '.ni-achat.ni-achat-natif form.cart{width:100%;margin:0}' + . '.ni-achat.ni-achat-natif table.variations{width:100%;border:0;border-collapse:collapse;margin:0}' + . '.ni-achat.ni-achat-natif table.variations tbody{display:grid;gap:12px}' + . '.ni-achat.ni-achat-natif table.variations tr{display:grid;grid-template-columns:110px minmax(0,1fr);align-items:center;column-gap:16px}' + . '.ni-achat.ni-achat-natif table.variations th,.ni-achat.ni-achat-natif table.variations td{display:block;padding:0;border:0;background:none;text-align:left;vertical-align:middle;line-height:1.3}' + . '.ni-achat.ni-achat-natif table.variations th label{margin:0;font-weight:700;font-size:var(--t-corps,1rem)}' + . '.ni-achat.ni-achat-natif .bde-woo-select{position:relative;display:block;width:100%}' + . '.ni-achat.ni-achat-natif .bde-woo-select select{display:block;width:100%!important;height:52px;margin:0;padding:0 44px 0 16px!important;border:1px solid #161616!important;border-radius:0;background:#fff!important;color:#161616;font:inherit;font-size:var(--t-corps,1rem);line-height:50px;-webkit-appearance:none;appearance:none;cursor:pointer;box-sizing:border-box}' + . '.ni-achat.ni-achat-natif .bde-woo-select select:focus-visible{outline:2px solid #161616;outline-offset:2px}' + . '.ni-achat.ni-achat-natif .bde-woo-select__arrow{position:absolute;top:50%;right:18px;width:8px;height:8px;margin-top:-6px;border-right:2px solid #161616;border-bottom:2px solid #161616;transform:rotate(45deg);pointer-events:none}' + . '.ni-achat.ni-achat-natif .reset_variations{display:inline-block;margin-top:10px;font-size:.875rem;color:#161616;text-decoration:underline;text-underline-offset:3px}' + . '.ni-achat.ni-achat-natif .single_variation_wrap{margin-top:20px}' + . '.ni-achat.ni-achat-natif .woocommerce-variation-price{margin:0 0 16px;font-size:1.5rem;font-weight:700}' + . '.ni-achat.ni-achat-natif .woocommerce-variation-description p,.ni-achat.ni-achat-natif .woocommerce-variation-availability p{margin:0 0 12px}' + . '.ni-achat.ni-achat-natif .woocommerce-variation-add-to-cart,.ni-achat.ni-achat-natif form.cart:not(.variations_form){display:flex;align-items:stretch;gap:12px;flex-wrap:wrap}' + . '.ni-achat.ni-achat-natif .quantity{display:inline-flex;align-items:stretch;height:52px;margin:0;border:1px solid #161616;background:#fff;box-sizing:border-box}' + . '.ni-achat.ni-achat-natif .quantity .qty{width:52px;height:100%;margin:0;padding:0;border:0!important;background:none;text-align:center;font:inherit;font-size:var(--t-corps,1rem);font-weight:600;color:#161616;-moz-appearance:textfield;appearance:textfield}' + . '.ni-achat.ni-achat-natif .quantity .qty::-webkit-outer-spin-button,.ni-achat.ni-achat-natif .quantity .qty::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}' + . '.ni-achat.ni-achat-natif .bde-quantity-button{position:relative;width:44px;height:100%;margin:0;padding:0;border:0;border-radius:0;background:none;cursor:pointer;flex:0 0 44px}' + . '.ni-achat.ni-achat-natif .bde-quantity-button:hover{background:#F3E9D2}' + . '.ni-achat.ni-achat-natif .bde-quantity-button:focus-visible{outline:2px solid #161616;outline-offset:-2px}' + . '.ni-achat.ni-achat-natif .bde-quantity-button::before,.ni-achat.ni-achat-natif .bde-quantity-button--inc::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:2px;margin:-1px 0 0 -6px;background:#161616}' + . '.ni-achat.ni-achat-natif .bde-quantity-button--inc::after{transform:rotate(90deg)}' + . '.ni-achat.ni-achat-natif .single_add_to_cart_button{flex:1 1 200px;min-height:52px;height:52px;margin:0!important;padding:0 28px!important;border:1px solid #161616!important;border-radius:0!important;background:#161616!important;color:#fff!important;font-family:inherit;font-size:var(--t-corps,1rem);font-weight:700;letter-spacing:.04em;text-transform:uppercase;opacity:1;cursor:pointer;transition:background-color .3s ease,border-color .3s ease}' + . '.ni-achat.ni-achat-natif .single_add_to_cart_button:hover,.ni-achat.ni-achat-natif .single_add_to_cart_button:focus-visible{background:#F0447A!important;border-color:#F0447A!important}' + . '.ni-achat.ni-achat-natif .single_add_to_cart_button.disabled,.ni-achat.ni-achat-natif .single_add_to_cart_button:disabled{opacity:.35;cursor:not-allowed;background:#161616!important;border-color:#161616!important}' + . '@media (max-width:479px){.ni-achat.ni-achat-natif table.variations tr{grid-template-columns:1fr;row-gap:6px}.ni-achat.ni-achat-natif .single_add_to_cart_button{flex-basis:100%}}' . ''; } /** Description complète de l'œuvre (contenu du produit), mise en forme comme la prose du site. */ diff -ruN a/nolanizreal-site/nolanizreal-variations.php b/nolanizreal-site/nolanizreal-variations.php --- a/nolanizreal-site/nolanizreal-variations.php 2026-09-25 10:24:22 +++ b/nolanizreal-site/nolanizreal-variations.php 2026-09-25 10:24:22 @@ -2,7 +2,7 @@ /** * Variations & prix communs à tous les posters (Réglages du site → « Variations & prix »). * - une ligne = une variation (Format × Support × Accroche, FR/EN), prix, dimensions (PDF), poids, active ou non ; - * - bouton « Appliquer » : met à jour toutes les œuvres FR/EN (variations mises à jour sur place, ajoutées si absentes, + * - bouton « Enregistrer et appliquer » : enregistre la liste puis met à jour toutes les œuvres FR/EN (variations mises à jour sur place, ajoutées si absentes, * passées en privé si retirées de la liste — jamais supprimées) ; * - un produit publié sans variation hérite automatiquement de la liste ; case « Ne pas synchroniser » par produit. * Le répéteur garde le nom ni_mir_offres : Make it real l'utilise pour les posters commandés. @@ -12,23 +12,22 @@ add_action( 'acf/init', function () { if ( ! function_exists( 'acf_add_local_field_group' ) ) { return; } $unite = get_option( 'woocommerce_weight_unit', 'kg' ); - $url = wp_nonce_url( admin_url( 'admin-post.php?action=ni_appliquer_variations' ), 'ni_appliquer_variations' ); acf_add_local_field_group( array( 'key' => 'group_ni_variations', 'title' => 'Variations & prix des posters', 'menu_order' => 5, + 'label_placement' => 'top', 'instruction_placement' => 'field', 'location' => array( array( array( 'param' => 'options_page', 'operator' => '==', 'value' => 'ni-reglages' ) ) ), 'fields' => array( array( 'key' => 'field_ni_variations_aide', 'label' => '', 'name' => '', 'type' => 'message', 'new_lines' => '', 'message' => '

Une ligne = une variation proposée sur chaque poster (œuvres et posters Make it real). Seules les lignes actives et avec un prix sont achetables.

' - . '

1. Modifiez la liste puis cliquez sur Mettre à jour (en haut à droite). 2. Cliquez sur Appliquer à tous les posters

' + . '

Mettre à jour enregistre la liste seulement. Enregistrer et appliquer l’enregistre puis la reporte sur tous les posters (sauf ceux marqués « Ne pas synchroniser »).

' . '

Les variations existantes sont mises à jour sur place (mêmes numéros, images conservées) ; celles retirées de la liste passent en privé, sans être supprimées. Un libellé modifié est traité comme une nouvelle variation. Tout nouveau poster publié sans variation hérite automatiquement de cette liste.

' ), - array( 'key' => 'field_ni_mir_offres', 'label' => 'Variations', 'name' => 'ni_mir_offres', 'type' => 'repeater', 'layout' => 'block', 'button_label' => 'Ajouter une variation', 'collapsed' => 'field_ni_mir_of_format_fr', + array( 'key' => 'field_ni_mir_offres', 'label' => 'Variations', 'name' => 'ni_mir_offres', 'type' => 'repeater', 'layout' => 'block', 'button_label' => 'Ajouter une variation', 'sub_fields' => array( - array( 'key' => 'field_ni_mir_of_actif', 'label' => 'Active', 'name' => 'actif', 'type' => 'true_false', 'ui' => 1, 'default_value' => 1, 'wrapper' => array( 'width' => 12 ) ), - array( 'key' => 'field_ni_mir_of_prix', 'label' => 'Prix TTC (€)', 'name' => 'prix', 'type' => 'number', 'step' => '0.01', 'min' => 0, 'wrapper' => array( 'width' => 16 ) ), - array( 'key' => 'field_ni_mir_of_largeur', 'label' => 'Largeur (mm)', 'name' => 'largeur_mm', 'type' => 'number', 'wrapper' => array( 'width' => 16 ) ), - array( 'key' => 'field_ni_mir_of_hauteur', 'label' => 'Hauteur (mm)', 'name' => 'hauteur_mm', 'type' => 'number', 'wrapper' => array( 'width' => 16 ) ), - array( 'key' => 'field_ni_mir_of_poids', 'label' => 'Poids (' . $unite . ')', 'name' => 'poids', 'type' => 'number', 'step' => '0.001', 'min' => 0, 'wrapper' => array( 'width' => 16 ), 'instructions' => 'Colis compris, pour les frais de port.' ), - array( 'key' => 'field_ni_mir_of_vide', 'label' => '', 'name' => '', 'type' => 'message', 'message' => '', 'wrapper' => array( 'width' => 24 ) ), + array( 'key' => 'field_ni_mir_of_actif', 'label' => 'Active', 'name' => 'actif', 'type' => 'true_false', 'ui' => 1, 'default_value' => 1, 'wrapper' => array( 'width' => 12, 'class' => 'ni-var-actif' ) ), + array( 'key' => 'field_ni_mir_of_prix', 'label' => 'Prix TTC (€)', 'name' => 'prix', 'type' => 'number', 'step' => 'any', 'min' => 0, 'wrapper' => array( 'width' => 22 ) ), + array( 'key' => 'field_ni_mir_of_largeur', 'label' => 'Largeur (mm)', 'name' => 'largeur_mm', 'type' => 'number', 'min' => 0, 'wrapper' => array( 'width' => 22 ) ), + array( 'key' => 'field_ni_mir_of_hauteur', 'label' => 'Hauteur (mm)', 'name' => 'hauteur_mm', 'type' => 'number', 'min' => 0, 'wrapper' => array( 'width' => 22 ) ), + array( 'key' => 'field_ni_mir_of_poids', 'label' => 'Poids (' . $unite . ')', 'name' => 'poids', 'type' => 'number', 'step' => 'any', 'min' => 0, 'wrapper' => array( 'width' => 22 ), 'instructions' => 'Colis compris, pour les frais de port.' ), array( 'key' => 'field_ni_mir_of_format_fr', 'label' => 'Format (FR)', 'name' => 'format_fr', 'type' => 'text', 'required' => 1, 'wrapper' => array( 'width' => 33 ) ), array( 'key' => 'field_ni_mir_of_support_fr', 'label' => 'Support (FR)', 'name' => 'support_fr', 'type' => 'text', 'wrapper' => array( 'width' => 33 ) ), array( 'key' => 'field_ni_mir_of_accroche_fr', 'label' => 'Accroche (FR)', 'name' => 'accroche_fr', 'type' => 'text', 'wrapper' => array( 'width' => 34 ) ), @@ -147,8 +146,8 @@ return get_posts( array( 'post_type' => 'product', 'post_status' => array( 'publish', 'private', 'draft', 'pending', 'future' ), 'posts_per_page' => -1, 'fields' => 'ids', 'suppress_filters' => true, 'lang' => '', 'ni_mir_inclure' => 1 ) ); } -add_action( 'admin_post_ni_appliquer_variations', function () { - if ( ! current_user_can( 'manage_woocommerce' ) || ! wp_verify_nonce( $_GET['_wpnonce'] ?? '', 'ni_appliquer_variations' ) ) { wp_die( 'Accès refusé' ); } +/** Applique la liste à tous les posters et mémorise le bilan pour la notice. */ +function ni_variations_appliquer_tout() { @set_time_limit( 300 ); $bilan = array( 'produits' => 0, 'exclus' => 0, 'maj' => 0, 'ajout' => 0, 'desactive' => 0, 'erreurs' => 0 ); foreach ( ni_variations_produits() as $pid ) { @@ -157,12 +156,49 @@ $bilan['produits']++; foreach ( array( 'maj', 'ajout', 'desactive' ) as $k ) { $bilan[ $k ] += $r[ $k ]; } } set_transient( 'ni_variations_bilan_' . get_current_user_id(), $bilan, 120 ); - wp_safe_redirect( admin_url( 'admin.php?page=ni-reglages&ni_variations=1' ) ); +} + +/* Bouton « Enregistrer et appliquer » : soumet le formulaire des Réglages (les saisies sont enregistrées), puis applique */ +add_action( 'acf/render_field/key=field_ni_variations_aide', function () { + wp_nonce_field( 'ni_appliquer_variations', 'ni_variations_nonce', false ); + echo ''; + echo '

'; + echo ''; +} ); +add_action( 'acf/save_post', function ( $post_id ) { + if ( 'options' !== $post_id || empty( $_POST['ni_variations_appliquer'] ) ) { return; } + if ( ! current_user_can( 'manage_woocommerce' ) || ! wp_verify_nonce( $_POST['ni_variations_nonce'] ?? '', 'ni_appliquer_variations' ) ) { return; } + ni_variations_appliquer_tout(); +}, 20 ); + +/* Ancien lien direct (admin-post), conservé pour les favoris */ +add_action( 'admin_post_ni_appliquer_variations', function () { + if ( ! current_user_can( 'manage_woocommerce' ) || ! wp_verify_nonce( $_GET['_wpnonce'] ?? '', 'ni_appliquer_variations' ) ) { wp_die( 'Accès refusé' ); } + ni_variations_appliquer_tout(); + wp_safe_redirect( admin_url( 'admin.php?page=ni-reglages' ) ); exit; } ); +/* Mise en forme du répéteur : champs d'une même ligne alignés, descriptions sous les champs */ +add_action( 'acf/input/admin_head', function () { + $r = '.acf-field[data-key="field_ni_mir_offres"] .acf-row > .acf-fields > .acf-field'; + echo ''; +} ); + add_action( 'admin_notices', function () { - if ( empty( $_GET['ni_variations'] ) ) { return; } + if ( empty( $_GET['page'] ) || 'ni-reglages' !== $_GET['page'] ) { return; } $b = get_transient( 'ni_variations_bilan_' . get_current_user_id() ); if ( ! $b ) { return; } delete_transient( 'ni_variations_bilan_' . get_current_user_id() );