/** * Woodmart Child Om v1.0.7 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * WP Rocket — désactiver les « éléments prioritaires » automatiques. * * Sur ce catalogue multilingue, ces analyses sont recalculées pour un très * grand nombre d'URL et ont rempli plusieurs tables techniques de WP Rocket. * Les optimisations manuelles du thème restent actives. */ add_filter( 'rocket_above_the_fold_optimization', '__return_false', 999 ); add_filter( 'rocket_lrc_optimization', '__return_false', 999 ); add_filter( 'rocket_preconnect_external_domains_optimization', '__return_false', 999 ); add_action( 'wp_enqueue_scripts', function(){ $parent = wp_get_theme( get_template() ); /* * OMYOKI — conserver le chargement du tear-off strictement inchangé, * tout en évitant de forcer une deuxième fois le gros style.css global * de Woodmart quand le thème a déjà enregistré son handle natif. * * Sécurité visuelle : * - si Woodmart a bien enregistré "woodmart-style", "woodmart-parent" * devient uniquement un alias de dépendance, sans fichier CSS supplémentaire ; * - sinon, fallback automatique sur l'ancien chargement de style.css. */ if ( wp_style_is( 'woodmart-style', 'registered' ) ) { wp_register_style( 'woodmart-parent', false, array( 'woodmart-style' ), $parent->get('Version') ); } else { wp_enqueue_style( 'woodmart-parent', get_template_directory_uri() . '/style.css', array(), $parent->get('Version') ); } // IMPORTANT : chargement du tear-off volontairement inchangé. wp_enqueue_style( 'woodmart-child-om', get_stylesheet_directory_uri() . '/assets/css/tearoff.css', array('woodmart-parent'), filemtime( get_stylesheet_directory() . '/assets/css/tearoff.css' ) ); }, 20 ); add_filter( 'body_class', function( $classes ){ foreach ( array('omy-tearoff-enabled','omy-footer-tearoff') as $c ) { if ( ! in_array( $c, $classes, true ) ) $classes[] = $c; } return $classes; } ); // VC Row: checkbox param add_action( 'init', function(){ if ( function_exists( 'vc_add_param' ) ) { vc_add_param( 'vc_row', array( 'type' => 'checkbox', 'heading' => __( 'Tear-off Effect', 'woodmart-child-om' ), 'param_name' => 'omy_tearoff', 'description' => __( 'Ajoute un bord déchiré (haut/bas) – style Zass', 'woodmart-child-om' ), 'value' => array( __( 'Activer', 'woodmart-child-om' ) => 'yes' ), 'group' => __( 'Design Options', 'js_composer' ), ) ); } }, 15 ); // VC Row: honor legacy Zass + checkbox add_filter( 'shortcode_atts_vc_row', function( $out, $pairs, $atts ){ $legacy = ( isset( $atts['point_down'] ) && in_array( strtolower( (string) $atts['point_down'] ), array('yes','true','1'), true ) ) || ( isset( $atts['el_class'] ) && false !== strpos( (string) $atts['el_class'], 'zass-point-down' ) ); $enabled = ( isset( $atts['omy_tearoff'] ) && 'yes' === $atts['omy_tearoff'] ) || $legacy; if ( $enabled ) { $el = isset( $out['el_class'] ) ? trim( (string) $out['el_class'] ) : ''; if ( false === strpos( $el, 'omy-row-tearoff' ) ) $el .= ( $el ? ' ' : '' ) . 'omy-row-tearoff'; if ( false === strpos( $el, 'zass-point-down' ) ) $el .= ' zass-point-down'; $out['el_class'] = trim( $el ); } return $out; }, 11, 3 ); // Legacy shortcode: [zass_icon_box] – render icon + title + text add_action( 'init', function(){ add_shortcode( 'zass_icon_box', function( $atts, $content = '' ){ $atts = shortcode_atts( array( 'title' => '', 'icon_fontawesome' => '', 'icon_style' => '', 'color' => '', ), $atts, 'zass_icon_box' ); $title = sanitize_text_field( $atts['title'] ); $icon = sanitize_text_field( $atts['icon_fontawesome'] ); $color = sanitize_hex_color( $atts['color'] ) ?: ''; $style = $color ? ' style="--omy-ibox-color:' . esc_attr( $color ) . ';"' : ''; $icon_html = $icon ? '
' : ''; $title_html = $title ? '

' . esc_html( $title ) . '

' : ''; $content = do_shortcode( $content ); $content_html= $content ? '
' . wp_kses_post( wpautop( $content ) ) . '
' : ''; return '
' . $icon_html . $title_html . $content_html . '
'; }); if ( function_exists( 'vc_map' ) ) { vc_map( array( 'name' => __( 'Zass Icon Box (legacy)', 'woodmart-child-om' ), 'base' => 'zass_icon_box', 'icon' => 'dashicons dashicons-image-filter', 'category' => __( 'OMYOKI (legacy)', 'woodmart-child-om' ), 'description' => __( 'Compat Zass : icône + titre + texte', 'woodmart-child-om' ), 'params' => array( array( 'type'=>'textfield', 'heading'=>__( 'Titre','woodmart-child-om' ), 'param_name'=>'title' ), array( 'type'=>'iconpicker','heading'=>__( 'Icône FA','woodmart-child-om' ), 'param_name'=>'icon_fontawesome', 'settings'=>array('emptyIcon'=>true,'iconsPerPage'=>200) ), array( 'type'=>'colorpicker','heading'=>__( 'Couleur','woodmart-child-om' ), 'param_name'=>'color' ), array( 'type'=>'textarea_html', 'holder'=>'div', 'heading'=>__( 'Contenu','woodmart-child-om' ), 'param_name'=>'content' ), ), ) ); } }, 20 ); // Admin tools (images, rows, and icon box conversion) require_once __DIR__ . '/admin/migrate-zass-to-woodmart.php'; // Fallback shortcode [omy_info_box] used by converter if Woodmart box unavailable add_action( 'init', function(){ add_shortcode( 'omy_info_box', function( $atts, $content = '' ){ $atts = shortcode_atts( array( 'title' => '', 'icon' => '', 'color' => '', ), $atts, 'omy_info_box' ); $title = sanitize_text_field($atts['title']); $icon = sanitize_text_field($atts['icon']); $color = sanitize_hex_color($atts['color']) ?: ''; $style = $color ? ' style="--omy-ibox-color:' . esc_attr( $color ) . ';"' : ''; $icon_h = $icon ? '
' : ''; $title_h= $title? '

' . esc_html($title) . '

' : ''; $content = do_shortcode( $content ); $text_h = $content ? '
' . wp_kses_post( wpautop($content) ) . '
' : ''; return '
' . $icon_h . $title_h . $text_h . '
'; } ); }, 30 ); /** * === OMYOKI additions (v1.0.9) === * Enqueue expand.css + force classic widgets + shortcode [expand]. */ add_action('wp_enqueue_scripts', function () { $base = get_stylesheet_directory(); $uri = get_stylesheet_directory_uri(); $css = $base . '/assets/css/expand.css'; if ( file_exists( $css ) ) { wp_enqueue_style('woodmart-child-om-expand', $uri . '/assets/css/expand.css', array(), filemtime( $css )); } }, 50); add_filter('use_widgets_block_editor', '__return_false'); add_action('init', function(){ if ( shortcode_exists('expand') ) { return; } add_shortcode('expand', function($atts, $content = ''){ $a = shortcode_atts(array( 'title' => 'Lire la suite', 'open' => 'no', 'id' => '', 'align' => '', ), $atts, 'expand'); $open = in_array(strtolower((string)$a['open']), array('yes','true','1','open'), true) ? ' open' : ''; $id = $a['id'] ? sanitize_html_class($a['id']) : 'exp-' . wp_unique_id('omy_'); $title = wp_kses_post($a['title']); $align = in_array(strtolower((string)$a['align']), array('left','center','right'), true) ? strtolower($a['align']) : ''; $data_align = $align ? ' data-align="' . esc_attr($align) . '"' : ''; $class_align = $align ? ' wd-expand--' . esc_attr($align) : ''; $body = do_shortcode($content); return '
' . '' . $title . '' . '
' . wpautop($body) . '
' . '
'; }); }); /* === End OMYOKI additions === */ /** * === OMYOKI — Breadcrumbs for products (show ONLY Yoast in header) === */ add_action('init', function() { remove_action('woocommerce_before_main_content', 'woocommerce_breadcrumb', 20); if ( function_exists('woodmart_woocommerce_breadcrumbs') ) { remove_action('woocommerce_before_main_content', 'woodmart_woocommerce_breadcrumbs', 20); } }, 9); add_filter('wpseo_breadcrumb_links', function($links){ if ( ! function_exists('is_singular') || ! is_singular('product') ) return $links; $product_id = get_queried_object_id(); if ( ! $product_id ) return $links; // Determine Yoast Primary product_cat (fallback: first assigned) $primary_id = (int) get_post_meta( $product_id, '_yoast_wpseo_primary_product_cat', true ); $term = $primary_id ? get_term( $primary_id, 'product_cat' ) : null; if ( ! $term || is_wp_error($term) ) { $terms = get_the_terms( $product_id, 'product_cat' ); $term = ( $terms && ! is_wp_error($terms) ) ? array_shift( $terms ) : null; } // Build new breadcrumb: Shop -> Primary category -> Product $new = array(); if ( function_exists('wc_get_page_id') ) { $shop_id = wc_get_page_id('shop'); if ( $shop_id && $shop_id > 0 ) { $new[] = array( 'url' => get_permalink($shop_id), 'text' => get_the_title($shop_id) ); } } if ( $term ) { $new[] = array( 'url' => get_term_link( $term, 'product_cat' ), 'text' => $term->name ); } $new[] = array( 'url' => '', 'text' => get_the_title( $product_id ) ); return $new; }, 999); /** * Wishlist : message pour les invités avec CTA "Se connecter / Créer un compte" * S'affiche seulement sur la page contenant [woodmart_wishlist] et pour les non-connectés. */ add_filter( 'the_content', function( $content ) { if ( is_user_logged_in() ) { return $content; } if ( is_page() ) { // Détection robuste : la page contient le shortcode de wishlist if ( has_shortcode( $content, 'woodmart_wishlist' ) ) { $myaccount_url = wc_get_page_permalink( 'myaccount' ); // Le double class 'wd-login-opener js-open-login-modal' déclenche le popup WoodMart si dispo, // sinon l'ancre reste un lien classique vers /mon-compte/. $btn = ''; $btn .= esc_html__( 'Se connecter / Créer un compte', 'woodmart-child' ) . ''; $notice = '
'; $notice .= esc_html__( 'Cette liste disparaîtra à la fermeture du site web. Créez un compte pour sauvegarder votre wishlist.', 'woodmart-child' ) . ' '; $notice .= $btn . '
'; return $notice . $content; } } return $content; }, 9 ); add_action('wp_enqueue_scripts', function () { if (is_front_page() || is_page('boutique-en-ligne')) { global $wp_styles; if (!isset($wp_styles->queue)) return; foreach ($wp_styles->queue as $handle) { $reg = $wp_styles->registered[$handle] ?? null; if (!$reg) continue; $src = $reg->src; // Désactive les CSS de ces plugins sur home & boutique if ( strpos($src, 'customer-reviews-woocommerce/css/frontend.css') !== false || strpos($src, 'customer-reviews-woocommerce/css/badges.css') !== false || strpos($src, 'yith-woocommerce-points-and-rewards-premium') !== false || strpos($src, 'cleantalk-') !== false ) { wp_dequeue_style($handle); } } } }, 200); add_action('woocommerce_proceed_to_checkout', function () { echo do_shortcode('[html_block id="34833"]'); }, 40); add_action('woocommerce_review_order_after_submit', function () { echo do_shortcode('[html_block id="34833"]'); }, 30); /** * OMYOKI — Produits similaires (stable et pertinent) * * Objectif: * - Un produit "bague" affiche des bagues ; un "bracelet" affiche des bracelets, etc. * - On évite le mélange dû aux tags génériques ("fait-main", etc.) * - On privilégie la catégorie principale Yoast si elle correspond au type. * - On FORCE une liste finale de produits issus d’une seule catégorie "type", * en sur-échantillonnant puis en filtrant la visibilité, pour obtenir (presque) toujours 8 produits. */ // Ignore le matching par tags (très souvent la cause du mélange) add_filter( 'woocommerce_product_related_posts_relate_by_tag', '__return_false', 9999 ); add_filter( 'woocommerce_get_related_product_tag_terms', '__return_empty_array', 9999, 2 ); // Indique à WooCommerce une seule catégorie à utiliser pour le calcul interne add_filter( 'woocommerce_get_related_product_cat_terms', function( $term_ids, $product_id ) { $cat_id = omyoki_pick_type_category_id( (int) $product_id ); return $cat_id ? array( $cat_id ) : $term_ids; }, 9999, 2 ); // Force la liste finale des produits similaires (même si WooCommerce mélange) add_filter( 'woocommerce_related_products', function( $related, $product_id, $args ) { $product_id = (int) $product_id; $cat_id = omyoki_pick_type_category_id( $product_id ); if ( ! $cat_id ) { return $related; } $limit = ! empty( $args['posts_per_page'] ) ? (int) $args['posts_per_page'] : 8; $limit = max( 1, $limit ); // Sur-échantillonnage : on récupère plus d'IDs pour compenser les exclusions (visibilité / stock / etc.) $fetch = min( 80, max( $limit * 8, $limit + 20 ) ); // Exclure les produits masqués (catalog/search/outofstock) via product_visibility $vis_terms = function_exists( 'wc_get_product_visibility_term_ids' ) ? wc_get_product_visibility_term_ids() : array(); $exclude_vis = array(); foreach ( array( 'exclude-from-catalog', 'exclude-from-search', 'outofstock' ) as $k ) { if ( ! empty( $vis_terms[ $k ] ) ) { $exclude_vis[] = (int) $vis_terms[ $k ]; } } $tax_query = array( 'relation' => 'AND', array( 'taxonomy' => 'product_cat', 'field' => 'term_id', 'terms' => array( (int) $cat_id ), 'include_children' => true, // IMPORTANT : beaucoup de produits sont souvent dans les sous-catégories ), ); if ( $exclude_vis ) { $tax_query[] = array( 'taxonomy' => 'product_visibility', 'field' => 'term_taxonomy_id', 'terms' => $exclude_vis, 'operator' => 'NOT IN', ); } $q = new WP_Query( array( 'post_type' => 'product', 'post_status' => 'publish', 'fields' => 'ids', 'posts_per_page' => $fetch, 'post__not_in' => array( $product_id ), 'orderby' => 'rand', 'no_found_rows' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'tax_query' => $tax_query, ) ); $ids = is_array( $q->posts ) ? $q->posts : array(); if ( empty( $ids ) ) { return $related; } // Filtre "visible" comme WooCommerce, puis on prend les $limit premiers $out = array(); $seen = array(); foreach ( $ids as $id ) { $id = (int) $id; if ( $id <= 0 || isset( $seen[ $id ] ) ) { continue; } $seen[ $id ] = true; $p = wc_get_product( $id ); if ( ! $p ) { continue; } // Même si la tax_query exclut déjà, on garde le filtre WooCommerce pour être cohérent if ( function_exists( 'wc_products_array_filter_visible' ) && ! wc_products_array_filter_visible( $p ) ) { continue; } $out[] = $id; if ( count( $out ) >= $limit ) { break; } } return ! empty( $out ) ? $out : $related; }, 9999, 3 ); function omyoki_pick_type_category_id( $product_id ) { $product = function_exists( 'wc_get_product' ) ? wc_get_product( $product_id ) : null; // Matière (bonus) $material = $product ? (string) $product->get_attribute( 'pa_matiere' ) : ''; $mat_l = strtolower( remove_accents( $material ) ); $want_argent = ( strpos( $mat_l, 'argent' ) !== false ); $want_dore = ( strpos( $mat_l, 'dor' ) !== false ) || ( strpos( $mat_l, 'laiton' ) !== false ) || ( strpos( $mat_l, 'or' ) !== false ); // Mots-clés "type" $type_keys = array( 'bague', 'bracelet', 'boucle', 'collier', 'pendentif', 'sautoir', 'cheville' ); // 1) Yoast Primary (si c’est une catégorie "type") $primary = (int) get_post_meta( $product_id, '_yoast_wpseo_primary_product_cat', true ); if ( $primary > 0 ) { $t = get_term( $primary, 'product_cat' ); if ( $t && ! is_wp_error( $t ) ) { $txt = strtolower( remove_accents( $t->slug . ' ' . $t->name ) ); foreach ( $type_keys as $k ) { if ( false !== strpos( $txt, $k ) ) { return $primary; } } } } // 2) Sinon: meilleure catégorie assignée qui contient un mot "type" $terms = get_the_terms( $product_id, 'product_cat' ); if ( empty( $terms ) || is_wp_error( $terms ) ) { return 0; } $best_id = 0; $best_score = -999999; foreach ( $terms as $t ) { $txt = strtolower( remove_accents( $t->slug . ' ' . $t->name ) ); $is_type = false; foreach ( $type_keys as $k ) { if ( false !== strpos( $txt, $k ) ) { $is_type = true; break; } } if ( ! $is_type ) { continue; } $depth = count( get_ancestors( (int) $t->term_id, 'product_cat' ) ); $score = $depth * 10; if ( $want_argent && false !== strpos( $txt, 'argent' ) ) { $score += 50; } if ( $want_dore && ( false !== strpos( $txt, 'dor' ) || false !== strpos( $txt, 'laiton' ) || false !== strpos( $txt, 'or' ) ) ) { $score += 50; } if ( $score > $best_score ) { $best_score = $score; $best_id = (int) $t->term_id; } } return $best_id; } /** * OMYOKI — Lien "Guide des tailles" à côté du sélecteur de taille (variations), * uniquement pour les produits dans les catégories bagues / bagues-homme-argent * + attribut de variation "taille". */ add_filter('woocommerce_dropdown_variation_attribute_options_html', 'omyoki_add_size_guide_link_next_to_size_dropdown', 20, 2); function omyoki_add_size_guide_link_next_to_size_dropdown($html, $args) { if (empty($args['product']) || ! is_a($args['product'], 'WC_Product')) { return $html; } $product_id = $args['product']->get_id(); // 1) Catégories ciblées (slugs) $target_cat_slugs = array('bagues', 'bagues-homme-argent'); if (! omyoki_product_in_categories_or_ancestors($product_id, $target_cat_slugs)) { return $html; } // 2) Attribut ciblé : "taille" (slug = taille => taxonomy = pa_taille) $attr = isset($args['attribute']) ? (string) $args['attribute'] : ''; $size_attribute_names = array('pa_taille', 'taille'); if (! in_array($attr, $size_attribute_names, true)) { return $html; } // 3) URL du guide des tailles (page_id=2356) $guide_url = function_exists('get_permalink') ? get_permalink(2356) : home_url('/?page_id=2356'); $link = sprintf( 'Guide des tailles', esc_url($guide_url) ); return $html . $link; } /** * True si le produit est dans une des catégories ciblées OU dans un de leurs enfants. * (plus robuste que has_term seul, selon votre arborescence) */ function omyoki_product_in_categories_or_ancestors($product_id, array $target_slugs) { $terms = get_the_terms($product_id, 'product_cat'); if (empty($terms) || is_wp_error($terms)) { return false; } $target_slugs = array_map('sanitize_title', $target_slugs); foreach ($terms as $term) { // Catégorie directe if (in_array($term->slug, $target_slugs, true)) { return true; } // Ancêtres (si le produit est dans une sous-catégorie) $ancestors = get_ancestors($term->term_id, 'product_cat'); if (! empty($ancestors)) { foreach ($ancestors as $ancestor_id) { $ancestor = get_term($ancestor_id, 'product_cat'); if ($ancestor && ! is_wp_error($ancestor) && in_array($ancestor->slug, $target_slugs, true)) { return true; } } } } return false; } /** * OMYOKI — Badge "PIÈCE UNIQUE" inline juste après le prix (même ligne), * uniquement pour la catégorie "bijoux-pieces-uniques" et uniquement sur la fiche produit. */ add_filter('woocommerce_get_price_html', 'omyoki_append_unique_badge_to_price_html', 20, 2); function omyoki_append_unique_badge_to_price_html($price_html, $product) { // Uniquement sur la fiche produit if ( ! function_exists('is_product') || ! is_product() ) { return $price_html; } if ( ! $product || ! is_a($product, 'WC_Product') ) { return $price_html; } $product_id = $product->get_id(); if ( ! has_term('bijoux-pieces-uniques', 'product_cat', $product_id) ) { return $price_html; } $badge = 'Pièce unique'; return $price_html . ' ' . $badge; } /** * OMYOKI — Désactiver le champ coupon UNIQUEMENT sur la page panier */ add_filter('woocommerce_coupons_enabled', function($enabled){ if (function_exists('is_cart') && is_cart()) { return false; } return $enabled; }); /** * OMYOKI — Add-ons cadeau sous la liste des produits du panier (style Woodmart) * Affiche les produits via le shortcode WooCommerce => même rendu + même add-to-cart */ add_action('woocommerce_after_cart_table', function(){ if ( ! function_exists('WC') || ! WC()->cart || WC()->cart->is_empty() ) return; $slugs = array( 'paquet-cadeau-2', 'carte-de-voeux-vegetale', 'pochon-a-bijoux-equitable-2', ); $ids = array(); foreach ($slugs as $slug) { $post = get_page_by_path($slug, OBJECT, 'product'); if ($post && ! empty($post->ID)) $ids[] = (int) $post->ID; } $ids = array_values(array_unique(array_filter($ids))); if (empty($ids)) return; echo '
'; echo '

C’est pour un cadeau ?

'; // Important: utilise les templates produits du thème (Woodmart) + boutons AJAX natifs echo do_shortcode('[products ids="' . esc_attr(implode(',', $ids)) . '" columns="3" limit="3" orderby="post__in"]'); echo '
'; }, 25); /** * OMYOKI — Shortcode recommandations panier (Woodmart carousel) * Usage: [omyoki_cart_recos] * Logique: pierre (pa_pierres/pa_pierre/product_tag) -> fallback catégorie (product_cat) */ add_shortcode('omyoki_cart_recos', function ($atts) { // Sécurité / contexte if (is_admin() && !defined('DOING_AJAX')) return ''; if (!function_exists('WC') || !WC()->cart) return ''; if (WC()->cart->is_empty()) return ''; if (!shortcode_exists('woodmart_products')) return ''; $atts = shortcode_atts(array( 'limit' => 8, 'per_view' => 4, 'tablet' => 3, 'mobile' => 2, ), $atts, 'omyoki_cart_recos'); $limit = max(1, min(24, (int)$atts['limit'])); $per_view = max(1, min(6, (int)$atts['per_view'])); $tablet = max(1, min(6, (int)$atts['tablet'])); $mobile = max(1, min(6, (int)$atts['mobile'])); // Produits du panier (référence + exclusions) $cart_ids = array(); foreach (WC()->cart->get_cart() as $item) { if (!empty($item['product_id'])) $cart_ids[] = (int)$item['product_id']; } $cart_ids = array_values(array_unique(array_filter($cart_ids))); if (empty($cart_ids)) return ''; $main_id = $cart_ids[0]; // Exclure aussi les 3 add-ons cadeaux $addon_slugs = array('paquet-cadeau-2', 'carte-de-voeux-vegetale', 'pochon-a-bijoux-equitable-2'); $addon_ids = array(); foreach ($addon_slugs as $slug) { $p = get_page_by_path($slug, OBJECT, 'product'); if ($p && !empty($p->ID)) $addon_ids[] = (int)$p->ID; } $exclude_ids = array_values(array_unique(array_merge($cart_ids, $addon_ids))); // 1) Détecter une "pierre" (priorité) $tax_candidates = array('pa_pierres', 'pa_pierre', 'product_tag'); $chosen_tax = ''; $term_ids = array(); foreach ($tax_candidates as $tax) { if (!taxonomy_exists($tax)) continue; $tmp = array(); foreach ($cart_ids as $pid) { $t = wp_get_post_terms($pid, $tax, array('fields' => 'ids')); if (!is_wp_error($t) && !empty($t)) $tmp = array_merge($tmp, $t); } $tmp = array_values(array_unique(array_map('intval', $tmp))); if (!empty($tmp)) { $chosen_tax = $tax; $term_ids = array_slice($tmp, 0, 3); // limite à 3 "pierres" break; } } // 2) Fallback catégorie si pas de pierre if (empty($chosen_tax) || empty($term_ids)) { $chosen_tax = 'product_cat'; $cats = wp_get_post_terms($main_id, 'product_cat', array('fields' => 'ids')); if (!is_wp_error($cats) && !empty($cats)) { $term_ids = array_slice(array_map('intval', $cats), 0, 2); } } if (empty($term_ids)) return ''; // Query légère (8 produits) $q = new WP_Query(array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => $limit, 'no_found_rows' => true, 'fields' => 'ids', 'post__not_in' => $exclude_ids, 'orderby' => 'date', 'order' => 'DESC', 'meta_query' => array( array( 'key' => '_stock_status', 'value' => 'instock', ), ), 'tax_query' => array( array( 'taxonomy' => $chosen_tax, 'field' => 'term_id', 'terms' => $term_ids, 'operator' => 'IN', ), array( 'taxonomy' => 'product_visibility', 'field' => 'name', 'terms' => array('exclude-from-catalog', 'outofstock'), 'operator' => 'NOT IN', ), ), )); $ids = $q->posts; if (empty($ids)) return ''; // Carousel Woodmart (format comme fiches produits) return '
' . do_shortcode( '[woodmart_products post_type="ids" include="' . esc_attr(implode(',', $ids)) . '" ' . 'layout="carousel" items_per_page="' . (int)$limit . '" ' . 'slides_per_view="' . (int)$per_view . '" slides_per_view_tablet="' . (int)$tablet . '" slides_per_view_mobile="' . (int)$mobile . '" ' . 'orderby="post__in" product_hover="icons" lazy_loading="yes"]' ) . '
'; }); /** * OMYOKI — Envoi authentifie des e-mails WordPress et WooCommerce par OVH. * Les identifiants sont definis dans wp-config.php. */ add_action( 'phpmailer_init', function ( $mailer ) { if ( ! defined( 'OMYOKI_SMTP_HOST' ) || ! defined( 'OMYOKI_SMTP_PORT' ) || ! defined( 'OMYOKI_SMTP_USER' ) || ! defined( 'OMYOKI_SMTP_PASSWORD' ) ) { return; } $mailer->isSMTP(); $mailer->Host = OMYOKI_SMTP_HOST; $mailer->Port = (int) OMYOKI_SMTP_PORT; $mailer->SMTPAuth = true; $mailer->SMTPSecure = 'tls'; $mailer->Username = OMYOKI_SMTP_USER; $mailer->Password = OMYOKI_SMTP_PASSWORD; $mailer->Sender = OMYOKI_SMTP_USER; $mailer->Timeout = 20; } ); add_filter( 'wp_mail_from', function () { return defined( 'OMYOKI_SMTP_USER' ) ? OMYOKI_SMTP_USER : 'infos@omyoki.com'; } ); add_filter( 'wp_mail_from_name', function () { return 'Omyoki'; } ); https://www.omyoki.com/post-sitemap.xml 2026-07-28T15:51:26+00:00 https://www.omyoki.com/page-sitemap.xml 2026-08-19T09:29:25+00:00 https://www.omyoki.com/product-sitemap.xml 2026-08-28T07:25:17+00:00 https://www.omyoki.com/product-sitemap2.xml 2026-08-28T07:25:17+00:00 https://www.omyoki.com/category-sitemap.xml 2026-07-28T15:51:26+00:00 https://www.omyoki.com/post_tag-sitemap.xml 2024-11-26T08:38:03+00:00 https://www.omyoki.com/product_cat-sitemap.xml 2026-08-28T07:25:17+00:00 https://www.omyoki.com/product_tag-sitemap.xml 2026-08-28T07:25:17+00:00 https://www.omyoki.com/pa_genre-sitemap.xml 2026-08-28T07:25:17+00:00