[ 'type' => 'datetime-local', 'class' => 'hvac-datetime-field', 'validate' => ['datetime'], 'sanitize' => 'datetime', ], 'event-title' => [ 'type' => 'text', 'class' => 'hvac-event-title', 'validate' => ['min_length' => 3, 'max_length' => 200], 'sanitize' => 'text', ], 'event-description' => [ 'type' => 'textarea', 'class' => 'hvac-event-description', 'validate' => ['max_length' => 2000], 'sanitize' => 'textarea', ], 'venue-select' => [ 'type' => 'select', 'class' => 'hvac-venue-select', 'options' => [], 'validate' => [], 'sanitize' => 'int', ], 'organizer-select' => [ 'type' => 'select', 'class' => 'hvac-organizer-select', 'options' => [], 'validate' => [], 'sanitize' => 'int', ], 'capacity' => [ 'type' => 'number', 'class' => 'hvac-capacity-field', 'validate' => ['min_value' => 1, 'max_value' => 10000], 'sanitize' => 'int', ], 'cost' => [ 'type' => 'number', 'step' => '0.01', 'class' => 'hvac-cost-field', 'validate' => ['min_value' => 0], 'sanitize' => 'float', ], 'template-selector' => [ 'type' => 'template-select', 'class' => 'hvac-template-selector', 'options' => [], 'validate' => [], 'sanitize' => 'text', ], ]; /** * Cache instance for performance optimization (optional) * * @var mixed */ private $cache = null; /** * Constructor * * @param string $nonce_action Nonce action for form security * @param bool $enable_templates Whether to enable template functionality */ public function __construct(string $nonce_action, bool $enable_templates = true) { parent::__construct($nonce_action); $this->template_manager = HVAC_Event_Template_Manager::instance(); $this->template_mode_enabled = $enable_templates; // Initialize cache if available $this->cache = class_exists('HVAC_Event_Cache') ? HVAC_Event_Cache::instance() : null; if (!$this->cache && defined('WP_DEBUG') && WP_DEBUG) { error_log('HVAC Event Cache unavailable - performance may be impacted'); } $this->init_event_form_hooks(); } /** * Initialize event form specific hooks */ private function init_event_form_hooks(): void { if ($this->template_mode_enabled) { add_action('wp_enqueue_scripts', [$this, 'enqueue_template_assets']); add_action('wp_ajax_hvac_load_template_data', [$this, 'ajax_load_template_data']); add_action('wp_ajax_hvac_save_as_template', [$this, 'ajax_save_as_template']); } } /** * Create complete event form with template integration * * @param array $config Form configuration options * @return self */ public function create_event_form(array $config = []): self { $defaults = [ 'include_template_selector' => $this->template_mode_enabled, 'include_venue_fields' => true, 'include_organizer_fields' => true, 'include_cost_fields' => true, 'include_capacity_fields' => true, 'include_datetime_fields' => true, 'template_categories' => ['general', 'training', 'workshop'], ]; $config = array_merge($defaults, $config); // Add template selector first if enabled if ($config['include_template_selector']) { $this->add_template_selector($config['template_categories']); } // Basic event fields $this->add_basic_event_fields(); /** * Action hook for TEC ticketing integration * * Allows other components to add fields after basic event fields * are rendered but before optional field groups like datetime fields. * * @param HVAC_Event_Form_Builder $form_builder Current form instance * @since 3.2.0 (Phase 2B - TEC Integration) */ do_action('hvac_event_form_after_basic_fields', $this); // Optional field groups if ($config['include_datetime_fields']) { $this->add_datetime_fields(); } if ($config['include_venue_fields']) { $this->add_venue_fields(); } if ($config['include_organizer_fields']) { $this->add_organizer_fields(); } // Add categories field - new feature for enhanced categorization $this->add_categories_fields(); if ($config['include_capacity_fields']) { $this->add_capacity_field(); } if ($config['include_cost_fields']) { $this->add_cost_fields(); } // Add progressive disclosure toggle $this->add_progressive_disclosure(); // Mark certain fields as advanced $this->mark_field_as_advanced('event_capacity') ->mark_field_as_advanced('event_cost') ->mark_field_as_advanced('event_timezone'); // Template actions if enabled if ($this->template_mode_enabled) { $this->add_template_actions(); // Mark template actions as advanced $this->mark_field_as_advanced('save_as_template'); } return $this; } /** * Add template selector field * * @param array $categories Template categories to include */ public function add_template_selector(array $categories = []): self { if (!$this->template_mode_enabled) { return $this; } // Get available templates $filters = []; if (!empty($categories)) { $templates = []; foreach ($categories as $category) { $category_templates = $this->template_manager->get_templates(['category' => $category]); $templates = array_merge($templates, $category_templates); } } else { $templates = $this->template_manager->get_templates(); } // Group templates by category for enhanced UI $templates_by_category = []; foreach ($templates as $template) { $category = $template['category'] ?? 'general'; if (!isset($templates_by_category[$category])) { $templates_by_category[$category] = []; } $templates_by_category[$category][] = $template; } // Prepare enhanced template options with categories $template_options = ['0' => '-- Select a Template --']; foreach ($templates_by_category as $category => $category_templates) { $template_options['optgroup_' . $category] = [ 'label' => ucfirst($category) . ' Templates', 'options' => [] ]; foreach ($category_templates as $template) { $template_options['optgroup_' . $category]['options'][$template['id']] = esc_html($template['name']) . (!empty($template['description']) ? ' - ' . wp_trim_words($template['description'], 8) : ''); } } // Create accordion-style template selector $accordion_template_field = [ 'type' => 'custom', 'name' => 'accordion_template_selector', 'custom_html' => $this->render_accordion_template_selector($template_options, $templates), 'wrapper_class' => 'form-row template-selector-row', ]; $this->add_field($accordion_template_field); // Add template preview area $preview_field = [ 'type' => 'custom', 'name' => 'template_preview', 'custom_html' => $this->render_template_preview_area(), 'wrapper_class' => 'form-row template-preview-row', ]; $this->add_field($preview_field); return $this; } /** * Add basic event fields */ public function add_basic_event_fields(): self { // Event title $title_field = array_merge($this->event_field_defaults['event-title'], [ 'name' => 'event_title', 'label' => 'Event Title', 'placeholder' => 'Enter event title...', 'required' => true, ]); // Event description with WordPress rich text editor $description_field = [ 'type' => 'custom', 'name' => 'event_description', 'custom_html' => $this->render_wp_editor_field(), 'wrapper_class' => 'form-row event-description-field' ]; // Description field uses rich text editor above $this->add_field($title_field); $this->add_field($description_field); return $this; } /** * Add datetime fields for event scheduling */ public function add_datetime_fields(): self { // DateTime section grouping - same row on desktop, columns on mobile $this->add_field([ 'type' => 'custom', 'name' => 'datetime_row_group', 'custom_html' => '
', 'wrapper_class' => '' ]); // Start date/time $start_datetime_field = array_merge($this->event_field_defaults['datetime-local'], [ 'name' => 'event_start_datetime', 'label' => 'Start Date & Time', 'required' => true, 'wrapper_class' => 'form-row-half datetime-field start-datetime', ]); // End date/time $end_datetime_field = array_merge($this->event_field_defaults['datetime-local'], [ 'name' => 'event_end_datetime', 'label' => 'End Date & Time', 'required' => true, 'wrapper_class' => 'form-row-half datetime-field end-datetime', ]); // Timezone $timezone_field = [ 'type' => 'select', 'name' => 'event_timezone', 'label' => 'Timezone', 'options' => $this->get_timezone_options(), 'value' => wp_timezone_string(), 'class' => 'hvac-timezone-select', 'wrapper_class' => 'form-row timezone-row', ]; $this->add_field($start_datetime_field); $this->add_field($end_datetime_field); // Close the datetime row group $this->add_field([ 'type' => 'custom', 'name' => 'datetime_row_group_end', 'custom_html' => '
', 'wrapper_class' => '' ]); $this->add_field($timezone_field); return $this; } /** * Add venue selection and management fields */ public function add_venue_fields(): self { // Dynamic single-select venue selector with autocomplete and modal creation $venue_field = [ 'type' => 'custom', 'name' => 'event_venue', 'custom_html' => $this->render_searchable_venue_selector(), 'wrapper_class' => 'form-row venue-field', ]; $this->add_field($venue_field); return $this; } /** * Add organizer selection and management fields */ public function add_organizer_fields(): self { // Dynamic multi-select organizer selector with autocomplete $organizer_field = [ 'type' => 'custom', 'name' => 'event_organizer', 'custom_html' => $this->render_searchable_organizer_selector(), 'wrapper_class' => 'form-row organizer-field', ]; $this->add_field($organizer_field); return $this; } /** * Add categories field with multi-select search functionality */ public function add_categories_fields(): self { // Dynamic multi-select category selector with autocomplete (limited for trainers) $categories_field = [ 'type' => 'custom', 'name' => 'event_categories', 'custom_html' => $this->render_searchable_category_selector(), 'wrapper_class' => 'form-row categories-field', ]; $this->add_field($categories_field); return $this; } /** * Add capacity field */ public function add_capacity_field(): self { $capacity_field = array_merge($this->event_field_defaults['capacity'], [ 'name' => 'event_capacity', 'label' => 'Capacity', 'placeholder' => 'Maximum attendees', 'min' => 1, 'max' => 10000, 'wrapper_class' => 'form-row capacity-row', ]); $this->add_field($capacity_field); return $this; } /** * Add cost-related fields */ public function add_cost_fields(): self { $cost_field = array_merge($this->event_field_defaults['cost'], [ 'name' => 'event_cost', 'label' => 'Event Cost ($)', 'placeholder' => '0.00', 'min' => 0, 'wrapper_class' => 'form-row cost-row', ]); $this->add_field($cost_field); return $this; } /** * Add template action buttons */ public function add_template_actions(): self { if (!$this->template_mode_enabled) { return $this; } // Save as template button $save_template_field = [ 'type' => 'button', 'name' => 'save_as_template', 'label' => '', 'value' => 'Save as Template', 'class' => 'button button-secondary hvac-save-template', 'wrapper_class' => 'form-row template-actions', 'onclick' => 'hvacShowSaveTemplateDialog(event)', ]; $this->add_field($save_template_field); return $this; } /** * Add progressive disclosure section */ public function add_progressive_disclosure(): self { // Advanced options toggle $advanced_toggle_field = [ 'type' => 'custom', 'name' => 'advanced_options_toggle', 'custom_html' => $this->render_advanced_options_toggle(), 'wrapper_class' => 'form-row advanced-toggle-row', ]; $this->add_field($advanced_toggle_field); return $this; } /** * Mark field as advanced (for progressive disclosure) */ public function mark_field_as_advanced(string $field_name): self { foreach ($this->fields as &$field) { if ($field['name'] === $field_name) { $field['wrapper_class'] = ($field['wrapper_class'] ?? '') . ' advanced-field'; $field['data_attributes'] = array_merge($field['data_attributes'] ?? [], [ 'advanced' => 'true' ]); break; } } return $this; } /** * Render advanced options toggle button */ private function render_advanced_options_toggle(): string { $html = '
'; $html .= ''; $html .= 'Additional settings for power users'; $html .= '
'; return $html; } /** * Load template data into form * * @param string $template_id Template ID to load * @return bool Success status */ public function load_template(string $template_id): bool { if (!$this->template_mode_enabled) { return false; } $template = $this->template_manager->get_template($template_id); if (!$template) { return false; } $this->current_template = $template; // Set form data from template if (!empty($template['field_data'])) { $this->set_data($template['field_data']); } // Apply template-specific validation rules if (!empty($template['validation_rules'])) { $this->apply_template_validation_rules($template['validation_rules']); } return true; } /** * Save current form configuration as template * * @param array $template_config Template configuration * @return array Result with success status */ public function save_as_template(array $template_config): array { if (!$this->template_mode_enabled) { return [ 'success' => false, 'error' => __('Template functionality is not enabled', 'hvac-community-events') ]; } // Get current form data $current_data = $this->get_current_form_data(); // Prepare template data $template_data = [ 'name' => sanitize_text_field($template_config['name']), 'description' => sanitize_textarea_field($template_config['description']), 'category' => sanitize_text_field($template_config['category']), 'is_public' => (bool) ($template_config['is_public'] ?? false), 'field_data' => $current_data, 'meta_data' => $template_config['meta_data'] ?? [], ]; return $this->template_manager->create_template($template_data); } /** * Get current form data from fields * * @return array Current form data */ private function get_current_form_data(): array { $data = []; foreach ($this->fields as $field) { if (isset($_POST[$field['name']])) { $data[$field['name']] = $this->sanitize_field_value($field, $_POST[$field['name']]); } } return $data; } /** * Sanitize field value based on field type * * @param array $field Field configuration * @param mixed $value Field value to sanitize * @return mixed Sanitized value */ private function sanitize_field_value(array $field, $value) { $sanitize_type = $field['sanitize'] ?? 'text'; return match($sanitize_type) { 'text' => sanitize_text_field($value), 'textarea' => sanitize_textarea_field($value), 'int' => absint($value), 'float' => floatval($value), 'datetime' => sanitize_text_field($value), 'url' => esc_url_raw($value), 'email' => sanitize_email($value), default => sanitize_text_field($value) }; } /** * Apply template validation rules to form fields * * @param array $validation_rules Template validation rules */ private function apply_template_validation_rules(array $validation_rules): void { // Apply additional validation rules from template foreach ($validation_rules as $field_name => $rules) { // Find field and update validation rules foreach ($this->fields as &$field) { if ($field['name'] === $field_name) { $field['validate'] = array_merge($field['validate'], $rules); break; } } } } /** * Render accordion-style template selector * * @param array $template_options Template options array * @param array $templates Raw templates data * @return string Accordion HTML */ private function render_accordion_template_selector(array $template_options, array $templates): string { $html = '
'; $html .= '
'; $html .= '

Use Template

'; $html .= ''; $html .= '
'; $html .= '
'; // Build select options HTML $html .= ''; $html .= '

Select a template to pre-fill form fields with common settings.

'; $html .= '
'; $html .= '
'; return $html; } /** * Render template preview area * * @return string Preview HTML */ private function render_template_preview_area(): string { $html = ''; return $html; } /** * Render save template dialog * * @return string Dialog HTML */ private function render_save_template_dialog(): string { $html = ' HTML; } /** * Render "Create New" button with role-based permissions */ private function render_create_new_button(string $type, bool $can_create): string { if (!$can_create) { if ($type === 'category') { return '
Only Master Trainers can create new categories
'; } return ''; } $label = ucfirst($type); return << HTML; } /** * Render WordPress rich text editor field * * @return string HTML for WordPress editor */ private function render_wp_editor_field(): string { ob_start(); ?>
'event_description', 'textarea_rows' => 10, 'media_buttons' => false, 'teeny' => false, 'tinymce' => [ 'toolbar1' => 'formatselect,bold,italic,underline,strikethrough,|,bullist,numlist,|,link,unlink,|,blockquote,hr,|,alignleft,aligncenter,alignright,|,undo,redo', 'toolbar2' => '', 'block_formats' => 'Paragraph=p;Heading 2=h2;Heading 3=h3;Heading 4=h4;Preformatted=pre', 'forced_root_block' => 'p', 'force_p_newlines' => true, 'remove_redundant_brs' => true, 'convert_urls' => false, 'paste_as_text' => false, 'paste_auto_cleanup_on_paste' => true, 'paste_remove_spans' => true, 'paste_remove_styles' => true, 'paste_strip_class_attributes' => 'all', 'valid_elements' => 'p,br,strong,em,ul,ol,li,h2,h3,h4,h5,h6,blockquote,a[href|title],hr', 'valid_children' => '+p[strong|em|a|br],+ul[li],+ol[li],+li[strong|em|a|br|p]' ], 'quicktags' => [ 'buttons' => 'strong,em,ul,ol,li,link,close' ] ]; wp_editor('', 'event_description', $editor_settings); ?> Use the editor above to format your event description with headings, lists, and formatting.