/** * Enhanced shortcode to display the booking form - PHP 7.4 Compatible */ function goeast_trial_booking_shortcode($atts) { // Ensure WordPress is fully loaded if (!function_exists('wp_enqueue_script') || !function_exists('wp_localize_script')) { return '

Error: WordPress not properly loaded.

'; } // Enqueue jQuery with proper dependency handling wp_enqueue_script('jquery'); $template_path = plugin_dir_path(__FILE__) . 'templates/booking-form-template.php'; if (!file_exists($template_path)) { error_log('GoEast Error: Template file not found at ' . $template_path); return '

Error: Booking form template not found.

'; } // Enqueue enhanced styles and scripts with proper versioning wp_enqueue_style( 'goeast-trial-booking-style', plugin_dir_url(__FILE__) . 'assets/booking-form.css', array(), '3.9.0' // Updated version for PHP 7.4 compatibility ); // Enqueue JavaScript with proper WordPress dependencies wp_enqueue_script( 'goeast-acuity-integration', plugin_dir_url(__FILE__) . 'assets/acuity-integration.js', array('jquery', 'wp-util'), // Added wp-util for better WordPress integration '3.9.0', // Updated version true // Load in footer for better performance ); // Enhanced localize script with PHP 7.4 compatible array syntax wp_localize_script('goeast-acuity-integration', 'goeastAcuity', [ 'apiUrl' => rest_url('goeast/v1/'), 'nonce' => wp_create_nonce('wp_rest'), 'appointmentTypeId' => '27631030', 'defaultTimezone' => 'UTC+8', 'supportedTimezones' => [ 'UTC+8', 'UTC+9', 'UTC+7', 'UTC+5:30', 'UTC+0', 'UTC+1', 'UTC+2', 'UTC-5', 'UTC-6', 'UTC-8', 'UTC+10', 'UTC-3' ], 'wpVersion' => get_bloginfo('version'), // Add WordPress version for debugging 'phpVersion' => PHP_VERSION // Add PHP version for debugging ]); // Load template with proper error handling ob_start(); try { include $template_path; return ob_get_clean(); } catch (Exception $e) { ob_end_clean(); error_log('GoEast Error: Template include failed - ' . $e->getMessage()); return '

Error: Unable to load booking form.

'; } } add_shortcode('goeast_trial_booking', 'goeast_trial_booking_shortcode'); /** * Enhanced GDPR Compliance with country data */ class GoEastGDPRCompliance { public function __construct() { add_action('wp_privacy_personal_data_exporters', array($this, 'register_exporter')); add_action('wp_privacy_personal_data_erasers', array($this, 'register_eraser')); add_action('wp_scheduled_delete', array($this, 'cleanup_old_data')); } public function register_exporter($exporters) { $exporters['goeast-trial-bookings'] = array( 'exporter_friendly_name' => 'GoEast Trial Bookings', 'callback' => array($this, 'export_user_data'), ); return $exporters; } public function register_eraser($erasers) { $erasers['goeast-trial-bookings'] = array( 'eraser_friendly_name' => 'GoEast Trial Bookings', 'callback' => array($this, 'erase_user_data'), ); return $erasers; } public function export_user_data($email_address) { global $wpdb; $table_name = $wpdb->prefix . 'goeast_trial_bookings'; $bookings = $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$table_name} WHERE email = %s", $email_address )); $export_items = array(); foreach ($bookings as $booking) { $export_items[] = array( 'group_id' => 'goeast-trial-bookings', 'group_label' => 'GoEast Trial Bookings', 'item_id' => "booking-{$booking->id}", 'data' => array( array('name' => 'Name', 'value' => $booking->first_name . ' ' . $booking->last_name), array('name' => 'Email', 'value' => $booking->email), array('name' => 'Phone', 'value' => $booking->phone), array('name' => 'Country', 'value' => $booking->country), array('name' => 'Timezone', 'value' => $booking->selected_timezone), array('name' => 'Chinese Hours', 'value' => $booking->chinese_hours), array('name' => 'HSK Level', 'value' => $booking->hsk_level), array('name' => 'Preferred Course', 'value' => $booking->preferred_course), array('name' => 'Chinese Skills', 'value' => $booking->chinese_skills), array('name' => 'Practice Goals', 'value' => $booking->practice_goals), array('name' => 'Additional Info', 'value' => $booking->additional_info), array('name' => 'Booking Date', 'value' => $booking->created_at), array('name' => 'Acuity Appointment ID', 'value' => $booking->acuity_appointment_id), ), ); } return array( 'data' => $export_items, 'done' => true, ); } public function erase_user_data($email_address) { global $wpdb; $table_name = $wpdb->prefix . 'goeast_trial_bookings'; $items_removed = $wpdb->delete( $table_name, array('email' => $email_address), array('%s') ); return array( 'items_removed' => $items_removed, 'items_retained' => false, 'messages' => array(), 'done' => true, ); } public function cleanup_old_data() { global $wpdb; $table_name = $wpdb->prefix . 'goeast_trial_bookings'; // Clean up abandoned bookings older than 30 days $wpdb->query($wpdb->prepare( "DELETE FROM {$table_name} WHERE status = 'abandoned' AND created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)" )); // Mark in-progress bookings as abandoned after 7 days $wpdb->query($wpdb->prepare( "UPDATE {$table_name} SET status = 'abandoned' WHERE status = 'in_progress' AND created_at < DATE_SUB(NOW(), INTERVAL 7 DAY)" )); } } // Initialize enhanced GDPR compliance new GoEastGDPRCompliance(); /** * Key Information Retention for Abandoned Sessions * PHP 7.4 Compatible Implementation */ class GoEastAbandonedSessionTracker { private $table_name; public function __construct() { global $wpdb; $this->table_name = $wpdb->prefix . 'goeast_abandoned_sessions'; // Hook into WordPress initialization add_action('init', array($this, 'init')); add_action('wp_ajax_goeast_save_session_data', array($this, 'save_session_data')); add_action('wp_ajax_nopriv_goeast_save_session_data', array($this, 'save_session_data')); // Add AJAX endpoint for immediate abandoned session check (for testing) add_action('wp_ajax_goeast_check_abandoned_now', array($this, 'ajax_check_abandoned_sessions')); add_action('wp_ajax_nopriv_goeast_check_abandoned_now', array($this, 'ajax_check_abandoned_sessions')); // Schedule cleanup and notification tasks add_action('goeast_check_abandoned_sessions', array($this, 'check_and_notify_abandoned_sessions')); // Register activation hook for database setup register_activation_hook(__FILE__, array($this, 'create_abandoned_sessions_table')); } public function init() { // For testing: Schedule frequent checks (every 15 seconds) // In production, change back to 'hourly' or 'daily' if (!wp_next_scheduled('goeast_check_abandoned_sessions')) { // Add custom interval for testing add_filter('cron_schedules', array($this, 'add_custom_cron_interval')); wp_schedule_event(time(), 'every_15_seconds', 'goeast_check_abandoned_sessions'); } } /** * Add custom cron interval for testing (15 seconds) */ public function add_custom_cron_interval($schedules) { $schedules['every_15_seconds'] = array( 'interval' => 15, 'display' => __('Every 15 Seconds') ); return $schedules; } /** * Create database table for abandoned sessions - PHP 7.4 compatible */ public function create_abandoned_sessions_table() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} ( id int(11) NOT NULL AUTO_INCREMENT, session_id varchar(255) NOT NULL, email varchar(255) NOT NULL, first_name varchar(255) DEFAULT '', last_name varchar(255) DEFAULT '', phone varchar(50) DEFAULT '', country varchar(100) DEFAULT '', chinese_hours varchar(100) DEFAULT '', hsk_level varchar(50) DEFAULT '', preferred_course varchar(100) DEFAULT '', chinese_skills text DEFAULT '', practice_goals text DEFAULT '', additional_info text DEFAULT '', hear_about_us varchar(100) DEFAULT '', current_step int(11) DEFAULT 1, user_ip varchar(45) DEFAULT '', user_agent text DEFAULT '', timezone varchar(50) DEFAULT '', status varchar(20) DEFAULT 'active', notified tinyint(1) DEFAULT 0, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_email (email), KEY idx_status (status), KEY idx_created_at (created_at) ) {$charset_collate};"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); error_log('GoEast: Abandoned sessions table created/updated'); } /** * Save session data via AJAX - PHP 7.4 compatible */ public function save_session_data() { // Verify nonce for security if (!wp_verify_nonce($_POST['nonce'] ?? '', 'goeast_booking_nonce')) { wp_die('Security check failed'); } global $wpdb; // Get form data $session_id = sanitize_text_field($_POST['session_id'] ?? uniqid('goeast_', true)); $email = sanitize_email($_POST['email'] ?? ''); // Only proceed if we have an email if (empty($email)) { wp_send_json_error('Email is required'); } // Prepare data array - PHP 7.4 compatible $data = array( 'session_id' => $session_id, 'email' => $email, 'first_name' => sanitize_text_field($_POST['firstName'] ?? ''), 'last_name' => sanitize_text_field($_POST['lastName'] ?? ''), 'phone' => sanitize_text_field($_POST['phone'] ?? ''), 'whatsapp_number' => sanitize_text_field($_POST['whatsappNumber'] ?? ''), 'wechat_id' => sanitize_text_field($_POST['wechatId'] ?? ''), 'country' => sanitize_text_field($_POST['country'] ?? ''), 'chinese_hours' => sanitize_text_field($_POST['chineseHours'] ?? ''), 'hsk_level' => sanitize_text_field($_POST['hskLevel'] ?? ''), 'preferred_course' => sanitize_text_field($_POST['preferredCourse'] ?? ''), 'chinese_skills' => sanitize_textarea_field($_POST['chineseSkills'] ?? ''), 'practice_goals' => sanitize_textarea_field($_POST['practiceGoals'] ?? ''), 'additional_info' => sanitize_textarea_field($_POST['additionalInfo'] ?? ''), 'hear_about_us' => sanitize_text_field($_POST['hearAboutUs'] ?? ''), 'current_step' => intval($_POST['currentStep'] ?? 1), 'user_ip' => $this->get_user_ip(), 'user_agent' => sanitize_text_field($_SERVER['HTTP_USER_AGENT'] ?? ''), 'timezone' => sanitize_text_field($_POST['timezone'] ?? 'UTC+8'), 'status' => sanitize_text_field($_POST['status'] ?? 'active') ); // Check if session already exists $existing = $wpdb->get_row($wpdb->prepare( "SELECT id FROM {$this->table_name} WHERE session_id = %s OR email = %s ORDER BY created_at DESC LIMIT 1", $session_id, $email )); if ($existing) { // Update existing record $data['updated_at'] = current_time('mysql'); $result = $wpdb->update( $this->table_name, $data, array('id' => $existing->id), array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s'), array('%d') ); } else { // Insert new record $result = $wpdb->insert( $this->table_name, $data, array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s') ); } if ($result !== false) { wp_send_json_success(array( 'message' => 'Session data saved successfully', 'session_id' => $session_id )); } else { error_log('GoEast: Failed to save session data - ' . $wpdb->last_error); wp_send_json_error('Failed to save session data'); } } /** * Check for abandoned sessions and send notifications */ public function check_and_notify_abandoned_sessions() { global $wpdb; // For testing: Find sessions that are 10+ seconds old, have email, but haven't been notified // In production, change INTERVAL 10 SECOND back to INTERVAL 30 MINUTE $abandoned_sessions = $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE status = 'active' AND notified = 0 AND email != '' AND created_at < DATE_SUB(NOW(), INTERVAL 10 SECOND) ORDER BY created_at DESC LIMIT 50" )); error_log('GoEast: Checking for abandoned sessions - found ' . count($abandoned_sessions) . ' candidates'); foreach ($abandoned_sessions as $session) { $this->send_abandoned_session_notification($session); // Mark as notified $wpdb->update( $this->table_name, array('notified' => 1, 'status' => 'abandoned'), array('id' => $session->id), array('%d', '%s'), array('%d') ); } error_log('GoEast: Processed ' . count($abandoned_sessions) . ' abandoned sessions'); } /** * Send abandoned session notification email to team */ private function send_abandoned_session_notification($session) { // Get admin email or use default $admin_email = get_option('admin_email', 'admin@goeastmandarin.com'); $team_emails = array( $admin_email, 'bookings@goeastmandarin.com', // Add your team emails here 'support@goeastmandarin.com' ); $subject = '🚨 Abandoned Trial Booking - ' . $session->first_name . ' ' . $session->last_name; // Build comprehensive email content $message = $this->build_abandoned_session_email($session); // Email headers $headers = array( 'Content-Type: text/html; charset=UTF-8', 'From: GoEast Booking System ', 'Reply-To: support@goeastmandarin.com' ); // Send to each team member $sent_count = 0; foreach ($team_emails as $email) { if (is_email($email)) { $result = wp_mail($email, $subject, $message, $headers); if ($result) { $sent_count++; error_log('GoEast: Successfully sent abandoned session email to ' . $email); } else { error_log('GoEast: Failed to send abandoned session email to ' . $email); } } else { error_log('GoEast: Invalid email address: ' . $email); } } error_log('GoEast: Sent abandoned session notification for ' . $session->email . ' to ' . $sent_count . ' recipients'); } /** * Build comprehensive abandoned session email content */ private function build_abandoned_session_email($session) { $site_url = get_site_url(); $time_ago = human_time_diff(strtotime($session->created_at), current_time('timestamp')); $progress_percentage = ($session->current_step / 4) * 100; $progress_text = $this->get_step_description($session->current_step); $html = ' Abandoned Trial Booking Alert

🚨 Abandoned Trial Booking Alert

A potential student started but didn\'t complete their trial booking

⏰ Time Since Started: ' . $time_ago . ' ago
📍 Progress: ' . $progress_text . ' (' . round($progress_percentage) . '% complete)

📋 Captured Information

FieldValue
📧 Email' . esc_html($session->email) . '
👤 Name' . esc_html($session->first_name . ' ' . $session->last_name) . '
📱 Phone' . esc_html($session->phone ?: 'Not provided') . '
💬 WhatsApp' . esc_html($session->whatsapp_number ?: 'Not provided') . '
💬 WeChat ID' . esc_html($session->wechat_id ?: 'Not provided') . '
🌍 Country' . esc_html($session->country ?: 'Not provided') . '
📚 Chinese Hours' . esc_html($session->chinese_hours ?: 'Not selected') . '
🎯 HSK Level' . esc_html($session->hsk_level ?: 'Not selected') . '
👥 Preferred Course' . esc_html($session->preferred_course ?: 'Not selected') . '
🗣️ Chinese Skills' . esc_html($session->chinese_skills ?: 'Not provided') . '
🎯 Practice Goals' . esc_html($session->practice_goals ?: 'Not provided') . '
💭 Additional Info' . esc_html($session->additional_info ?: 'Not provided') . '
📢 How They Found Us' . esc_html($session->hear_about_us ?: 'Not provided') . '

🔍 Technical Details

🌐 IP Address' . esc_html($session->user_ip) . '
🕒 Timezone' . esc_html($session->timezone) . '
💻 User Agent' . esc_html(substr($session->user_agent, 0, 100)) . '...
📅 Started At' . esc_html($session->created_at) . '
🔄 Last Updated' . esc_html($session->updated_at) . '

🎯 Recommended Actions

  • 📧 Send follow-up email within 2 hours for best conversion
  • 📱 Call if phone provided - personal touch increases booking rate
  • 🎁 Offer incentive - free materials or priority scheduling
  • Create urgency - limited trial slots available
📧 Email Student Now '; // Add WhatsApp quick action if WhatsApp number is provided if (!empty($session->whatsapp_number)) { $whatsapp_number = preg_replace('/[^0-9+]/', '', $session->whatsapp_number); $whatsapp_message = urlencode("Hi {$session->first_name}, I noticed you started booking a trial class with GoEast but didn't finish. I'd love to help you complete your booking! 😊"); $html .= ' 💬 WhatsApp Student '; } $html .= ' 📊 View All Bookings

This automated alert was generated by the GoEast Trial Booking System.
Student data is stored securely and will be automatically cleaned up after 30 days if no action is taken.

'; return $html; } /** * Get step description for progress tracking */ private function get_step_description($step) { $steps = array( 1 => 'Basic Information (Name & Email)', 2 => 'Learning Background', 3 => 'Learning Goals & Skills', 4 => 'Schedule Selection' ); return $steps[$step] ?? 'Unknown Step'; } /** * Get user IP address - PHP 7.4 compatible */ private function get_user_ip() { $ip_keys = array('HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR'); foreach ($ip_keys as $key) { if (array_key_exists($key, $_SERVER) === true) { $ip = $_SERVER[$key]; if (strpos($ip, ',') !== false) { $ip = explode(',', $ip)[0]; } $ip = trim($ip); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { return $ip; } } } return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; } /** * AJAX handler for immediate abandoned session check (for testing) */ public function ajax_check_abandoned_sessions() { // Allow this for testing - in production you might want to add nonce verification $this->check_and_notify_abandoned_sessions(); wp_send_json_success(array('message' => 'Abandoned session check completed')); } /** * Test function to create a sample abandoned session (for testing only) */ public function create_test_abandoned_session() { if (!current_user_can('manage_options')) { wp_die('Unauthorized'); } global $wpdb; // Create a test session that should trigger notification $test_data = array( 'session_id' => 'test_' . time(), 'email' => 'test@example.com', 'first_name' => 'Test', 'last_name' => 'User', 'phone' => '+1234567890', 'country' => 'Test Country', 'current_step' => 2, 'user_ip' => '127.0.0.1', 'user_agent' => 'Test Browser', 'timezone' => 'UTC+8', 'status' => 'active', 'notified' => 0, 'created_at' => date('Y-m-d H:i:s', strtotime('-15 seconds')) // 15 seconds ago ); $result = $wpdb->insert( $this->table_name, $test_data, array('%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%d', '%s') ); if ($result) { error_log('GoEast: Test abandoned session created successfully'); // Immediately check for abandoned sessions $this->check_and_notify_abandoned_sessions(); wp_redirect(admin_url('admin.php?page=goeast-bookings&message=test_created')); } else { error_log('GoEast: Failed to create test abandoned session - ' . $wpdb->last_error); wp_die('Failed to create test session'); } exit; } /** * Manual trigger for testing (admin only) */ public function manual_check_abandoned_sessions() { if (!current_user_can('manage_options')) { wp_die('Unauthorized'); } $this->check_and_notify_abandoned_sessions(); wp_redirect(admin_url('admin.php?page=goeast-bookings&message=checked')); exit; } } // Initialize the abandoned session tracker new GoEastAbandonedSessionTracker(); ?> How to Say Thank You in Chinese - GoEast Mandarin Skip to content
🌟Join 20,000+ students who have mastered Mandarin with GoEast since 2012