'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(
'
',
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'] )
);
}
}