init_hooks(); } /** * Check if we're in production environment */ private function is_production() { // Check if we're on upskillhvac.com domain (production) $site_url = get_site_url(); return strpos($site_url, 'upskillhvac.com') !== false; } /** * Debug logging wrapper */ private function debug_log($message) { if (!$this->is_production() && defined('WP_DEBUG') && WP_DEBUG) { error_log('HVAC MapGeo: ' . $message); } } /** * Initialize hooks */ private function init_hooks() { // Hook into MapGeo data filter - modify layout and inject trainer modal data add_filter('igm_add_meta', [$this, 'modify_map_layout'], 10, 2); // Hook into MapGeo marker data to add our trainer information add_filter('igm_marker_data', [$this, 'inject_trainer_modal_data'], 10, 2); // AJAX handlers for our own filtering add_action('wp_ajax_hvac_filter_trainers', [$this, 'ajax_filter_trainers']); add_action('wp_ajax_nopriv_hvac_filter_trainers', [$this, 'ajax_filter_trainers']); // AJAX handler to get trainer certification type for Champions check add_action('wp_ajax_hvac_get_trainer_certification', [$this, 'ajax_get_trainer_certification']); add_action('wp_ajax_nopriv_hvac_get_trainer_certification', [$this, 'ajax_get_trainer_certification']); // AJAX handler to get complete trainer profile data add_action('wp_ajax_hvac_get_trainer_profile', [$this, 'ajax_get_trainer_profile']); add_action('wp_ajax_nopriv_hvac_get_trainer_profile', [$this, 'ajax_get_trainer_profile']); add_action('wp_ajax_hvac_search_trainers', [$this, 'ajax_search_trainers']); add_action('wp_ajax_nopriv_hvac_search_trainers', [$this, 'ajax_search_trainers']); // Add JavaScript to handle MapGeo marker clicks add_action('wp_footer', [$this, 'add_mapgeo_click_handlers']); } /** * Debug MapGeo integration */ public function debug_mapgeo_integration() { if (isset($_GET['debug_mapgeo']) && current_user_can('manage_options')) { $this->debug_log('Debug: Integration loaded'); $this->debug_log('Debug: Filters registered for igm_add_meta'); } } /** * Modify map layout to prevent sidebar display and inject trainer profile IDs * * @param array $meta Map metadata * @param int $map_id Map ID * @return array Modified metadata */ public function modify_map_layout($meta, $map_id = null) { // Only process if meta is an array if (!is_array($meta)) { return $meta; } // Only process for our specific map (5872) if ($map_id && $map_id != $this->map_id) { return $meta; } error_log('HVAC MapGeo: Processing map layout modification for map ' . $map_id); error_log('HVAC MapGeo: Meta keys: ' . implode(', ', array_keys($meta))); // Check for different marker types that MapGeo might use $marker_types = ['roundMarkers', 'iconMarkers', 'markers', 'customMarkers']; // Check if map has any existing markers $has_existing_markers = false; foreach ($marker_types as $marker_type) { if (isset($meta[$marker_type]) && is_array($meta[$marker_type]) && !empty($meta[$marker_type])) { $has_existing_markers = true; break; } } // If no existing markers, create them from trainer data if (!$has_existing_markers) { error_log('HVAC MapGeo: No existing markers found, creating from trainer data'); $trainers = $this->get_geocoded_trainers(); error_log('HVAC MapGeo: Found ' . count($trainers) . ' geocoded trainers'); if (!empty($trainers)) { $trainer_markers = array_values(array_filter( array_map([$this, 'format_trainer_for_mapgeo'], $trainers) )); if (!empty($trainer_markers)) { $meta['roundMarkers'] = $trainer_markers; error_log('HVAC MapGeo: Created ' . count($trainer_markers) . ' trainer markers'); } } } foreach ($marker_types as $marker_type) { if (isset($meta[$marker_type]) && is_array($meta[$marker_type])) { error_log('HVAC MapGeo: Found ' . count($meta[$marker_type]) . ' markers of type: ' . $marker_type); foreach ($meta[$marker_type] as $index => &$marker) { // Log marker structure for debugging error_log('HVAC MapGeo: Marker ' . $index . ' keys: ' . implode(', ', array_keys($marker))); // Check if this marker has trainer data we can identify $trainer_name = null; if (isset($marker['title']) && !empty($marker['title'])) { $trainer_name = $marker['title']; } elseif (isset($marker['name']) && !empty($marker['name'])) { $trainer_name = $marker['name']; } if ($trainer_name) { error_log('HVAC MapGeo: Looking for trainer profile for: ' . $trainer_name); // Try to find matching trainer profile by name $trainer_profile_id = $this->find_trainer_profile_by_name($trainer_name); if ($trainer_profile_id) { error_log('HVAC MapGeo: Found profile ID ' . $trainer_profile_id . ' for trainer: ' . $trainer_name); // Set custom click action for trainer modal with profile ID $marker['action'] = 'hvac_show_trainer_modal'; $marker['hvac_profile_id'] = $trainer_profile_id; // Also add to marker ID for easy identification $marker['id'] = 'trainer_' . $trainer_profile_id; // Remove sidebar display if it was set if (isset($marker['action']) && $marker['action'] === 'igm_display_right_1_3') { $marker['action'] = 'hvac_show_trainer_modal'; } error_log('HVAC MapGeo: Configured marker for trainer ' . $trainer_name . ' with profile ID ' . $trainer_profile_id); } else { error_log('HVAC MapGeo: No profile found for trainer: ' . $trainer_name); // Fallback to tooltip for non-trainer markers if (isset($marker['action']) && $marker['action'] === 'igm_display_right_1_3') { $marker['action'] = 'tooltip'; } } } else { error_log('HVAC MapGeo: Marker ' . $index . ' has no identifiable trainer name'); } } } } error_log('HVAC MapGeo: Map layout modification complete'); return $meta; } /** * Find trainer profile ID by trainer name * * @param string $trainer_name * @return int|false Profile ID or false if not found */ private function find_trainer_profile_by_name($trainer_name) { $args = [ 'post_type' => 'trainer_profile', 'posts_per_page' => 1, 'post_status' => 'publish', 'meta_query' => [ [ 'key' => 'trainer_display_name', 'value' => $trainer_name, 'compare' => '=' ] ] ]; $query = new WP_Query($args); if ($query->have_posts()) { return $query->posts[0]->ID; } wp_reset_postdata(); return false; } /** * AJAX handler to get trainer certification type */ public function ajax_get_trainer_certification() { // Verify nonce if (!wp_verify_nonce($_POST['nonce'], 'hvac_find_trainer')) { wp_send_json_error('Invalid nonce'); return; } $profile_id = intval($_POST['profile_id']); if (!$profile_id) { wp_send_json_error('Invalid profile ID'); return; } // Get the certification type and color for this profile $certification_type = get_post_meta($profile_id, 'certification_type', true); $certification_color = get_post_meta($profile_id, 'certification_color', true); wp_send_json_success([ 'certification_type' => $certification_type ?: 'HVAC Trainer', 'certification_color' => $certification_color ?: '#f0f7e8' ]); } /** * AJAX handler to get complete trainer profile data */ public function ajax_get_trainer_profile() { error_log('HVAC MapGeo: ajax_get_trainer_profile called'); // Verify nonce if (!wp_verify_nonce($_POST['nonce'], 'hvac_find_trainer')) { error_log('HVAC MapGeo: Invalid nonce in ajax_get_trainer_profile'); wp_send_json_error('Invalid nonce'); return; } $profile_id = intval($_POST['profile_id']); error_log('HVAC MapGeo: Processing profile ID: ' . $profile_id); if (!$profile_id) { error_log('HVAC MapGeo: Invalid profile ID: ' . $profile_id); wp_send_json_error('Invalid profile ID'); return; } // Get the trainer profile post $profile_post = get_post($profile_id); if (!$profile_post || $profile_post->post_type !== 'trainer_profile') { error_log('HVAC MapGeo: Invalid trainer profile - post type: ' . ($profile_post ? $profile_post->post_type : 'null')); wp_send_json_error('Invalid trainer profile'); return; } error_log('HVAC MapGeo: Valid profile found: ' . $profile_post->post_title); // Get user ID for event count $user_id = get_post_meta($profile_id, 'user_id', true); // Get event count $event_count = 0; if ($user_id && function_exists('tribe_get_events')) { $events = tribe_get_events([ 'author' => $user_id, 'eventDisplay' => 'all', 'posts_per_page' => -1, 'fields' => 'ids' ]); $event_count = count($events); } // Build complete trainer data structure try { // Get business type safely $business_types = wp_get_post_terms($profile_id, 'business_type', ['fields' => 'names']); $business_type = 'Independent Contractor'; if (!is_wp_error($business_types) && !empty($business_types)) { $business_type = implode(', ', $business_types); } else { $business_type_meta = get_post_meta($profile_id, 'business_type', true); if ($business_type_meta) { $business_type = $business_type_meta; } } error_log('HVAC MapGeo: Business type: ' . $business_type); // Get training formats safely $formats = wp_get_post_terms($profile_id, 'training_format', ['fields' => 'names']); $training_formats = 'In-Person, Virtual'; if (!is_wp_error($formats) && !empty($formats)) { $training_formats = implode(', ', $formats); } error_log('HVAC MapGeo: Training formats: ' . $training_formats); // Get training locations safely $resources = wp_get_post_terms($profile_id, 'training_resources', ['fields' => 'names']); $training_locations = 'On-site, Remote'; if (!is_wp_error($resources) && !empty($resources)) { $training_locations = implode(', ', $resources); } error_log('HVAC MapGeo: Training locations: ' . $training_locations); $trainer_data = [ 'profile_id' => $profile_id, 'user_id' => $user_id, 'name' => get_post_meta($profile_id, 'trainer_display_name', true) ?: $profile_post->post_title, 'city' => get_post_meta($profile_id, 'trainer_city', true) ?: 'Location not available', 'state' => get_post_meta($profile_id, 'trainer_state', true) ?: '', 'certification_type' => get_post_meta($profile_id, 'certification_type', true) ?: 'HVAC Trainer', 'profile_image' => get_post_meta($profile_id, 'profile_image_url', true) ?: '', 'business_type' => $business_type, 'event_count' => $event_count, 'training_formats' => $training_formats, 'training_locations' => $training_locations, 'upcoming_events' => [] ]; error_log('HVAC MapGeo: Successfully built trainer data for: ' . $trainer_data['name']); wp_send_json_success($trainer_data); } catch (Exception $e) { error_log('HVAC MapGeo: Error building trainer data: ' . $e->getMessage()); wp_send_json_error('Error building trainer data: ' . $e->getMessage()); } catch (Error $e) { error_log('HVAC MapGeo: Fatal error building trainer data: ' . $e->getMessage()); wp_send_json_error('Fatal error building trainer data: ' . $e->getMessage()); } } /** * Add JavaScript to handle MapGeo custom click actions */ public function add_mapgeo_click_handlers() { // Only add on find trainer page if (!is_page() || get_post_field('post_name') !== 'find-a-trainer') { return; } ?> %s
%s, %s
%s
View Profile ', esc_html($trainer_name), esc_html($city), esc_html($state), esc_html($certification), $profile_id ); return [ 'id' => 'trainer_' . $profile_id, 'coordinates' => [ 'lat' => floatval($lat), 'lng' => floatval($lng) ], 'tooltipContent' => $tooltip, 'action' => 'hvac_show_trainer_modal', // Use custom action for trainer modal 'value' => '1', 'radius' => '10', 'fill' => $certification === 'Certified measureQuick Champion' ? '#FFD700' : '#0073aa', 'fillOpacity' => '0.8', 'borderColor' => '#005a87', 'borderWidth' => '2' ]; } /** * Get all geocoded trainers * * @return array */ private function get_geocoded_trainers() { $args = [ 'post_type' => 'trainer_profile', 'posts_per_page' => -1, 'post_status' => 'publish', 'meta_query' => [ 'relation' => 'AND', [ 'key' => 'is_public_profile', 'value' => '1', 'compare' => '=' ], [ 'key' => 'latitude', 'compare' => 'EXISTS' ], [ 'key' => 'longitude', 'compare' => 'EXISTS' ] ] ]; // Add user status filter $this->add_user_status_filter($args); $query = new WP_Query($args); $trainers = []; if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); $trainers[] = get_the_ID(); } } wp_reset_postdata(); return $trainers; } /** * Format trainer data as map marker * * @param int $profile_id Trainer profile post ID * @return array Marker data */ private function format_trainer_marker($profile_id) { $user_id = get_post_meta($profile_id, 'user_id', true); $trainer_name = get_post_meta($profile_id, 'trainer_display_name', true); $city = get_post_meta($profile_id, 'trainer_city', true); $state = get_post_meta($profile_id, 'trainer_state', true); $lat = get_post_meta($profile_id, 'latitude', true); $lng = get_post_meta($profile_id, 'longitude', true); $certification = get_post_meta($profile_id, 'certification_type', true); $profile_image = get_post_meta($profile_id, 'profile_image_url', true); // Get taxonomies $business_types = wp_get_post_terms($profile_id, 'business_type', ['fields' => 'names']); $training_formats = wp_get_post_terms($profile_id, 'training_formats', ['fields' => 'names']); $training_resources = wp_get_post_terms($profile_id, 'training_resources', ['fields' => 'names']); // Count upcoming events $event_count = $this->get_trainer_event_count($user_id); return [ 'id' => 'trainer_' . $profile_id, 'title' => $trainer_name, 'lat' => floatval($lat), 'lng' => floatval($lng), 'icon' => $this->get_marker_icon($certification), 'content' => $this->generate_marker_popup($profile_id), 'data' => [ 'trainer_id' => $user_id, 'profile_id' => $profile_id, 'name' => $trainer_name, 'city' => $city, 'state' => $state, 'certification' => $certification, 'business_type' => $business_types, 'training_formats' => $training_formats, 'training_resources' => $training_resources, 'event_count' => $event_count, 'profile_image' => $profile_image ] ]; } /** * Generate marker popup content * * @param int $profile_id Trainer profile post ID * @return string HTML content */ private function generate_marker_popup($profile_id) { $trainer_name = get_post_meta($profile_id, 'trainer_display_name', true); $city = get_post_meta($profile_id, 'trainer_city', true); $state = get_post_meta($profile_id, 'trainer_state', true); $certification = get_post_meta($profile_id, 'certification_type', true); $profile_image = get_post_meta($profile_id, 'profile_image_url', true); ob_start(); ?>
<?php echo esc_attr($trainer_name); ?>

,

$user_id, 'eventDisplay' => 'all', 'posts_per_page' => -1, 'fields' => 'ids' ]); return count($events); } /** * Update marker when trainer profile is updated * * @param int $profile_id Trainer profile post ID */ public function update_trainer_marker($profile_id) { // Clear any cached map data delete_transient('hvac_mapgeo_markers_' . $this->map_id); // Trigger geocoding if needed $lat = get_post_meta($profile_id, 'latitude', true); $lng = get_post_meta($profile_id, 'longitude', true); if (empty($lat) || empty($lng)) { $this->geocode_trainer($profile_id); } } /** * Geocode a trainer's address * * @param int $profile_id Trainer profile post ID * @return bool Success status */ private function geocode_trainer($profile_id) { $address_parts = []; $street = get_post_meta($profile_id, 'trainer_street', true); $city = get_post_meta($profile_id, 'trainer_city', true); $state = get_post_meta($profile_id, 'trainer_state', true); $zip = get_post_meta($profile_id, 'trainer_zip', true); $country = get_post_meta($profile_id, 'trainer_country', true) ?: 'USA'; if ($street) $address_parts[] = $street; if ($city) $address_parts[] = $city; if ($state) $address_parts[] = $state; if ($zip) $address_parts[] = $zip; if ($country) $address_parts[] = $country; if (empty($address_parts)) { return false; } $address = implode(', ', $address_parts); // Use Google Maps Geocoding API if available $api_key = get_option('hvac_google_maps_api_key'); if ($api_key) { $response = wp_remote_get('https://maps.googleapis.com/maps/api/geocode/json?' . http_build_query([ 'address' => $address, 'key' => $api_key ])); if (!is_wp_error($response)) { $data = json_decode(wp_remote_retrieve_body($response), true); if ($data['status'] === 'OK' && !empty($data['results'][0])) { $location = $data['results'][0]['geometry']['location']; update_post_meta($profile_id, 'latitude', $location['lat']); update_post_meta($profile_id, 'longitude', $location['lng']); return true; } } } return false; } /** * Batch geocode all trainers without coordinates */ public function batch_geocode_trainers() { $args = [ 'post_type' => 'trainer_profile', 'posts_per_page' => -1, 'post_status' => 'publish', 'meta_query' => [ 'relation' => 'OR', [ 'key' => 'latitude', 'compare' => 'NOT EXISTS' ], [ 'key' => 'longitude', 'compare' => 'NOT EXISTS' ] ] ]; $query = new WP_Query($args); $geocoded = 0; if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); if ($this->geocode_trainer(get_the_ID())) { $geocoded++; // Rate limiting - wait 100ms between requests usleep(100000); } } } wp_reset_postdata(); return $geocoded; } /** * AJAX handler for filtering trainers */ public function ajax_filter_trainers() { check_ajax_referer('hvac_find_trainer', 'nonce'); $filters = $_POST['filters'] ?? []; $page = isset($_POST['page']) ? intval($_POST['page']) : 1; $per_page = 12; // Build query args $args = $this->build_trainer_query_args($filters, $page, $per_page); $query = new WP_Query($args); $trainers = []; if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); $trainers[] = $this->format_trainer_card_data(get_the_ID()); } } wp_reset_postdata(); // Sort trainers: Certified measureQuick Trainers first, Champions last usort($trainers, function($a, $b) { $a_cert = $a['certification']; $b_cert = $b['certification']; // Define sort order: Trainers = 1, Champions = 2, Others = 3 $a_priority = 3; // Default for others $b_priority = 3; // Default for others if ($a_cert === 'Certified measureQuick Trainer') { $a_priority = 1; } elseif ($a_cert === 'Certified measureQuick Champion') { $a_priority = 2; } if ($b_cert === 'Certified measureQuick Trainer') { $b_priority = 1; } elseif ($b_cert === 'Certified measureQuick Champion') { $b_priority = 2; } // Primary sort by certification priority if ($a_priority !== $b_priority) { return $a_priority - $b_priority; } // Secondary sort by name (alphabetical) return strcasecmp($a['name'], $b['name']); }); // Generate HTML for trainer cards ob_start(); if (!empty($trainers)) { foreach ($trainers as $trainer) { $this->render_trainer_card($trainer); } } else { echo '

No trainers found matching your criteria. Try adjusting your filters.

'; } $html = ob_get_clean(); // Generate pagination HTML $pagination_html = $this->generate_pagination($query->max_num_pages, $page); wp_send_json_success([ 'html' => $html, 'pagination' => $pagination_html, 'count' => $query->found_posts, 'page' => $page, 'max_pages' => $query->max_num_pages ]); } /** * AJAX handler for searching trainers */ public function ajax_search_trainers() { check_ajax_referer('hvac_find_trainer', 'nonce'); $search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : ''; $page = isset($_POST['page']) ? intval($_POST['page']) : 1; $per_page = 12; $filters = ['search' => $search]; $args = $this->build_trainer_query_args($filters, $page, $per_page); $query = new WP_Query($args); $trainers = []; if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); $trainers[] = $this->format_trainer_card_data(get_the_ID()); } } wp_reset_postdata(); // Sort trainers: Certified measureQuick Trainers first, Champions last usort($trainers, function($a, $b) { $a_cert = $a['certification']; $b_cert = $b['certification']; // Define sort order: Trainers = 1, Champions = 2, Others = 3 $a_priority = 3; // Default for others $b_priority = 3; // Default for others if ($a_cert === 'Certified measureQuick Trainer') { $a_priority = 1; } elseif ($a_cert === 'Certified measureQuick Champion') { $a_priority = 2; } if ($b_cert === 'Certified measureQuick Trainer') { $b_priority = 1; } elseif ($b_cert === 'Certified measureQuick Champion') { $b_priority = 2; } // Primary sort by certification priority if ($a_priority !== $b_priority) { return $a_priority - $b_priority; } // Secondary sort by name (alphabetical) return strcasecmp($a['name'], $b['name']); }); // Generate HTML ob_start(); foreach ($trainers as $trainer) { $this->render_trainer_card($trainer); } $html = ob_get_clean(); $pagination_html = $this->generate_pagination($query->max_num_pages, $page); wp_send_json_success([ 'html' => $html, 'pagination' => $pagination_html, 'count' => $query->found_posts ]); } /** * Build query args for trainer search/filter */ private function build_trainer_query_args($filters, $page = 1, $per_page = 12) { $args = [ 'post_type' => 'trainer_profile', 'posts_per_page' => $per_page, 'paged' => $page, 'post_status' => 'publish', 'meta_query' => [ 'relation' => 'AND', [ 'key' => 'is_public_profile', 'value' => '1', 'compare' => '=' ] ] ]; // Add user status filter - only show profiles for approved/active/inactive users $this->add_user_status_filter($args); // Apply search if (!empty($filters['search'])) { $args['meta_query'][] = [ 'key' => 'trainer_display_name', 'value' => $filters['search'], 'compare' => 'LIKE' ]; } // Apply state filter - handle both single value and array if (!empty($filters['state'])) { if (is_array($filters['state'])) { // Multiple states selected $state_values = array_map('sanitize_text_field', $filters['state']); $args['meta_query'][] = [ 'key' => 'trainer_state', 'value' => $state_values, 'compare' => 'IN' ]; } else { // Single state $args['meta_query'][] = [ 'key' => 'trainer_state', 'value' => sanitize_text_field($filters['state']), 'compare' => '=' ]; } } // Apply taxonomy filters - handle arrays from multi-select $tax_query = []; if (!empty($filters['business_type'])) { $terms = is_array($filters['business_type']) ? array_map('sanitize_text_field', $filters['business_type']) : [sanitize_text_field($filters['business_type'])]; $tax_query[] = [ 'taxonomy' => 'business_type', 'field' => 'name', // Changed from 'slug' to 'name' to match the values being sent 'terms' => $terms ]; } if (!empty($filters['training_format'])) { $terms = is_array($filters['training_format']) ? array_map('sanitize_text_field', $filters['training_format']) : [sanitize_text_field($filters['training_format'])]; $tax_query[] = [ 'taxonomy' => 'training_formats', 'field' => 'name', // Changed from 'slug' to 'name' 'terms' => $terms ]; } if (!empty($filters['training_resources'])) { $terms = is_array($filters['training_resources']) ? array_map('sanitize_text_field', $filters['training_resources']) : [sanitize_text_field($filters['training_resources'])]; $tax_query[] = [ 'taxonomy' => 'training_resources', 'field' => 'name', // Changed from 'slug' to 'name' 'terms' => $terms ]; } if (!empty($tax_query)) { $args['tax_query'] = $tax_query; } return $args; } /** * Add user status filter to query args * Only show profiles for users with status: approved, active, or inactive * Exclude: pending, disabled */ private function add_user_status_filter(&$args) { // Get all trainer profile user IDs first, then filter by status $user_query = new WP_User_Query([ 'meta_query' => [ [ 'key' => 'account_status', 'value' => ['approved', 'active', 'inactive'], 'compare' => 'IN' ] ], 'fields' => 'ID' ]); $approved_user_ids = $user_query->get_results(); if (!empty($approved_user_ids)) { // Filter trainer profiles to only those belonging to approved users $args['meta_query'][] = [ 'key' => 'user_id', 'value' => $approved_user_ids, 'compare' => 'IN' ]; } else { // If no approved users found, still show all profiles (fallback for development) // In production, you might want to return no results instead error_log('HVAC Debug: No users found with approved account status'); } } /** * Format trainer data for card display */ private function format_trainer_card_data($profile_id) { $user_id = get_post_meta($profile_id, 'user_id', true); $trainer_name = get_post_meta($profile_id, 'trainer_display_name', true); $city = get_post_meta($profile_id, 'trainer_city', true); $state = get_post_meta($profile_id, 'trainer_state', true); $certification = get_post_meta($profile_id, 'certification_type', true); $profile_image = get_post_meta($profile_id, 'profile_image_url', true); // Get real event count for this trainer $event_count = $this->get_trainer_event_count($user_id); return [ 'profile_id' => $profile_id, 'user_id' => $user_id, 'name' => $trainer_name, 'city' => $city, 'state' => $state, 'certification' => $certification, 'profile_image' => $profile_image, 'event_count' => $event_count, 'profile_url' => '#' // Will open modal ]; } /** * Render trainer card HTML */ private function render_trainer_card($trainer) { ?>
<?php echo esc_attr($trainer['name']); ?>
measureQuick Certified Trainer

,

'; // Previous if ($current_page > 1) { $html .= sprintf( '%s', $current_page - 1, '«' ); } // Page numbers for ($i = 1; $i <= $max_pages; $i++) { if ($i == $current_page) { $html .= sprintf('%d', $i); } else { $html .= sprintf( '%d', $i, $i ); } } // Next if ($current_page < $max_pages) { $html .= sprintf( '%s', $current_page + 1, '»' ); } $html .= ''; return $html; } /** * Check for MapGeo plugin conflicts */ public function check_conflicts() { $known_conflicts = [ 'nextgen-gallery/nggallery.php', 'wp-ulike/wp-ulike.php', 'testimonials-widget/testimonials-widget.php', 'wp-leaflet-maps-pro/wp-leaflet-maps-pro.php' ]; $active_conflicts = []; foreach ($known_conflicts as $plugin) { if (is_plugin_active($plugin)) { $active_conflicts[] = $plugin; } } if (!empty($active_conflicts)) { add_action('admin_notices', function() use ($active_conflicts) { ?>

HVAC Plugin Warning: The following plugins may conflict with MapGeo integration: