- Remove dangerous set_time_limit() calls in AJAX handlers to prevent resource exhaustion - Restrict debug logging GET parameter access to administrators only - Addresses remaining critical issues from security audit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			951 lines
		
	
	
		
			No EOL
		
	
	
		
			48 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			951 lines
		
	
	
		
			No EOL
		
	
	
		
			48 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * HVAC Geocoding AJAX Handler
 | |
|  * 
 | |
|  * Provides AJAX endpoints for triggering geocoding operations
 | |
|  *
 | |
|  * @package HVAC_Community_Events
 | |
|  * @since 2.0.0
 | |
|  */
 | |
| 
 | |
| if (!defined('ABSPATH')) {
 | |
|     exit;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * HVAC_Geocoding_Ajax class
 | |
|  */
 | |
| class HVAC_Geocoding_Ajax {
 | |
|     
 | |
|     /**
 | |
|      * Instance
 | |
|      * 
 | |
|      * @var HVAC_Geocoding_Ajax
 | |
|      */
 | |
|     private static $instance = null;
 | |
|     
 | |
|     /**
 | |
|      * Get instance
 | |
|      * 
 | |
|      * @return HVAC_Geocoding_Ajax
 | |
|      */
 | |
|     public static function get_instance() {
 | |
|         if (null === self::$instance) {
 | |
|             self::$instance = new self();
 | |
|         }
 | |
|         return self::$instance;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Constructor
 | |
|      */
 | |
|     private function __construct() {
 | |
|         add_action('wp_ajax_hvac_trigger_geocoding', array($this, 'trigger_geocoding'));
 | |
|         add_action('wp_ajax_hvac_get_geocoding_stats', array($this, 'get_geocoding_stats'));
 | |
|         add_action('wp_ajax_hvac_remigrate_csv_data', array($this, 'remigrate_csv_data'));
 | |
|         add_action('wp_ajax_hvac_reconstruct_import_log', array($this, 'reconstruct_import_log'));
 | |
|         add_action('wp_ajax_hvac_run_enhanced_import', array($this, 'run_enhanced_import'));
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Trigger geocoding for all profiles
 | |
|      */
 | |
|     public function trigger_geocoding() {
 | |
|         // Verify nonce
 | |
|         if (!wp_verify_nonce($_POST['nonce'], 'hvac_ajax_nonce')) {
 | |
|             wp_send_json_error('Invalid nonce');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         // Check permissions
 | |
|         if (!current_user_can('hvac_master_trainer') && !current_user_can('administrator')) {
 | |
|             wp_send_json_error('Insufficient permissions');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         try {
 | |
|             // Note: Removed set_time_limit to prevent resource exhaustion  
 | |
|             // Large geocoding operations should use background processing instead
 | |
|             
 | |
|             $results = $this->execute_geocoding();
 | |
|             
 | |
|             wp_send_json_success($results);
 | |
|             
 | |
|         } catch (Exception $e) {
 | |
|             wp_send_json_error('Geocoding error: ' . $e->getMessage());
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get geocoding statistics
 | |
|      */
 | |
|     public function get_geocoding_stats() {
 | |
|         // Verify nonce
 | |
|         if (!wp_verify_nonce($_POST['nonce'], 'hvac_ajax_nonce')) {
 | |
|             wp_send_json_error('Invalid nonce');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         // Check permissions
 | |
|         if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('administrator')) {
 | |
|             wp_send_json_error('Insufficient permissions');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         try {
 | |
|             if (!class_exists('HVAC_Trainer_Profile_Settings')) {
 | |
|                 wp_send_json_error('Profile settings class not available');
 | |
|                 return;
 | |
|             }
 | |
|             
 | |
|             $settings = HVAC_Trainer_Profile_Settings::get_instance();
 | |
|             
 | |
|             // Security: Check if the method is publicly accessible instead of using reflection
 | |
|             if (!method_exists($settings, 'get_profile_statistics_public')) {
 | |
|                 wp_send_json_error('Statistics method not available');
 | |
|                 return;
 | |
|             }
 | |
|             
 | |
|             $stats = $settings->get_profile_statistics_public();
 | |
|             
 | |
|             wp_send_json_success($stats);
 | |
|             
 | |
|         } catch (Exception $e) {
 | |
|             wp_send_json_error('Statistics error: ' . $e->getMessage());
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Re-migrate CSV data to trainer profiles
 | |
|      */
 | |
|     public function remigrate_csv_data() {
 | |
|         // Verify nonce
 | |
|         if (!wp_verify_nonce($_POST['nonce'], 'hvac_ajax_nonce')) {
 | |
|             wp_send_json_error('Invalid nonce');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         // Check permissions
 | |
|         if (!current_user_can('hvac_master_trainer') && !current_user_can('administrator')) {
 | |
|             wp_send_json_error('Insufficient permissions');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         try {
 | |
|             // Note: Removed set_time_limit to prevent resource exhaustion
 | |
|             // Large operations should use background processing instead
 | |
|             
 | |
|             $results = $this->execute_csv_remigration();
 | |
|             
 | |
|             wp_send_json_success($results);
 | |
|             
 | |
|         } catch (Exception $e) {
 | |
|             wp_send_json_error('Re-migration error: ' . $e->getMessage());
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Reconstruct CSV import log from existing data
 | |
|      */
 | |
|     public function reconstruct_import_log() {
 | |
|         // Verify nonce
 | |
|         if (!wp_verify_nonce($_POST['nonce'], 'hvac_ajax_nonce')) {
 | |
|             wp_send_json_error('Invalid nonce');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         // Check permissions
 | |
|         if (!current_user_can('hvac_master_trainer') && !current_user_can('administrator')) {
 | |
|             wp_send_json_error('Insufficient permissions');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         try {
 | |
|             $results = $this->execute_log_reconstruction();
 | |
|             wp_send_json_success($results);
 | |
|         } catch (Exception $e) {
 | |
|             wp_send_json_error('Reconstruction error: ' . $e->getMessage());
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Run enhanced CSV import from actual file
 | |
|      */
 | |
|     public function run_enhanced_import() {
 | |
|         // Verify nonce
 | |
|         if (!wp_verify_nonce($_POST['nonce'], 'hvac_ajax_nonce')) {
 | |
|             wp_send_json_error('Invalid nonce');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         // Check permissions
 | |
|         if (!current_user_can('hvac_master_trainer') && !current_user_can('administrator')) {
 | |
|             wp_send_json_error('Insufficient permissions');
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         try {
 | |
|             // Note: Removed set_time_limit to prevent resource exhaustion
 | |
|             // Large operations should use background processing instead
 | |
|             
 | |
|             // Include the enhanced CSV import class
 | |
|             require_once plugin_dir_path(__FILE__) . 'enhanced-csv-import-from-file.php';
 | |
|             
 | |
|             $results = execute_enhanced_csv_import();
 | |
|             wp_send_json_success($results);
 | |
|         } catch (Exception $e) {
 | |
|             wp_send_json_error('Enhanced import error: ' . $e->getMessage());
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Execute geocoding for all profiles
 | |
|      * 
 | |
|      * @return array Results summary
 | |
|      */
 | |
|     private function execute_geocoding() {
 | |
|         // Check required classes
 | |
|         $required_classes = [
 | |
|             'HVAC_Trainer_Profile_Manager',
 | |
|             'HVAC_Geocoding_Service',
 | |
|             'HVAC_Trainer_Profile_Settings'
 | |
|         ];
 | |
|         
 | |
|         foreach ($required_classes as $class) {
 | |
|             if (!class_exists($class)) {
 | |
|                 throw new Exception("Required class {$class} not found");
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Get geocoding service
 | |
|         $geocoding_service = HVAC_Geocoding_Service::get_instance();
 | |
|         
 | |
|         // Get all trainer profiles
 | |
|         $profiles = get_posts([
 | |
|             'post_type' => 'trainer_profile',
 | |
|             'post_status' => 'publish',
 | |
|             'posts_per_page' => -1,
 | |
|             'fields' => 'ids'
 | |
|         ]);
 | |
|         
 | |
|         if (empty($profiles)) {
 | |
|             throw new Exception('No trainer profiles found');
 | |
|         }
 | |
|         
 | |
|         $results = [
 | |
|             'total_profiles' => count($profiles),
 | |
|             'geocoded_count' => 0,
 | |
|             'skipped_count' => 0,
 | |
|             'error_count' => 0,
 | |
|             'details' => [],
 | |
|             'start_time' => current_time('mysql'),
 | |
|             'api_key_valid' => !empty(get_option('hvac_google_maps_api_key'))
 | |
|         ];
 | |
|         
 | |
|         foreach ($profiles as $profile_id) {
 | |
|             $profile = get_post($profile_id);
 | |
|             $profile_title = $profile->post_title ?: "Profile #{$profile_id}";
 | |
|             
 | |
|             $detail = [
 | |
|                 'id' => $profile_id,
 | |
|                 'title' => $profile_title,
 | |
|                 'status' => 'processing'
 | |
|             ];
 | |
|             
 | |
|             // Check if already geocoded
 | |
|             $existing_lat = get_post_meta($profile_id, 'latitude', true);
 | |
|             $existing_lng = get_post_meta($profile_id, 'longitude', true);
 | |
|             
 | |
|             if (!empty($existing_lat) && !empty($existing_lng)) {
 | |
|                 $detail['status'] = 'already_geocoded';
 | |
|                 $detail['coordinates'] = ['lat' => $existing_lat, 'lng' => $existing_lng];
 | |
|                 $results['geocoded_count']++;
 | |
|                 $results['details'][] = $detail;
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Get address components
 | |
|             $address_parts = [];
 | |
|             $city = get_post_meta($profile_id, 'trainer_city', true);
 | |
|             $state = get_post_meta($profile_id, 'trainer_state', true);
 | |
|             $country = get_post_meta($profile_id, 'trainer_country', true);
 | |
|             $address = get_post_meta($profile_id, 'trainer_address', true);
 | |
|             
 | |
|             if ($address) $address_parts[] = $address;
 | |
|             if ($city) $address_parts[] = $city;
 | |
|             if ($state) $address_parts[] = $state;
 | |
|             if ($country) $address_parts[] = $country;
 | |
|             
 | |
|             if (empty($address_parts)) {
 | |
|                 $detail['status'] = 'no_address';
 | |
|                 $detail['message'] = 'No address data found';
 | |
|                 $results['skipped_count']++;
 | |
|                 $results['details'][] = $detail;
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             $full_address = implode(', ', $address_parts);
 | |
|             $detail['address'] = $full_address;
 | |
|             
 | |
|             // Attempt geocoding
 | |
|             try {
 | |
|                 $coordinates = $geocoding_service->geocode_address($full_address);
 | |
|                 
 | |
|                 if ($coordinates && isset($coordinates['lat']) && isset($coordinates['lng'])) {
 | |
|                     // Store coordinates
 | |
|                     update_post_meta($profile_id, 'latitude', $coordinates['lat']);
 | |
|                     update_post_meta($profile_id, 'longitude', $coordinates['lng']);
 | |
|                     update_post_meta($profile_id, 'geocoded_at', current_time('mysql'));
 | |
|                     update_post_meta($profile_id, 'geocoding_source', $coordinates['source'] ?? 'ajax');
 | |
|                     
 | |
|                     $detail['status'] = 'geocoded';
 | |
|                     $detail['coordinates'] = ['lat' => $coordinates['lat'], 'lng' => $coordinates['lng']];
 | |
|                     $results['geocoded_count']++;
 | |
|                     
 | |
|                     // Small delay to respect API rate limits
 | |
|                     usleep(500000); // 0.5 seconds
 | |
|                     
 | |
|                 } else {
 | |
|                     $detail['status'] = 'failed';
 | |
|                     $detail['message'] = 'No coordinates returned';
 | |
|                     $results['error_count']++;
 | |
|                 }
 | |
|                 
 | |
|             } catch (Exception $e) {
 | |
|                 $detail['status'] = 'error';
 | |
|                 $detail['message'] = $e->getMessage();
 | |
|                 $results['error_count']++;
 | |
|             }
 | |
|             
 | |
|             $results['details'][] = $detail;
 | |
|         }
 | |
|         
 | |
|         $results['end_time'] = current_time('mysql');
 | |
|         $results['duration'] = time() - strtotime($results['start_time']);
 | |
|         
 | |
|         return $results;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Execute CSV re-migration for all profiles
 | |
|      * 
 | |
|      * @return array Results summary
 | |
|      */
 | |
|     private function execute_csv_remigration() {
 | |
|         // Get the CSV import log
 | |
|         $csv_import_log = get_option('hvac_csv_import_log', []);
 | |
|         
 | |
|         if (empty($csv_import_log)) {
 | |
|             throw new Exception('No CSV import log found. Please run the original CSV import first.');
 | |
|         }
 | |
|         
 | |
|         $results = [
 | |
|             'total_sessions' => count($csv_import_log),
 | |
|             'profiles_processed' => 0,
 | |
|             'profiles_updated' => 0,
 | |
|             'errors' => 0,
 | |
|             'fields_updated' => 0,
 | |
|             'geocoding_scheduled' => 0,
 | |
|             'details' => [],
 | |
|             'start_time' => current_time('mysql')
 | |
|         ];
 | |
|         
 | |
|         // Process each import session
 | |
|         foreach ($csv_import_log as $session_id => $import_session) {
 | |
|             if (!isset($import_session['users']) || !is_array($import_session['users'])) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             foreach ($import_session['users'] as $user_email => $user_data) {
 | |
|                 $results['profiles_processed']++;
 | |
|                 
 | |
|                 $detail = [
 | |
|                     'email' => $user_email,
 | |
|                     'status' => 'processing',
 | |
|                     'fields_updated' => 0
 | |
|                 ];
 | |
|                 
 | |
|                 try {
 | |
|                     // Get the user
 | |
|                     $user = get_user_by('email', $user_email);
 | |
|                     if (!$user) {
 | |
|                         $detail['status'] = 'user_not_found';
 | |
|                         $results['errors']++;
 | |
|                         $results['details'][] = $detail;
 | |
|                         continue;
 | |
|                     }
 | |
|                     
 | |
|                     // Get the trainer profile
 | |
|                     if (!class_exists('HVAC_Trainer_Profile_Manager')) {
 | |
|                         throw new Exception('Profile manager not available');
 | |
|                     }
 | |
|                     
 | |
|                     $profile_manager = HVAC_Trainer_Profile_Manager::get_instance();
 | |
|                     $profile = $profile_manager->get_trainer_profile($user->ID);
 | |
|                     
 | |
|                     if (!$profile) {
 | |
|                         $detail['status'] = 'profile_not_found';
 | |
|                         $results['errors']++;
 | |
|                         $results['details'][] = $detail;
 | |
|                         continue;
 | |
|                     }
 | |
|                     
 | |
|                     $detail['profile_id'] = $profile->ID;
 | |
|                     $detail['name'] = $user->display_name;
 | |
|                     
 | |
|                     // Check if CSV data exists
 | |
|                     if (!isset($user_data['csv_data']) || !is_array($user_data['csv_data'])) {
 | |
|                         $detail['status'] = 'no_csv_data';
 | |
|                         $results['details'][] = $detail;
 | |
|                         continue;
 | |
|                     }
 | |
|                     
 | |
|                     $csv_data = $user_data['csv_data'];
 | |
|                     
 | |
|                     // Apply field mapping logic (excluding taxonomy fields - they're handled separately)
 | |
|                     $field_priority_mappings = [
 | |
|                         'trainer_city' => ['City'],
 | |
|                         'trainer_state' => ['State'], 
 | |
|                         'trainer_country' => ['Country'],
 | |
|                         'date_certified' => ['standardized_date', 'Date Certified,'],
 | |
|                         'role' => ['mapped_role', 'Role'],
 | |
|                         'organization_name' => ['Company Name'],
 | |
|                         'certification_type' => ['Certification Type'],
 | |
|                         'certification_status' => ['Certification Status'],
 | |
|                         'business_website' => ['Company Website'],
 | |
|                         'business_phone' => ['Phone Number'],
 | |
|                         'application_details' => ['Application Details']
 | |
|                     ];
 | |
|                     
 | |
|                     foreach ($field_priority_mappings as $profile_field => $csv_keys) {
 | |
|                         $value = null;
 | |
|                         $used_key = null;
 | |
|                         
 | |
|                         // Try each CSV key in priority order
 | |
|                         foreach ($csv_keys as $csv_key) {
 | |
|                             if (isset($csv_data[$csv_key]) && !empty(trim($csv_data[$csv_key]))) {
 | |
|                                 $value = trim($csv_data[$csv_key]);
 | |
|                                 $used_key = $csv_key;
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
|                         
 | |
|                         if ($value) {
 | |
|                             // Check if field already has a value
 | |
|                             $current_value = get_post_meta($profile->ID, $profile_field, true);
 | |
|                             if (empty($current_value)) {
 | |
|                                 update_post_meta($profile->ID, $profile_field, sanitize_text_field($value));
 | |
|                                 $detail['fields_updated']++;
 | |
|                                 $results['fields_updated']++;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     
 | |
|                     // Handle taxonomy fields from CSV data
 | |
|                     $taxonomy_fields_updated = $this->update_profile_taxonomies_with_count($profile->ID, $csv_data);
 | |
|                     if ($taxonomy_fields_updated > 0) {
 | |
|                         $detail['fields_updated'] += $taxonomy_fields_updated;
 | |
|                         $results['fields_updated'] += $taxonomy_fields_updated;
 | |
|                     }
 | |
|                     
 | |
|                     if ($detail['fields_updated'] > 0) {
 | |
|                         $results['profiles_updated']++;
 | |
|                         $detail['status'] = 'updated';
 | |
|                         
 | |
|                         // Check if we should schedule geocoding
 | |
|                         $city = get_post_meta($profile->ID, 'trainer_city', true);
 | |
|                         $state = get_post_meta($profile->ID, 'trainer_state', true);
 | |
|                         $country = get_post_meta($profile->ID, 'trainer_country', true);
 | |
|                         
 | |
|                         if (!empty($city) || !empty($state) || !empty($country)) {
 | |
|                             wp_schedule_single_event(time() + rand(5, 30), 'hvac_geocode_address', [$profile->ID]);
 | |
|                             $results['geocoding_scheduled']++;
 | |
|                             $detail['geocoding_scheduled'] = true;
 | |
|                         }
 | |
|                     } else {
 | |
|                         $detail['status'] = 'no_updates_needed';
 | |
|                     }
 | |
|                     
 | |
|                 } catch (Exception $e) {
 | |
|                     $detail['status'] = 'error';
 | |
|                     $detail['error'] = $e->getMessage();
 | |
|                     $results['errors']++;
 | |
|                 }
 | |
|                 
 | |
|                 $results['details'][] = $detail;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         $results['end_time'] = current_time('mysql');
 | |
|         $results['duration'] = time() - strtotime($results['start_time']);
 | |
|         
 | |
|         return $results;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Execute CSV import log reconstruction
 | |
|      * 
 | |
|      * @return array Results summary
 | |
|      */
 | |
|     private function execute_log_reconstruction() {
 | |
|         // Try multiple possible locations for the CSV file
 | |
|         $possible_csv_paths = [
 | |
|             HVAC_PLUGIN_DIR . 'CSV_Trainers_Import_1Aug2025_FIXED.csv',
 | |
|             ABSPATH . 'CSV_Trainers_Import_1Aug2025_FIXED.csv',
 | |
|             WP_CONTENT_DIR . '/CSV_Trainers_Import_1Aug2025_FIXED.csv',
 | |
|             WP_CONTENT_DIR . '/uploads/CSV_Trainers_Import_1Aug2025_FIXED.csv'
 | |
|         ];
 | |
|         
 | |
|         $csv_file = null;
 | |
|         foreach ($possible_csv_paths as $path) {
 | |
|             if (file_exists($path)) {
 | |
|                 $csv_file = $path;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         if (!$csv_file) {
 | |
|             throw new Exception("CSV file not found in any of these locations: " . implode(', ', $possible_csv_paths));
 | |
|         }
 | |
|         
 | |
|         $results = [
 | |
|             'csv_file' => $csv_file,
 | |
|             'csv_rows_read' => 0,
 | |
|             'matched_users' => 0,
 | |
|             'missing_users' => 0,
 | |
|             'import_log_created' => false,
 | |
|             'session_id' => null,
 | |
|             'start_time' => current_time('mysql')
 | |
|         ];
 | |
|         
 | |
|         // Parse CSV file
 | |
|         $csv_data = [];
 | |
|         if (($handle = fopen($csv_file, 'r')) !== FALSE) {
 | |
|             $headers = fgetcsv($handle); // Get headers
 | |
|             
 | |
|             while (($row = fgetcsv($handle)) !== FALSE) {
 | |
|                 $results['csv_rows_read']++;
 | |
|                 if (count($row) === count($headers)) {
 | |
|                     $csv_data[] = array_combine($headers, $row);
 | |
|                 }
 | |
|             }
 | |
|             fclose($handle);
 | |
|         } else {
 | |
|             throw new Exception("Could not open CSV file: {$csv_file}");
 | |
|         }
 | |
|         
 | |
|         // Create synthetic import log
 | |
|         $session_id = 'reconstructed_' . date('Y-m-d_H-i-s');
 | |
|         $results['session_id'] = $session_id;
 | |
|         
 | |
|         $import_session = [
 | |
|             'timestamp' => time(),
 | |
|             'file' => basename($csv_file),
 | |
|             'total_rows' => count($csv_data),
 | |
|             'users' => []
 | |
|         ];
 | |
|         
 | |
|         // Match CSV data with existing users
 | |
|         foreach ($csv_data as $index => $row) {
 | |
|             $email = trim($row['Work Email'] ?? '');
 | |
|             
 | |
|             if (empty($email)) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Check if user exists
 | |
|             $user = get_user_by('email', $email);
 | |
|             
 | |
|             if ($user) {
 | |
|                 $results['matched_users']++;
 | |
|                 
 | |
|                 // Store CSV data for this user
 | |
|                 $import_session['users'][$email] = [
 | |
|                     'user_id' => $user->ID,
 | |
|                     'user_login' => $user->user_login,
 | |
|                     'csv_data' => $row,
 | |
|                     'imported_at' => time(),
 | |
|                     'status' => 'existing_user_matched'
 | |
|                 ];
 | |
|             } else {
 | |
|                 $results['missing_users']++;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Save the reconstructed import log
 | |
|         $import_log = [$session_id => $import_session];
 | |
|         update_option('hvac_csv_import_log', $import_log);
 | |
|         
 | |
|         // Verify the save
 | |
|         $saved_log = get_option('hvac_csv_import_log', []);
 | |
|         $results['import_log_created'] = !empty($saved_log);
 | |
|         
 | |
|         $results['end_time'] = current_time('mysql');
 | |
|         $results['duration'] = time() - strtotime($results['start_time']);
 | |
|         
 | |
|         return $results;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Execute enhanced CSV import
 | |
|      * 
 | |
|      * @return array Results summary
 | |
|      */
 | |
|     private function execute_enhanced_import() {
 | |
|         $results = [
 | |
|             'total_rows' => 0,
 | |
|             'users_created' => 0,
 | |
|             'users_updated' => 0,
 | |
|             'profiles_created' => 0,
 | |
|             'profiles_updated' => 0,
 | |
|             'errors' => 0,
 | |
|             'geocoding_scheduled' => 0,
 | |
|             'session_id' => null,
 | |
|             'import_log_saved' => false,
 | |
|             'start_time' => current_time('mysql')
 | |
|         ];
 | |
|         
 | |
|         // Complete CSV data from the actual CSV file with correct emails
 | |
|         $csv_data = [
 | |
|             ['Name' => 'Brynn', 'Last Name' => 'Cooksey', 'Email' => 'brynn@hvactrain.com', 'City' => 'Southfield', 'State' => 'Michigan', 'Country' => 'United States', 'Company Name' => 'HVAC U', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Thomas', 'Last Name' => 'Hoffmaster II', 'Email' => 'thoffmaster@hoffmastermechanical.com', 'City' => 'Bunker Hill', 'State' => 'West Virginia', 'Country' => 'United States', 'Company Name' => 'Hoffmaster Mechanical & Consulting LLC', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Eric', 'Last Name' => 'Kjelshus', 'Email' => 'eric.energy@gmail.com', 'City' => 'Greenwood', 'State' => 'Missouri', 'Country' => 'United States', 'Company Name' => 'Eric Kjelshus Energy Heating And Cooling', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Marco', 'Last Name' => 'Nantel', 'Email' => 'mnantel@republicsupplyco.com', 'City' => 'Norwood', 'State' => 'Massachusetts', 'Country' => 'United States', 'Company Name' => 'Republic Supply', 'Role' => 'Operations Manager', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'David', 'Last Name' => 'Petz', 'Email' => 'dpetz@johnstonenjpa.com', 'City' => 'Philadelphia', 'State' => 'Pennsylvania', 'Country' => 'United States', 'Company Name' => 'Johnstone Supply', 'Role' => 'Educator/Tech support', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'John', 'Last Name' => 'Anderson', 'Email' => 'janderson@sila.com', 'City' => 'King of Prussia', 'State' => 'Pennsylvania', 'Country' => 'United States', 'Company Name' => 'Sila Services', 'Role' => 'Supervisor', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'David', 'Last Name' => 'Norman', 'Email' => 'david@hvacinstituteinc.com', 'City' => 'Kent', 'State' => 'Washington', 'Country' => 'United States', 'Company Name' => 'Hvac Institute Inc.', 'Role' => 'Technician and Instructor', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Greg', 'Last Name' => 'Kula', 'Email' => 'Greg.a.kula@gmail.com', 'City' => 'Goshen', 'State' => 'New York', 'Country' => 'United States', 'Company Name' => 'Jones Services', 'Role' => 'Service Manager', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'William', 'Last Name' => 'Ramsey', 'Email' => 'wramsey@wrbristow.com', 'City' => 'Marietta', 'State' => 'Georgia', 'Country' => 'United States', 'Company Name' => 'Bristow University', 'Role' => 'Director of Education', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Jeremy', 'Last Name' => 'Begley', 'Email' => 'jbegley@hvac2homeperformance.com', 'City' => 'Knoxville', 'State' => 'Tennessee', 'Country' => 'United States', 'Company Name' => 'HVAC 2 Home Performance', 'Role' => 'Founding Shareholder', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Robert', 'Last Name' => 'Larson', 'Email' => 'robl@generalplumbingsupply.net', 'City' => 'Piscataway', 'State' => 'New Jersey', 'Country' => 'United States', 'Company Name' => 'General plumbing supply', 'Role' => 'HVAC Tech (Therapy) Support', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'William', 'Last Name' => 'Lombard', 'Email' => 'william.lombard@century.edu', 'City' => 'White Bear Lake', 'State' => 'Minnesota', 'Country' => 'United States', 'Company Name' => 'Century College', 'Role' => 'Faculty/Program Director', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Stephen', 'Last Name' => 'Boane', 'Email' => 'steve@elevationha.com', 'City' => 'Denver', 'State' => 'Colorado', 'Country' => 'United States', 'Company Name' => 'Elevation Heating & Air', 'Role' => 'Chief Executive Officer', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Scott', 'Last Name' => 'Suddreth', 'Email' => 'scotts@blueskytraining.com', 'City' => 'Fort Collins', 'State' => 'Colorado', 'Country' => 'United States', 'Company Name' => 'Blue Sky Training', 'Role' => 'Director of Training', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Tom', 'Last Name' => 'Hunt', 'Email' => 'tomhunt@arhvacr.org', 'City' => 'Jacksonville', 'State' => 'Arkansas', 'Country' => 'United States', 'Company Name' => 'Arkansas HVACR Association', 'Role' => 'Executive Director', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Dan', 'Last Name' => 'Wildenhaus', 'Email' => 'dwildenhaus@mncee.org', 'City' => 'Minneapolis', 'State' => 'Minnesota', 'Country' => 'United States', 'Company Name' => 'Center for Energy and Environment', 'Role' => 'Sr Technical Manager', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Petro', 'Last Name' => 'Tsynik', 'Email' => 'ptsinyk@sunrisecomfort.com', 'City' => 'Newtown', 'State' => 'Pennsylvania', 'Country' => 'United States', 'Company Name' => 'Sunrise Comfort', 'Role' => 'Operation Manager', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Ben', 'Last Name' => 'Chouinard', 'Email' => 'BChouinard@unifiedakron.com', 'City' => 'Akron', 'State' => 'Ohio', 'Country' => 'United States', 'Company Name' => 'Unified Comfort Systems', 'Role' => 'Vice President', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Mike', 'Last Name' => 'Edwards', 'Email' => 'tech3@echolsheating.com', 'City' => 'Akron', 'State' => 'Ohio', 'Country' => 'United States', 'Company Name' => 'Echols', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Jason', 'Last Name' => 'Julian', 'Email' => 'jason@julianheatandair.com', 'City' => 'Heber Springs', 'State' => 'Arkansas', 'Country' => 'United States', 'Company Name' => 'Julian Heating & Air', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Abe', 'Last Name' => 'Engholm', 'Email' => 'abe@julianheatandair.com', 'City' => 'Heber Springs', 'State' => 'Arkansas', 'Country' => 'United States', 'Company Name' => 'Julian Heating & Air', 'Role' => 'Trainer', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Robert', 'Last Name' => 'McKeraghan', 'Email' => 'bob@cancoclimatecare.com', 'City' => 'Newmarket', 'State' => 'Ontario', 'Country' => 'Canada', 'Company Name' => 'Canco ClimateCare', 'Role' => 'President', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Shaun', 'Last Name' => 'Penny', 'Email' => 'shaun@pennyairsolutions.com', 'City' => 'Cave Springs', 'State' => 'Arkansas', 'Country' => 'United States', 'Company Name' => 'Penny Air Solutions', 'Role' => 'Owner/Operator', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Andrew', 'Last Name' => 'Godby', 'Email' => 'andrew.godby88@gmail.com', 'City' => 'Hilliard', 'State' => 'Ohio', 'Country' => 'United States', 'Company Name' => 'The Eco Plumbers', 'Role' => 'Service Technician', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Hunter', 'Last Name' => 'Heavilin', 'Email' => 'hunter@simpsonsalute.com', 'City' => 'New Philadelphia', 'State' => 'Ohio', 'Country' => 'United States', 'Company Name' => 'Simpson Salute Heating & Air', 'Role' => 'Install Technician', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Gary', 'Last Name' => 'Ranallo', 'Email' => 'granallo@stackheating.com', 'City' => 'Cleveland', 'State' => 'Ohio', 'Country' => 'United States', 'Company Name' => 'Stack Heating, Cooling and Electric', 'Role' => 'Senior Trainer', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Edward', 'Last Name' => 'Wronski', 'Email' => 'edward.wronski@stylecrest.net', 'City' => 'Melbourne', 'State' => 'Florida', 'Country' => 'United States', 'Company Name' => 'Style Crest Heating & Air Conditioning', 'Role' => 'Service Manager', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Tina', 'Last Name' => 'Marsh', 'Email' => 'tina@crkurtz.com', 'City' => 'Canton', 'State' => 'Ohio', 'Country' => 'United States', 'Company Name' => 'C.R. Kurtz Heating & Cooling', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Rusty', 'Last Name' => 'Barnes', 'Email' => 'rusty@bradhambrothers.com', 'City' => 'Charlotte', 'State' => 'North Carolina', 'Country' => 'United States', 'Company Name' => 'Bradham Brothers Heating, Cooling and Electrical', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Reginald', 'Last Name' => 'Lowe', 'Email' => 'Reggieapex@yahoo.com', 'City' => 'Riverdale', 'State' => 'Georgia', 'Country' => 'United States', 'Company Name' => 'Apex Residential Solutions', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Nathan', 'Last Name' => 'Richards', 'Email' => 'nathanr@kliemannbros.com', 'City' => 'Tacoma', 'State' => 'Washington', 'Country' => 'United States', 'Company Name' => 'Kliemann Brothers', 'Role' => 'Training and Technical Support Specialist', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Clint', 'Last Name' => 'Powers', 'Email' => 'clint@aircontrolaz.com', 'City' => 'Lake Havasu City', 'State' => 'Arizona', 'Country' => 'United States', 'Company Name' => 'Air Control Home Services', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'MARIO', 'Last Name' => 'GARCIA', 'Email' => 'CONTACT@360HVACPRO.COM', 'City' => 'Denver', 'State' => 'Michigan', 'Country' => 'United States', 'Company Name' => '360 HVAC PRO LLC', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Sam(Qingzhang)', 'Last Name' => 'Sheng', 'Email' => 'enzeprocom@gmail.com', 'City' => 'Kelowna', 'State' => 'British Columbia', 'Country' => 'Canada', 'Company Name' => 'Enze Pro HVAC Support Ltd', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Christian', 'Last Name' => 'Ortiz', 'Email' => 'christian@shiftair.ca', 'City' => 'Calgary', 'State' => 'Alberta', 'Country' => 'Canada', 'Company Name' => 'Shift Air Mechanical Ltd.', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Adrain', 'Last Name' => 'Felix', 'Email' => 'afelix@franklinenergy.com', 'City' => 'Washington', 'State' => 'Wisconson', 'Country' => 'United States', 'Company Name' => 'None', 'Role' => 'Consultant', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Andrew', 'Last Name' => 'Sweetman', 'Email' => 'andys@genzryan.com', 'City' => 'Burnsville', 'State' => 'Minnesota', 'Country' => 'United States', 'Company Name' => 'Genz-Ryan', 'Role' => 'Head of Training And Development', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Doug', 'Last Name' => 'Larson', 'Email' => 'dougl@genzryan.com', 'City' => 'Burnsville', 'State' => 'Minnesota', 'Country' => 'United States', 'Company Name' => 'Genz-Ryan', 'Role' => 'Director of Operations', 'Certification Type' => 'Certified measureQuick Champion', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Eric', 'Last Name' => 'Kaiser', 'Email' => 'ekaiser@trutechtools.com', 'City' => 'Akron', 'State' => 'Ohio', 'Country' => 'United States', 'Company Name' => 'Trutech Tools', 'Role' => 'Educator', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Kent', 'Last Name' => 'Papczun', 'Email' => 'kent.papczun@webbsupply.com', 'City' => 'Akron', 'State' => 'Ohio', 'Country' => 'United States', 'Company Name' => 'Webb Supply', 'Role' => 'Technical Support/Training & Teaching Manager', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'William', 'Last Name' => 'Fisher', 'Email' => 'hhrhandc@gmail.com', 'City' => 'Luray', 'State' => 'Virginia', 'Country' => 'United States', 'Company Name' => 'Hawksbill Home Comfort', 'Role' => 'Owner/Operator', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Mike', 'Last Name' => 'Henderson', 'Email' => 'Mike@dwyeroil.com', 'City' => 'Oreland', 'State' => 'Pennsylvania', 'Country' => 'United States', 'Company Name' => 'Reit Energy', 'Role' => 'Installation Manager/ Trainer', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active'],
 | |
|             ['Name' => 'Andy', 'Last Name' => 'Holt', 'Email' => 'andy@toprate.com', 'City' => 'LaGrange', 'State' => 'Georgia', 'Country' => 'United States', 'Company Name' => 'Outdoor University', 'Role' => 'Owner', 'Certification Type' => 'Certified measureQuick Trainer', 'Certification Status' => 'Active']
 | |
|         ];
 | |
|         
 | |
|         $results['total_rows'] = count($csv_data);
 | |
|         $session_id = 'enhanced_' . date('Y-m-d_H-i-s');
 | |
|         $results['session_id'] = $session_id;
 | |
|         
 | |
|         $import_log = [
 | |
|             'timestamp' => time(),
 | |
|             'file' => 'enhanced_csv_import_ajax',
 | |
|             'total_rows' => count($csv_data),
 | |
|             'users' => []
 | |
|         ];
 | |
|         
 | |
|         // Process each row
 | |
|         foreach ($csv_data as $index => $row) {
 | |
|             try {
 | |
|                 $email = trim($row['Email']);
 | |
|                 
 | |
|                 // Check if user exists
 | |
|                 $user = get_user_by('email', $email);
 | |
|                 
 | |
|                 if (!$user) {
 | |
|                     // User doesn't exist - would create but we're just updating existing ones
 | |
|                     continue;
 | |
|                 } else {
 | |
|                     // Update existing user's profile
 | |
|                     $user_id = $user->ID;
 | |
|                     $results['users_updated']++;
 | |
|                     
 | |
|                     // Get or create trainer profile
 | |
|                     if (class_exists('HVAC_Trainer_Profile_Manager')) {
 | |
|                         $profile_manager = HVAC_Trainer_Profile_Manager::get_instance();
 | |
|                         $profile = $profile_manager->get_trainer_profile($user_id);
 | |
|                         
 | |
|                         if ($profile) {
 | |
|                             $profile_id = $profile->ID;
 | |
|                             $results['profiles_updated']++;
 | |
|                             
 | |
|                             // Update profile metadata
 | |
|                             $this->update_profile_metadata_ajax($profile_id, $row);
 | |
|                             
 | |
|                             // Schedule geocoding if location data exists
 | |
|                             $city = trim($row['City'] ?? '');
 | |
|                             $state = trim($row['State'] ?? '');
 | |
|                             $country = trim($row['Country'] ?? '');
 | |
|                             
 | |
|                             if (!empty($city) || !empty($state) || !empty($country)) {
 | |
|                                 wp_schedule_single_event(time() + rand(5, 30), 'hvac_geocode_address', [$profile_id]);
 | |
|                                 $results['geocoding_scheduled']++;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     
 | |
|                     // Add to import log
 | |
|                     $import_log['users'][$email] = [
 | |
|                         'user_id' => $user_id,
 | |
|                         'user_login' => $user->user_login,
 | |
|                         'csv_data' => $row,
 | |
|                         'imported_at' => time(),
 | |
|                         'status' => 'updated'
 | |
|                     ];
 | |
|                 }
 | |
|                 
 | |
|             } catch (Exception $e) {
 | |
|                 $results['errors']++;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Save import log
 | |
|         $existing_log = get_option('hvac_csv_import_log', []);
 | |
|         $existing_log[$session_id] = $import_log;
 | |
|         update_option('hvac_csv_import_log', $existing_log);
 | |
|         $results['import_log_saved'] = true;
 | |
|         
 | |
|         $results['end_time'] = current_time('mysql');
 | |
|         $results['duration'] = time() - strtotime($results['start_time']);
 | |
|         
 | |
|         return $results;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Update trainer profile metadata via AJAX
 | |
|      */
 | |
|     private function update_profile_metadata_ajax($profile_id, $row) {
 | |
|         $field_mappings = [
 | |
|             'trainer_city' => ['City'],
 | |
|             'trainer_state' => ['State'],
 | |
|             'trainer_country' => ['Country'],
 | |
|             'organization_name' => ['Company Name'],
 | |
|             'certification_type' => ['Certification Type'],
 | |
|             'certification_status' => ['Certification Status'],
 | |
|             'date_certified' => ['standardized_date'],
 | |
|             'role' => ['mapped_role', 'Role'],
 | |
|             'training_audience' => ['parsed_training_audience', 'Training Audience'],
 | |
|             'business_website' => ['Company Website'],
 | |
|             'business_phone' => ['Phone Number'],
 | |
|             'application_details' => ['Application Details']
 | |
|         ];
 | |
|         
 | |
|         foreach ($field_mappings as $profile_field => $csv_keys) {
 | |
|             $value = null;
 | |
|             
 | |
|             // Try each CSV key until we find a value
 | |
|             foreach ($csv_keys as $csv_key) {
 | |
|                 if (isset($row[$csv_key]) && !empty(trim($row[$csv_key]))) {
 | |
|                     $value = trim($row[$csv_key]);
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if ($value) {
 | |
|                 update_post_meta($profile_id, $profile_field, sanitize_text_field($value));
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Handle taxonomy fields
 | |
|         $this->update_profile_taxonomies($profile_id, $row);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Update taxonomy fields for a trainer profile from CSV data (with count)
 | |
|      */
 | |
|     private function update_profile_taxonomies_with_count($profile_id, $row) {
 | |
|         $updated_count = 0;
 | |
|         
 | |
|         // Define taxonomy mappings with their CSV field names
 | |
|         $taxonomy_mappings = [
 | |
|             'business_type' => ['mapped_business_type', 'Organizer Category'],
 | |
|             'training_audience' => ['parsed_training_audience', 'Training Audience'],
 | |
|             'training_formats' => ['Training Formats', 'training_formats'],
 | |
|             'training_locations' => ['Training Locations', 'training_locations'],
 | |
|             'training_resources' => ['Training Resources', 'training_resources']
 | |
|         ];
 | |
|         
 | |
|         foreach ($taxonomy_mappings as $taxonomy => $csv_keys) {
 | |
|             // Check if taxonomy already has terms assigned
 | |
|             $existing_terms = wp_get_post_terms($profile_id, $taxonomy);
 | |
|             if (!empty($existing_terms) && !is_wp_error($existing_terms)) {
 | |
|                 continue; // Skip if already has terms
 | |
|             }
 | |
|             
 | |
|             $value = null;
 | |
|             
 | |
|             // Try each CSV key until we find a value
 | |
|             foreach ($csv_keys as $csv_key) {
 | |
|                 if (isset($row[$csv_key]) && !empty(trim($row[$csv_key]))) {
 | |
|                     $value = trim($row[$csv_key]);
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if (empty($value)) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Parse the value into terms
 | |
|             $term_names = [];
 | |
|             if (is_array($value)) {
 | |
|                 $term_names = $value;
 | |
|             } elseif (is_string($value)) {
 | |
|                 // Handle comma-separated values or semicolon-separated values
 | |
|                 $separators = [',', ';', '|'];
 | |
|                 $parsed = false;
 | |
|                 
 | |
|                 foreach ($separators as $separator) {
 | |
|                     if (strpos($value, $separator) !== false) {
 | |
|                         $term_names = array_map('trim', explode($separator, $value));
 | |
|                         $parsed = true;
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 if (!$parsed) {
 | |
|                     $term_names = [$value];
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if (empty($term_names)) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Find or create terms and collect their IDs
 | |
|             $terms_to_assign = [];
 | |
|             foreach ($term_names as $term_name) {
 | |
|                 if (empty(trim($term_name))) {
 | |
|                     continue;
 | |
|                 }
 | |
|                 
 | |
|                 $term_name = trim($term_name);
 | |
|                 $term = get_term_by('name', $term_name, $taxonomy);
 | |
|                 
 | |
|                 if (!$term) {
 | |
|                     // Create the term if it doesn't exist
 | |
|                     $term_result = wp_insert_term($term_name, $taxonomy);
 | |
|                     if (!is_wp_error($term_result)) {
 | |
|                         $terms_to_assign[] = $term_result['term_id'];
 | |
|                     }
 | |
|                 } else {
 | |
|                     $terms_to_assign[] = $term->term_id;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Assign terms to the profile
 | |
|             if (!empty($terms_to_assign)) {
 | |
|                 $result = wp_set_post_terms($profile_id, $terms_to_assign, $taxonomy, false);
 | |
|                 if (!is_wp_error($result)) {
 | |
|                     $updated_count++;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         return $updated_count;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Update taxonomy fields for a trainer profile from CSV data
 | |
|      */
 | |
|     private function update_profile_taxonomies($profile_id, $row) {
 | |
|         // Define taxonomy mappings with their CSV field names
 | |
|         $taxonomy_mappings = [
 | |
|             'business_type' => ['mapped_business_type', 'Organizer Category'],
 | |
|             'training_audience' => ['parsed_training_audience', 'Training Audience'],
 | |
|             'training_formats' => ['Training Formats', 'training_formats'],
 | |
|             'training_locations' => ['Training Locations', 'training_locations'],
 | |
|             'training_resources' => ['Training Resources', 'training_resources']
 | |
|         ];
 | |
|         
 | |
|         foreach ($taxonomy_mappings as $taxonomy => $csv_keys) {
 | |
|             $value = null;
 | |
|             
 | |
|             // Try each CSV key until we find a value
 | |
|             foreach ($csv_keys as $csv_key) {
 | |
|                 if (isset($row[$csv_key]) && !empty(trim($row[$csv_key]))) {
 | |
|                     $value = trim($row[$csv_key]);
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if (empty($value)) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Parse the value into terms
 | |
|             $term_names = [];
 | |
|             if (is_array($value)) {
 | |
|                 $term_names = $value;
 | |
|             } elseif (is_string($value)) {
 | |
|                 // Handle comma-separated values or semicolon-separated values
 | |
|                 $separators = [',', ';', '|'];
 | |
|                 $parsed = false;
 | |
|                 
 | |
|                 foreach ($separators as $separator) {
 | |
|                     if (strpos($value, $separator) !== false) {
 | |
|                         $term_names = array_map('trim', explode($separator, $value));
 | |
|                         $parsed = true;
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 
 | |
|                 if (!$parsed) {
 | |
|                     $term_names = [$value];
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if (empty($term_names)) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Find or create terms and collect their IDs
 | |
|             $terms_to_assign = [];
 | |
|             foreach ($term_names as $term_name) {
 | |
|                 if (empty(trim($term_name))) {
 | |
|                     continue;
 | |
|                 }
 | |
|                 
 | |
|                 $term_name = trim($term_name);
 | |
|                 $term = get_term_by('name', $term_name, $taxonomy);
 | |
|                 
 | |
|                 if (!$term) {
 | |
|                     // Create the term if it doesn't exist
 | |
|                     $term_result = wp_insert_term($term_name, $taxonomy);
 | |
|                     if (!is_wp_error($term_result)) {
 | |
|                         $terms_to_assign[] = $term_result['term_id'];
 | |
|                     }
 | |
|                 } else {
 | |
|                     $terms_to_assign[] = $term->term_id;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Assign terms to the profile
 | |
|             if (!empty($terms_to_assign)) {
 | |
|                 wp_set_post_terms($profile_id, $terms_to_assign, $taxonomy, false);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Initialize
 | |
| HVAC_Geocoding_Ajax::get_instance(); |