options = get_option('mxchat_options');
$this->chat_count = get_option('mxchat_chat_count', 0);
$this->is_activated = $this->is_license_active();
// Initialize default options if they are not set
if (!$this->options) {
$this->initialize_default_options();
}
// Add admin menu and initialize settings
add_action('admin_menu', array($this, 'mxchat_add_plugin_page'));
add_action('admin_init', array($this, 'mxchat_page_init'));
add_action('admin_init', array($this, 'mxchat_prompts_page_init'));
add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets'));
add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history'));
add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission'));
add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt'));
add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history'));
add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission'));
add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body'));
add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets'));
add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets'));
add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license'));
add_action('admin_notices', array($this, 'mxchat_display_admin_notice'));
add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt'));
add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts'));
add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent'));
add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent'));
add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold'));
add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent'));
add_action('save_post', array($this, 'handle_post_update'), 10, 3);
add_action('post_updated', array($this, 'handle_post_update'), 10, 3);
add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
add_action('wp_trash_post', array($this, 'mxchat_handle_post_delete'));
add_action('before_delete_post', array($this, 'mxchat_handle_post_delete'));
add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts'));
add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
if (isset($this->options['enable_woocommerce_integration']) &&
($this->options['enable_woocommerce_integration'] === '1' ||
$this->options['enable_woocommerce_integration'] === 'on')) {
//error_log('MxChat Admin: WooCommerce integration is enabled, adding hooks');
add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3);
add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete'));
add_action('before_delete_post', array($this, 'mxchat_handle_product_delete'));
}
}
// Method to check if the license is active
private function is_license_active() {
$license_status = get_option('mxchat_license_status', esc_html__('inactive', 'mxchat'));
return $license_status === esc_html__('active', 'mxchat');
}
// Initialize default options
private function initialize_default_options() {
$default_options = array(
'api_key' => '',
'xai_api_key' => '',
'claude_api_key' => '',
'deepseek_api_key' => '',
'system_prompt_instructions' => esc_html__('[EXAMPLE INSTRUCTIONS] You are an AI Chatbot assistant for this website. The primary subject you should focus on is [insert proper subject here]. Your main goal is to assist visitors with questions related to this specific topic. Here are some key things to keep in mind:
- Your name is [Chatbot Name].
- Stay focused on topics related to [insert proper subject here]. If a visitor asks about an unrelated topic, politely redirect the conversation to how you can assist them with this subject.
- Do not make up links to pages. Only use specific links you see in your knowledgebase from [insert your domain]. Do not make assumptions or make up URLs.
- Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction.
- Provide answers based on the knowledge available to you. If you do not have an answer to a specific question, let the visitor know that you don’t have the information.', 'mxchat'),
'model' => esc_html__('gpt-4o', 'mxchat'),
'rate_limit_logged_out' => esc_html__('100', 'mxchat'),
'role_rate_limits' => array(),
'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'),
'enable_email_block' => '',
'email_blocker_header_content' => esc_html__("
Welcome to Our Chat!
\n
Let's get started. Enter your email to begin chatting with us.
", 'mxchat'),
'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'),
'top_bar_title' => esc_html__('MxChat', 'mxchat'),
'intro_message' => esc_html__('Hello! How can I assist you today?', 'mxchat'),
'input_copy' => esc_html__('How can I assist?', 'mxchat'),
'append_to_body' => esc_html__('off', 'mxchat'),
'close_button_color' => esc_html__('#fff', 'mxchat'),
'chatbot_bg_color' => esc_html__('#fff', 'mxchat'),
'user_message_bg_color' => esc_html__('#fff', 'mxchat'),
'user_message_font_color' => esc_html__('#212121', 'mxchat'),
'bot_message_bg_color' => esc_html__('#212121', 'mxchat'),
'bot_message_font_color' => esc_html__('#fff', 'mxchat'),
'top_bar_bg_color' => esc_html__('#212121', 'mxchat'),
'send_button_font_color' => esc_html__('#212121', 'mxchat'),
'chat_input_font_color' => esc_html__('#212121', 'mxchat'),
'chatbot_background_color' => esc_html__('#212121', 'mxchat'),
'icon_color' => esc_html__('#fff', 'mxchat'),
'enable_woocommerce_integration' => esc_html__('0', 'mxchat'),
'link_target_toggle' => esc_html__('off', 'mxchat'),
'pre_chat_message' => esc_html__('Hey there! Ask me anything!', 'mxchat'),
// New fields for Loops Integration
'loops_api_key' => '',
'loops_mailing_list' => '',
'triggered_phrase_response' => esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
'email_capture_response' => esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'),
'popular_question_1' => '',
'popular_question_2' => '',
'popular_question_3' => '',
'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'),
'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
'pdf_max_pages' => 69,
// Live Agent Integration
'live_agent_webhook_url' => '',
'live_agent_secret_key' => '',
'live_agent_bot_token' => '',
'live_agent_message_bg_color' => esc_html__('#ffffff', 'mxchat'),
'live_agent_message_font_color' => esc_html__('#333333', 'mxchat'),
'chat_toolbar_toggle' => esc_html__('off', 'mxchat'),
'mode_indicator_bg_color' => esc_html__('#767676', 'mxchat'),
'mode_indicator_font_color' => esc_html__('#ffffff', 'mxchat'),
'toolbar_icon_color' => esc_html__('#212121', 'mxchat'),
);
// Merge existing options with defaults
$existing_options = get_option('mxchat_options', array());
$merged_options = wp_parse_args($existing_options, $default_options);
// Update the options if they have changed
if ($existing_options !== $merged_options) {
update_option('mxchat_options', $merged_options);
}
// Add default limits for each role
$roles = wp_roles()->get_names();
foreach ($roles as $role_id => $role_name) {
$default_options['role_rate_limits'][$role_id] = esc_html__('100', 'mxchat');
}
return $default_options;
// Update the $this->options property
$this->options = $merged_options;
}
public function mxchat_add_plugin_page() {
// Main menu page
add_menu_page(
esc_html__('MxChat Settings', 'mxchat'),
esc_html__('MxChat', 'mxchat'),
'manage_options',
'mxchat-max',
array($this, 'mxchat_create_admin_page'),
'dashicons-testimonial',
6
);
// Submenu page for Knowledge
add_submenu_page(
'mxchat-max',
esc_html__('Prompts', 'mxchat'),
esc_html__('Knowledge', 'mxchat'),
'manage_options',
'mxchat-prompts',
array($this, 'mxchat_create_prompts_page')
);
add_submenu_page(
'mxchat-max',
esc_html__('Chat Transcripts', 'mxchat'),
esc_html__('Transcripts', 'mxchat'),
'manage_options',
'mxchat-transcripts',
array($this, 'mxchat_create_transcripts_page')
);
add_submenu_page(
'mxchat-max',
esc_html__('MxChat Intents', 'mxchat'),
esc_html__('Intents', 'mxchat'),
'manage_options',
'mxchat-intents',
array($this, 'mxchat_intents_page_html')
);
add_submenu_page(
'mxchat-max',
esc_html__('Add Ons', 'mxchat'),
esc_html__('Add Ons', 'mxchat'),
'manage_options',
'mxchat-addons',
array($this, 'mxchat_create_addons_page')
);
// Submenu page for Activation Key
add_submenu_page(
'mxchat-max',
esc_html__('Pro Upgrade', 'mxchat'),
esc_html__('Pro Upgrade', 'mxchat'),
'manage_options',
'mxchat-activation',
array($this, 'mxchat_create_activation_page')
);
}
public function mxchat_create_addons_page() {
require_once plugin_dir_path(__FILE__) . 'class-mxchat-addons.php';
$addons_page = new MxChat_Addons();
$addons_page->render_page();
}
public function mxchat_save_setting_callback() {
check_ajax_referer('mxchat_save_setting_nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
}
$name = isset($_POST['name']) ? $_POST['name'] : '';
// Strip slashes from the value before saving
$value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
if (empty($name)) {
wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
}
// Load the full options array
$options = get_option('mxchat_options', []);
// Handle special cases
switch ($name) {
case 'additional_popular_questions':
$questions = json_decode($value, true); // No need for stripslashes here
if (is_array($questions)) {
$options[$name] = $questions;
// Also update old option for backwards compatibility
update_option('additional_popular_questions', $questions);
}
break;
case 'user_message_bg_color':
case 'user_message_font_color':
case 'bot_message_bg_color':
case 'bot_message_font_color':
case 'top_bar_bg_color':
case 'send_button_font_color':
case 'chatbot_background_color':
case 'icon_color':
case 'chat_input_font_color':
case 'live_agent_message_bg_color':
case 'live_agent_message_font_color':
case 'mode_indicator_bg_color':
case 'mode_indicator_font_color':
case 'toolbar_icon_color':
// Store color values directly
$options[$name] = $value;
break;
case 'live_agent_status':
// Set the new value
$options[$name] = ($value === 'on') ? 'on' : 'off';
break;
case 'enable_woocommerce_integration':
// Handle values that used to be 1/0
$options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
break;
default:
// Handle role rate limits
if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
// Extract role ID from the name
preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
if (isset($matches[1])) {
$role_id = $matches[1];
// Initialize role_rate_limits if it doesn't exist
if (!isset($options['role_rate_limits'])) {
$options['role_rate_limits'] = [];
}
// Update the specific role's rate limit
$options['role_rate_limits'][$role_id] = sanitize_text_field($value);
break;
}
}
// Handle toggles
if (strpos($name, 'toggle') !== false || in_array($name, [
'chat_persistence_toggle',
'privacy_toggle',
'complianz_toggle',
'chat_toolbar_toggle' // Removed live_agent_status from here
])) {
$options[$name] = ($value === 'on') ? 'on' : 'off';
} else {
// Store all other values directly
$options[$name] = $value;
}
break;
}
// Save all updates
$updated = update_option('mxchat_options', $options);
// Handle backwards compatibility for certain fields
$legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count',
'brave_country', 'brave_language', 'similarity_threshold'];
if (in_array($name, $legacy_fields)) {
// Also update the individual option for backwards compatibility
update_option($name, $value);
}
if ($updated) {
wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
} else {
wp_send_json_error(['message' => esc_html__('Update failed or no changes', 'mxchat')]);
}
}
/**
* Handles AJAX auto-save for prompts and auto-sync settings.
*/
public function mxchat_save_prompts_setting_callback() {
// Check the correct nonce for this action.
check_ajax_referer( 'mxchat_prompts_setting_nonce', '_ajax_nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ] );
}
$name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
$value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : '';
if ( empty( $name ) ) {
wp_send_json_error( [ 'message' => esc_html__( 'Invalid field name', 'mxchat' ) ] );
}
// Handle standalone auto-sync settings.
if ( in_array( $name, [ 'mxchat_auto_sync_posts', 'mxchat_auto_sync_pages' ], true ) ) {
// Convert checkbox value to "1" if checked, "0" if not.
$value = ( $value === 'on' ) ? '1' : '0';
update_option( $name, $value );
wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
}
// Handle fields stored in the 'mxchat_prompts_options' array.
// Handle fields stored in the 'mxchat_prompts_options' array.
if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) {
// Extract the key name using regex.
if ( preg_match( '/mxchat_prompts_options\[(.*?)\]/', $name, $matches ) && ! empty( $matches[1] ) ) {
$key = sanitize_text_field( $matches[1] );
$options = get_option( 'mxchat_prompts_options', [] );
// Remove Pinecone-specific settings from the main plugin handling.
if ( 'mxchat_use_pinecone' === $key ) {
// Optionally, you could simply ignore this field or set a default value.
// For example, ensure it's always 0 or remove it entirely:
// $options[ $key ] = '0';
// Or, if you prefer, just return an error so that the main plugin doesn't handle it:
wp_send_json_error( [ 'message' => esc_html__( 'Pinecone settings are now handled in the add-on.', 'mxchat' ) ] );
} else {
// For all other fields, use the existing logic.
$options[ $key ] = ( $value === 'on' || $value === '1' ) ? '1' : $value;
}
update_option( 'mxchat_prompts_options', $options );
wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] );
}
}
wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] );
}
public function mxchat_display_admin_notice() {
// Success notice
if ($message = get_transient('mxchat_admin_notice_success')) {
?>
prefix . 'mxchat_chat_transcripts';
// Get basic stats
$total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name");
$total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
// Count unique users with detailed breakdown
$total_users = $wpdb->get_var("
SELECT COUNT(DISTINCT
CASE
WHEN user_email != '' AND user_email IS NOT NULL THEN user_email
WHEN user_id != 0 THEN CONCAT('user_', user_id)
WHEN user_identifier NOT LIKE 'Tech-Savvy User'
AND user_identifier NOT LIKE 'Detail-Oriented User'
AND user_identifier NOT LIKE 'Language Learner'
AND user_identifier NOT LIKE 'Casual Browser'
AND user_identifier NOT LIKE 'Policy Enforcer'
AND user_identifier NOT LIKE 'Researcher'
AND user_identifier NOT LIKE 'Loyalty Member'
AND user_identifier NOT LIKE 'Gift Buyer'
AND user_identifier NOT LIKE 'Parent or Caregiver'
THEN user_identifier
ELSE session_id
END
)
FROM $table_name
WHERE role != 'assistant'
");
// Get user type breakdown
$registered_users = $wpdb->get_var("
SELECT COUNT(DISTINCT user_email)
FROM $table_name
WHERE user_email != '' AND user_email IS NOT NULL
");
$guest_users = $wpdb->get_var("
SELECT COUNT(DISTINCT user_identifier)
FROM $table_name
WHERE (user_email = '' OR user_email IS NULL)
AND role != 'assistant'
AND user_identifier NOT LIKE 'Tech-Savvy User'
AND user_identifier NOT LIKE 'Detail-Oriented User'
AND user_identifier NOT LIKE 'Language Learner'
AND user_identifier NOT LIKE 'Casual Browser'
AND user_identifier NOT LIKE 'Policy Enforcer'
AND user_identifier NOT LIKE 'Researcher'
AND user_identifier NOT LIKE 'Loyalty Member'
AND user_identifier NOT LIKE 'Gift Buyer'
AND user_identifier NOT LIKE 'Parent or Caregiver'
");
// Get agent test messages count
$agent_tests = $wpdb->get_var("
SELECT COUNT(DISTINCT session_id)
FROM $table_name
WHERE user_identifier IN (
'Tech-Savvy User',
'Detail-Oriented User',
'Language Learner',
'Casual Browser',
'Policy Enforcer',
'Researcher',
'Loyalty Member',
'Gift Buyer',
'Parent or Caregiver'
)
");
?>
Chat Transcripts
💬
📝
👥
prefix . 'mxchat_chat_transcripts';
// Get all transcripts ordered by session and timestamp
$results = $wpdb->get_results(
"SELECT session_id, user_email, user_identifier, role, message, timestamp
FROM {$table_name}
ORDER BY session_id, timestamp ASC"
);
if (empty($results)) {
wp_send_json_error(array('message' => 'No transcripts found.'));
wp_die();
}
// Set headers for CSV download
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"');
header('Pragma: no-cache');
header('Expires: 0');
// Create output stream
$output = fopen('php://output', 'w');
// Add UTF-8 BOM for proper Excel encoding
fputs($output, "\xEF\xBB\xBF");
// Add CSV headers
fputcsv($output, array(
'Session ID',
'Email',
'User Identifier',
'Role',
'Message',
'Timestamp'
));
// Add data rows
foreach ($results as $row) {
fputcsv($output, array(
$row->session_id,
$row->user_email,
$row->user_identifier,
$row->role,
$row->message,
$row->timestamp
));
}
fclose($output);
wp_die();
}
public function mxchat_fetch_chat_history() {
global $wpdb;
$table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
if (!current_user_can('manage_options')) {
wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat'));
}
// First get unique session IDs ordered by most recent message in each session
$session_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT DISTINCT session_id
FROM {$table_name}
GROUP BY session_id
ORDER BY MAX(timestamp) DESC"
)
);
if (empty($session_ids)) {
wp_die(esc_html__('No chat history available.', 'mxchat'));
}
ob_start();
echo '
';
// Iterate through sessions from newest to oldest
foreach ($session_ids as $session_id) {
// Get the email associated with this session (if available)
$email = $wpdb->get_var(
$wpdb->prepare(
"SELECT user_email
FROM {$table_name}
WHERE session_id = %s AND user_email != ''
ORDER BY timestamp ASC
LIMIT 1",
$session_id
)
);
// Debug: Check email retrieval
if (empty($email)) {
//error_log("No email found for session {$session_id}");
} else {
//error_log("Email for session {$session_id}: " . $email);
}
// Get messages for this session ordered by timestamp
$messages = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$table_name}
WHERE session_id = %s
ORDER BY timestamp ASC",
$session_id
)
);
// Start session block
echo '
';
echo '
';
// Wrap checkbox and session ID in one block
echo '