upskill-event-manager/includes/find-trainer/class-hvac-contact-form-handler.php
bengizmo 37f4180e1c feat: Add massive missing plugin infrastructure to repository
🚨 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>
2025-08-11 13:30:11 -03:00

603 lines
No EOL
23 KiB
PHP

<?php
/**
* Contact Form Handler for Find a Trainer
*
* @package HVAC_Plugin
* @since 1.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* Class HVAC_Contact_Form_Handler
* Handles contact form submissions and validation
*/
class HVAC_Contact_Form_Handler {
/**
* Instance of this class
*
* @var HVAC_Contact_Form_Handler
*/
private static $instance = null;
/**
* Rate limit settings
*/
const RATE_LIMIT_SUBMISSIONS = 5;
const RATE_LIMIT_WINDOW = HOUR_IN_SECONDS;
/**
* Get instance of this class
*
* @return HVAC_Contact_Form_Handler
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->init_hooks();
}
/**
* Initialize hooks
*/
private function init_hooks() {
// AJAX handlers
add_action('wp_ajax_hvac_submit_contact_form', [$this, 'ajax_submit_form']);
add_action('wp_ajax_nopriv_hvac_submit_contact_form', [$this, 'ajax_submit_form']);
// Admin hooks
add_action('admin_menu', [$this, 'add_admin_menu']);
add_action('admin_init', [$this, 'register_settings']);
// Cron job for cleanup
add_action('hvac_cleanup_old_submissions', [$this, 'cleanup_old_submissions']);
if (!wp_next_scheduled('hvac_cleanup_old_submissions')) {
wp_schedule_event(time(), 'daily', 'hvac_cleanup_old_submissions');
}
}
/**
* AJAX handler for form submission
*/
public function ajax_submit_form() {
check_ajax_referer('hvac_find_trainer', 'nonce');
$form_data = [
'trainer_id' => intval($_POST['trainer_id'] ?? 0),
'trainer_profile_id' => intval($_POST['trainer_profile_id'] ?? 0),
'first_name' => sanitize_text_field($_POST['first_name'] ?? ''),
'last_name' => sanitize_text_field($_POST['last_name'] ?? ''),
'email' => sanitize_email($_POST['email'] ?? ''),
'phone' => sanitize_text_field($_POST['phone'] ?? ''),
'city' => sanitize_text_field($_POST['city'] ?? ''),
'state_province' => sanitize_text_field($_POST['state_province'] ?? ''),
'company' => sanitize_text_field($_POST['company'] ?? ''),
'message' => sanitize_textarea_field($_POST['message'] ?? '')
];
// Validate form data
$validation = $this->validate_form_data($form_data);
if (!$validation['valid']) {
wp_send_json_error([
'message' => 'Please correct the following errors:',
'errors' => $validation['errors']
]);
}
// Check rate limiting
if (!$this->check_submission_rate_limit($form_data['email'])) {
wp_send_json_error([
'message' => 'You have reached the submission limit. Please try again later.'
]);
}
// Save submission
$submission_id = $this->save_submission($form_data);
if (!$submission_id) {
wp_send_json_error([
'message' => 'An error occurred while saving your submission. Please try again.'
]);
}
// Send notifications
$this->send_notifications($submission_id);
wp_send_json_success([
'message' => 'Your message has been sent successfully! The trainer will contact you soon.',
'submission_id' => $submission_id
]);
}
/**
* Validate form data
*
* @param array $data Form data
* @return array Validation result
*/
public function validate_form_data($data) {
$errors = [];
$valid = true;
// Required fields
$required_fields = [
'trainer_id' => 'Trainer ID',
'trainer_profile_id' => 'Trainer Profile ID',
'first_name' => 'First Name',
'last_name' => 'Last Name',
'email' => 'Email'
];
foreach ($required_fields as $field => $label) {
if (empty($data[$field])) {
$errors[$field] = $label . ' is required.';
$valid = false;
}
}
// Validate email format
if (!empty($data['email']) && !is_email($data['email'])) {
$errors['email'] = 'Please enter a valid email address.';
$valid = false;
}
// Validate phone format (optional)
if (!empty($data['phone'])) {
$phone = preg_replace('/[^0-9+()-.\s]/', '', $data['phone']);
if (strlen($phone) < 10) {
$errors['phone'] = 'Please enter a valid phone number.';
$valid = false;
}
}
// Validate trainer exists
if (!empty($data['trainer_id'])) {
$trainer = get_userdata($data['trainer_id']);
if (!$trainer || !in_array('hvac_trainer', $trainer->roles) && !in_array('hvac_master_trainer', $trainer->roles)) {
$errors['trainer_id'] = 'Invalid trainer selected.';
$valid = false;
}
}
// Validate trainer profile exists
if (!empty($data['trainer_profile_id'])) {
$profile = get_post($data['trainer_profile_id']);
if (!$profile || $profile->post_type !== 'trainer_profile') {
$errors['trainer_profile_id'] = 'Invalid trainer profile.';
$valid = false;
}
}
// Message length
if (!empty($data['message']) && strlen($data['message']) > 5000) {
$errors['message'] = 'Message is too long (maximum 5000 characters).';
$valid = false;
}
return [
'valid' => $valid,
'errors' => $errors
];
}
/**
* Check submission rate limit
*
* @param string $email Email address
* @return bool True if within limits
*/
public function check_submission_rate_limit($email) {
$transient_key = 'hvac_contact_' . md5($email);
$submissions = get_transient($transient_key);
if ($submissions === false) {
$submissions = 0;
}
if ($submissions >= self::RATE_LIMIT_SUBMISSIONS) {
return false;
}
set_transient($transient_key, $submissions + 1, self::RATE_LIMIT_WINDOW);
return true;
}
/**
* Save submission to database
*
* @param array $data Form data
* @return int|false Submission ID or false on failure
*/
public function save_submission($data) {
// Include the database table class
if (!class_exists('HVAC_Contact_Submissions_Table')) {
require_once HVAC_PLUGIN_DIR . 'includes/database/class-hvac-contact-submissions-table.php';
}
return HVAC_Contact_Submissions_Table::insert_submission($data);
}
/**
* Send notifications for new submission
*
* @param int $submission_id Submission ID
*/
public function send_notifications($submission_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'hvac_contact_submissions';
$submission = $wpdb->get_row(
$wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $submission_id)
);
if (!$submission) {
return;
}
// Get trainer email
$trainer = get_userdata($submission->trainer_id);
if (!$trainer) {
return;
}
// Send email to trainer
$this->send_trainer_notification($trainer, $submission);
// Send confirmation to submitter
$this->send_submitter_confirmation($submission);
// Send admin notification if enabled
if (get_option('hvac_contact_admin_notifications', false)) {
$this->send_admin_notification($submission);
}
}
/**
* Send notification email to trainer
*
* @param WP_User $trainer Trainer user object
* @param object $submission Submission data
*/
private function send_trainer_notification($trainer, $submission) {
$subject = sprintf(
'New Contact Request from %s %s',
$submission->first_name,
$submission->last_name
);
$message = $this->get_email_template('trainer_notification', [
'trainer_name' => $trainer->display_name,
'submitter_name' => $submission->first_name . ' ' . $submission->last_name,
'submitter_email' => $submission->email,
'submitter_phone' => $submission->phone,
'submitter_city' => $submission->city,
'submitter_state' => $submission->state_province,
'submitter_company' => $submission->company,
'submitter_message' => $submission->message,
'submission_date' => $submission->submission_date,
'dashboard_url' => home_url('/trainer/dashboard/')
]);
$headers = [
'Content-Type: text/html; charset=UTF-8',
'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>',
'Reply-To: ' . $submission->first_name . ' ' . $submission->last_name . ' <' . $submission->email . '>'
];
wp_mail($trainer->user_email, $subject, $message, $headers);
}
/**
* Send confirmation email to submitter
*
* @param object $submission Submission data
*/
private function send_submitter_confirmation($submission) {
$trainer = get_userdata($submission->trainer_id);
if (!$trainer) {
return;
}
$subject = 'Your message has been sent to ' . $trainer->display_name;
$message = $this->get_email_template('submitter_confirmation', [
'submitter_name' => $submission->first_name,
'trainer_name' => $trainer->display_name,
'message_copy' => $submission->message,
'submission_date' => $submission->submission_date
]);
$headers = [
'Content-Type: text/html; charset=UTF-8',
'From: ' . get_bloginfo('name') . ' <' . get_option('admin_email') . '>'
];
wp_mail($submission->email, $subject, $message, $headers);
}
/**
* Send admin notification
*
* @param object $submission Submission data
*/
private function send_admin_notification($submission) {
$admin_email = get_option('hvac_contact_admin_email', get_option('admin_email'));
$trainer = get_userdata($submission->trainer_id);
$subject = 'New Contact Form Submission on Find a Trainer';
$message = $this->get_email_template('admin_notification', [
'trainer_name' => $trainer ? $trainer->display_name : 'Unknown',
'submitter_name' => $submission->first_name . ' ' . $submission->last_name,
'submitter_email' => $submission->email,
'submitter_phone' => $submission->phone,
'submitter_company' => $submission->company,
'submission_date' => $submission->submission_date,
'admin_url' => admin_url('admin.php?page=hvac-contact-submissions')
]);
$headers = [
'Content-Type: text/html; charset=UTF-8',
'From: ' . get_bloginfo('name') . ' <noreply@' . parse_url(home_url(), PHP_URL_HOST) . '>'
];
wp_mail($admin_email, $subject, $message, $headers);
}
/**
* Get email template
*
* @param string $template Template name
* @param array $variables Template variables
* @return string Email HTML
*/
private function get_email_template($template, $variables = []) {
$template_file = HVAC_PLUGIN_DIR . 'templates/emails/' . $template . '.php';
if (!file_exists($template_file)) {
// Use default template
return $this->get_default_email_template($template, $variables);
}
extract($variables);
ob_start();
include $template_file;
return ob_get_clean();
}
/**
* Get default email template
*
* @param string $template Template name
* @param array $vars Template variables
* @return string Email HTML
*/
private function get_default_email_template($template, $vars) {
$html = '<html><body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">';
$html .= '<div style="max-width: 600px; margin: 0 auto; padding: 20px;">';
switch ($template) {
case 'trainer_notification':
$html .= '<h2>New Contact Request</h2>';
$html .= '<p>Hello ' . esc_html($vars['trainer_name']) . ',</p>';
$html .= '<p>You have received a new contact request through the Find a Trainer directory.</p>';
$html .= '<h3>Contact Details:</h3>';
$html .= '<ul>';
$html .= '<li><strong>Name:</strong> ' . esc_html($vars['submitter_name']) . '</li>';
$html .= '<li><strong>Email:</strong> <a href="mailto:' . esc_attr($vars['submitter_email']) . '">' . esc_html($vars['submitter_email']) . '</a></li>';
if ($vars['submitter_phone']) {
$html .= '<li><strong>Phone:</strong> ' . esc_html($vars['submitter_phone']) . '</li>';
}
if ($vars['submitter_company']) {
$html .= '<li><strong>Company:</strong> ' . esc_html($vars['submitter_company']) . '</li>';
}
if ($vars['submitter_city'] || $vars['submitter_state']) {
$html .= '<li><strong>Location:</strong> ' . esc_html($vars['submitter_city']) . ', ' . esc_html($vars['submitter_state']) . '</li>';
}
$html .= '</ul>';
if ($vars['submitter_message']) {
$html .= '<h3>Message:</h3>';
$html .= '<p style="background: #f5f5f5; padding: 15px; border-radius: 5px;">' . nl2br(esc_html($vars['submitter_message'])) . '</p>';
}
$html .= '<p><a href="' . esc_url($vars['dashboard_url']) . '" style="display: inline-block; padding: 10px 20px; background: #0073aa; color: white; text-decoration: none; border-radius: 5px;">View in Dashboard</a></p>';
break;
case 'submitter_confirmation':
$html .= '<h2>Message Sent Successfully</h2>';
$html .= '<p>Hello ' . esc_html($vars['submitter_name']) . ',</p>';
$html .= '<p>Your message has been successfully sent to ' . esc_html($vars['trainer_name']) . '. They will contact you soon.</p>';
if ($vars['message_copy']) {
$html .= '<h3>Your Message:</h3>';
$html .= '<p style="background: #f5f5f5; padding: 15px; border-radius: 5px;">' . nl2br(esc_html($vars['message_copy'])) . '</p>';
}
$html .= '<p>Thank you for using our Find a Trainer directory!</p>';
break;
case 'admin_notification':
$html .= '<h2>New Contact Form Submission</h2>';
$html .= '<p>A new contact form has been submitted on the Find a Trainer page.</p>';
$html .= '<h3>Details:</h3>';
$html .= '<ul>';
$html .= '<li><strong>Trainer:</strong> ' . esc_html($vars['trainer_name']) . '</li>';
$html .= '<li><strong>From:</strong> ' . esc_html($vars['submitter_name']) . '</li>';
$html .= '<li><strong>Email:</strong> ' . esc_html($vars['submitter_email']) . '</li>';
if ($vars['submitter_phone']) {
$html .= '<li><strong>Phone:</strong> ' . esc_html($vars['submitter_phone']) . '</li>';
}
if ($vars['submitter_company']) {
$html .= '<li><strong>Company:</strong> ' . esc_html($vars['submitter_company']) . '</li>';
}
$html .= '<li><strong>Date:</strong> ' . esc_html($vars['submission_date']) . '</li>';
$html .= '</ul>';
$html .= '<p><a href="' . esc_url($vars['admin_url']) . '" style="display: inline-block; padding: 10px 20px; background: #0073aa; color: white; text-decoration: none; border-radius: 5px;">View All Submissions</a></p>';
break;
}
$html .= '<hr style="margin-top: 30px; border: none; border-top: 1px solid #ddd;">';
$html .= '<p style="font-size: 12px; color: #666;">This is an automated message from ' . get_bloginfo('name') . '</p>';
$html .= '</div></body></html>';
return $html;
}
/**
* Add admin menu for contact submissions
*/
public function add_admin_menu() {
add_submenu_page(
'hvac-plugin',
'Contact Submissions',
'Contact Submissions',
'manage_options',
'hvac-contact-submissions',
[$this, 'render_admin_page']
);
}
/**
* Register plugin settings
*/
public function register_settings() {
register_setting('hvac_contact_settings', 'hvac_contact_admin_notifications');
register_setting('hvac_contact_settings', 'hvac_contact_admin_email');
register_setting('hvac_contact_settings', 'hvac_contact_retention_days');
}
/**
* Render admin page for contact submissions
*/
public function render_admin_page() {
if (!class_exists('HVAC_Contact_Submissions_Table')) {
require_once HVAC_PLUGIN_DIR . 'includes/database/class-hvac-contact-submissions-table.php';
}
// Handle status updates
if (isset($_POST['update_status']) && isset($_POST['submission_id'])) {
check_admin_referer('hvac_update_submission_status');
$submission_id = intval($_POST['submission_id']);
$new_status = sanitize_text_field($_POST['new_status']);
HVAC_Contact_Submissions_Table::update_status($submission_id, $new_status);
echo '<div class="notice notice-success"><p>Status updated successfully!</p></div>';
}
// Get submissions
$args = [
'limit' => 50,
'offset' => (get_query_var('paged', 1) - 1) * 50
];
if (isset($_GET['status'])) {
$args['status'] = sanitize_text_field($_GET['status']);
}
$submissions = HVAC_Contact_Submissions_Table::get_submissions($args);
?>
<div class="wrap">
<h1>Contact Submissions</h1>
<div class="tablenav top">
<div class="alignleft actions">
<select name="status_filter" id="status_filter">
<option value="">All Statuses</option>
<option value="new" <?php selected(isset($_GET['status']) && $_GET['status'] === 'new'); ?>>New</option>
<option value="read" <?php selected(isset($_GET['status']) && $_GET['status'] === 'read'); ?>>Read</option>
<option value="replied" <?php selected(isset($_GET['status']) && $_GET['status'] === 'replied'); ?>>Replied</option>
<option value="archived" <?php selected(isset($_GET['status']) && $_GET['status'] === 'archived'); ?>>Archived</option>
</select>
<button class="button" onclick="window.location.href='?page=hvac-contact-submissions&status=' + document.getElementById('status_filter').value">Filter</button>
</div>
</div>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>ID</th>
<th>Date</th>
<th>From</th>
<th>Email</th>
<th>Trainer</th>
<th>Message</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if ($submissions) : ?>
<?php foreach ($submissions as $submission) : ?>
<?php
$trainer = get_userdata($submission->trainer_id);
?>
<tr>
<td><?php echo esc_html($submission->id); ?></td>
<td><?php echo esc_html(date('Y-m-d H:i', strtotime($submission->submission_date))); ?></td>
<td><?php echo esc_html($submission->first_name . ' ' . $submission->last_name); ?></td>
<td><a href="mailto:<?php echo esc_attr($submission->email); ?>"><?php echo esc_html($submission->email); ?></a></td>
<td><?php echo $trainer ? esc_html($trainer->display_name) : 'Unknown'; ?></td>
<td><?php echo esc_html(substr($submission->message, 0, 100)) . (strlen($submission->message) > 100 ? '...' : ''); ?></td>
<td>
<span class="status-badge status-<?php echo esc_attr($submission->status); ?>">
<?php echo esc_html(ucfirst($submission->status)); ?>
</span>
</td>
<td>
<button class="button button-small view-details" data-id="<?php echo esc_attr($submission->id); ?>">View</button>
</td>
</tr>
<?php endforeach; ?>
<?php else : ?>
<tr>
<td colspan="8">No submissions found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<style>
.status-badge {
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
}
.status-new { background: #ffc107; color: #000; }
.status-read { background: #17a2b8; color: #fff; }
.status-replied { background: #28a745; color: #fff; }
.status-archived { background: #6c757d; color: #fff; }
</style>
<?php
}
/**
* Clean up old submissions
*/
public function cleanup_old_submissions() {
if (!class_exists('HVAC_Contact_Submissions_Table')) {
require_once HVAC_PLUGIN_DIR . 'includes/database/class-hvac-contact-submissions-table.php';
}
$retention_days = get_option('hvac_contact_retention_days', 90);
$deleted = HVAC_Contact_Submissions_Table::clean_old_submissions($retention_days);
if ($deleted > 0) {
error_log("HVAC Contact Form: Cleaned up $deleted old submissions");
}
}
}