🚨 CRITICAL: Fixed deployment blockers by adding missing core directories: **Community System (CRITICAL)** - includes/community/ - Login_Handler and all community classes - templates/community/ - Community login forms **Certificate System (CRITICAL)** - includes/certificates/ - 8+ certificate classes and handlers - templates/certificates/ - Certificate reports and generation templates **Core Individual Classes (CRITICAL)** - includes/class-hvac-event-summary.php - includes/class-hvac-trainer-profile-manager.php - includes/class-hvac-master-dashboard-data.php - Plus 40+ other individual HVAC classes **Major Feature Systems (HIGH)** - includes/database/ - Training leads database tables - includes/find-trainer/ - Find trainer directory and MapGeo integration - includes/google-sheets/ - Google Sheets integration system - includes/zoho/ - Complete Zoho CRM integration - includes/communication/ - Communication templates system **Template Infrastructure** - templates/attendee/, templates/email-attendees/ - templates/event-summary/, templates/status/ - templates/template-parts/ - Shared template components **Impact:** - 70+ files added covering 10+ missing directories - Resolves ALL deployment blockers and feature breakdowns - Plugin activation should now work correctly - Multi-machine deployment fully supported 🔧 Generated with Claude Code Co-Authored-By: Ben Reed <ben@tealmaker.com>
		
			
				
	
	
		
			541 lines
		
	
	
		
			No EOL
		
	
	
		
			22 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			541 lines
		
	
	
		
			No EOL
		
	
	
		
			22 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Certificate AJAX Handler Class
 | |
|  *
 | |
|  * Handles AJAX requests for certificate actions.
 | |
|  *
 | |
|  * @package HVAC_Community_Events
 | |
|  * @subpackage Certificates
 | |
|  */
 | |
| 
 | |
| // Exit if accessed directly
 | |
