{"path":"/public_html/wp-content/themes/planexpert-theme/functions.php","name":"functions.php","size":31452,"extension":".php","modified":"2026-06-04T19:18:17.958919132Z","mode":420,"isDir":false,"isSymlink":false,"type":"text","content":"\u003c?php\n/**\n * PlanExpert Theme Functions\n *\n * @package PlanExpert\n * @version 1.2.0\n */\n\nif ( ! defined( 'ABSPATH' ) ) exit;\n\ndefine( 'PLANEXPERT_VERSION', '2.2.0' );\ndefine( 'PLANEXPERT_DIR', get_template_directory() );\ndefine( 'PLANEXPERT_URI', get_template_directory_uri() );\n\n/**\n * Theme Setup\n */\nfunction planexpert_setup() {\n // Title tag support\n add_theme_support( 'title-tag' );\n\n // Post thumbnails\n add_theme_support( 'post-thumbnails' );\n\n // Custom logo\n add_theme_support( 'custom-logo', array(\n 'height' =\u003e 100,\n 'width' =\u003e 300,\n 'flex-height' =\u003e true,\n 'flex-width' =\u003e true,\n ) );\n\n // HTML5 support\n add_theme_support( 'html5', array(\n 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption', 'style', 'script',\n ) );\n\n // Register nav menus\n register_nav_menus( array(\n 'primary' =\u003e __( 'Menu principal', 'planexpert' ),\n 'footer' =\u003e __( 'Menu pied de page', 'planexpert' ),\n ) );\n}\nadd_action( 'after_setup_theme', 'planexpert_setup' );\n\n/**\n * Enqueue Styles \u0026 Scripts\n */\nfunction planexpert_scripts() {\n // Google Fonts: Poppins + Inter\n wp_enqueue_style(\n 'planexpert-fonts',\n 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600\u0026family=Poppins:wght@500;600;700\u0026display=swap',\n array(),\n null\n );\n\n // Main theme stylesheet\n wp_enqueue_style(\n 'planexpert-style',\n get_stylesheet_uri(),\n array( 'planexpert-fonts' ),\n PLANEXPERT_VERSION\n );\n\n // Theme JavaScript\n wp_enqueue_script(\n 'planexpert-main',\n PLANEXPERT_URI . '/assets/js/main.js',\n array(),\n PLANEXPERT_VERSION,\n true\n );\n}\nadd_action( 'wp_enqueue_scripts', 'planexpert_scripts' );\n\n/**\n * Custom Nav Walker for PlanExpert menu\n */\nclass PlanExpert_Nav_Walker extends Walker_Nav_Menu {\n function start_el( \u0026$output, $item, $depth = 0, $args = null, $id = 0 ) {\n $classes = empty( $item-\u003eclasses ) ? array() : (array) $item-\u003eclasses;\n $is_current = in_array( 'current-menu-item', $classes ) || in_array( 'current_page_item', $classes );\n $class_str = $is_current ? ' pe-nav__link--active' : '';\n\n $output .= '\u003ca href=\"' . esc_url( $item-\u003eurl ) . '\" class=\"pe-nav__link' . $class_str . '\"\u003e';\n $output .= esc_html( $item-\u003etitle );\n $output .= '\u003c/a\u003e';\n }\n\n // Skip li wrapper\n function start_lvl( \u0026$output, $depth = 0, $args = null ) {}\n function end_lvl( \u0026$output, $depth = 0, $args = null ) {}\n function end_el( \u0026$output, $item, $depth = 0, $args = null ) {}\n}\n\n/**\n * Disable WordPress emojis for performance\n */\nfunction planexpert_disable_emojis() {\n remove_action( 'wp_head', 'print_emoji_detection_script', 7 );\n remove_action( 'wp_print_styles', 'print_emoji_styles' );\n}\nadd_action( 'init', 'planexpert_disable_emojis' );\n\n/**\n * Clean up wp_head\n */\nremove_action( 'wp_head', 'wp_generator' );\nremove_action( 'wp_head', 'wlwmanifest_link' );\nremove_action( 'wp_head', 'rsd_link' );\nremove_action( 'wp_head', 'wp_shortlink_wp_head' );\n\n/**\n * Contact Form Handler (simple AJAX endpoint)\n */\nfunction planexpert_handle_contact() {\n check_ajax_referer( 'planexpert_contact', 'nonce' );\n\n $prenom = sanitize_text_field( $_POST['prenom'] ?? '' );\n $email = sanitize_email( $_POST['email'] ?? '' );\n $phone = sanitize_text_field( $_POST['phone'] ?? '' );\n $type = sanitize_text_field( $_POST['type_projet'] ?? '' );\n $message = sanitize_textarea_field( $_POST['message'] ?? '' );\n\n if ( empty( $prenom ) || empty( $email ) ) {\n wp_send_json_error( array( 'message' =\u003e 'Prénom et email requis.' ) );\n }\n\n // Build email\n $to = 'contact@planexpert.fr';\n $subject = sprintf( '[PlanExpert] Nouveau devis — %s', $prenom );\n $body = sprintf(\n \"Nouveau formulaire de contact PlanExpert\\n\\n\" .\n \"Prénom : %s\\nEmail : %s\\nTéléphone : %s\\nType de projet : %s\\n\\nMessage :\\n%s\",\n $prenom, $email, $phone, $type, $message\n );\n $headers = array(\n 'Content-Type: text/plain; charset=UTF-8',\n sprintf( 'Reply-To: %s \u003c%s\u003e', $prenom, $email ),\n );\n\n $sent = wp_mail( $to, $subject, $body, $headers );\n\n if ( $sent ) {\n wp_send_json_success( array( 'message' =\u003e 'Merci ! Nous revenons vers vous en moins de 24h.' ) );\n } else {\n wp_send_json_error( array( 'message' =\u003e 'Erreur lors de l\\'envoi. Veuillez réessayer.' ) );\n }\n}\nadd_action( 'wp_ajax_planexpert_contact', 'planexpert_handle_contact' );\nadd_action( 'wp_ajax_nopriv_planexpert_contact', 'planexpert_handle_contact' );\n\n/**\n * Pass AJAX URL and nonce to frontend\n */\nfunction planexpert_localize_script() {\n wp_localize_script( 'planexpert-main', 'planexpert', array(\n 'ajaxurl' =\u003e admin_url( 'admin-ajax.php' ),\n 'nonce' =\u003e wp_create_nonce( 'planexpert_contact' ),\n ) );\n}\nadd_action( 'wp_enqueue_scripts', 'planexpert_localize_script' );\n\n/**\n * Add body classes\n */\nfunction planexpert_body_classes( $classes ) {\n if ( is_front_page() ) {\n $classes[] = 'pe-home';\n }\n return $classes;\n}\nadd_filter( 'body_class', 'planexpert_body_classes' );\n\n\n/* ============================================================\n * PlanExpert - SEO, OG, Schema, Form handler, Favicon, Title fix\n * Added 2026-04-10\n * ============================================================ */\n\n/**\n * Return a per-page meta description.\n */\nfunction planexpert_meta_description() {\n if ( is_front_page() ) {\n return \"PlanExpert rédige votre business plan professionnel clé en main : étude de marché, prévisionnel 3 ans, modèle économique. Devis gratuit sous 24h.\";\n }\n if ( is_page( 'a-propos' ) || is_page( 'about' ) ) {\n return \"Découvrez PlanExpert : experts-comptables et consultants en stratégie dédiés à la réussite des entrepreneurs. +10 ans d'expérience, 250+ plans d'affaires livrés.\";\n }\n if ( is_page( 'services' ) || is_page( 'nos-offres' ) || is_page( 'tarifs' ) ) {\n return \"Nos offres de business plan : Essentiel, Pro, Premium et Investisseurs. Business plan complet avec prévisionnel 3 ans, à partir de 490€. Devis gratuit.\";\n }\n if ( is_page( 'portfolio' ) || is_page( 'realisations' ) || is_page( 'exemples' ) ) {\n return \"Découvrez des exemples de business plans réalisés par PlanExpert : restauration, e-commerce, tech, services, franchises. Des projets financés avec succès.\";\n }\n if ( is_page( 'faq' ) ) {\n return \"FAQ PlanExpert : délais, tarifs, processus, garanties, confidentialité. Toutes les réponses à vos questions sur la rédaction de votre business plan.\";\n }\n if ( is_page( 'contact' ) || is_page( 'devis' ) ) {\n return \"Demandez votre devis gratuit pour votre business plan PlanExpert. Réponse sous 24h, RDV de cadrage offert, sans engagement.\";\n }\n if ( is_page( 'politique-de-confidentialite' ) ) {\n return \"Politique de confidentialité PlanExpert : comment nous collectons, utilisons et protégeons vos données personnelles, conformément au RGPD.\";\n }\n if ( is_single() || is_singular() ) {\n $excerpt = get_the_excerpt();\n if ( $excerpt ) return wp_trim_words( $excerpt, 30, '...' );\n }\n return \"PlanExpert - Business plans professionnels, études de marché et prévisionnels financiers pour entrepreneurs. Devis gratuit sous 24h.\";\n}\n\n/**\n * Inject SEO meta tags, OG, Twitter Cards, favicon, JSON-LD in \u003chead\u003e.\n */\nfunction planexpert_head_seo() {\n $desc = planexpert_meta_description();\n $url = esc_url( home_url( add_query_arg( null, null ) ) );\n if ( is_singular() ) {\n $url = esc_url( get_permalink() );\n } elseif ( is_front_page() ) {\n $url = esc_url( home_url( '/' ) );\n }\n $title = wp_get_document_title();\n // Fix typo \"Plans d Affaires\" -\u003e \"Plans d'Affaires\"\n $title = str_replace( \"Plans d Affaires\", \"Plans d'Affaires\", $title );\n $site = get_bloginfo( 'name' );\n $logo = PLANEXPERT_URI . '/assets/images/planexpert-og.png';\n\n echo \"\\n\u003c!-- PlanExpert SEO --\u003e\\n\";\n echo '\u003cmeta name=\"description\" content=\"' . esc_attr( $desc ) . '\"\u003e' . \"\\n\";\n echo '\u003cmeta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\"\u003e' . \"\\n\";\n echo '\u003clink rel=\"canonical\" href=\"' . $url . '\"\u003e' . \"\\n\";\n\n // Favicon (inline SVG data URI — no file needed)\n $favicon_svg = '\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\"\u003e\u003crect width=\"64\" height=\"64\" rx=\"10\" fill=\"#0F3460\"/\u003e\u003ctext x=\"50%\" y=\"56%\" text-anchor=\"middle\" font-family=\"Georgia,serif\" font-weight=\"700\" font-size=\"36\" fill=\"#ffffff\" dominant-baseline=\"middle\"\u003ePE\u003c/text\u003e\u003crect x=\"0\" y=\"54\" width=\"64\" height=\"10\" fill=\"#E94560\"/\u003e\u003c/svg\u003e';\n echo '\u003clink rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml;utf8,' . rawurlencode( $favicon_svg ) . '\"\u003e' . \"\\n\";\n echo '\u003clink rel=\"apple-touch-icon\" href=\"data:image/svg+xml;utf8,' . rawurlencode( $favicon_svg ) . '\"\u003e' . \"\\n\";\n\n // Open Graph\n echo '\u003cmeta property=\"og:type\" content=\"website\"\u003e' . \"\\n\";\n echo '\u003cmeta property=\"og:site_name\" content=\"' . esc_attr( $site ) . '\"\u003e' . \"\\n\";\n echo '\u003cmeta property=\"og:title\" content=\"' . esc_attr( $title ) . '\"\u003e' . \"\\n\";\n echo '\u003cmeta property=\"og:description\" content=\"' . esc_attr( $desc ) . '\"\u003e' . \"\\n\";\n echo '\u003cmeta property=\"og:url\" content=\"' . $url . '\"\u003e' . \"\\n\";\n echo '\u003cmeta property=\"og:locale\" content=\"fr_FR\"\u003e' . \"\\n\";\n echo '\u003cmeta property=\"og:image\" content=\"' . esc_url( $logo ) . '\"\u003e' . \"\\n\";\n echo '\u003cmeta property=\"og:image:width\" content=\"1200\"\u003e' . \"\\n\";\n echo '\u003cmeta property=\"og:image:height\" content=\"630\"\u003e' . \"\\n\";\n\n // Twitter Cards\n echo '\u003cmeta name=\"twitter:card\" content=\"summary_large_image\"\u003e' . \"\\n\";\n echo '\u003cmeta name=\"twitter:title\" content=\"' . esc_attr( $title ) . '\"\u003e' . \"\\n\";\n echo '\u003cmeta name=\"twitter:description\" content=\"' . esc_attr( $desc ) . '\"\u003e' . \"\\n\";\n echo '\u003cmeta name=\"twitter:image\" content=\"' . esc_url( $logo ) . '\"\u003e' . \"\\n\";\n\n // Schema.org JSON-LD - LocalBusiness + Service\n if ( is_front_page() ) {\n $schema = array(\n '@context' =\u003e 'https://schema.org',\n '@graph' =\u003e array(\n array(\n '@type' =\u003e 'ProfessionalService',\n '@id' =\u003e home_url( '/#business' ),\n 'name' =\u003e 'PlanExpert',\n 'url' =\u003e home_url( '/' ),\n 'description' =\u003e 'Rédaction de business plans professionnels, études de marché et prévisionnels financiers pour entrepreneurs.',\n 'priceRange' =\u003e '€€',\n 'image' =\u003e $logo,\n 'areaServed' =\u003e array(\n '@type' =\u003e 'Country',\n 'name' =\u003e 'France',\n ),\n 'serviceType' =\u003e array(\n 'Business plan',\n 'Étude de marché',\n 'Prévisionnel financier',\n 'Conseil en stratégie',\n ),\n 'contactPoint' =\u003e array(\n '@type' =\u003e 'ContactPoint',\n 'contactType' =\u003e 'customer service',\n 'availableLanguage' =\u003e array( 'French', 'English' ),\n 'email' =\u003e 'contact@planexpert.fr',\n ),\n ),\n array(\n '@type' =\u003e 'WebSite',\n '@id' =\u003e home_url( '/#website' ),\n 'url' =\u003e home_url( '/' ),\n 'name' =\u003e $site,\n 'inLanguage' =\u003e 'fr-FR',\n ),\n array(\n '@type' =\u003e 'Service',\n 'serviceType' =\u003e 'Business plan professionnel',\n 'provider' =\u003e array(\n '@type' =\u003e 'ProfessionalService',\n 'name' =\u003e 'PlanExpert',\n '@id' =\u003e home_url( '/#business' ),\n ),\n 'offers' =\u003e array(\n array(\n '@type' =\u003e 'Offer',\n 'name' =\u003e 'Pack Essentiel',\n 'price' =\u003e '790',\n 'priceCurrency' =\u003e 'EUR',\n ),\n array(\n '@type' =\u003e 'Offer',\n 'name' =\u003e 'Pack Pro',\n 'price' =\u003e '1490',\n 'priceCurrency' =\u003e 'EUR',\n ),\n array(\n '@type' =\u003e 'Offer',\n 'name' =\u003e 'Pack Premium',\n 'price' =\u003e '2490',\n 'priceCurrency' =\u003e 'EUR',\n ),\n array(\n '@type' =\u003e 'Offer',\n 'name' =\u003e 'Pack Investisseurs',\n 'price' =\u003e '3990',\n 'priceCurrency' =\u003e 'EUR',\n ),\n ),\n ),\n ),\n );\n echo '\u003cscript type=\"application/ld+json\"\u003e' . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '\u003c/script\u003e' . \"\\n\";\n }\n echo \"\u003c!-- /PlanExpert SEO --\u003e\\n\\n\";\n}\nadd_action( 'wp_head', 'planexpert_head_seo', 2 );\n\n/**\n * Remove native WP meta/title duplicates where we output our own.\n */\nfunction planexpert_document_title_parts( $parts ) {\n if ( isset( $parts['title'] ) ) {\n $parts['title'] = str_replace( \"Plans d Affaires\", \"Plans d'Affaires\", $parts['title'] );\n }\n return $parts;\n}\nadd_filter( 'document_title_parts', 'planexpert_document_title_parts' );\n\n/**\n * Contact form handler.\n * Intercepts POST on any page when pe_contact_submit is set.\n */\nfunction planexpert_handle_contact_form() {\n if ( empty( $_POST['pe_contact_submit'] ) ) return;\n if ( ! isset( $_POST['pe_contact_nonce'] ) || ! wp_verify_nonce( $_POST['pe_contact_nonce'], 'pe_contact_form' ) ) {\n wp_safe_redirect( add_query_arg( 'pe_form', 'error', wp_get_referer() ?: home_url( '/' ) ) );\n exit;\n }\n // Honeypot\n if ( ! empty( $_POST['pe_website'] ) ) {\n wp_safe_redirect( add_query_arg( 'pe_form', 'success', home_url( '/merci/' ) ) );\n exit;\n }\n $name = isset( $_POST['pe_name'] ) ? sanitize_text_field( wp_unslash( $_POST['pe_name'] ) ) : '';\n $email = isset( $_POST['pe_email'] ) ? sanitize_email( wp_unslash( $_POST['pe_email'] ) ) : '';\n $phone = isset( $_POST['pe_phone'] ) ? sanitize_text_field( wp_unslash( $_POST['pe_phone'] ) ) : '';\n $project = isset( $_POST['pe_project'] ) ? sanitize_text_field( wp_unslash( $_POST['pe_project'] ) ) : '';\n $message = isset( $_POST['pe_message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['pe_message'] ) ) : '';\n\n if ( empty( $name ) || empty( $email ) || ! is_email( $email ) ) {\n wp_safe_redirect( add_query_arg( 'pe_form', 'invalid', wp_get_referer() ?: home_url( '/' ) ) );\n exit;\n }\n $to = 'contact@planexpert.fr';\n $subject = sprintf( '[PlanExpert] Nouvelle demande de devis - %s', $name );\n $body = \"Nouvelle demande via le formulaire PlanExpert :\\n\\n\";\n $body .= \"Nom : {$name}\\n\";\n $body .= \"Email : {$email}\\n\";\n $body .= \"Téléphone : {$phone}\\n\";\n $body .= \"Projet : {$project}\\n\";\n $body .= \"Message :\\n{$message}\\n\\n\";\n $body .= \"---\\nEnvoyé depuis : \" . ( wp_get_referer() ?: home_url( '/' ) ) . \"\\nIP : \" . ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( $_SERVER['REMOTE_ADDR'] ) : '' ) . \"\\n\";\n $headers = array(\n 'Content-Type: text/plain; charset=UTF-8',\n 'Reply-To: ' . $name . ' \u003c' . $email . '\u003e',\n );\n wp_mail( $to, $subject, $body, $headers );\n\n // Store lead in a CPT for safety (never lose a lead)\n $post_id = wp_insert_post( array(\n 'post_type' =\u003e 'pe_lead',\n 'post_status' =\u003e 'private',\n 'post_title' =\u003e $name . ' - ' . current_time( 'Y-m-d H:i' ),\n 'post_content' =\u003e $body,\n 'meta_input' =\u003e array(\n 'pe_lead_email' =\u003e $email,\n 'pe_lead_phone' =\u003e $phone,\n 'pe_lead_project' =\u003e $project,\n ),\n ) );\n\n wp_safe_redirect( add_query_arg( 'pe_form', 'success', home_url( '/merci/' ) ) );\n exit;\n}\nadd_action( 'template_redirect', 'planexpert_handle_contact_form' );\n\n/**\n * Register custom post type for leads storage.\n */\nfunction planexpert_register_leads_cpt() {\n register_post_type( 'pe_lead', array(\n 'label' =\u003e 'Leads',\n 'public' =\u003e false,\n 'show_ui' =\u003e true,\n 'show_in_menu' =\u003e true,\n 'menu_icon' =\u003e 'dashicons-email-alt',\n 'menu_position' =\u003e 20,\n 'supports' =\u003e array( 'title', 'editor', 'custom-fields' ),\n 'capability_type' =\u003e 'post',\n ) );\n}\nadd_action( 'init', 'planexpert_register_leads_cpt' );\n\n/**\n * Shortcode that outputs the full lead form (POST method + nonce + honeypot).\n * Usage: [pe_contact_form]\n */\nfunction planexpert_contact_form_shortcode() {\n ob_start();\n $status = isset( $_GET['pe_form'] ) ? sanitize_text_field( wp_unslash( $_GET['pe_form'] ) ) : '';\n ?\u003e\n \u003c?php if ( $status === 'success' ) : ?\u003e\n \u003cdiv class=\"pe-form-alert pe-form-alert--success\" role=\"status\"\u003e\n Merci ! Votre demande a bien été reçue. Nous vous répondons sous 24h ouvrées.\n \u003c/div\u003e\n \u003c?php elseif ( $status === 'error' || $status === 'invalid' ) : ?\u003e\n \u003cdiv class=\"pe-form-alert pe-form-alert--error\" role=\"alert\"\u003e\n Une erreur est survenue. Merci de vérifier vos informations et réessayer.\n \u003c/div\u003e\n \u003c?php endif; ?\u003e\n \u003cform class=\"pe-contact-form\" method=\"post\" action=\"\u003c?php echo esc_url( home_url( '/' ) ); ?\u003e\" novalidate\u003e\n \u003c?php wp_nonce_field( 'pe_contact_form', 'pe_contact_nonce' ); ?\u003e\n \u003cinput type=\"text\" name=\"pe_website\" value=\"\" tabindex=\"-1\" autocomplete=\"off\" style=\"position:absolute;left:-9999px;\" aria-hidden=\"true\"\u003e\n \u003cdiv class=\"pe-form-row\"\u003e\n \u003clabel for=\"pe_name\"\u003eNom complet *\u003c/label\u003e\n \u003cinput type=\"text\" id=\"pe_name\" name=\"pe_name\" required autocomplete=\"name\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"pe-form-row\"\u003e\n \u003clabel for=\"pe_email\"\u003eEmail professionnel *\u003c/label\u003e\n \u003cinput type=\"email\" id=\"pe_email\" name=\"pe_email\" required autocomplete=\"email\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"pe-form-row\"\u003e\n \u003clabel for=\"pe_phone\"\u003eTéléphone\u003c/label\u003e\n \u003cinput type=\"tel\" id=\"pe_phone\" name=\"pe_phone\" autocomplete=\"tel\"\u003e\n \u003c/div\u003e\n \u003cdiv class=\"pe-form-row\"\u003e\n \u003clabel for=\"pe_project\"\u003eType de projet\u003c/label\u003e\n \u003cselect id=\"pe_project\" name=\"pe_project\"\u003e\n \u003coption value=\"\"\u003e-- Sélectionnez --\u003c/option\u003e\n \u003coption value=\"Création\"\u003eCréation d'entreprise\u003c/option\u003e\n \u003coption value=\"Levée de fonds\"\u003eLevée de fonds\u003c/option\u003e\n \u003coption value=\"Prêt bancaire\"\u003ePrêt bancaire\u003c/option\u003e\n \u003coption value=\"Reprise\"\u003eReprise d'activité\u003c/option\u003e\n \u003coption value=\"Autre\"\u003eAutre\u003c/option\u003e\n \u003c/select\u003e\n \u003c/div\u003e\n \u003cdiv class=\"pe-form-row\"\u003e\n \u003clabel for=\"pe_message\"\u003eVotre projet en quelques mots\u003c/label\u003e\n \u003ctextarea id=\"pe_message\" name=\"pe_message\" rows=\"5\"\u003e\u003c/textarea\u003e\n \u003c/div\u003e\n \u003cbutton type=\"submit\" name=\"pe_contact_submit\" value=\"1\" class=\"pe-btn pe-btn--primary pe-btn--large\"\u003eObtenir mon devis gratuit\u003c/button\u003e\n \u003cp class=\"pe-form-note\"\u003eEn envoyant ce formulaire, vous acceptez notre \u003ca href=\"\u003c?php echo esc_url( home_url( '/politique-de-confidentialite/' ) ); ?\u003e\"\u003epolitique de confidentialité\u003c/a\u003e.\u003c/p\u003e\n \u003c/form\u003e\n \u003c?php\n return ob_get_clean();\n}\nadd_shortcode( 'pe_contact_form', 'planexpert_contact_form_shortcode' );\n\n\n// Custom SEO titles for internal pages\nfunction planexpert_custom_page_titles( $title ) {\n if ( is_page('tarifs') ) return 'Tarifs business plan professionnel dès 490 € HT – PlanExpert';\n if ( is_page('faq') ) return 'FAQ business plan : vos questions, nos réponses – PlanExpert';\n if ( is_page('blog') || is_home() ) return 'Blog PlanExpert : conseils business plan, financement, création';\n if ( is_page('contact') ) return 'Devis gratuit business plan en 24h – Contactez PlanExpert';\n if ( is_page('exemples') ) return 'Exemples de business plans réussis – PlanExpert';\n if ( is_page('a-propos') ) return 'À propos de PlanExpert – Experts en business plan depuis 2024';\n if ( is_page('portfolio') ) return 'Nos réalisations : business plans financés – Portfolio PlanExpert';\n if ( is_page('comment-ca-marche') ) return 'Comment obtenir votre business plan en 5 étapes – PlanExpert';\n return $title;\n}\nadd_filter( 'pre_get_document_title', 'planexpert_custom_page_titles' );\n\n\n\n\n// Service + Offer schema for /tarifs/\nfunction planexpert_tarifs_schema() {\n if ( ! is_page('tarifs') ) return;\n $schema = array(\n '@context' =\u003e 'https://schema.org',\n '@type' =\u003e 'Service',\n 'serviceType' =\u003e 'Rédaction de business plan',\n 'provider' =\u003e array( '@type' =\u003e 'ProfessionalService', '@id' =\u003e 'https://planexpert.fr/#business' ),\n 'hasOfferCatalog' =\u003e array(\n '@type' =\u003e 'OfferCatalog',\n 'name' =\u003e 'Formules business plan PlanExpert',\n 'itemListElement' =\u003e array(\n array( '@type' =\u003e 'Offer', 'name' =\u003e 'Pack Essentiel', 'description' =\u003e 'Business plan 20-25 pages, prévisionnel 3 ans, 1 révision, livraison 7 jours', 'price' =\u003e '490', 'priceCurrency' =\u003e 'EUR', 'url' =\u003e 'https://planexpert.fr/tarifs/' ),\n array( '@type' =\u003e 'Offer', 'name' =\u003e 'Pack Business', 'description' =\u003e 'Business plan 30-40 pages, 3 scénarios, étude de marché, 2 révisions, livraison 5 jours', 'price' =\u003e '790', 'priceCurrency' =\u003e 'EUR', 'url' =\u003e 'https://planexpert.fr/tarifs/' ),\n array( '@type' =\u003e 'Offer', 'name' =\u003e 'Pack Premium', 'description' =\u003e 'Business plan 40-60 pages, multi-scénarios, étude approfondie, coaching pitch 45min, 3 révisions', 'price' =\u003e '1290', 'priceCurrency' =\u003e 'EUR', 'url' =\u003e 'https://planexpert.fr/tarifs/' ),\n ),\n ),\n );\n echo '\u003c' . 'script type=\"application/ld+json\"\u003e' . wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) . '\u003c' . '/script\u003e' . \"\\n\";\n}\nadd_action( 'wp_head', 'planexpert_tarifs_schema' );\n\n// FAQPage schema for /faq/ and homepage FAQ section\nfunction planexpert_faq_schema() {\n if ( ! is_page('faq') \u0026\u0026 ! is_front_page() ) return;\n $questions = array(\n array( 'q' =\u003e 'Combien coûte un business plan professionnel ?', 'a' =\u003e 'Nos formules démarrent à 490 € pour un business plan de 20 à 25 pages et vont jusqu\\'à 1 290 € pour un accompagnement complet.' ),\n array( 'q' =\u003e 'Quel est le délai de livraison ?', 'a' =\u003e 'Le Pack Business est livré en 5 jours ouvrés, les Packs Essentiel et Premium en 7 jours ouvrés.' ),\n array( 'q' =\u003e 'Pour quel type de projet rédigez-vous des business plans ?', 'a' =\u003e 'Nous accompagnons tous les secteurs : restauration, e-commerce, SaaS, services, commerce de proximité, franchise, artisanat, conseil, immobilier.' ),\n array( 'q' =\u003e 'Quelle est la différence avec un business plan gratuit ?', 'a' =\u003e 'Nos business plans sont rédigés sur mesure par des experts, avec des données sourcées et un modèle financier construit hypothèse par hypothèse.' ),\n array( 'q' =\u003e 'Est-ce que ça marche pour obtenir un prêt bancaire ?', 'a' =\u003e '85 % de nos clients obtiennent le financement demandé après présentation de leur business plan PlanExpert.' ),\n array( 'q' =\u003e 'Comment se passe la collaboration ?', 'a' =\u003e 'Tout commence par un entretien stratégique de 45 minutes. Vous nous partagez vos informations via un questionnaire structuré.' ),\n array( 'q' =\u003e 'Puis-je demander des modifications ?', 'a' =\u003e 'Chaque pack inclut un nombre de révisions défini (1 à 3 selon la formule).' ),\n array( 'q' =\u003e 'Quels formats sont livrés ?', 'a' =\u003e 'Vous recevez votre business plan en PDF et en Word. Le prévisionnel financier est fourni en fichier Excel.' ),\n );\n $entities = array();\n foreach ( $questions as $item ) {\n $entities[] = array(\n '@type' =\u003e 'Question',\n 'name' =\u003e $item['q'],\n 'acceptedAnswer' =\u003e array( '@type' =\u003e 'Answer', 'text' =\u003e $item['a'] ),\n );\n }\n $schema = array( '@context' =\u003e 'https://schema.org', '@type' =\u003e 'FAQPage', 'mainEntity' =\u003e $entities );\n echo '\u003c' . 'script type=\"application/ld+json\"\u003e' . wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) . '\u003c' . '/script\u003e' . \"\\n\";\n}\nadd_action( 'wp_head', 'planexpert_faq_schema' );\n\n// BreadcrumbList schema for internal pages\nfunction planexpert_breadcrumb_schema() {\n if ( is_front_page() ) return;\n $page_title = get_the_title();\n $page_url = get_permalink();\n $schema = array(\n '@context' =\u003e 'https://schema.org',\n '@type' =\u003e 'BreadcrumbList',\n 'itemListElement' =\u003e array(\n array( '@type' =\u003e 'ListItem', 'position' =\u003e 1, 'name' =\u003e 'Accueil', 'item' =\u003e home_url('/') ),\n array( '@type' =\u003e 'ListItem', 'position' =\u003e 2, 'name' =\u003e $page_title, 'item' =\u003e $page_url ),\n ),\n );\n echo '\u003c' . 'script type=\"application/ld+json\"\u003e' . wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) . '\u003c' . '/script\u003e' . \"\\n\";\n}\n// add_action( 'wp_head', 'planexpert_breadcrumb_schema' ); // Disabled — handled by planexpert_seo_output @graph\n\n// Noindex sur la page /merci/\nadd_action('wp_head', function() {\n if (is_page('merci')) {\n echo '\u003cmeta name=\"robots\" content=\"noindex, nofollow\"\u003e' . \"\\n\";\n }\n});\n\n// Fix double H1 on pages using generic page.php template\n// The template already outputs \u003ch1\u003e via the_title(), so convert any H1 in content to H2\nfunction planexpert_fix_double_h1( $content ) {\n // Only on singular pages that use the default page.php template\n if ( ! is_page() ) return $content;\n // Skip pages with dedicated templates\n $skip = array( 'tarifs', 'faq', 'contact', 'exemples', 'a-propos', 'portfolio', 'comment-ca-marche', 'nos-offres', 'temoignages' );\n foreach ( $skip as $slug ) {\n if ( is_page( $slug ) ) return $content;\n }\n // Replace first \u003ch1\u003e with \u003ch2\u003e in the content\n $content = preg_replace( '/\u003ch1(\\s[^\u003e]*)?\u003e/', '\u003ch2$1\u003e', $content, 1 );\n $content = preg_replace( '/\u003c\\/h1\u003e/', '\u003c/h2\u003e', $content, 1 );\n return $content;\n}\nadd_filter( 'the_content', 'planexpert_fix_double_h1' );\n\n\n/**\n * SEO — Meta descriptions \u0026 Schema JSON-LD (@graph) for blog posts\n */\nfunction planexpert_seo_output() {\n // Meta description for single posts\n if ( is_singular( 'post' ) ) {\n global $post;\n $excerpt = get_the_excerpt( $post );\n if ( $excerpt ) {\n $desc = esc_attr( wp_strip_all_tags( $excerpt ) );\n echo '\u003cmeta name=\"description\" content=\"' . $desc . '\"\u003e' . \"\\n\";\n }\n }\n\n // Build @graph schema array\n if ( ! is_singular() ) return;\n\n global $post;\n $graph = array();\n\n // BreadcrumbList — all singular pages\n $breadcrumb_items = array(\n array( '@type' =\u003e 'ListItem', 'position' =\u003e 1, 'name' =\u003e 'Accueil', 'item' =\u003e home_url( '/' ) ),\n );\n if ( is_singular( 'post' ) ) {\n $breadcrumb_items[] = array( '@type' =\u003e 'ListItem', 'position' =\u003e 2, 'name' =\u003e 'Blog', 'item' =\u003e home_url( '/blog/' ) );\n $breadcrumb_items[] = array( '@type' =\u003e 'ListItem', 'position' =\u003e 3, 'name' =\u003e get_the_title() );\n }\n $graph[] = array(\n '@type' =\u003e 'BreadcrumbList',\n 'itemListElement' =\u003e $breadcrumb_items,\n );\n\n // Article schema — single posts only\n if ( is_singular( 'post' ) ) {\n $excerpt = get_the_excerpt( $post );\n $graph[] = array(\n '@type' =\u003e 'Article',\n 'headline' =\u003e get_the_title( $post ),\n 'description' =\u003e wp_strip_all_tags( $excerpt ),\n 'author' =\u003e array( '@type' =\u003e 'Organization', 'name' =\u003e 'PlanExpert' ),\n 'publisher' =\u003e array( '@type' =\u003e 'Organization', 'name' =\u003e 'PlanExpert' ),\n 'datePublished' =\u003e get_the_date( 'c', $post ),\n 'dateModified' =\u003e get_the_modified_date( 'c', $post ),\n 'mainEntityOfPage' =\u003e get_permalink( $post ),\n );\n\n // FAQ schema — posts with H3 questions\n $content = $post-\u003epost_content;\n preg_match_all( '/\u003ch3[^\u003e]*\u003e\\\\s*([^\u003c]+?)\\\\s*\u003c\\/h3\u003e\\\\s*\u003cp\u003e(.*?)\u003c\\/p\u003e/si', $content, $matches, PREG_SET_ORDER );\n $faq_items = array();\n if ( ! empty( $matches ) ) {\n foreach ( $matches as $m ) {\n $question = wp_strip_all_tags( trim( $m[1] ) );\n if ( substr( $question, -1 ) !== '?' ) continue;\n $answer = wp_strip_all_tags( trim( $m[2] ) );\n $faq_items[] = array(\n '@type' =\u003e 'Question',\n 'name' =\u003e $question,\n 'acceptedAnswer' =\u003e array( '@type' =\u003e 'Answer', 'text' =\u003e $answer ),\n );\n }\n }\n if ( count( $faq_items ) \u003e= 2 ) {\n $graph[] = array(\n '@type' =\u003e 'FAQPage',\n 'mainEntity' =\u003e $faq_items,\n );\n }\n }\n\n // Output single @graph JSON-LD\n $schema = array(\n '@context' =\u003e 'https://schema.org',\n '@graph' =\u003e $graph,\n );\n echo '\u003cscript type=\"application/ld+json\"\u003e' . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '\u003c/script\u003e' . \"\\n\";\n}\nadd_action( 'wp_head', 'planexpert_seo_output' );\n\n/**\n * Sitemap — Exclude /merci/ and remove users sitemap\n */\nfunction planexpert_sitemap_exclude_merci( $args, $post_type ) {\n if ( 'page' === $post_type ) {\n $merci = get_page_by_path( 'merci' );\n if ( $merci ) {\n $args['post__not_in'] = isset( $args['post__not_in'] ) ? $args['post__not_in'] : array();\n $args['post__not_in'][] = $merci-\u003eID;\n }\n }\n return $args;\n}\nadd_filter( 'wp_sitemaps_posts_query_args', 'planexpert_sitemap_exclude_merci', 10, 2 );\n\n// Remove users sitemap provider\nadd_filter( 'wp_sitemaps_add_provider', function( $provider, $name ) {\n if ( 'users' === $name ) return false;\n return $provider;\n}, 10, 2 );\n\n/**\n * Google Ads Tag (gtag.js) — conversion tracking\n */\nfunction planexpert_google_ads_tag() {\n echo '\u003cscript async src=\"https://www.googletagmanager.com/gtag/js?id=G-T9SJD93Y1Z\"\u003e\u003c/script\u003e' . \"\\n\";\n echo '\u003cscript\u003ewindow.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag(\"js\",new Date());gtag(\"config\",\"G-T9SJD93Y1Z\");\u003c/script\u003e' . \"\\n\";\n}\nadd_action( 'wp_head', 'planexpert_google_ads_tag', 1 ); /** * Google Ads Conversion on /merci/ */ function planexpert_google_ads_conversion() { if ( is_page( 'merci' ) ) { echo '' . "\n"; } } add_action( 'wp_footer', 'planexpert_google_ads_conversion' );\n","link":""} https://planexpert.fr/wp-sitemap-posts-post-1.xmlhttps://planexpert.fr/wp-sitemap-posts-page-1.xmlhttps://planexpert.fr/wp-sitemap-taxonomies-category-1.xmlhttps://planexpert.fr/wp-sitemap-users-1.xml