[ 'type' => 'text', 'label' => 'First Name', 'required' => true, 'sanitize' => 'text' ], 'last_name' => [ 'type' => 'text', 'label' => 'Last Name', 'required' => true, 'sanitize' => 'text' ] ]; /** * Get singleton instance * * @return HVAC_TEC_Tickets */ public static function instance(): HVAC_TEC_Tickets { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ private function __construct() { $this->init_hooks(); } /** * Initialize hooks */ private function init_hooks(): void { // Hook into TEC event creation add_action('tribe_events_event_save', [$this, 'handle_event_save'], 10, 2); // Add ticket management to HVAC forms add_action('hvac_event_form_after_basic_fields', [$this, 'add_ticket_fields']); // Handle ticket creation AJAX add_action('wp_ajax_hvac_create_event_tickets', [$this, 'ajax_create_event_tickets']); add_action('wp_ajax_hvac_update_event_tickets', [$this, 'ajax_update_event_tickets']); // Add mandatory attendee fields to ticket forms add_filter('tribe_tickets_attendee_registration_form_fields', [$this, 'add_mandatory_attendee_fields'], 10, 2); // Validate mandatory fields on ticket purchase add_action('tribe_tickets_before_save_attendee_data', [$this, 'validate_mandatory_attendee_data'], 10, 3); // Enqueue ticketing assets add_action('wp_enqueue_scripts', [$this, 'enqueue_ticketing_assets']); } /** * Handle event save and create associated tickets * * @param int $event_id Event ID * @param array $data Event data */ public function handle_event_save(int $event_id, array $data): void { // Only process HVAC-created events if (!get_post_meta($event_id, '_hvac_event', true)) { return; } $ticket_data = get_post_meta($event_id, '_hvac_ticket_data', true); if (!empty($ticket_data)) { $this->create_tickets_for_event($event_id, $ticket_data); } } /** * Create tickets for an event * * @param int $event_id Event ID * @param array $tickets_data Tickets configuration * @return array Result with success status */ public function create_tickets_for_event(int $event_id, array $tickets_data): array { $results = []; foreach ($tickets_data as $ticket_config) { $ticket_id = $this->create_tec_ticket($event_id, $ticket_config); if ($ticket_id) { // Add mandatory fieldset requirements $this->add_fieldset_requirements($ticket_id, $this->default_fieldset_id); $results[] = ['success' => true, 'ticket_id' => $ticket_id]; } else { $results[] = ['success' => false, 'error' => 'Failed to create ticket']; } } return [ 'success' => count(array_filter($results, fn($r) => $r['success'])) > 0, 'tickets' => $results ]; } /** * Create a TEC ticket * * @param int $event_id Event ID * @param array $ticket_config Ticket configuration * @return int|false Ticket ID or false on failure */ private function create_tec_ticket(int $event_id, array $ticket_config) { // Validate TEC is available if (!class_exists('Tribe__Tickets__Tickets')) { error_log('HVAC TEC Tickets: TEC Tickets plugin not available'); return false; } $ticket_data = [ 'ticket_name' => sanitize_text_field($ticket_config['name'] ?? 'Event Ticket'), 'ticket_description' => wp_kses_post($ticket_config['description'] ?? ''), 'ticket_price' => floatval($ticket_config['price'] ?? 0), 'ticket_start_date' => sanitize_text_field($ticket_config['start_date'] ?? ''), 'ticket_end_date' => sanitize_text_field($ticket_config['end_date'] ?? ''), 'ticket_stock' => intval($ticket_config['capacity'] ?? -1), // -1 = unlimited 'ticket_sku' => '', // Trainers don't set SKUs per requirements ]; // Create ticket using TEC API try { $ticket_handler = tribe('tickets.handler'); $ticket_id = $ticket_handler->create_ticket($event_id, $ticket_data); if ($ticket_id) { // Add HVAC-specific metadata update_post_meta($ticket_id, '_hvac_ticket', true); update_post_meta($ticket_id, '_hvac_ticket_config', $ticket_config); return $ticket_id; } } catch (Exception $e) { error_log('HVAC TEC Tickets: Error creating ticket - ' . $e->getMessage()); } return false; } /** * Add fieldset requirements to ticket * * @param int $ticket_id Ticket ID * @param int $fieldset_id Fieldset ID */ private function add_fieldset_requirements(int $ticket_id, int $fieldset_id): void { $fieldset_fields = $this->get_tec_fieldset_fields($fieldset_id); if (!empty($fieldset_fields)) { update_post_meta($ticket_id, '_tribe_tickets_attendee_info_fieldset', $fieldset_id); // Ensure mandatory fields are included $all_fields = array_merge($this->mandatory_fields, $fieldset_fields); update_post_meta($ticket_id, '_hvac_ticket_mandatory_fields', $all_fields); } } /** * Get TEC fieldset fields * * @param int $fieldset_id Fieldset ID * @return array Fieldset fields */ private function get_tec_fieldset_fields(int $fieldset_id): array { // Check if fieldset exists $fieldset_post = get_post($fieldset_id); if (!$fieldset_post || $fieldset_post->post_type !== 'tribe_rsvp_tickets_ticket_fieldset') { // Return basic fields if fieldset not found return [ 'email' => [ 'type' => 'email', 'label' => 'Email Address', 'required' => true, 'sanitize' => 'email' ], 'phone' => [ 'type' => 'tel', 'label' => 'Phone Number', 'required' => false, 'sanitize' => 'text' ] ]; } // Parse fieldset structure - this would need to be adapted based on TEC's actual fieldset format $fieldset_meta = get_post_meta($fieldset_id, '_tribe_tickets_fieldset_fields', true); if (is_array($fieldset_meta)) { return $this->parse_fieldset_structure($fieldset_meta); } return []; } /** * Parse fieldset structure into standardized format * * @param array $fieldset_data Raw fieldset data * @return array Parsed fields */ private function parse_fieldset_structure(array $fieldset_data): array { $parsed_fields = []; foreach ($fieldset_data as $field) { if (!isset($field['slug']) || !isset($field['type'])) { continue; } $parsed_fields[$field['slug']] = [ 'type' => $this->normalize_field_type($field['type']), 'label' => $field['label'] ?? ucfirst(str_replace('_', ' ', $field['slug'])), 'required' => !empty($field['required']), 'sanitize' => $this->get_sanitization_method($field['type']), 'options' => $field['options'] ?? null ]; } return $parsed_fields; } /** * Normalize field type for consistency * * @param string $field_type Original field type * @return string Normalized field type */ private function normalize_field_type(string $field_type): string { $type_map = [ 'text' => 'text', 'textarea' => 'textarea', 'email' => 'email', 'select' => 'select', 'radio' => 'radio', 'checkbox' => 'checkbox', 'url' => 'url', 'tel' => 'tel', 'number' => 'number', 'date' => 'date' ]; return $type_map[$field_type] ?? 'text'; } /** * Get sanitization method for field type * * @param string $field_type Field type * @return string Sanitization method */ private function get_sanitization_method(string $field_type): string { $sanitization_map = [ 'text' => 'text', 'textarea' => 'textarea', 'email' => 'email', 'url' => 'url', 'tel' => 'text', 'number' => 'int' ]; return $sanitization_map[$field_type] ?? 'text'; } /** * Add ticket fields to HVAC event forms * * @param object $form_builder Form builder instance */ public function add_ticket_fields($form_builder): void { if (!method_exists($form_builder, 'add_field')) { return; } // Ticket section header $form_builder->add_field([ 'type' => 'custom', 'name' => 'ticket_section_header', 'custom_html' => '

Event Ticketing

', 'wrapper_class' => 'form-section ticket-section' ]); // Enable ticketing checkbox $form_builder->add_field([ 'type' => 'checkbox', 'name' => 'enable_ticketing', 'label' => 'Enable Ticketing', 'description' => 'Create tickets for this event with pricing and attendee collection', 'value' => '1', 'wrapper_class' => 'form-row enable-ticketing-row', 'onchange' => 'hvacToggleTicketFields(this.checked)' ]); // Ticket configuration container (initially hidden) $form_builder->add_field([ 'type' => 'custom', 'name' => 'ticket_config_start', 'custom_html' => '', 'wrapper_class' => '' ]); } /** * Add mandatory attendee fields to ticket forms * * @param array $fields Existing fields * @param int $ticket_id Ticket ID * @return array Modified fields */ public function add_mandatory_attendee_fields(array $fields, int $ticket_id): array { // Only add to HVAC tickets if (!get_post_meta($ticket_id, '_hvac_ticket', true)) { return $fields; } // Get mandatory fields for this ticket $mandatory_fields = get_post_meta($ticket_id, '_hvac_ticket_mandatory_fields', true); if (empty($mandatory_fields)) { $mandatory_fields = $this->mandatory_fields; } // Merge mandatory fields with existing fields return array_merge($mandatory_fields, $fields); } /** * Validate mandatory attendee data * * @param int $attendee_id Attendee ID * @param array $attendee_data Attendee data * @param int $ticket_id Ticket ID */ public function validate_mandatory_attendee_data(int $attendee_id, array $attendee_data, int $ticket_id): void { // Only validate HVAC tickets if (!get_post_meta($ticket_id, '_hvac_ticket', true)) { return; } $mandatory_fields = get_post_meta($ticket_id, '_hvac_ticket_mandatory_fields', true); if (empty($mandatory_fields)) { $mandatory_fields = $this->mandatory_fields; } $errors = []; foreach ($mandatory_fields as $field_name => $field_config) { if (!empty($field_config['required'])) { $value = $attendee_data[$field_name] ?? ''; if (empty(trim($value))) { $errors[] = sprintf('Field "%s" is required', $field_config['label']); } } } if (!empty($errors)) { wp_die( 'Please complete all required fields: ' . implode(', ', $errors), 'Required Fields Missing', ['response' => 400, 'back_link' => true] ); } } /** * AJAX handler for creating event tickets */ public function ajax_create_event_tickets(): void { // Security check if (!wp_verify_nonce($_POST['nonce'] ?? '', 'hvac_ticket_nonce')) { wp_send_json_error(['message' => __('Security check failed', 'hvac-community-events')]); return; } $event_id = intval($_POST['event_id'] ?? 0); $ticket_data = $_POST['ticket_data'] ?? []; if (!$event_id || empty($ticket_data)) { wp_send_json_error(['message' => __('Missing required data', 'hvac-community-events')]); return; } $result = $this->create_tickets_for_event($event_id, $ticket_data); if ($result['success']) { wp_send_json_success($result); } else { wp_send_json_error($result); } } /** * AJAX handler for updating event tickets */ public function ajax_update_event_tickets(): void { // Security check if (!wp_verify_nonce($_POST['nonce'] ?? '', 'hvac_ticket_nonce')) { wp_send_json_error(['message' => __('Security check failed', 'hvac-community-events')]); return; } $ticket_id = intval($_POST['ticket_id'] ?? 0); $ticket_data = $_POST['ticket_data'] ?? []; if (!$ticket_id || empty($ticket_data)) { wp_send_json_error(['message' => __('Missing required data', 'hvac-community-events')]); return; } // Update ticket - implementation would depend on TEC's update API $success = $this->update_tec_ticket($ticket_id, $ticket_data); if ($success) { wp_send_json_success(['message' => __('Ticket updated successfully', 'hvac-community-events')]); } else { wp_send_json_error(['message' => __('Failed to update ticket', 'hvac-community-events')]); } } /** * Update a TEC ticket * * @param int $ticket_id Ticket ID * @param array $ticket_data Updated ticket data * @return bool Success status */ private function update_tec_ticket(int $ticket_id, array $ticket_data): bool { // Implementation would depend on TEC's update API // For now, update basic post data $update_data = [ 'ID' => $ticket_id, 'post_title' => sanitize_text_field($ticket_data['name'] ?? ''), 'post_content' => wp_kses_post($ticket_data['description'] ?? '') ]; $result = wp_update_post($update_data); if (!is_wp_error($result)) { // Update ticket meta if (isset($ticket_data['price'])) { update_post_meta($ticket_id, '_price', floatval($ticket_data['price'])); } if (isset($ticket_data['capacity'])) { update_post_meta($ticket_id, '_stock', intval($ticket_data['capacity'])); } return true; } return false; } /** * Enqueue ticketing assets */ public function enqueue_ticketing_assets(): void { // Only enqueue on event creation/edit pages if (!$this->is_hvac_event_page()) { return; } wp_enqueue_script( 'hvac-tec-tickets', HVAC_PLUGIN_URL . 'assets/js/hvac-tec-tickets.js', ['jquery'], HVAC_VERSION, true ); wp_localize_script('hvac-tec-tickets', 'hvacTecTickets', [ 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('hvac_ticket_nonce'), 'strings' => [ 'ticketCreated' => __('Ticket created successfully', 'hvac-community-events'), 'ticketUpdated' => __('Ticket updated successfully', 'hvac-community-events'), 'error' => __('An error occurred. Please try again.', 'hvac-community-events'), 'confirmRemove' => __('Are you sure you want to remove this ticket?', 'hvac-community-events') ] ]); wp_enqueue_style( 'hvac-tec-tickets', HVAC_PLUGIN_URL . 'assets/css/hvac-tec-tickets.css', [], HVAC_VERSION ); } /** * Check if current page is an HVAC event page * * @return bool */ private function is_hvac_event_page(): bool { global $wp; if (!is_page()) { return false; } $current_url = home_url(add_query_arg([], $wp->request)); $event_patterns = [ '/trainer/events/create', '/trainer/events/edit', '/trainer/events/manage' ]; foreach ($event_patterns as $pattern) { if (strpos($current_url, $pattern) !== false) { return true; } } return false; } } // Initialize the TEC Tickets integration HVAC_TEC_Tickets::instance();