🚨 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
|
|
));
|
|
}
|
|
}
|
|
} |