'fullpage', 'position' => 'right', 'config_id' => '', 'width' => '', 'height' => '', 'auto_open' => '', 'header_text' => '', 'accent_color' => '', 'bubble_label' => '', 'class' => '', 'container_height' => '600px', // Per-shortcode chatbot behavior overrides 'scenario' => '', 'context_hint' => '', 'hide_bubble' => '', // "1"/"true" hides the floating bubble ], (array) $atts, 'agentsia_chatbot' ); $hide_bubble_raw = strtolower( (string) $atts['hide_bubble'] ); $hide_bubble = in_array( $hide_bubble_raw, [ '1', 'true', 'yes' ], true ); $overrides = [ 'mode' => in_array( $atts['mode'], [ 'bubble', 'modal', 'fullpage' ], true ) ? $atts['mode'] : 'fullpage', 'position' => in_array( $atts['position'], [ 'left', 'right' ], true ) ? $atts['position'] : 'right', 'chatbot_config_id' => sanitize_text_field( $atts['config_id'] ), 'width' => sanitize_text_field( $atts['width'] ), 'height' => sanitize_text_field( $atts['height'] ), 'auto_open' => sanitize_text_field( $atts['auto_open'] ), 'header_text' => sanitize_text_field( $atts['header_text'] ), 'accent_color' => sanitize_text_field( $atts['accent_color'] ), 'bubble_label' => sanitize_text_field( $atts['bubble_label'] ), 'scenario' => preg_replace( '/\s+/u', '-', sanitize_text_field( $atts['scenario'] ) ), 'context_hint' => sanitize_textarea_field( $atts['context_hint'] ), ]; if ( $hide_bubble_raw !== '' ) { $overrides['hide_bubble'] = $hide_bubble; } // Fall back to the per-page overrides (sidebar panel / meta box) for values // the shortcode didn't set — so a page's chosen scenario also applies to an // embedded chatbot, not only the global floating bubble. $post_overrides = Post_Meta::instance()->resolve_overrides_for_current_request(); if ( empty( $overrides['scenario'] ) && ! empty( $post_overrides['scenario'] ) ) { $overrides['scenario'] = $post_overrides['scenario']; } if ( empty( $overrides['context_hint'] ) && ! empty( $post_overrides['context_hint'] ) ) { $overrides['context_hint'] = $post_overrides['context_hint']; } $container_attrs = [ 'class' => sanitize_html_class( $atts['class'] ), 'style' => 'width:100%;min-height:' . esc_attr( $atts['container_height'] ) . ';', ]; return Chatbot::instance()->enqueue_widget( $overrides, $container_attrs ); } public function render_booking( $atts ): string { $settings = Plugin::get_settings(); $atts = shortcode_atts( [ 'slug' => $settings['default_booking_slug'], 'height' => '780', 'width' => '100%', 'class' => '', ], (array) $atts, 'agentsia_booking' ); // Slug vide (`slug=""`, bloc laissé sur « Calendrier par défaut ») : celui des réglages. $ref = self::normalize_booking_ref( (string) $atts['slug'] ); if ( $ref === '' ) { $ref = self::normalize_booking_ref( (string) $settings['default_booking_slug'] ); } if ( $ref === '' ) { if ( current_user_can( 'manage_options' ) ) { return sprintf( '
%s %s
', esc_html__( 'Agentsia Booking : aucun slug n’est défini.', 'agentsia-agents' ), esc_url( admin_url( 'options-general.php?page=agentsia-agents' ) ), esc_html__( 'Configurer', 'agentsia-agents' ) ); } return ''; } $api_base = untrailingslashit( $settings['api_base'] ?: AGENTSIA_AGENTS_DEFAULT_API_BASE ); $src = $api_base . self::booking_path( $ref ); $height = preg_match( '/^\d+$/', (string) $atts['height'] ) ? $atts['height'] . 'px' : sanitize_text_field( $atts['height'] ); $width = sanitize_text_field( $atts['width'] ); $class = trim( 'agentsia-booking-iframe ' . sanitize_html_class( $atts['class'] ) ); // Écouteur postMessage : l'iframe s'auto-redimensionne en hauteur. // Le `height` ci-dessus n'est plus que la hauteur initiale. $resize_handle = 'agentsia-booking-resize'; if ( ! wp_script_is( $resize_handle, 'registered' ) ) { wp_register_script( $resize_handle, AGENTSIA_AGENTS_URL . 'assets/js/booking-resize.js', [], AGENTSIA_AGENTS_VERSION, true ); } wp_enqueue_script( $resize_handle ); return sprintf( '', esc_url( $src ), esc_attr( $class ), esc_attr( $width ), esc_attr( $height ), esc_attr__( 'Prendre rendez-vous', 'agentsia-agents' ) ); } /** * Calendrier saisi dans le shortcode, le bloc ou le réglage : `slug`, `espace/slug`, ou l'adresse recopiée * depuis l'app (`https://app.agentsia.fr/book/espace/slug`). Rend 1 ou 2 segments passés par sanitize_title, * joints par `/` (sanitize_title seul retirerait le `/` et collerait les deux) ; chaîne vide sinon. */ public static function normalize_booking_ref( string $raw ): string { $raw = (string) preg_replace( '/[?#].*$/s', '', trim( $raw ) ); $pos = strpos( $raw, '/book/' ); if ( $pos !== false ) { $raw = substr( $raw, $pos + strlen( '/book/' ) ); } $segments = array_values( array_filter( array_map( 'sanitize_title', explode( '/', $raw ) ), static function ( string $segment ): bool { return $segment !== ''; } ) ); if ( count( $segments ) < 1 || count( $segments ) > 2 ) { return ''; } return implode( '/', $segments ); } /** * Chemin de la page de rendez-vous. Depuis les adresses par espace de l'app, `/book//` : le slug * d'un calendrier n'est plus unique que dans son espace. * - `espace/slug` saisi : ce chemin, tel quel ; * - `slug` seul : le chemin que l'API donne pour ce calendrier de l'espace de la clé, sinon `/book/`, * que l'app sert encore (slug d'avant les adresses par espace, ou porté par un seul calendrier actif ; * à défaut, elle affiche un formulaire de contact). * Chaque segment est encodé à part : rawurlencode sur le chemin entier changerait `/` en `%2F`. */ private static function booking_path( string $ref ): string { if ( strpos( $ref, '/' ) === false ) { $path = Api_Client::booking_calendar_path( $ref ); if ( $path !== null ) { $ref = substr( $path, strlen( '/book/' ) ); } } return '/book/' . implode( '/', array_map( 'rawurlencode', explode( '/', $ref ) ) ); } public function render_demo_cta( $atts ): string { $settings = Plugin::get_settings(); $atts = shortcode_atts( [ 'label' => __( 'Demander une démo', 'agentsia-agents' ), 'path' => '/demo', 'target' => '_self', 'class' => '', 'style' => 'primary', ], (array) $atts, 'agentsia_demo' ); $api_base = untrailingslashit( $settings['api_base'] ?: AGENTSIA_AGENTS_DEFAULT_API_BASE ); $path = '/' . ltrim( sanitize_text_field( $atts['path'] ), '/' ); $url = $api_base . $path; $base_class = $atts['style'] === 'outline' ? 'agentsia-btn agentsia-btn-outline' : 'agentsia-btn agentsia-btn-primary'; $final_class = trim( $base_class . ' ' . sanitize_html_class( $atts['class'] ) ); $target = in_array( $atts['target'], [ '_self', '_blank' ], true ) ? $atts['target'] : '_self'; $rel = $target === '_blank' ? ' rel="noopener noreferrer"' : ''; return sprintf( '%5$s', esc_url( $url ), esc_attr( $final_class ), esc_attr( $target ), $rel, esc_html( $atts['label'] ) ); } }