| if (!defined('ABSPATH')) {
 | |
|     exit;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Certificate AJAX Handler class.
 | |
|  *
 | |
|  * Processes AJAX requests for certificate actions.
 | |
|  *
 | |
|  * @since 1.0.0
 | |
|  */
 | |
| class HVAC_Certificate_AJAX_Handler {
 | |
| 
 | |
|     /**
 | |
|      * The single instance of the class.
 | |
|      *
 | |
|      * @var HVAC_Certificate_AJAX_Handler
 | |
|      */
 | |
|     protected static $_instance = null;
 | |
| 
 | |
|     /**
 | |
|      * Certificate manager instance.
 | |
|      *
 | |
|      * @var HVAC_Certificate_Manager
 | |
|      */
 | |
|     protected $certificate_manager;
 | |
| 
 | |
|     /**
 | |
|      * Certificate security instance.
 | |
|      *
 | |
|      * @var HVAC_Certificate_Security
 | |
|      */
 | |
|     protected $certificate_security;
 | |
| 
 | |
|     /**
 | |
|      * Main HVAC_Certificate_AJAX_Handler Instance.
 | |
|      *
 | |
|      * Ensures only one instance of HVAC_Certificate_AJAX_Handler is loaded or can be loaded.
 | |
|      *
 | |
|      * @return HVAC_Certificate_AJAX_Handler - Main instance.
 | |
|      */
 | |
|     public static function instance() {
 | |
|         if (is_null(self::$_instance)) {
 | |
|             self::$_instance = new self();
 | |
|         }
 | |
|         return self::$_instance;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Constructor.
 | |
|      */
 | |
|     public function __construct() {
 | |
|         // Load dependencies
 | |
|         require_once HVAC_PLUGIN_DIR . 'includes/certificates/class-certificate-manager.php';
 | |
|         require_once HVAC_PLUGIN_DIR . 'includes/certificates/class-certificate-security.php';
 | |
|         
 | |
|         $this->certificate_manager = HVAC_Certificate_Manager::instance();
 | |
|         $this->certificate_security = HVAC_Certificate_Security::instance();
 | |
|         
 | |
|         // Initialize hooks
 | |
|         $this->init_hooks();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Initialize hooks.
 | |
|      */
 | |
|     protected function init_hooks() {
 | |
|         // Register AJAX handlers
 | |
|         add_action('wp_ajax_hvac_get_certificate_url', array($this, 'get_certificate_url'));
 | |
|         add_action('wp_ajax_hvac_email_certificate', array($this, 'email_certificate'));
 | |
|         add_action('wp_ajax_hvac_revoke_certificate', array($this, 'revoke_certificate'));
 | |
|         add_action('wp_ajax_hvac_generate_certificates', array($this, 'generate_certificates'));
 | |
|         add_action('wp_ajax_hvac_get_event_attendees', array($this, 'get_event_attendees'));
 | |
|         
 | |
|         // Enqueue scripts
 | |
|         add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Enqueue scripts and localize data.
 | |
|      */
 | |
|     public function enqueue_scripts() {
 | |
|         // Only load on certificate pages
 | |
|         if (is_page('certificate-reports') || is_page('generate-certificates')) {
 | |
|             // Enqueue UX enhancements first
 | |
|             wp_enqueue_style(
 | |
|                 'hvac-ux-enhancements-css',
 | |
|                 HVAC_PLUGIN_URL . 'assets/css/hvac-ux-enhancements.css',
 | |
|                 array(),
 | |
|                 HVAC_PLUGIN_VERSION
 | |
|             );
 | |
|             
 | |
|             wp_enqueue_script(
 | |
|                 'hvac-ux-enhancements-js',
 | |
|                 HVAC_PLUGIN_URL . 'assets/js/hvac-ux-enhancements.js',
 | |
|                 array('jquery'),
 | |
|                 HVAC_PLUGIN_VERSION,
 | |
|                 true
 | |
|             );
 | |
|             
 | |
|             // Enqueue certificate actions JS
 | |
|             wp_enqueue_script(
 | |
|                 'hvac-certificate-actions-js',
 | |
|                 HVAC_PLUGIN_URL . 'assets/js/hvac-certificate-actions.js',
 | |
|                 array('jquery', 'hvac-ux-enhancements-js'),
 | |
|                 HVAC_PLUGIN_VERSION,
 | |
|                 true
 | |
|             );
 | |
|             
 | |
|             // Localize script with AJAX data
 | |
|             wp_localize_script('hvac-certificate-actions-js', 'hvacCertificateData', array(
 | |
|                 'ajaxUrl' => admin_url('admin-ajax.php'),
 | |
|                 'viewNonce' => wp_create_nonce('hvac_view_certificate'),
 | |
|                 'emailNonce' => wp_create_nonce('hvac_email_certificate'),
 | |
|                 'revokeNonce' => wp_create_nonce('hvac_revoke_certificate'),
 | |
|                 'generateNonce' => wp_create_nonce('hvac_generate_certificates')
 | |
|             ));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * AJAX handler for getting a certificate download URL.
 | |
|      */
 | |
|     public function get_certificate_url() {
 | |
|         // Verify nonce
 | |
|         if (
 | |
|             (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_view_certificate')) && 
 | |
|             (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_certificate_actions'))
 | |
|         ) {
 | |
|             wp_send_json_error(array('message' => 'Security check failed'));
 | |
|         }
 | |
|         
 | |
|         // Get certificate by different methods
 | |
|         $certificate = null;
 | |
|         
 | |
|         // Method 1: Direct certificate ID
 | |
|         if (isset($_POST['certificate_id']) && absint($_POST['certificate_id'])) {
 | |
|             $certificate_id = absint($_POST['certificate_id']);
 | |
|             $certificate = $this->certificate_manager->get_certificate($certificate_id);
 | |
|         } 
 | |
|         // Method 2: Event ID and Attendee ID
 | |
|         elseif (isset($_POST['event_id']) && isset($_POST['attendee_id'])) {
 | |
|             $event_id = absint($_POST['event_id']);
 | |
|             $attendee_id = absint($_POST['attendee_id']);
 | |
|             $certificate = $this->certificate_manager->get_certificate_by_attendee($event_id, $attendee_id);
 | |
|         } else {
 | |
|             wp_send_json_error(array('message' => 'Missing certificate information'));
 | |
|         }
 | |
|         
 | |
|         // Check if certificate exists
 | |
|         if (!$certificate) {
 | |
|             wp_send_json_error(array('message' => 'Certificate not found'));
 | |
|         }
 | |
|         
 | |
|         // Shorthand for certificate ID
 | |
|         $certificate_id = $certificate->certificate_id;
 | |
|         
 | |
|         // Check user permissions (must be the event author or admin)
 | |
|         $event = get_post($certificate->event_id);
 | |
|         
 | |
|         if (!$event || !current_user_can('edit_post', $event->ID)) {
 | |
|             wp_send_json_error(array('message' => 'You do not have permission to view this certificate'));
 | |
|         }
 | |
|         
 | |
|         // Get attendee name
 | |
|         $attendee_name = get_post_meta($certificate->attendee_id, '_tribe_tickets_full_name', true);
 | |
|         if (empty($attendee_name)) {
 | |
|             $attendee_name = 'Attendee #' . $certificate->attendee_id;
 | |
|         }
 | |
|         
 | |
|         // Generate secure download URL
 | |
|         $certificate_data = array(
 | |
|             'file_path' => $certificate->file_path,
 | |
|             'event_name' => $event->post_title,
 | |
|             'attendee_name' => $attendee_name
 | |
|         );
 | |
|         
 | |
|         $download_url = $this->certificate_security->generate_download_token($certificate_id, $certificate_data);
 | |
|         
 | |
|         if (!$download_url) {
 | |
|             wp_send_json_error(array('message' => 'Failed to generate download URL'));
 | |
|         }
 | |
|         
 | |
|         wp_send_json_success(array('url' => $download_url));
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * AJAX handler for emailing a certificate.
 | |
|      */
 | |
|     public function email_certificate() {
 | |
|         // Verify nonce
 | |
|         if (
 | |
|             (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_email_certificate')) && 
 | |
|             (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_certificate_actions'))
 | |
|         ) {
 | |
|             wp_send_json_error(array('message' => 'Security check failed'));
 | |
|         }
 | |
|         
 | |
|         // Get certificate by different methods
 | |
|         $certificate = null;
 | |
|         
 | |
|         // Method 1: Direct certificate ID
 | |
|         if (isset($_POST['certificate_id']) && absint($_POST['certificate_id'])) {
 | |
|             $certificate_id = absint($_POST['certificate_id']);
 | |
|             $certificate = $this->certificate_manager->get_certificate($certificate_id);
 | |
|         } 
 | |
|         // Method 2: Event ID and Attendee ID
 | |
|         elseif (isset($_POST['event_id']) && isset($_POST['attendee_id'])) {
 | |
|             $event_id = absint($_POST['event_id']);
 | |
|             $attendee_id = absint($_POST['attendee_id']);
 | |
|             $certificate = $this->certificate_manager->get_certificate_by_attendee($event_id, $attendee_id);
 | |
|         } else {
 | |
|             wp_send_json_error(array('message' => 'Missing certificate information'));
 | |
|         }
 | |
|         
 | |
|         // Check if certificate exists
 | |
|         if (!$certificate) {
 | |
|             wp_send_json_error(array('message' => 'Certificate not found'));
 | |
|         }
 | |
|         
 | |
|         // Shorthand for certificate ID
 | |
|         $certificate_id = $certificate->certificate_id;
 | |
|         
 | |
|         // Check if certificate is revoked
 | |
|         if ($certificate->revoked) {
 | |
|             wp_send_json_error(array('message' => 'Cannot email a revoked certificate'));
 | |
|         }
 | |
|         
 | |
|         // Check user permissions (must be the event author or admin)
 | |
|         $event = get_post($certificate->event_id);
 | |
|         
 | |
|         if (!$event || !current_user_can('edit_post', $event->ID)) {
 | |
|             wp_send_json_error(array('message' => 'You do not have permission to email this certificate'));
 | |
|         }
 | |
|         
 | |
|         // Get attendee email
 | |
|         $attendee_email = get_post_meta($certificate->attendee_id, '_tribe_tickets_email', true);
 | |
|         
 | |
|         if (empty($attendee_email)) {
 | |
|             wp_send_json_error(array('message' => 'Attendee email not found'));
 | |
|         }
 | |
|         
 | |
|         // Get attendee name
 | |
|         $attendee_name = get_post_meta($certificate->attendee_id, '_tribe_tickets_full_name', true);
 | |
|         if (empty($attendee_name)) {
 | |
|             $attendee_name = 'Attendee';
 | |
|         }
 | |
|         
 | |
|         // Generate secure download URL (expires in 7 days)
 | |
|         $certificate_data = array(
 | |
|             'file_path' => $certificate->file_path,
 | |
|             'event_name' => $event->post_title,
 | |
|             'attendee_name' => $attendee_name
 | |
|         );
 | |
|         
 | |
|         $download_url = $this->certificate_security->generate_download_token($certificate_id, $certificate_data, 7 * DAY_IN_SECONDS);
 | |
|         
 | |
|         if (!$download_url) {
 | |
|             wp_send_json_error(array('message' => 'Failed to generate download URL'));
 | |
|         }
 | |
|         
 | |
|         // Get current user (sender) info
 | |
|         $sender_name = wp_get_current_user()->display_name;
 | |
|         
 | |
|         // Email subject
 | |
|         $subject = sprintf(
 | |
|             __('Your Certificate for %s', 'hvac-community-events'),
 | |
|             $event->post_title
 | |
|         );
 | |
|         
 | |
|         // Email body
 | |
|         $message = sprintf(
 | |
|             __("Hello %s,\n\nThank you for attending %s.\n\nYour certificate of completion is now available. Please click the link below to download your certificate:\n\n%s\n\nThis link will expire in 7 days.\n\nRegards,\n%s", 'hvac-community-events'),
 | |
|             $attendee_name,
 | |
|             $event->post_title,
 | |
|             $download_url,
 | |
|             $sender_name
 | |
|         );
 | |
|         
 | |
|         // Send email
 | |
|         $headers = array('Content-Type: text/plain; charset=UTF-8');
 | |
|         $sent = wp_mail($attendee_email, $subject, $message, $headers);
 | |
|         
 | |
|         if ($sent) {
 | |
|             // Record email sent
 | |
|             $this->certificate_manager->mark_certificate_emailed($certificate_id);
 | |
|             
 | |
|             wp_send_json_success(array('message' => 'Certificate sent successfully'));
 | |
|         } else {
 | |
|             wp_send_json_error(array('message' => 'Failed to send email'));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * AJAX handler for revoking a certificate.
 | |
|      */
 | |
|     public function revoke_certificate() {
 | |
|         // Verify nonce
 | |
|         if (
 | |
|             (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_revoke_certificate')) && 
 | |
|             (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_certificate_actions'))
 | |
|         ) {
 | |
|             wp_send_json_error(array('message' => 'Security check failed'));
 | |
|         }
 | |
|         
 | |
|         // Get reason for revocation
 | |
|         $reason = isset($_POST['reason']) ? sanitize_text_field($_POST['reason']) : '';
 | |
|         
 | |
|         // Get certificate by different methods
 | |
|         $certificate = null;
 | |
|         
 | |
|         // Method 1: Direct certificate ID
 | |
|         if (isset($_POST['certificate_id']) && absint($_POST['certificate_id'])) {
 | |
|             $certificate_id = absint($_POST['certificate_id']);
 | |
|             $certificate = $this->certificate_manager->get_certificate($certificate_id);
 | |
|         } 
 | |
|         // Method 2: Event ID and Attendee ID
 | |
|         elseif (isset($_POST['event_id']) && isset($_POST['attendee_id'])) {
 | |
|             $event_id = absint($_POST['event_id']);
 | |
|             $attendee_id = absint($_POST['attendee_id']);
 | |
|             $certificate = $this->certificate_manager->get_certificate_by_attendee($event_id, $attendee_id);
 | |
|         } else {
 | |
|             wp_send_json_error(array('message' => 'Missing certificate information'));
 | |
|         }
 | |
|         
 | |
|         // Check if certificate exists
 | |
|         if (!$certificate) {
 | |
|             wp_send_json_error(array('message' => 'Certificate not found'));
 | |
|         }
 | |
|         
 | |
|         // Shorthand for certificate ID
 | |
|         $certificate_id = $certificate->certificate_id;
 | |
|         
 | |
|         // Check if certificate is already revoked
 | |
|         if ($certificate->revoked) {
 | |
|             wp_send_json_error(array('message' => 'Certificate is already revoked'));
 | |
|         }
 | |
|         
 | |
|         // Check user permissions (must be the event author or admin)
 | |
|         $event = get_post($certificate->event_id);
 | |
|         
 | |
|         if (!$event || !current_user_can('edit_post', $event->ID)) {
 | |
|             wp_send_json_error(array('message' => 'You do not have permission to revoke this certificate'));
 | |
|         }
 | |
|         
 | |
|         // Revoke the certificate
 | |
|         $revoked = $this->certificate_manager->revoke_certificate(
 | |
|             $certificate_id,
 | |
|             get_current_user_id(),
 | |
|             $reason
 | |
|         );
 | |
|         
 | |
|         if ($revoked) {
 | |
|             // Get updated certificate for revocation date
 | |
|             $updated_certificate = $this->certificate_manager->get_certificate($certificate_id);
 | |
|             $revoked_date = date_i18n(get_option('date_format'), strtotime($updated_certificate->revoked_date));
 | |
|             
 | |
|             wp_send_json_success(array(
 | |
|                 'message' => 'Certificate revoked successfully',
 | |
|                 'revoked_date' => $revoked_date
 | |
|             ));
 | |
|         } else {
 | |
|             wp_send_json_error(array('message' => 'Failed to revoke certificate'));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * AJAX handler for getting event attendees.
 | |
|      */
 | |
|     public function get_event_attendees() {
 | |
|         // Verify nonce
 | |
|         if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_generate_certificates')) {
 | |
|             wp_send_json_error(array('message' => 'Security check failed'));
 | |
|         }
 | |
| 
 | |
|         $event_id = isset($_POST['event_id']) ? absint($_POST['event_id']) : 0;
 | |
|         
 | |
|         if (!$event_id) {
 | |
|             wp_send_json_error(array('message' => 'Event ID is required'));
 | |
|         }
 | |
| 
 | |
|         // Check user permissions
 | |
|         $event = get_post($event_id);
 | |
|         if (!$event || !current_user_can('edit_post', $event->ID)) {
 | |
|             wp_send_json_error(array('message' => 'You do not have permission to view this event'));
 | |
|         }
 | |
| 
 | |
|         // Get attendees using direct database query (same as Generate Certificates template)
 | |
|         global $wpdb;
 | |
|         $tec_attendees = $wpdb->get_results($wpdb->prepare(
 | |
|             "SELECT 
 | |
|                 p.ID as attendee_id,
 | |
|                 p.post_parent as event_id,
 | |
|                 COALESCE(tec_full_name.meta_value, tpp_full_name.meta_value, tickets_full_name.meta_value, 'Unknown Attendee') as holder_name,
 | |
|                 COALESCE(tec_email.meta_value, tpp_email.meta_value, tickets_email.meta_value, tpp_attendee_email.meta_value, 'no-email@example.com') as holder_email,
 | |
|                 COALESCE(checked_in.meta_value, '0') as check_in
 | |
|             FROM {$wpdb->posts} p
 | |
|             LEFT JOIN {$wpdb->postmeta} tec_full_name ON p.ID = tec_full_name.post_id AND tec_full_name.meta_key = '_tec_tickets_commerce_full_name'
 | |
|             LEFT JOIN {$wpdb->postmeta} tpp_full_name ON p.ID = tpp_full_name.post_id AND tpp_full_name.meta_key = '_tribe_tpp_full_name'
 | |
|             LEFT JOIN {$wpdb->postmeta} tickets_full_name ON p.ID = tickets_full_name.post_id AND tickets_full_name.meta_key = '_tribe_tickets_full_name'
 | |
|             LEFT JOIN {$wpdb->postmeta} tec_email ON p.ID = tec_email.post_id AND tec_email.meta_key = '_tec_tickets_commerce_email'
 | |
|             LEFT JOIN {$wpdb->postmeta} tpp_email ON p.ID = tpp_email.post_id AND tpp_email.meta_key = '_tribe_tpp_email'
 | |
|             LEFT JOIN {$wpdb->postmeta} tickets_email ON p.ID = tickets_email.post_id AND tickets_email.meta_key = '_tribe_tickets_email'
 | |
|             LEFT JOIN {$wpdb->postmeta} tpp_attendee_email ON p.ID = tpp_attendee_email.post_id AND tpp_attendee_email.meta_key = '_tribe_tpp_attendee_email'
 | |
|             LEFT JOIN {$wpdb->postmeta} checked_in ON p.ID = checked_in.post_id AND checked_in.meta_key = '_tribe_tickets_attendee_checked_in'
 | |
|             WHERE p.post_type IN ('tec_tc_attendee', 'tribe_tpp_attendees') 
 | |
|             AND p.post_parent = %d
 | |
|             ORDER BY p.ID ASC",
 | |
|             $event_id
 | |
|         ));
 | |
|         
 | |
|         $attendees = array();
 | |
|         foreach ($tec_attendees as $attendee) {
 | |
|             // Check if certificate already exists
 | |
|             $has_certificate = $this->certificate_manager->certificate_exists($event_id, $attendee->attendee_id);
 | |
|             
 | |
|             $attendees[] = array(
 | |
|                 'attendee_id' => $attendee->attendee_id,
 | |
|                 'event_id' => $attendee->event_id,
 | |
|                 'holder_name' => $attendee->holder_name,
 | |
|                 'holder_email' => $attendee->holder_email,
 | |
|                 'check_in' => intval($attendee->check_in),
 | |
|                 'has_certificate' => $has_certificate
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         wp_send_json_success(array(
 | |
|             'attendees' => $attendees,
 | |
|             'event_title' => $event->post_title
 | |
|         ));
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * AJAX handler for generating certificates.
 | |
|      */
 | |
|     public function generate_certificates() {
 | |
|         // Verify nonce
 | |
|         if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_generate_certificates')) {
 | |
|             wp_send_json_error(array('message' => 'Security check failed'));
 | |
|         }
 | |
| 
 | |
|         $event_id = isset($_POST['event_id']) ? absint($_POST['event_id']) : 0;
 | |
|         $attendee_ids = isset($_POST['attendee_ids']) && is_array($_POST['attendee_ids']) ? array_map('absint', $_POST['attendee_ids']) : array();
 | |
|         $checked_in_only = isset($_POST['checked_in_only']) && $_POST['checked_in_only'] === 'yes';
 | |
| 
 | |
|         if (!$event_id) {
 | |
|             wp_send_json_error(array('message' => 'Event ID is required'));
 | |
|         }
 | |
| 
 | |
|         if (empty($attendee_ids)) {
 | |
|             wp_send_json_error(array('message' => 'Please select at least one attendee'));
 | |
|         }
 | |
| 
 | |
|         // Check user permissions
 | |
|         $event = get_post($event_id);
 | |
|         if (!$event || !current_user_can('edit_post', $event->ID)) {
 | |
|             wp_send_json_error(array('message' => 'You do not have permission to generate certificates for this event'));
 | |
|         }
 | |
| 
 | |
|         // Load certificate generator
 | |
|         if (!class_exists('HVAC_Certificate_Generator')) {
 | |
|             require_once HVAC_PLUGIN_DIR . 'includes/certificates/class-certificate-generator.php';
 | |
|         }
 | |
|         $certificate_generator = HVAC_Certificate_Generator::instance();
 | |
| 
 | |
|         // Generate certificates in batch
 | |
|         $generation_results = $certificate_generator->generate_certificates_batch(
 | |
|             $event_id,
 | |
|             $attendee_ids,
 | |
|             array(), // Custom data (none for now)
 | |
|             get_current_user_id(), // Generated by current user
 | |
|             $checked_in_only // Only for checked-in attendees if selected
 | |
|         );
 | |
| 
 | |
|         // Format response message
 | |
|         $message_parts = array();
 | |
|         if ($generation_results['success'] > 0) {
 | |
|             $message_parts[] = sprintf('Successfully generated %d certificate(s).', $generation_results['success']);
 | |
|         }
 | |
|         
 | |
|         if ($generation_results['duplicate'] > 0) {
 | |
|             $message_parts[] = sprintf('%d duplicate(s) skipped.', $generation_results['duplicate']);
 | |
|         }
 | |
|         
 | |
|         if ($generation_results['not_checked_in'] > 0) {
 | |
|             $message_parts[] = sprintf('%d attendee(s) not checked in.', $generation_results['not_checked_in']);
 | |
|         }
 | |
|         
 | |
|         if ($generation_results['error'] > 0) {
 | |
|             $message_parts[] = sprintf('%d error(s).', $generation_results['error']);
 | |
|         }
 | |
| 
 | |
|         if ($generation_results['success'] > 0) {
 | |
|             // Generate preview URLs for the certificates just created
 | |
|             $preview_urls = array();
 | |
|             if (!empty($generation_results['certificate_ids'])) {
 | |
|                 foreach ($generation_results['certificate_ids'] as $certificate_id) {
 | |
|                     $certificate = $this->certificate_manager->get_certificate($certificate_id);
 | |
|                     if ($certificate && $certificate->file_path) {
 | |
|                         // Generate secure download token for preview
 | |
|                         $security = HVAC_Certificate_Security::instance();
 | |
|                         $preview_url = $security->generate_download_token($certificate_id, array(
 | |
|                             'file_path' => $certificate->file_path,
 | |
|                             'event_name' => get_the_title($certificate->event_id),
 | |
|                             'attendee_name' => $certificate->attendee_name
 | |
|                         ));
 | |
|                         if ($preview_url) {
 | |
|                             $preview_urls[] = array(
 | |
|                                 'certificate_id' => $certificate_id,
 | |
|                                 'attendee_name' => $certificate->attendee_name,
 | |
|                                 'preview_url' => $preview_url
 | |
|                             );
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             wp_send_json_success(array(
 | |
|                 'message' => implode(' ', $message_parts),
 | |
|                 'results' => $generation_results,
 | |
|                 'preview_urls' => $preview_urls
 | |
|             ));
 | |
|         } else {
 | |
|             wp_send_json_error(array(
 | |
|                 'message' => 'Failed to generate certificates. ' . implode(' ', $message_parts),
 | |
|                 'results' => $generation_results
 | |
|             ));
 | |
|         }
 | |
|     }
 | |
| } |