load_api_key(); $this->init_hooks(); } /** * Load Google Maps API key from secure storage */ private function load_api_key(): void { if (class_exists('HVAC_Secure_Storage')) { $this->api_key = HVAC_Secure_Storage::get_credential('hvac_google_maps_api_key', ''); } } /** * Initialize WordPress hooks */ private function init_hooks(): void { // Page registration add_action('init', [$this, 'register_page'], 15); // Asset enqueuing add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']); // Body classes add_filter('body_class', [$this, 'add_body_classes']); // AJAX handlers add_action('wp_ajax_hvac_get_training_map_data', [$this, 'ajax_get_map_data']); add_action('wp_ajax_nopriv_hvac_get_training_map_data', [$this, 'ajax_get_map_data']); add_action('wp_ajax_hvac_filter_training_map', [$this, 'ajax_filter_map']); add_action('wp_ajax_nopriv_hvac_filter_training_map', [$this, 'ajax_filter_map']); add_action('wp_ajax_hvac_get_trainer_profile_modal', [$this, 'ajax_get_trainer_profile']); add_action('wp_ajax_nopriv_hvac_get_trainer_profile_modal', [$this, 'ajax_get_trainer_profile']); add_action('wp_ajax_hvac_get_venue_info', [$this, 'ajax_get_venue_info']); add_action('wp_ajax_nopriv_hvac_get_venue_info', [$this, 'ajax_get_venue_info']); // Redirect from old page add_action('template_redirect', [$this, 'maybe_redirect_from_old_page']); } /** * Register the Find Training page */ public function register_page(): void { $page = get_page_by_path($this->page_slug); if (!$page) { $this->create_page(); } } /** * Create the Find Training page in WordPress */ private function create_page(): void { $page_data = [ 'post_title' => 'Find Training', 'post_name' => $this->page_slug, 'post_content' => '', 'post_status' => 'publish', 'post_type' => 'page', 'post_author' => 1, 'comment_status' => 'closed', 'ping_status' => 'closed', 'meta_input' => [ '_wp_page_template' => 'page-find-training.php', 'ast-site-content-layout' => 'page-builder', 'site-post-title' => 'disabled', 'site-sidebar-layout' => 'no-sidebar', 'theme-transparent-header-meta' => 'disabled' ] ]; $page_id = wp_insert_post($page_data); if ($page_id && !is_wp_error($page_id)) { update_option('hvac_find_training_page_id', $page_id); } } /** * Check if current page is the Find Training page * * @return bool */ public function is_find_training_page(): bool { return is_page($this->page_slug) || is_page(get_option('hvac_find_training_page_id')); } /** * Enqueue page assets */ public function enqueue_assets(): void { if (!$this->is_find_training_page()) { return; } // Enqueue CSS wp_enqueue_style( 'hvac-find-training', HVAC_PLUGIN_URL . 'assets/css/find-training-map.css', ['astra-theme-css'], HVAC_VERSION ); // Enqueue Google Maps API with MarkerClusterer if (!empty($this->api_key)) { wp_enqueue_script( 'google-maps-api', 'https://maps.googleapis.com/maps/api/js?key=' . esc_attr($this->api_key) . '&libraries=places&callback=Function.prototype', [], null, true ); // MarkerClusterer library wp_enqueue_script( 'google-maps-markerclusterer', 'https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js', ['google-maps-api'], '2.5.3', true ); } // Enqueue main map JavaScript wp_enqueue_script( 'hvac-find-training-map', HVAC_PLUGIN_URL . 'assets/js/find-training-map.js', ['jquery', 'google-maps-api', 'google-maps-markerclusterer'], HVAC_VERSION, true ); // Enqueue filter JavaScript wp_enqueue_script( 'hvac-find-training-filters', HVAC_PLUGIN_URL . 'assets/js/find-training-filters.js', ['jquery', 'hvac-find-training-map'], HVAC_VERSION, true ); // Localize script with data wp_localize_script('hvac-find-training-map', 'hvacFindTraining', [ 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('hvac_find_training'), 'api_key' => !empty($this->api_key) ? 'configured' : '', // Don't expose actual key 'map_center' => [ 'lat' => 39.8283, // US center 'lng' => -98.5795 ], 'default_zoom' => 4, 'cluster_zoom' => 8, 'messages' => [ 'loading' => __('Loading...', 'hvac-community-events'), 'error' => __('An error occurred. Please try again.', 'hvac-community-events'), 'no_results' => __('No trainers or venues found matching your criteria.', 'hvac-community-events'), 'geolocation_error' => __('Unable to get your location. Please check your browser settings.', 'hvac-community-events'), 'geolocation_unsupported' => __('Geolocation is not supported by your browser.', 'hvac-community-events') ], 'marker_icons' => [ 'trainer' => HVAC_PLUGIN_URL . 'assets/images/marker-trainer.svg', 'venue' => HVAC_PLUGIN_URL . 'assets/images/marker-venue.svg' ] ]); } /** * Add body classes for the page * * @param array $classes Existing body classes * @return array Modified body classes */ public function add_body_classes(array $classes): array { if ($this->is_find_training_page()) { $classes[] = 'hvac-find-training-page'; $classes[] = 'hvac-full-width'; $classes[] = 'hvac-page'; } return $classes; } /** * Redirect from old /find-a-trainer page to /find-training */ public function maybe_redirect_from_old_page(): void { if (is_page('find-a-trainer')) { wp_safe_redirect(home_url('/find-training/'), 301); exit; } } /** * AJAX: Get all map data (trainers and venues) */ public function ajax_get_map_data(): void { // Verify nonce if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_find_training')) { wp_send_json_error(['message' => 'Invalid security token']); return; } $data_provider = HVAC_Training_Map_Data::get_instance(); $trainers = $data_provider->get_trainer_markers(); $venues = $data_provider->get_venue_markers(); wp_send_json_success([ 'trainers' => $trainers, 'venues' => $venues, 'total_trainers' => count($trainers), 'total_venues' => count($venues) ]); } /** * AJAX: Filter map markers */ public function ajax_filter_map(): void { // Verify nonce if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_find_training')) { wp_send_json_error(['message' => 'Invalid security token']); return; } $filters = [ 'state' => sanitize_text_field($_POST['state'] ?? ''), 'certification' => sanitize_text_field($_POST['certification'] ?? ''), 'training_format' => sanitize_text_field($_POST['training_format'] ?? ''), 'search' => sanitize_text_field($_POST['search'] ?? ''), 'show_trainers' => filter_var($_POST['show_trainers'] ?? true, FILTER_VALIDATE_BOOLEAN), 'show_venues' => filter_var($_POST['show_venues'] ?? true, FILTER_VALIDATE_BOOLEAN), 'lat' => isset($_POST['lat']) ? floatval($_POST['lat']) : null, 'lng' => isset($_POST['lng']) ? floatval($_POST['lng']) : null, 'radius' => isset($_POST['radius']) ? intval($_POST['radius']) : 100 // km ]; $data_provider = HVAC_Training_Map_Data::get_instance(); $result = [ 'trainers' => [], 'venues' => [] ]; if ($filters['show_trainers']) { $result['trainers'] = $data_provider->get_trainer_markers($filters); } if ($filters['show_venues']) { $result['venues'] = $data_provider->get_venue_markers($filters); } $result['total_trainers'] = count($result['trainers']); $result['total_venues'] = count($result['venues']); $result['filters_applied'] = array_filter($filters, function($v) { return !empty($v) && $v !== true; }); wp_send_json_success($result); } /** * AJAX: Get trainer profile for modal */ public function ajax_get_trainer_profile(): void { // Verify nonce if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_find_training')) { wp_send_json_error(['message' => 'Invalid security token']); return; } $profile_id = absint($_POST['profile_id'] ?? 0); if (!$profile_id) { wp_send_json_error(['message' => 'Invalid profile ID']); return; } $data_provider = HVAC_Training_Map_Data::get_instance(); $trainer_data = $data_provider->get_trainer_full_profile($profile_id); if (!$trainer_data) { wp_send_json_error(['message' => 'Trainer not found']); return; } // Generate modal HTML ob_start(); $this->render_trainer_modal_content($trainer_data); $html = ob_get_clean(); wp_send_json_success([ 'trainer' => $trainer_data, 'html' => $html ]); } /** * AJAX: Get venue info for info window */ public function ajax_get_venue_info(): void { // Verify nonce if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_find_training')) { wp_send_json_error(['message' => 'Invalid security token']); return; } $venue_id = absint($_POST['venue_id'] ?? 0); if (!$venue_id) { wp_send_json_error(['message' => 'Invalid venue ID']); return; } $data_provider = HVAC_Training_Map_Data::get_instance(); $venue_data = $data_provider->get_venue_full_info($venue_id); if (!$venue_data) { wp_send_json_error(['message' => 'Venue not found']); return; } wp_send_json_success(['venue' => $venue_data]); } /** * Render trainer modal content * * @param array $trainer Trainer data */ private function render_trainer_modal_content(array $trainer): void { ?>