feat: implement Phase 1A native WordPress event form system
✅ HVAC_Event_Form_Builder Implementation: - Native WordPress event form builder extending HVAC_Form_Builder - Complete datetime field types (start/end dates, timezone, all-day) - Comprehensive venue field group (name, address, capacity, coordinates) - Organizer field group (name, email, phone, website) with validation - HVAC-specific fields (trainer requirements, certifications, equipment) - Featured image upload support with security validation - WordPress-native security integration (nonces, sanitization) - Comprehensive form validation and error handling 🏗️ Architecture Improvements: - Extract HVAC_Singleton_Trait to standalone file for better organization - Add proper file loading order in HVAC_Plugin class - Include core security framework and form builder dependencies 🧪 Testing Infrastructure: - Native event test template for Phase 1A validation - Staging deployment and testing completed successfully - All form fields render and validate correctly 🎯 Strategic Progress: - Phase 1A complete: Native form foundation established - Eliminates dependency on problematic TEC Community Events forms - Provides foundation for Phase 1B tribe_events post creation 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5ab180b5d0
commit
60e7ae33af
5 changed files with 985 additions and 18 deletions
680
includes/class-hvac-event-form-builder.php
Normal file
680
includes/class-hvac-event-form-builder.php
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* HVAC Event Form Builder
|
||||
*
|
||||
* Extended form builder for native WordPress event management
|
||||
* Replaces TEC Community Events forms with comprehensive field control
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @subpackage Includes
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class HVAC_Event_Form_Builder
|
||||
*
|
||||
* Extends HVAC_Form_Builder with event-specific field types and functionality
|
||||
*/
|
||||
class HVAC_Event_Form_Builder extends HVAC_Form_Builder {
|
||||
|
||||
/**
|
||||
* Event-specific field defaults
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private array $event_field_defaults = [
|
||||
'datetime-local' => [
|
||||
'type' => 'datetime-local',
|
||||
'class' => 'hvac-datetime-field',
|
||||
'validate' => ['datetime'],
|
||||
'sanitize' => 'datetime',
|
||||
],
|
||||
'venue-group' => [
|
||||
'type' => 'venue-group',
|
||||
'class' => 'hvac-venue-group',
|
||||
'wrapper_class' => 'form-group venue-group',
|
||||
],
|
||||
'organizer-group' => [
|
||||
'type' => 'organizer-group',
|
||||
'class' => 'hvac-organizer-group',
|
||||
'wrapper_class' => 'form-group organizer-group',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* WordPress timezone list for event timezone selection
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
private ?array $timezone_list = null;
|
||||
|
||||
/**
|
||||
* Constructor with promoted property.
|
||||
*
|
||||
* @param string $nonce_action Nonce action for the form
|
||||
*/
|
||||
public function __construct(string $nonce_action = 'hvac_event_form') {
|
||||
parent::__construct($nonce_action);
|
||||
$this->init_event_specific_features();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize event-specific features
|
||||
*/
|
||||
private function init_event_specific_features(): void {
|
||||
// Set form enctype for file uploads (featured images)
|
||||
$this->set_attributes(['enctype' => 'multipart/form-data']);
|
||||
|
||||
// Load timezone list
|
||||
$this->timezone_list = $this->get_wordpress_timezones();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event datetime field
|
||||
*
|
||||
* @param array $field_config Field configuration
|
||||
* @return self
|
||||
*/
|
||||
public function add_datetime_field(array $field_config): self {
|
||||
$defaults = array_merge($this->event_field_defaults['datetime-local'], [
|
||||
'required' => true,
|
||||
'description' => 'Select date and time for the event',
|
||||
]);
|
||||
|
||||
$field = wp_parse_args($field_config, $defaults);
|
||||
|
||||
// Ensure proper ID and name
|
||||
if (empty($field['id']) && !empty($field['name'])) {
|
||||
$field['id'] = sanitize_html_class($field['name']);
|
||||
}
|
||||
|
||||
return $this->add_field($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event timezone selection field
|
||||
*
|
||||
* @param array $field_config Field configuration
|
||||
* @return self
|
||||
*/
|
||||
public function add_timezone_field(array $field_config = []): self {
|
||||
$defaults = [
|
||||
'type' => 'select',
|
||||
'name' => 'event_timezone',
|
||||
'label' => 'Event Timezone',
|
||||
'options' => $this->timezone_list ?? [],
|
||||
'value' => wp_timezone_string(),
|
||||
'required' => true,
|
||||
'class' => 'hvac-timezone-field',
|
||||
'description' => 'Select the timezone for this event',
|
||||
];
|
||||
|
||||
$field = wp_parse_args($field_config, $defaults);
|
||||
return $this->add_field($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all-day event toggle
|
||||
*
|
||||
* @param array $field_config Field configuration
|
||||
* @return self
|
||||
*/
|
||||
public function add_all_day_field(array $field_config = []): self {
|
||||
$defaults = [
|
||||
'type' => 'checkbox',
|
||||
'name' => 'event_all_day',
|
||||
'label' => 'All Day Event',
|
||||
'value' => '1',
|
||||
'class' => 'hvac-all-day-field',
|
||||
'description' => 'Check if this is an all-day event',
|
||||
];
|
||||
|
||||
$field = wp_parse_args($field_config, $defaults);
|
||||
return $this->add_field($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add venue field group
|
||||
*
|
||||
* @param array $field_config Field configuration
|
||||
* @return self
|
||||
*/
|
||||
public function add_venue_group(array $field_config = []): self {
|
||||
$venue_fields = [
|
||||
[
|
||||
'type' => 'text',
|
||||
'name' => 'venue_name',
|
||||
'label' => 'Venue Name',
|
||||
'required' => true,
|
||||
'class' => 'hvac-venue-name',
|
||||
'placeholder' => 'Enter venue name',
|
||||
],
|
||||
[
|
||||
'type' => 'textarea',
|
||||
'name' => 'venue_address',
|
||||
'label' => 'Address',
|
||||
'required' => false,
|
||||
'class' => 'hvac-venue-address',
|
||||
'placeholder' => 'Street address',
|
||||
'validate' => ['max_length' => 500],
|
||||
],
|
||||
[
|
||||
'type' => 'text',
|
||||
'name' => 'venue_city',
|
||||
'label' => 'City',
|
||||
'required' => false,
|
||||
'class' => 'hvac-venue-city',
|
||||
],
|
||||
[
|
||||
'type' => 'text',
|
||||
'name' => 'venue_state',
|
||||
'label' => 'State/Province',
|
||||
'required' => false,
|
||||
'class' => 'hvac-venue-state',
|
||||
],
|
||||
[
|
||||
'type' => 'text',
|
||||
'name' => 'venue_zip',
|
||||
'label' => 'Postal Code',
|
||||
'required' => false,
|
||||
'class' => 'hvac-venue-zip',
|
||||
'validate' => ['pattern' => '/^[A-Za-z0-9\s\-]{3,10}$/'],
|
||||
],
|
||||
[
|
||||
'type' => 'number',
|
||||
'name' => 'venue_capacity',
|
||||
'label' => 'Venue Capacity',
|
||||
'required' => false,
|
||||
'class' => 'hvac-venue-capacity',
|
||||
'validate' => ['min' => 1, 'max' => 10000],
|
||||
'description' => 'Maximum number of attendees (optional)',
|
||||
],
|
||||
[
|
||||
'type' => 'hidden',
|
||||
'name' => 'venue_latitude',
|
||||
'class' => 'hvac-venue-lat',
|
||||
],
|
||||
[
|
||||
'type' => 'hidden',
|
||||
'name' => 'venue_longitude',
|
||||
'class' => 'hvac-venue-lng',
|
||||
],
|
||||
];
|
||||
|
||||
// Add all venue fields
|
||||
foreach ($venue_fields as $field) {
|
||||
$this->add_field($field);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add organizer field group
|
||||
*
|
||||
* @param array $field_config Field configuration
|
||||
* @return self
|
||||
*/
|
||||
public function add_organizer_group(array $field_config = []): self {
|
||||
$organizer_fields = [
|
||||
[
|
||||
'type' => 'text',
|
||||
'name' => 'organizer_name',
|
||||
'label' => 'Organizer Name',
|
||||
'required' => true,
|
||||
'class' => 'hvac-organizer-name',
|
||||
'placeholder' => 'Event organizer name',
|
||||
],
|
||||
[
|
||||
'type' => 'email',
|
||||
'name' => 'organizer_email',
|
||||
'label' => 'Organizer Email',
|
||||
'required' => false,
|
||||
'class' => 'hvac-organizer-email',
|
||||
'validate' => ['email'],
|
||||
'sanitize' => 'email',
|
||||
],
|
||||
[
|
||||
'type' => 'tel',
|
||||
'name' => 'organizer_phone',
|
||||
'label' => 'Organizer Phone',
|
||||
'required' => false,
|
||||
'class' => 'hvac-organizer-phone',
|
||||
'validate' => ['pattern' => '/^[\+]?[1-9][\d]{0,15}$/'],
|
||||
],
|
||||
[
|
||||
'type' => 'url',
|
||||
'name' => 'organizer_website',
|
||||
'label' => 'Organizer Website',
|
||||
'required' => false,
|
||||
'class' => 'hvac-organizer-website',
|
||||
'validate' => ['url'],
|
||||
'sanitize' => 'url',
|
||||
'placeholder' => 'https://example.com',
|
||||
],
|
||||
];
|
||||
|
||||
// Add all organizer fields
|
||||
foreach ($organizer_fields as $field) {
|
||||
$this->add_field($field);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add HVAC-specific event fields
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function add_hvac_fields(): self {
|
||||
$hvac_fields = [
|
||||
[
|
||||
'type' => 'select',
|
||||
'name' => 'trainer_requirements',
|
||||
'label' => 'Trainer Requirements',
|
||||
'options' => $this->get_trainer_requirement_options(),
|
||||
'required' => false,
|
||||
'class' => 'hvac-trainer-requirements',
|
||||
'description' => 'What type of trainer is required for this event?',
|
||||
],
|
||||
[
|
||||
'type' => 'checkbox',
|
||||
'name' => 'certification_levels',
|
||||
'label' => 'Certification Levels',
|
||||
'options' => $this->get_certification_level_options(),
|
||||
'required' => false,
|
||||
'class' => 'hvac-certification-levels',
|
||||
'description' => 'Which certification levels does this event support?',
|
||||
],
|
||||
[
|
||||
'type' => 'textarea',
|
||||
'name' => 'equipment_needed',
|
||||
'label' => 'Equipment Needed',
|
||||
'required' => false,
|
||||
'class' => 'hvac-equipment-needed',
|
||||
'placeholder' => 'List any specific equipment attendees should bring',
|
||||
'validate' => ['max_length' => 1000],
|
||||
],
|
||||
[
|
||||
'type' => 'textarea',
|
||||
'name' => 'prerequisites',
|
||||
'label' => 'Prerequisites',
|
||||
'required' => false,
|
||||
'class' => 'hvac-prerequisites',
|
||||
'placeholder' => 'Any required knowledge or certifications for attendees',
|
||||
'validate' => ['max_length' => 1000],
|
||||
],
|
||||
];
|
||||
|
||||
// Add all HVAC-specific fields
|
||||
foreach ($hvac_fields as $field) {
|
||||
$this->add_field($field);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add featured image upload field
|
||||
*
|
||||
* @param array $field_config Field configuration
|
||||
* @return self
|
||||
*/
|
||||
public function add_featured_image_field(array $field_config = []): self {
|
||||
$defaults = [
|
||||
'type' => 'file',
|
||||
'name' => 'event_featured_image',
|
||||
'label' => 'Featured Image',
|
||||
'required' => false,
|
||||
'class' => 'hvac-featured-image',
|
||||
'description' => 'Upload a featured image for this event (JPG, PNG, WebP)',
|
||||
'validate' => ['file_type' => ['jpg', 'jpeg', 'png', 'webp']],
|
||||
];
|
||||
|
||||
$field = wp_parse_args($field_config, $defaults);
|
||||
return $this->add_field($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced validation for event-specific fields
|
||||
*
|
||||
* @param array $data Form data to validate
|
||||
* @return array Validation errors
|
||||
*/
|
||||
public function validate(array $data): array {
|
||||
$errors = parent::validate($data);
|
||||
|
||||
// Event datetime validation
|
||||
$errors = array_merge($errors, $this->validate_event_datetime($data));
|
||||
|
||||
// Venue validation
|
||||
$errors = array_merge($errors, $this->validate_venue_data($data));
|
||||
|
||||
// HVAC-specific validation
|
||||
$errors = array_merge($errors, $this->validate_hvac_fields($data));
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate event datetime fields
|
||||
*
|
||||
* @param array $data Form data
|
||||
* @return array Validation errors
|
||||
*/
|
||||
private function validate_event_datetime(array $data): array {
|
||||
$errors = [];
|
||||
|
||||
if (isset($data['event_start_date']) && isset($data['event_end_date'])) {
|
||||
$start_time = strtotime($data['event_start_date']);
|
||||
$end_time = strtotime($data['event_end_date']);
|
||||
|
||||
if ($start_time === false) {
|
||||
$errors['event_start_date'] = 'Invalid start date format.';
|
||||
} elseif ($start_time < time()) {
|
||||
$errors['event_start_date'] = 'Start date cannot be in the past.';
|
||||
}
|
||||
|
||||
if ($end_time === false) {
|
||||
$errors['event_end_date'] = 'Invalid end date format.';
|
||||
} elseif ($start_time !== false && $end_time !== false && $end_time <= $start_time) {
|
||||
$errors['event_end_date'] = 'End date must be after start date.';
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate venue data
|
||||
*
|
||||
* @param array $data Form data
|
||||
* @return array Validation errors
|
||||
*/
|
||||
private function validate_venue_data(array $data): array {
|
||||
$errors = [];
|
||||
|
||||
// Venue capacity validation
|
||||
if (!empty($data['venue_capacity'])) {
|
||||
$capacity = intval($data['venue_capacity']);
|
||||
if ($capacity < 1) {
|
||||
$errors['venue_capacity'] = 'Venue capacity must be at least 1.';
|
||||
} elseif ($capacity > 10000) {
|
||||
$errors['venue_capacity'] = 'Venue capacity cannot exceed 10,000.';
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate HVAC-specific fields
|
||||
*
|
||||
* @param array $data Form data
|
||||
* @return array Validation errors
|
||||
*/
|
||||
private function validate_hvac_fields(array $data): array {
|
||||
$errors = [];
|
||||
|
||||
// Validate trainer requirements against available options
|
||||
if (!empty($data['trainer_requirements'])) {
|
||||
$valid_requirements = array_keys($this->get_trainer_requirement_options());
|
||||
if (!in_array($data['trainer_requirements'], $valid_requirements)) {
|
||||
$errors['trainer_requirements'] = 'Invalid trainer requirement selected.';
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced sanitization for event fields
|
||||
*
|
||||
* @param array $data Raw form data
|
||||
* @return array Sanitized data
|
||||
*/
|
||||
public function sanitize(array $data): array {
|
||||
$sanitized = parent::sanitize($data);
|
||||
|
||||
// Event-specific sanitization
|
||||
$event_fields = [
|
||||
'event_start_date' => 'datetime',
|
||||
'event_end_date' => 'datetime',
|
||||
'event_timezone' => 'text',
|
||||
'venue_name' => 'text',
|
||||
'venue_address' => 'textarea',
|
||||
'venue_city' => 'text',
|
||||
'venue_state' => 'text',
|
||||
'venue_zip' => 'text',
|
||||
'venue_capacity' => 'int',
|
||||
'organizer_name' => 'text',
|
||||
'organizer_email' => 'email',
|
||||
'organizer_phone' => 'text',
|
||||
'organizer_website' => 'url',
|
||||
'trainer_requirements' => 'text',
|
||||
'equipment_needed' => 'textarea',
|
||||
'prerequisites' => 'textarea',
|
||||
];
|
||||
|
||||
foreach ($event_fields as $field => $sanitize_type) {
|
||||
if (isset($data[$field])) {
|
||||
switch ($sanitize_type) {
|
||||
case 'datetime':
|
||||
$sanitized[$field] = $this->sanitize_datetime($data[$field]);
|
||||
break;
|
||||
case 'int':
|
||||
$sanitized[$field] = absint($data[$field]);
|
||||
break;
|
||||
case 'email':
|
||||
$sanitized[$field] = sanitize_email($data[$field]);
|
||||
break;
|
||||
case 'url':
|
||||
$sanitized[$field] = esc_url_raw($data[$field]);
|
||||
break;
|
||||
case 'textarea':
|
||||
$sanitized[$field] = sanitize_textarea_field($data[$field]);
|
||||
break;
|
||||
default:
|
||||
$sanitized[$field] = sanitize_text_field($data[$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize datetime input
|
||||
*
|
||||
* @param string $datetime Raw datetime input
|
||||
* @return string Sanitized datetime
|
||||
*/
|
||||
private function sanitize_datetime(string $datetime): string {
|
||||
$timestamp = strtotime($datetime);
|
||||
return $timestamp !== false ? date('Y-m-d\TH:i', $timestamp) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get WordPress timezone options
|
||||
*
|
||||
* @return array Timezone options
|
||||
*/
|
||||
private function get_wordpress_timezones(): array {
|
||||
$zones = wp_timezone_choice('UTC');
|
||||
$timezone_options = [];
|
||||
|
||||
// Parse the HTML select options into key-value pairs
|
||||
if (preg_match_all('/<option value="([^"]*)"[^>]*>([^<]*)<\/option>/', $zones, $matches)) {
|
||||
foreach ($matches[1] as $index => $value) {
|
||||
$timezone_options[$value] = $matches[2][$index];
|
||||
}
|
||||
}
|
||||
|
||||
return $timezone_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trainer requirement options
|
||||
*
|
||||
* @return array Trainer requirement options
|
||||
*/
|
||||
private function get_trainer_requirement_options(): array {
|
||||
return [
|
||||
'' => 'No specific requirement',
|
||||
'certified_trainer' => 'Certified HVAC Trainer',
|
||||
'master_trainer' => 'Master Trainer',
|
||||
'industry_expert' => 'Industry Expert',
|
||||
'manufacturer_rep' => 'Manufacturer Representative',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get certification level options
|
||||
*
|
||||
* @return array Certification level options
|
||||
*/
|
||||
private function get_certification_level_options(): array {
|
||||
return [
|
||||
'basic' => 'Basic HVAC',
|
||||
'intermediate' => 'Intermediate HVAC',
|
||||
'advanced' => 'Advanced HVAC',
|
||||
'commercial' => 'Commercial Systems',
|
||||
'residential' => 'Residential Systems',
|
||||
'refrigeration' => 'Refrigeration',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create complete event creation form
|
||||
*
|
||||
* @return self Configured form builder
|
||||
*/
|
||||
public function create_event_form(): self {
|
||||
// Basic event information
|
||||
$this->add_field([
|
||||
'type' => 'text',
|
||||
'name' => 'event_title',
|
||||
'label' => 'Event Title',
|
||||
'required' => true,
|
||||
'class' => 'hvac-event-title',
|
||||
'placeholder' => 'Enter event title',
|
||||
]);
|
||||
|
||||
$this->add_field([
|
||||
'type' => 'textarea',
|
||||
'name' => 'event_description',
|
||||
'label' => 'Event Description',
|
||||
'required' => true,
|
||||
'class' => 'hvac-event-description',
|
||||
'placeholder' => 'Describe the event in detail',
|
||||
'validate' => ['min_length' => 50],
|
||||
]);
|
||||
|
||||
$this->add_field([
|
||||
'type' => 'textarea',
|
||||
'name' => 'event_excerpt',
|
||||
'label' => 'Event Summary',
|
||||
'required' => false,
|
||||
'class' => 'hvac-event-excerpt',
|
||||
'placeholder' => 'Brief summary for event listings',
|
||||
'validate' => ['max_length' => 300],
|
||||
]);
|
||||
|
||||
// DateTime fields
|
||||
$this->add_datetime_field([
|
||||
'name' => 'event_start_date',
|
||||
'label' => 'Start Date & Time',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->add_datetime_field([
|
||||
'name' => 'event_end_date',
|
||||
'label' => 'End Date & Time',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
$this->add_all_day_field();
|
||||
$this->add_timezone_field();
|
||||
|
||||
// Event URL
|
||||
$this->add_field([
|
||||
'type' => 'url',
|
||||
'name' => 'event_url',
|
||||
'label' => 'Event Website',
|
||||
'required' => false,
|
||||
'class' => 'hvac-event-url',
|
||||
'placeholder' => 'https://example.com/event-info',
|
||||
'validate' => ['url'],
|
||||
]);
|
||||
|
||||
// Venue and organizer information
|
||||
$this->add_venue_group();
|
||||
$this->add_organizer_group();
|
||||
|
||||
// HVAC-specific fields
|
||||
$this->add_hvac_fields();
|
||||
|
||||
// Featured image
|
||||
$this->add_featured_image_field();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create event editing form with pre-populated data
|
||||
*
|
||||
* @param int $event_id Event post ID
|
||||
* @return self Configured form builder
|
||||
*/
|
||||
public function create_edit_form(int $event_id): self {
|
||||
// Create the form structure
|
||||
$this->create_event_form();
|
||||
|
||||
// Pre-populate with event data
|
||||
$event_data = $this->get_event_data($event_id);
|
||||
if (!empty($event_data)) {
|
||||
$this->set_data($event_data);
|
||||
}
|
||||
|
||||
// Add event ID as hidden field
|
||||
$this->add_field([
|
||||
'type' => 'hidden',
|
||||
'name' => 'event_id',
|
||||
'value' => (string)$event_id,
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event data for form pre-population
|
||||
*
|
||||
* @param int $event_id Event post ID
|
||||
* @return array Event data
|
||||
*/
|
||||
private function get_event_data(int $event_id): array {
|
||||
$post = get_post($event_id);
|
||||
if (!$post || $post->post_type !== 'tribe_events') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'event_title' => $post->post_title,
|
||||
'event_description' => $post->post_content,
|
||||
'event_excerpt' => $post->post_excerpt,
|
||||
'event_start_date' => get_post_meta($event_id, '_EventStartDate', true),
|
||||
'event_end_date' => get_post_meta($event_id, '_EventEndDate', true),
|
||||
'event_timezone' => get_post_meta($event_id, '_EventTimezone', true),
|
||||
'event_url' => get_post_meta($event_id, '_EventURL', true),
|
||||
// Add more meta field mappings as needed
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1038,21 +1038,5 @@ final class HVAC_Event_Manager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton trait for memory efficiency
|
||||
*/
|
||||
trait HVAC_Singleton_Trait {
|
||||
private static ?self $instance = null;
|
||||
|
||||
public static function instance(): self {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __clone() {}
|
||||
public function __wakeup() {
|
||||
throw new Exception("Cannot unserialize singleton");
|
||||
}
|
||||
}
|
||||
// HVAC_Singleton_Trait moved to standalone file: trait-hvac-singleton.php
|
||||
// This provides better code organization and prevents conflicts
|
||||
|
|
@ -193,6 +193,18 @@ final class HVAC_Plugin {
|
|||
// TEC Integration - Load early for proper routing
|
||||
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-tec-integration.php';
|
||||
|
||||
// Load singleton trait first (required by multiple classes)
|
||||
require_once HVAC_PLUGIN_DIR . 'includes/trait-hvac-singleton.php';
|
||||
|
||||
// Core security framework
|
||||
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-security.php';
|
||||
|
||||
// Form building framework
|
||||
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-form-builder.php';
|
||||
|
||||
// Native event form builder (Phase 1A - Native WordPress Events)
|
||||
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-event-form-builder.php';
|
||||
|
||||
// Unified Event Management System (replaces 8+ fragmented implementations)
|
||||
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-event-manager.php';
|
||||
|
||||
|
|
|
|||
59
includes/trait-hvac-singleton.php
Normal file
59
includes/trait-hvac-singleton.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* HVAC Singleton Trait
|
||||
*
|
||||
* Provides singleton pattern implementation for HVAC classes
|
||||
* Ensures memory efficiency and single instance management
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @subpackage Includes
|
||||
* @since 3.0.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trait HVAC_Singleton_Trait
|
||||
*
|
||||
* Memory-efficient singleton implementation for HVAC classes
|
||||
*/
|
||||
trait HVAC_Singleton_Trait {
|
||||
|
||||
/**
|
||||
* Single instance of the class
|
||||
*
|
||||
* @var static|null
|
||||
*/
|
||||
private static ?self $instance = null;
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function instance(): self {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent cloning
|
||||
*/
|
||||
private function __clone() {}
|
||||
|
||||
/**
|
||||
* Prevent unserialization
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __wakeup() {
|
||||
throw new Exception("Cannot unserialize singleton");
|
||||
}
|
||||
}
|
||||
232
templates/page-native-event-test.php
Normal file
232
templates/page-native-event-test.php
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
/**
|
||||
* Template Name: Native Event Test
|
||||
* Description: Test template for native WordPress event form functionality
|
||||
*/
|
||||
|
||||
// Define constant to indicate we are in a page template
|
||||
define('HVAC_IN_PAGE_TEMPLATE', true);
|
||||
|
||||
get_header();
|
||||
?>
|
||||
|
||||
<style>
|
||||
.hvac-native-event-test {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.hvac-native-event-test h1 {
|
||||
color: #1a1a1a;
|
||||
font-size: 28px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hvac-form-notice {
|
||||
background: #f0f7ff;
|
||||
border: 1px solid #0073aa;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hvac-form-notice p {
|
||||
margin: 0;
|
||||
color: #0073aa;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group textarea,
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
height: 100px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.venue-group,
|
||||
.organizer-group {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.venue-group h3,
|
||||
.organizer-group h3 {
|
||||
margin-top: 0;
|
||||
color: #0073aa;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #d00;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #d00;
|
||||
font-size: 14px;
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.button {
|
||||
background: #0073aa;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #005177;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
background: #d4edda;
|
||||
border: 1px solid #c3e6cb;
|
||||
color: #155724;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="hvac-native-event-test">
|
||||
<?php
|
||||
// Display trainer navigation menu
|
||||
if (class_exists('HVAC_Menu_System')) {
|
||||
HVAC_Menu_System::instance()->render_trainer_menu();
|
||||
}
|
||||
?>
|
||||
|
||||
<h1>Native Event Form Builder Test</h1>
|
||||
|
||||
<div class="hvac-form-notice">
|
||||
<p><strong>Phase 1A Testing:</strong> This page tests the new HVAC_Event_Form_Builder with datetime, venue, organizer, and HVAC-specific fields.</p>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$form_submitted = false;
|
||||
$form_errors = [];
|
||||
$success_message = '';
|
||||
|
||||
// Handle form submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['hvac_event_form_nonce'])) {
|
||||
|
||||
// Verify nonce for security
|
||||
if (wp_verify_nonce($_POST['hvac_event_form_nonce'], 'hvac_event_form')) {
|
||||
|
||||
try {
|
||||
// Create event form builder instance
|
||||
$event_form = new HVAC_Event_Form_Builder('hvac_event_form');
|
||||
$event_form->create_event_form();
|
||||
|
||||
// Validate submitted data
|
||||
$form_errors = $event_form->validate($_POST);
|
||||
|
||||
if (empty($form_errors)) {
|
||||
// Sanitize data
|
||||
$sanitized_data = $event_form->sanitize($_POST);
|
||||
|
||||
// Process the form (this would normally create the event)
|
||||
$success_message = 'Event form validation successful! Data has been sanitized and is ready for processing.';
|
||||
$form_submitted = true;
|
||||
|
||||
// In Phase 1B, this is where we would create the tribe_events post
|
||||
|
||||
} else {
|
||||
$event_form->set_errors($form_errors);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$form_errors['general'] = 'Form processing error: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
} else {
|
||||
$form_errors['nonce'] = 'Security check failed. Please refresh the page and try again.';
|
||||
}
|
||||
}
|
||||
|
||||
// Display success message
|
||||
if ($success_message) {
|
||||
echo '<div class="success-message">' . esc_html($success_message) . '</div>';
|
||||
}
|
||||
|
||||
// Display general errors
|
||||
if (isset($form_errors['general'])) {
|
||||
echo '<div class="error" style="background: #f8d7da; border: 1px solid #f5c6cb; padding: 12px; border-radius: 4px; margin-bottom: 20px;">';
|
||||
echo esc_html($form_errors['general']);
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
if (isset($form_errors['nonce'])) {
|
||||
echo '<div class="error" style="background: #f8d7da; border: 1px solid #f5c6cb; padding: 12px; border-radius: 4px; margin-bottom: 20px;">';
|
||||
echo esc_html($form_errors['nonce']);
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
// Create and display the event form
|
||||
try {
|
||||
$event_form = new HVAC_Event_Form_Builder('hvac_event_form');
|
||||
$event_form->create_event_form();
|
||||
|
||||
// Set any existing errors
|
||||
if (!empty($form_errors)) {
|
||||
$event_form->set_errors($form_errors);
|
||||
}
|
||||
|
||||
// Set form data if submitted (for re-population on errors)
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$event_form->set_data($_POST);
|
||||
}
|
||||
|
||||
// Add some custom wrapper HTML
|
||||
echo '<div class="native-event-form-wrapper">';
|
||||
echo '<h3>Event Information</h3>';
|
||||
echo $event_form->render();
|
||||
echo '</div>';
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo '<div class="error" style="background: #f8d7da; border: 1px solid #f5c6cb; padding: 12px; border-radius: 4px;">';
|
||||
echo 'Form Builder Error: ' . esc_html($e->getMessage());
|
||||
echo '</div>';
|
||||
}
|
||||
?>
|
||||
|
||||
<div style="margin-top: 30px; padding: 15px; background: #f9f9f9; border-radius: 4px;">
|
||||
<h4>Phase 1A Implementation Status:</h4>
|
||||
<ul>
|
||||
<li>✅ HVAC_Event_Form_Builder class created</li>
|
||||
<li>✅ DateTime field types (start/end dates, timezone, all-day)</li>
|
||||
<li>✅ Venue field group (name, address, capacity, coordinates)</li>
|
||||
<li>✅ Organizer field group (name, email, phone, website)</li>
|
||||
<li>✅ HVAC-specific fields (trainer requirements, certifications, equipment)</li>
|
||||
<li>✅ Featured image upload support</li>
|
||||
<li>✅ WordPress security integration (nonce, sanitization, validation)</li>
|
||||
<li>✅ Form validation and error handling</li>
|
||||
<li>⏳ Next: Phase 1B - tribe_events post creation integration</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
?>
|
||||
Loading…
Reference in a new issue