| 1 |
<?php |
| 2 |
if (!defined('ABSPATH')) { |
| 3 |
exit; // Exit if accessed directly |
| 4 |
} |
| 5 |
|
| 6 |
/** |
| 7 |
* Gutenberg block for placing the chatbot (plan 95dd1e). |
| 8 |
* |
| 9 |
* A thin wrapper over the [mxchat_chatbot] shortcode — the block never |
| 10 |
* reimplements rendering, so it inherits every shortcode behavior including |
| 11 |
* the floating/Auto-Display collision handling and per-page bot settings. |
| 12 |
* |
| 13 |
* The editor canvas gets a STATIC placeholder only (see chatbot-block.js): |
| 14 |
* a live chatbot in the editor would fire the front-end script and could |
| 15 |
* start a chat session for the person editing. That is why this block does |
| 16 |
* NOT use ServerSideRender — the render callback below runs on the front |
| 17 |
* end only. |
| 18 |
*/ |
| 19 |
class MxChat_Block { |
| 20 |
|
| 21 |
public static function init() { |
| 22 |
add_action('init', array(__CLASS__, 'register_block')); |
| 23 |
} |
| 24 |
|
| 25 |
public static function register_block() { |
| 26 |
// WP < 5.0 (readme floor) — no block editor, quiet no-op. |
| 27 |
if (!function_exists('register_block_type')) { |
| 28 |
return; |
| 29 |
} |
| 30 |
|
| 31 |
$main_file = dirname(__DIR__) . '/mxchat-basic.php'; |
| 32 |
$ver = defined('MXCHAT_VERSION') ? MXCHAT_VERSION : '1.0'; |
| 33 |
|
| 34 |
wp_register_script( |
| 35 |
'mxchat-chatbot-block', |
| 36 |
plugins_url('assets/block/chatbot-block.js', $main_file), |
| 37 |
array('wp-blocks', 'wp-element', 'wp-components', 'wp-i18n'), |
| 38 |
$ver, |
| 39 |
true |
| 40 |
); |
| 41 |
|
| 42 |
wp_register_style( |
| 43 |
'mxchat-chatbot-block-editor', |
| 44 |
plugins_url('assets/block/chatbot-block-editor.css', $main_file), |
| 45 |
array(), |
| 46 |
$ver |
| 47 |
); |
| 48 |
|
| 49 |
if (function_exists('wp_set_script_translations')) { |
| 50 |
wp_set_script_translations('mxchat-chatbot-block', 'mxchat', dirname(__DIR__) . '/languages'); |
| 51 |
} |
| 52 |
|
| 53 |
wp_localize_script('mxchat-chatbot-block', 'MxChatBlockData', array( |
| 54 |
// Non-empty only when Multi-Bot is active: the bot select is hidden |
| 55 |
// otherwise. 'default' maps to '' so an untouched block behaves like |
| 56 |
// a bare [mxchat_chatbot] — it follows the per-page bot setting |
| 57 |
// instead of force-pinning the default bot. |
| 58 |
'bots' => self::get_bot_choices(), |
| 59 |
)); |
| 60 |
|
| 61 |
register_block_type('mxchat/chatbot', array( |
| 62 |
'api_version' => 2, |
| 63 |
'editor_script' => 'mxchat-chatbot-block', |
| 64 |
'editor_style' => 'mxchat-chatbot-block-editor', |
| 65 |
'render_callback' => array(__CLASS__, 'render'), |
| 66 |
'attributes' => array( |
| 67 |
'displayMode' => array('type' => 'string', 'default' => 'inline'), |
| 68 |
'botId' => array('type' => 'string', 'default' => ''), |
| 69 |
), |
| 70 |
)); |
| 71 |
} |
| 72 |
|
| 73 |
/** |
| 74 |
* Bot choices for the editor's select. Empty array when Multi-Bot is |
| 75 |
* inactive — the JS hides the control entirely rather than showing a |
| 76 |
* one-entry dropdown. Guard mirrors class-mxchat-public.php:869. |
| 77 |
* |
| 78 |
* Public: the Elementor widget (class-mxchat-elementor-widget.php) builds |
| 79 |
* its Bot control from this same list — one source, never a second copy. |
| 80 |
*/ |
| 81 |
public static function get_bot_choices() { |
| 82 |
if (!class_exists('MxChat_Multi_Bot_Manager') || !class_exists('MxChat_Multi_Bot_Core_Manager')) { |
| 83 |
return array(); |
| 84 |
} |
| 85 |
|
| 86 |
$manager = MxChat_Multi_Bot_Core_Manager::get_instance(); |
| 87 |
if (!method_exists($manager, 'get_available_bots')) { |
| 88 |
return array(); |
| 89 |
} |
| 90 |
|
| 91 |
$choices = array(); |
| 92 |
foreach ($manager->get_available_bots() as $bot_id => $bot_name) { |
| 93 |
$choices[] = array( |
| 94 |
'value' => $bot_id === 'default' ? '' : (string) $bot_id, |
| 95 |
'label' => (string) $bot_name, |
| 96 |
); |
| 97 |
} |
| 98 |
return $choices; |
| 99 |
} |
| 100 |
|
| 101 |
/** |
| 102 |
* Front-end render: delegate to the shortcode. |
| 103 |
* |
| 104 |
* Consent mirrors append_chatbot_to_body() exactly (class-mxchat-public.php) |
| 105 |
* so a block placement cannot bypass a Complianz gate the footer path |
| 106 |
* respects. Everything else — bot resolution, per-page settings, the |
| 107 |
* floating/Auto-Display collision — is the shortcode's existing logic. |
| 108 |
*/ |
| 109 |
public static function render($attributes) { |
| 110 |
$floating = isset($attributes['displayMode']) && $attributes['displayMode'] === 'floating'; |
| 111 |
$bot_id = isset($attributes['botId']) ? sanitize_key($attributes['botId']) : ''; |
| 112 |
|
| 113 |
$options = get_option('mxchat_options', array()); |
| 114 |
$has_consent = true; |
| 115 |
if (is_array($options) |
| 116 |
&& isset($options['complianz_toggle']) |
| 117 |
&& $options['complianz_toggle'] === 'on' |
| 118 |
&& function_exists('cmplz_has_consent') |
| 119 |
) { |
| 120 |
$has_consent = cmplz_has_consent('marketing'); |
| 121 |
} |
| 122 |
|
| 123 |
$shortcode = sprintf( |
| 124 |
'[mxchat_chatbot floating="%s" has_consent="%s"', |
| 125 |
$floating ? 'yes' : 'no', |
| 126 |
$has_consent ? 'yes' : 'no' |
| 127 |
); |
| 128 |
// Omit bot_id when unset: determine_bot_for_shortcode() then honors the |
| 129 |
// page-level bot selection, same as a bare shortcode. |
| 130 |
if ($bot_id !== '' && $bot_id !== 'default') { |
| 131 |
$shortcode .= ' bot_id="' . esc_attr($bot_id) . '"'; |
| 132 |
} |
| 133 |
$shortcode .= ']'; |
| 134 |
|
| 135 |
return do_shortcode($shortcode); |
| 136 |
} |
| 137 |
} |
| 138 |
|