- 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(); |