feat: implement comprehensive featured image system for events, organizers, and venues
- Add featured image field to main event creation form with WordPress media uploader - Implement featured image upload in organizer and venue creation modals - Update AJAX handlers to process and validate featured image attachments - Add comprehensive media upload UI with preview and removal functionality - Include proper permission validation for administrator, trainer, and master trainer roles - Create authoritative documentation for complete event creation page functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
c3806f01c3
commit
91873c6a9c
4 changed files with 1002 additions and 6 deletions
|
|
@ -47,6 +47,17 @@
|
|||
this.closeModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Media upload handlers
|
||||
$(document).on('click', '.hvac-modal .select-image-btn', (e) => {
|
||||
e.preventDefault();
|
||||
this.openMediaUploader(e.target);
|
||||
});
|
||||
|
||||
$(document).on('click', '.hvac-modal .remove-image', (e) => {
|
||||
e.preventDefault();
|
||||
this.removeImage(e.target);
|
||||
});
|
||||
}
|
||||
|
||||
createModalContainer() {
|
||||
|
|
@ -104,7 +115,8 @@
|
|||
{ name: 'organizer_name', label: 'Organizer Name', type: 'text', required: true },
|
||||
{ name: 'organizer_email', label: 'Email', type: 'email', required: false },
|
||||
{ name: 'organizer_website', label: 'Website', type: 'url', required: false },
|
||||
{ name: 'organizer_phone', label: 'Phone', type: 'tel', required: false }
|
||||
{ name: 'organizer_phone', label: 'Phone', type: 'tel', required: false },
|
||||
{ name: 'organizer_featured_image', label: 'Featured Image', type: 'media', required: false }
|
||||
],
|
||||
action: 'hvac_create_organizer'
|
||||
},
|
||||
|
|
@ -127,7 +139,8 @@
|
|||
{ name: 'venue_zip', label: 'Zip/Postal Code', type: 'text', required: false },
|
||||
{ name: 'venue_country', label: 'Country', type: 'text', required: false },
|
||||
{ name: 'venue_website', label: 'Website', type: 'url', required: false },
|
||||
{ name: 'venue_phone', label: 'Phone', type: 'tel', required: false }
|
||||
{ name: 'venue_phone', label: 'Phone', type: 'tel', required: false },
|
||||
{ name: 'venue_featured_image', label: 'Featured Image', type: 'media', required: false }
|
||||
],
|
||||
action: 'hvac_create_venue'
|
||||
}
|
||||
|
|
@ -184,6 +197,33 @@
|
|||
`;
|
||||
}
|
||||
|
||||
if (field.type === 'media') {
|
||||
return `
|
||||
<div class="hvac-form-field hvac-media-field" data-field-name="${field.name}">
|
||||
<label>${field.label}${requiredMark}</label>
|
||||
<div class="media-upload-container">
|
||||
<div class="image-preview-container" style="margin-bottom: 10px;">
|
||||
<div class="image-preview" style="display: none; position: relative; max-width: 200px;">
|
||||
<img src="" alt="Preview" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<button type="button" class="remove-image" style="position: absolute; top: 5px; right: 5px; background: #d63638; color: white; border: none; border-radius: 50%; width: 20px; height: 20px; cursor: pointer; font-size: 12px;">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-controls">
|
||||
<button type="button" class="select-image-btn hvac-btn hvac-btn-secondary">
|
||||
<span class="dashicons dashicons-format-image" style="vertical-align: middle; margin-right: 5px;"></span>
|
||||
Select Image
|
||||
</button>
|
||||
<input type="hidden" name="${field.name}" class="image-id-input" value="">
|
||||
<input type="hidden" name="${field.name}_url" class="image-url-input" value="">
|
||||
</div>
|
||||
<p class="description" style="margin-top: 5px; font-size: 12px; color: #666;">
|
||||
Recommended: 300x300 pixels or larger
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="hvac-form-field">
|
||||
<label for="${field.name}">${field.label}${requiredMark}</label>
|
||||
|
|
@ -284,6 +324,68 @@
|
|||
}, 5000);
|
||||
}
|
||||
|
||||
openMediaUploader(button) {
|
||||
const $button = $(button);
|
||||
const $fieldContainer = $button.closest('.hvac-media-field');
|
||||
const $imagePreview = $fieldContainer.find('.image-preview');
|
||||
const $previewImg = $fieldContainer.find('.image-preview img');
|
||||
const $imageIdInput = $fieldContainer.find('.image-id-input');
|
||||
const $imageUrlInput = $fieldContainer.find('.image-url-input');
|
||||
|
||||
// Create WordPress media frame
|
||||
const mediaUploader = wp.media({
|
||||
title: 'Select Image',
|
||||
button: {
|
||||
text: 'Select Image'
|
||||
},
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
}
|
||||
});
|
||||
|
||||
// When an image is selected
|
||||
mediaUploader.on('select', () => {
|
||||
const attachment = mediaUploader.state().get('selection').first().toJSON();
|
||||
|
||||
// Update hidden inputs
|
||||
$imageIdInput.val(attachment.id);
|
||||
$imageUrlInput.val(attachment.url);
|
||||
|
||||
// Update preview
|
||||
$previewImg.attr('src', attachment.url);
|
||||
$previewImg.attr('alt', attachment.alt || attachment.title || 'Selected image');
|
||||
$imagePreview.show();
|
||||
|
||||
// Update button text
|
||||
$button.html('<span class="dashicons dashicons-format-image" style="vertical-align: middle; margin-right: 5px;"></span>Change Image');
|
||||
});
|
||||
|
||||
// Open the media modal
|
||||
mediaUploader.open();
|
||||
}
|
||||
|
||||
removeImage(button) {
|
||||
const $button = $(button);
|
||||
const $fieldContainer = $button.closest('.hvac-media-field');
|
||||
const $imagePreview = $fieldContainer.find('.image-preview');
|
||||
const $previewImg = $fieldContainer.find('.image-preview img');
|
||||
const $imageIdInput = $fieldContainer.find('.image-id-input');
|
||||
const $imageUrlInput = $fieldContainer.find('.image-url-input');
|
||||
const $selectBtn = $fieldContainer.find('.select-image-btn');
|
||||
|
||||
// Clear inputs
|
||||
$imageIdInput.val('');
|
||||
$imageUrlInput.val('');
|
||||
|
||||
// Hide preview
|
||||
$imagePreview.hide();
|
||||
$previewImg.attr('src', '');
|
||||
|
||||
// Reset button text
|
||||
$selectBtn.html('<span class="dashicons dashicons-format-image" style="vertical-align: middle; margin-right: 5px;"></span>Select Image');
|
||||
}
|
||||
|
||||
capitalizeFirst(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
|
|
|||
739
docs/EVENT-CREATION-PAGE-DOCUMENTATION.md
Normal file
739
docs/EVENT-CREATION-PAGE-DOCUMENTATION.md
Normal file
|
|
@ -0,0 +1,739 @@
|
|||
# HVAC Community Events - Event Creation Page Documentation
|
||||
|
||||
**Version:** 3.2.0
|
||||
**Last Updated:** January 2025
|
||||
**Status:** Production Ready
|
||||
|
||||
## Overview
|
||||
|
||||
The HVAC Community Events plugin provides a comprehensive event creation system designed specifically for HVAC training organizations. This document serves as the authoritative reference for the event creation page functionality, architecture, and implementation details.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [System Architecture](#system-architecture)
|
||||
2. [User Interface Components](#user-interface-components)
|
||||
3. [Form Fields Reference](#form-fields-reference)
|
||||
4. [Dynamic Features](#dynamic-features)
|
||||
5. [Role-Based Access Control](#role-based-access-control)
|
||||
6. [API Endpoints](#api-endpoints)
|
||||
7. [Security Implementation](#security-implementation)
|
||||
8. [Template System](#template-system)
|
||||
9. [Integration Points](#integration-points)
|
||||
10. [Performance Considerations](#performance-considerations)
|
||||
11. [Troubleshooting Guide](#troubleshooting-guide)
|
||||
|
||||
---
|
||||
|
||||
## System Architecture
|
||||
|
||||
### Core Components
|
||||
|
||||
The event creation system is built on a modular architecture with the following primary components:
|
||||
|
||||
#### 1. **HVAC_Event_Form_Builder**
|
||||
- **File:** `includes/class-hvac-event-form-builder.php`
|
||||
- **Purpose:** Central form generation and validation engine
|
||||
- **Pattern:** Singleton with fluent interface
|
||||
- **Responsibilities:**
|
||||
- Form field rendering and validation
|
||||
- Progressive disclosure management
|
||||
- Template integration
|
||||
- Media upload handling
|
||||
- Security implementation
|
||||
|
||||
#### 2. **HVAC_Ajax_Handlers**
|
||||
- **File:** `includes/class-hvac-ajax-handlers.php`
|
||||
- **Purpose:** Server-side AJAX request processing
|
||||
- **Pattern:** Singleton with dependency injection
|
||||
- **Responsibilities:**
|
||||
- AI-powered event population
|
||||
- Dynamic search functionality
|
||||
- Entity creation (venues, organizers, categories)
|
||||
- Security validation and nonce verification
|
||||
|
||||
#### 3. **Frontend JavaScript Components**
|
||||
- **AI Assistant:** `assets/js/hvac-ai-assist.js`
|
||||
- **Searchable Selectors:** `assets/js/hvac-searchable-selectors.js`
|
||||
- **Modal Forms:** `assets/js/hvac-modal-forms.js`
|
||||
- **Purpose:** Rich user interface interactions
|
||||
- **Pattern:** ES6 classes with event delegation
|
||||
|
||||
#### 4. **Template System**
|
||||
- **File:** `templates/page-tec-create-event.php`
|
||||
- **Purpose:** WordPress-native page template
|
||||
- **Integration:** The Events Calendar compatibility
|
||||
- **Features:** Responsive design, accessibility compliance
|
||||
|
||||
---
|
||||
|
||||
## User Interface Components
|
||||
|
||||
### Layout Structure
|
||||
|
||||
```
|
||||
Event Creation Page
|
||||
├── Header Section
|
||||
│ ├── Page Title
|
||||
│ ├── Breadcrumb Navigation
|
||||
│ └── User Context Information
|
||||
├── Main Form Container
|
||||
│ ├── Basic Event Fields
|
||||
│ ├── Featured Image Upload
|
||||
│ ├── DateTime Management
|
||||
│ ├── Venue Selection
|
||||
│ ├── Organizer Management
|
||||
│ ├── Category Assignment
|
||||
│ └── Advanced Options (Collapsible)
|
||||
├── AI Assistant Panel
|
||||
│ ├── Input Methods (URL, Text, Manual)
|
||||
│ ├── Processing Indicators
|
||||
│ └── Confidence Metrics
|
||||
└── Footer Actions
|
||||
├── Save Draft
|
||||
├── Preview
|
||||
└── Publish Event
|
||||
```
|
||||
|
||||
### Progressive Disclosure
|
||||
|
||||
The interface implements progressive disclosure to reduce cognitive load:
|
||||
|
||||
- **Basic Fields:** Always visible, essential information
|
||||
- **Advanced Options:** Collapsible section for power users
|
||||
- **Modal Forms:** Overlay interfaces for entity creation
|
||||
- **AI Assistant:** Contextual panel that appears when needed
|
||||
|
||||
---
|
||||
|
||||
## Form Fields Reference
|
||||
|
||||
### Basic Event Information
|
||||
|
||||
#### Event Title
|
||||
- **Type:** Text input
|
||||
- **Validation:** 3-200 characters, required
|
||||
- **Sanitization:** `sanitize_text_field()`
|
||||
- **Purpose:** Primary event identifier
|
||||
|
||||
#### Event Description
|
||||
- **Type:** WordPress TinyMCE rich text editor
|
||||
- **Features:**
|
||||
- Custom toolbar: Format, Bold, Italic, Lists, Links, Alignment
|
||||
- Markdown-to-HTML conversion support
|
||||
- Paste cleanup for external content
|
||||
- **Validation:** HTML content filtering with `wp_kses`
|
||||
- **Purpose:** Detailed event information
|
||||
|
||||
#### Featured Image
|
||||
- **Type:** WordPress Media Uploader
|
||||
- **Constraints:** Image files only
|
||||
- **Recommended:** 1200x630 pixels
|
||||
- **Storage:** WordPress attachment with post thumbnail relationship
|
||||
- **Purpose:** Visual representation for event listings
|
||||
|
||||
### Date and Time Management
|
||||
|
||||
#### Start Date & Time
|
||||
- **Type:** HTML5 `datetime-local` input
|
||||
- **Validation:** Must be future date
|
||||
- **Format:** WordPress-compatible datetime
|
||||
- **Integration:** The Events Calendar meta fields
|
||||
|
||||
#### End Date & Time
|
||||
- **Type:** HTML5 `datetime-local` input
|
||||
- **Validation:** Must be after start date
|
||||
- **Default:** 2 hours after start time
|
||||
- **Integration:** TEC `_EventEndDate` meta field
|
||||
|
||||
#### Timezone
|
||||
- **Type:** Dropdown select
|
||||
- **Options:** WordPress timezone list
|
||||
- **Default:** Site timezone setting
|
||||
- **Storage:** `_EventTimezone` meta field
|
||||
- **Advanced:** Hidden by default, revealed in advanced options
|
||||
|
||||
### Venue Management
|
||||
|
||||
#### Venue Selection
|
||||
- **Type:** Searchable single-select
|
||||
- **Search:** Real-time AJAX with 2+ character minimum
|
||||
- **Features:**
|
||||
- Autocomplete with venue address display
|
||||
- "Add New" modal for inline venue creation
|
||||
- Address validation and formatting
|
||||
- **Integration:** TEC `_EventVenueID` relationship
|
||||
|
||||
#### Venue Creation Modal
|
||||
- **Fields:**
|
||||
- Venue Name (required)
|
||||
- Address, City, State, Zip, Country
|
||||
- Website, Phone
|
||||
- Featured Image
|
||||
- **Permissions:** Trainers, Master Trainers, Administrators
|
||||
- **Validation:** Address normalization, phone formatting
|
||||
|
||||
### Organizer Management
|
||||
|
||||
#### Organizer Selection
|
||||
- **Type:** Searchable multi-select
|
||||
- **Limit:** Maximum 3 organizers per event
|
||||
- **Search:** Real-time AJAX with contact information display
|
||||
- **Features:**
|
||||
- Selected item display with removal
|
||||
- "Add New" modal for inline organizer creation
|
||||
- Email and phone display in search results
|
||||
- **Integration:** TEC `_EventOrganizerID` array
|
||||
|
||||
#### Organizer Creation Modal
|
||||
- **Fields:**
|
||||
- Organizer Name (required)
|
||||
- Email, Website, Phone
|
||||
- Featured Image
|
||||
- **Permissions:** Trainers, Master Trainers, Administrators
|
||||
- **Validation:** Email format, URL validation
|
||||
|
||||
### Category Assignment
|
||||
|
||||
#### Category Selection
|
||||
- **Type:** Searchable multi-select
|
||||
- **Limit:** Maximum 3 categories per event
|
||||
- **Source:** WordPress `tribe_events_cat` taxonomy
|
||||
- **Features:**
|
||||
- Hierarchical category display
|
||||
- Description tooltips
|
||||
- Role-based creation permissions
|
||||
- **Integration:** WordPress taxonomy relationship
|
||||
|
||||
#### Category Creation Modal
|
||||
- **Fields:**
|
||||
- Category Name (required)
|
||||
- Description
|
||||
- **Permissions:** Master Trainers only
|
||||
- **Validation:** Taxonomy name sanitization
|
||||
|
||||
### Advanced Fields
|
||||
|
||||
#### Event Capacity
|
||||
- **Type:** Number input
|
||||
- **Range:** 1-10,000 attendees
|
||||
- **Purpose:** Registration limit setting
|
||||
- **Integration:** TEC capacity management
|
||||
- **Advanced:** Hidden by default
|
||||
|
||||
#### Event Cost
|
||||
- **Type:** Number input with decimal support
|
||||
- **Format:** Currency display
|
||||
- **Purpose:** Event pricing information
|
||||
- **Integration:** TEC cost meta fields
|
||||
- **Advanced:** Hidden by default
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Features
|
||||
|
||||
### AI Assistant System
|
||||
|
||||
#### Input Methods
|
||||
|
||||
**1. URL Processing**
|
||||
- **Supported Platforms:** EventBrite, Facebook Events, Meetup, General URLs
|
||||
- **Process:**
|
||||
1. URL validation and platform detection
|
||||
2. Content extraction with timeout handling (60 seconds)
|
||||
3. Data parsing and confidence scoring
|
||||
4. Form field population with validation
|
||||
- **Rate Limiting:** 10 requests per hour per user
|
||||
|
||||
**2. Text Extraction**
|
||||
- **Purpose:** Process existing event descriptions or marketing copy
|
||||
- **Features:**
|
||||
- Smart field detection (title, date, location extraction)
|
||||
- Markdown processing with list formatting
|
||||
- Content cleanup and normalization
|
||||
- **Validation:** 10+ character minimum
|
||||
|
||||
**3. Manual Description**
|
||||
- **Purpose:** AI-generated content from user prompts
|
||||
- **Features:**
|
||||
- Context-aware content generation
|
||||
- Professional formatting
|
||||
- Industry-specific terminology
|
||||
- **Output:** Structured markdown converted to HTML
|
||||
|
||||
#### Processing Flow
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[User Input] --> B[Input Validation]
|
||||
B --> C[AI Processing Request]
|
||||
C --> D[Progress Indication]
|
||||
D --> E[Response Processing]
|
||||
E --> F[Confidence Analysis]
|
||||
F --> G[Form Population]
|
||||
G --> H[User Validation]
|
||||
```
|
||||
|
||||
#### Confidence Scoring
|
||||
|
||||
The AI system provides confidence levels for each populated field:
|
||||
|
||||
- **High (90-100%):** Green indicator, auto-accept
|
||||
- **Medium (70-89%):** Yellow indicator, user review recommended
|
||||
- **Low (50-69%):** Orange indicator, manual verification required
|
||||
- **Very Low (<50%):** Red indicator, suggests manual entry
|
||||
|
||||
### Searchable Selectors
|
||||
|
||||
#### Real-time Search Implementation
|
||||
|
||||
**Debounced AJAX Requests:**
|
||||
- **Trigger:** 2+ characters entered
|
||||
- **Delay:** 300ms debounce
|
||||
- **Caching:** Client-side result caching for 5 minutes
|
||||
- **Error Handling:** Graceful degradation with fallback options
|
||||
|
||||
**Search Results Display:**
|
||||
- **Venue Results:** Name, full address, phone
|
||||
- **Organizer Results:** Name, email, organization
|
||||
- **Category Results:** Name, description, event count
|
||||
|
||||
#### Selection Management
|
||||
|
||||
**Multi-select Behavior (Organizers, Categories):**
|
||||
- Visual selected item display with removal buttons
|
||||
- Duplicate prevention
|
||||
- Maximum selection enforcement
|
||||
- Keyboard navigation support
|
||||
|
||||
**Single-select Behavior (Venues):**
|
||||
- Replacement selection model
|
||||
- Clear selection option
|
||||
- Visual state management
|
||||
|
||||
### Modal Creation Forms
|
||||
|
||||
#### WordPress Media Integration
|
||||
|
||||
**Featured Image Upload:**
|
||||
- **Interface:** Native WordPress Media Library
|
||||
- **Features:**
|
||||
- Image preview with removal option
|
||||
- File type validation
|
||||
- Size recommendations
|
||||
- Alt text management
|
||||
- **Storage:** WordPress attachment system
|
||||
|
||||
#### Form Validation
|
||||
|
||||
**Client-side Validation:**
|
||||
- Real-time field validation
|
||||
- Visual error indicators
|
||||
- Progressive enhancement
|
||||
|
||||
**Server-side Validation:**
|
||||
- Comprehensive input sanitization
|
||||
- Business rule enforcement
|
||||
- Security validation
|
||||
|
||||
---
|
||||
|
||||
## Role-Based Access Control
|
||||
|
||||
### Permission Levels
|
||||
|
||||
#### Unauthenticated Users
|
||||
- **Access:** Redirected to login page
|
||||
- **Message:** Clear authentication requirement
|
||||
|
||||
#### Standard WordPress Users
|
||||
- **Access:** Denied with capability check
|
||||
- **Requirement:** Must have HVAC-specific roles
|
||||
|
||||
#### HVAC Trainers
|
||||
- **Permissions:**
|
||||
- Create and edit events
|
||||
- Create organizers and venues
|
||||
- Use AI assistant features
|
||||
- Access basic template system
|
||||
- **Restrictions:**
|
||||
- Cannot create categories
|
||||
- Limited template management
|
||||
|
||||
#### HVAC Master Trainers
|
||||
- **Permissions:**
|
||||
- All trainer permissions
|
||||
- Create and manage categories
|
||||
- Full template system access
|
||||
- Advanced configuration options
|
||||
- User management capabilities
|
||||
|
||||
#### Administrators
|
||||
- **Permissions:**
|
||||
- Complete system access
|
||||
- Plugin configuration
|
||||
- Role management
|
||||
- System maintenance
|
||||
|
||||
### Security Implementation
|
||||
|
||||
#### Nonce Verification
|
||||
```php
|
||||
// Example nonce verification
|
||||
$security_check = HVAC_Ajax_Security::verify_ajax_request(
|
||||
'create_event',
|
||||
HVAC_Ajax_Security::NONCE_GENERAL,
|
||||
array('hvac_trainer', 'hvac_master_trainer'),
|
||||
false
|
||||
);
|
||||
```
|
||||
|
||||
#### Input Sanitization
|
||||
- **Text Fields:** `sanitize_text_field()`
|
||||
- **Email Fields:** `sanitize_email()`
|
||||
- **URL Fields:** `esc_url_raw()`
|
||||
- **HTML Content:** `wp_kses()` with allowed tags
|
||||
- **Number Fields:** `absint()` or `floatval()`
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Core AJAX Handlers
|
||||
|
||||
#### Event Population
|
||||
- **Endpoint:** `hvac_ai_populate_event`
|
||||
- **Method:** POST
|
||||
- **Purpose:** AI-powered event data extraction
|
||||
- **Parameters:**
|
||||
- `input_text` (string): URL or text content
|
||||
- `input_type` (enum): 'url', 'text', 'description'
|
||||
- `nonce` (string): Security token
|
||||
- **Response:** JSON with populated field data and confidence scores
|
||||
|
||||
#### Search Endpoints
|
||||
|
||||
**Organizer Search:**
|
||||
- **Endpoint:** `hvac_search_organizers`
|
||||
- **Parameters:** `search` (string, 2+ chars)
|
||||
- **Response:** Array of organizer objects with contact info
|
||||
|
||||
**Venue Search:**
|
||||
- **Endpoint:** `hvac_search_venues`
|
||||
- **Parameters:** `search` (string, 2+ chars)
|
||||
- **Response:** Array of venue objects with address info
|
||||
|
||||
**Category Search:**
|
||||
- **Endpoint:** `hvac_search_categories`
|
||||
- **Parameters:** `search` (string, 2+ chars)
|
||||
- **Response:** Array of category objects with descriptions
|
||||
|
||||
#### Entity Creation
|
||||
|
||||
**Create Organizer:**
|
||||
- **Endpoint:** `hvac_create_organizer`
|
||||
- **Required:** `organizer_name`
|
||||
- **Optional:** `organizer_email`, `organizer_website`, `organizer_phone`, `organizer_featured_image`
|
||||
- **Response:** Created organizer object
|
||||
|
||||
**Create Venue:**
|
||||
- **Endpoint:** `hvac_create_venue`
|
||||
- **Required:** `venue_name`
|
||||
- **Optional:** Address fields, contact info, featured image
|
||||
- **Response:** Created venue object
|
||||
|
||||
**Create Category:**
|
||||
- **Endpoint:** `hvac_create_category`
|
||||
- **Required:** `category_name`
|
||||
- **Optional:** `category_description`
|
||||
- **Permissions:** Master Trainers only
|
||||
- **Response:** Created category object
|
||||
|
||||
### Response Format
|
||||
|
||||
#### Success Response
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": 123,
|
||||
"title": "Created Item Name",
|
||||
"subtitle": "Additional information",
|
||||
"confidence": 85
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Error Response
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"data": {
|
||||
"message": "Error description",
|
||||
"code": "error_type",
|
||||
"field_errors": {
|
||||
"field_name": "Specific field error"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Implementation
|
||||
|
||||
### Authentication & Authorization
|
||||
|
||||
#### WordPress Integration
|
||||
- **User Authentication:** WordPress session management
|
||||
- **Capability Checking:** Custom capabilities with role mapping
|
||||
- **Nonce System:** Action-specific tokens with expiration
|
||||
|
||||
#### HVAC_Ajax_Security Class
|
||||
```php
|
||||
class HVAC_Ajax_Security {
|
||||
const NONCE_GENERAL = 'hvac_general';
|
||||
const NONCE_AI = 'hvac_ai';
|
||||
|
||||
public static function verify_ajax_request($action, $nonce_action, $required_roles, $check_capabilities) {
|
||||
// Comprehensive security validation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Input Validation
|
||||
|
||||
#### Validation Rules
|
||||
```php
|
||||
$input_rules = array(
|
||||
'event_title' => array(
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'min_length' => 3,
|
||||
'max_length' => 200
|
||||
),
|
||||
'event_description' => array(
|
||||
'type' => 'html',
|
||||
'required' => false,
|
||||
'allowed_tags' => 'p,br,strong,em,ul,ol,li,h2,h3,h4,h5,h6'
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
#### XSS Prevention
|
||||
- **Output Escaping:** All user content escaped with appropriate functions
|
||||
- **HTML Filtering:** Allowed tag whitelist with attribute sanitization
|
||||
- **JavaScript Safety:** JSON data properly escaped for client consumption
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
#### AI Endpoint Protection
|
||||
- **Rate:** 10 requests per hour per user
|
||||
- **Storage:** WordPress transients
|
||||
- **Reset:** Hourly cleanup with WP Cron
|
||||
- **Error Handling:** Clear rate limit messages
|
||||
|
||||
---
|
||||
|
||||
## Template System
|
||||
|
||||
### Template Architecture
|
||||
|
||||
#### Template Storage
|
||||
- **Format:** JSON with metadata
|
||||
- **Location:** WordPress options table
|
||||
- **Versioning:** Timestamp-based versioning
|
||||
- **Backup:** Automatic backup on modification
|
||||
|
||||
#### Template Categories
|
||||
- **General:** Basic event templates
|
||||
- **Training:** Technical training sessions
|
||||
- **Workshop:** Hands-on workshops
|
||||
- **Certification:** Certification programs
|
||||
- **Conference:** Large-scale events
|
||||
|
||||
### Template Management
|
||||
|
||||
#### Template Creation
|
||||
```javascript
|
||||
// Save current form as template
|
||||
const templateData = {
|
||||
name: 'Template Name',
|
||||
category: 'training',
|
||||
fields: {
|
||||
event_title: 'Template Title',
|
||||
event_description: 'Template Description',
|
||||
// ... other fields
|
||||
},
|
||||
metadata: {
|
||||
created_by: userId,
|
||||
created_date: timestamp,
|
||||
usage_count: 0
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### Template Application
|
||||
- **Field Mapping:** Intelligent field matching
|
||||
- **Conflict Resolution:** User confirmation for overrides
|
||||
- **Partial Application:** Selective field application
|
||||
- **Confidence Scoring:** Template fit analysis
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
### The Events Calendar (TEC)
|
||||
|
||||
#### Post Type Integration
|
||||
- **Events:** `tribe_events` post type
|
||||
- **Venues:** `tribe_venue` post type
|
||||
- **Organizers:** `tribe_organizer` post type
|
||||
- **Categories:** `tribe_events_cat` taxonomy
|
||||
|
||||
#### Meta Field Mapping
|
||||
```php
|
||||
// TEC meta field integration
|
||||
$tec_meta = array(
|
||||
'_EventStartDate' => $start_datetime,
|
||||
'_EventEndDate' => $end_datetime,
|
||||
'_EventTimezone' => $timezone,
|
||||
'_EventVenueID' => $venue_id,
|
||||
'_EventOrganizerID' => $organizer_ids,
|
||||
'_EventCost' => $event_cost,
|
||||
'_EventCapacity' => $event_capacity
|
||||
);
|
||||
```
|
||||
|
||||
### WordPress Core
|
||||
|
||||
#### Media Library
|
||||
- **Featured Images:** Post thumbnail system
|
||||
- **File Handling:** WordPress upload system
|
||||
- **Security:** File type validation
|
||||
|
||||
#### User Management
|
||||
- **Roles:** Custom HVAC roles
|
||||
- **Capabilities:** Fine-grained permissions
|
||||
- **Session Handling:** WordPress authentication
|
||||
|
||||
#### Taxonomy System
|
||||
- **Categories:** Native WordPress taxonomy
|
||||
- **Hierarchical:** Support for nested categories
|
||||
- **Meta:** Additional category metadata
|
||||
|
||||
---
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Frontend Optimization
|
||||
|
||||
#### JavaScript Loading
|
||||
- **Conditional Loading:** Scripts only on event creation page
|
||||
- **Dependency Management:** Proper WordPress enqueueing
|
||||
- **Minification:** Production asset minification
|
||||
- **Caching:** Browser caching headers
|
||||
|
||||
#### AJAX Optimization
|
||||
- **Request Debouncing:** Reduced server requests
|
||||
- **Response Caching:** Client-side result caching
|
||||
- **Pagination:** Large result set pagination
|
||||
- **Compression:** Gzip response compression
|
||||
|
||||
### Backend Optimization
|
||||
|
||||
#### Database Queries
|
||||
- **Prepared Statements:** SQL injection prevention
|
||||
- **Query Optimization:** Efficient database queries
|
||||
- **Indexing:** Proper database indexing
|
||||
- **Caching:** WordPress object caching
|
||||
|
||||
#### Memory Management
|
||||
- **Object Lifecycle:** Proper object destruction
|
||||
- **Image Processing:** Optimized image handling
|
||||
- **Garbage Collection:** PHP memory management
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Guide
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### AI Assistant Not Working
|
||||
**Symptoms:** AI requests fail or timeout
|
||||
**Causes:**
|
||||
- API rate limiting exceeded
|
||||
- Network connectivity issues
|
||||
- Invalid input format
|
||||
**Solutions:**
|
||||
1. Check rate limit status
|
||||
2. Verify network connectivity
|
||||
3. Validate input format
|
||||
4. Check error logs
|
||||
|
||||
#### Search Not Returning Results
|
||||
**Symptoms:** Empty search results despite existing data
|
||||
**Causes:**
|
||||
- Insufficient search term length
|
||||
- Database connectivity issues
|
||||
- Permission problems
|
||||
**Solutions:**
|
||||
1. Ensure 2+ character search terms
|
||||
2. Verify database connection
|
||||
3. Check user permissions
|
||||
4. Clear search cache
|
||||
|
||||
#### Modal Forms Not Opening
|
||||
**Symptoms:** "Add New" buttons not working
|
||||
**Causes:**
|
||||
- JavaScript errors
|
||||
- Permission restrictions
|
||||
- Missing dependencies
|
||||
**Solutions:**
|
||||
1. Check browser console for errors
|
||||
2. Verify user role permissions
|
||||
3. Ensure WordPress media scripts loaded
|
||||
4. Clear browser cache
|
||||
|
||||
### Error Logging
|
||||
|
||||
#### WordPress Debug Integration
|
||||
```php
|
||||
// Enable debug logging
|
||||
define('WP_DEBUG', true);
|
||||
define('WP_DEBUG_LOG', true);
|
||||
|
||||
// Log custom events
|
||||
error_log('HVAC Event Creation: ' . $message);
|
||||
```
|
||||
|
||||
#### Custom Error Tracking
|
||||
- **Error Categorization:** System, user, validation errors
|
||||
- **Context Capture:** User, action, environment data
|
||||
- **Notification System:** Admin notifications for critical errors
|
||||
|
||||
### Performance Monitoring
|
||||
|
||||
#### Key Metrics
|
||||
- **Page Load Time:** Target < 2 seconds
|
||||
- **AJAX Response Time:** Target < 500ms
|
||||
- **Database Queries:** Monitor N+1 queries
|
||||
- **Memory Usage:** Monitor PHP memory consumption
|
||||
|
||||
#### Monitoring Tools
|
||||
- **WordPress Debug Bar:** Development debugging
|
||||
- **Query Monitor:** Database query analysis
|
||||
- **Server Monitoring:** Application performance monitoring
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The HVAC Community Events plugin provides a comprehensive, secure, and user-friendly event creation system. This documentation serves as the authoritative reference for developers, administrators, and users working with the system.
|
||||
|
||||
For additional support or feature requests, please refer to the project repository or contact the development team.
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.0
|
||||
**Next Review:** July 2025
|
||||
**Maintained By:** HVAC Development Team
|
||||
|
|
@ -1287,7 +1287,7 @@ class HVAC_Ajax_Handlers {
|
|||
$security_check = HVAC_Ajax_Security::verify_ajax_request(
|
||||
'create_organizer',
|
||||
HVAC_Ajax_Security::NONCE_GENERAL,
|
||||
array('hvac_trainer', 'hvac_master_trainer'),
|
||||
array('administrator', 'hvac_trainer', 'hvac_master_trainer'),
|
||||
false
|
||||
);
|
||||
|
||||
|
|
@ -1319,6 +1319,10 @@ class HVAC_Ajax_Handlers {
|
|||
'type' => 'text',
|
||||
'required' => false,
|
||||
'max_length' => 20
|
||||
),
|
||||
'organizer_featured_image' => array(
|
||||
'type' => 'text',
|
||||
'required' => false
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -1361,6 +1365,14 @@ class HVAC_Ajax_Handlers {
|
|||
update_post_meta($organizer_id, '_OrganizerPhone', $params['organizer_phone']);
|
||||
}
|
||||
|
||||
// Set featured image if provided
|
||||
if (!empty($params['organizer_featured_image'])) {
|
||||
$image_id = absint($params['organizer_featured_image']);
|
||||
if ($image_id && wp_attachment_is_image($image_id)) {
|
||||
set_post_thumbnail($organizer_id, $image_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Return created organizer data
|
||||
wp_send_json_success(array(
|
||||
'id' => $organizer_id,
|
||||
|
|
@ -1456,7 +1468,7 @@ class HVAC_Ajax_Handlers {
|
|||
$security_check = HVAC_Ajax_Security::verify_ajax_request(
|
||||
'create_venue',
|
||||
HVAC_Ajax_Security::NONCE_GENERAL,
|
||||
array('hvac_trainer', 'hvac_master_trainer'),
|
||||
array('administrator', 'hvac_trainer', 'hvac_master_trainer'),
|
||||
false
|
||||
);
|
||||
|
||||
|
|
@ -1509,6 +1521,10 @@ class HVAC_Ajax_Handlers {
|
|||
'type' => 'text',
|
||||
'required' => false,
|
||||
'max_length' => 20
|
||||
),
|
||||
'venue_featured_image' => array(
|
||||
'type' => 'text',
|
||||
'required' => false
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -1557,6 +1573,14 @@ class HVAC_Ajax_Handlers {
|
|||
}
|
||||
}
|
||||
|
||||
// Set featured image if provided
|
||||
if (!empty($params['venue_featured_image'])) {
|
||||
$image_id = absint($params['venue_featured_image']);
|
||||
if ($image_id && wp_attachment_is_image($image_id)) {
|
||||
set_post_thumbnail($venue_id, $image_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Build subtitle for display
|
||||
$subtitle_parts = array_filter(array(
|
||||
$params['venue_address'],
|
||||
|
|
|
|||
|
|
@ -172,6 +172,9 @@ class HVAC_Event_Form_Builder extends HVAC_Form_Builder {
|
|||
// Basic event fields
|
||||
$this->add_basic_event_fields();
|
||||
|
||||
// Featured image field
|
||||
$this->add_featured_image_field();
|
||||
|
||||
/**
|
||||
* Action hook for TEC ticketing integration
|
||||
*
|
||||
|
|
@ -323,6 +326,23 @@ class HVAC_Event_Form_Builder extends HVAC_Form_Builder {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add featured image field for event
|
||||
*/
|
||||
public function add_featured_image_field(): self {
|
||||
$featured_image_field = [
|
||||
'type' => 'custom',
|
||||
'name' => 'event_featured_image',
|
||||
'label' => 'Featured Image',
|
||||
'custom_html' => $this->render_media_upload_field('event_featured_image', 'Select Event Image'),
|
||||
'wrapper_class' => 'form-row featured-image-field'
|
||||
];
|
||||
|
||||
$this->add_field($featured_image_field);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add datetime fields for event scheduling
|
||||
*/
|
||||
|
|
@ -1475,7 +1495,9 @@ class HVAC_Event_Form_Builder extends HVAC_Form_Builder {
|
|||
*/
|
||||
private function render_searchable_organizer_selector(): string {
|
||||
$current_user = wp_get_current_user();
|
||||
$can_create = in_array('hvac_trainer', $current_user->roles) || in_array('hvac_master_trainer', $current_user->roles);
|
||||
$can_create = in_array('administrator', $current_user->roles) ||
|
||||
in_array('hvac_trainer', $current_user->roles) ||
|
||||
in_array('hvac_master_trainer', $current_user->roles);
|
||||
|
||||
return <<<HTML
|
||||
<div class="form-row organizer-selector-wrapper">
|
||||
|
|
@ -1569,7 +1591,9 @@ HTML;
|
|||
*/
|
||||
private function render_searchable_venue_selector(): string {
|
||||
$current_user = wp_get_current_user();
|
||||
$can_create = in_array('hvac_trainer', $current_user->roles) || in_array('hvac_master_trainer', $current_user->roles);
|
||||
$can_create = in_array('administrator', $current_user->roles) ||
|
||||
in_array('hvac_trainer', $current_user->roles) ||
|
||||
in_array('hvac_master_trainer', $current_user->roles);
|
||||
|
||||
return <<<HTML
|
||||
<div class="form-row venue-selector-wrapper">
|
||||
|
|
@ -1710,4 +1734,111 @@ HTML;
|
|||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render media upload field for featured images
|
||||
*
|
||||
* @param string $field_name The input field name
|
||||
* @param string $button_text The button text
|
||||
* @return string
|
||||
*/
|
||||
private function render_media_upload_field(string $field_name, string $button_text = 'Select Image'): string {
|
||||
ob_start();
|
||||
?>
|
||||
<div class="media-upload-field" data-field-name="<?php echo esc_attr($field_name); ?>">
|
||||
<div class="image-preview-container" style="margin-bottom: 10px;">
|
||||
<div class="image-preview" style="display: none; position: relative; max-width: 300px;">
|
||||
<img src="" alt="Preview" style="max-width: 100%; height: auto; border: 1px solid #ddd; border-radius: 4px;">
|
||||
<button type="button" class="remove-image" style="position: absolute; top: 5px; right: 5px; background: #d63638; color: white; border: none; border-radius: 50%; width: 24px; height: 24px; cursor: pointer; font-size: 12px;">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-controls">
|
||||
<button type="button" class="select-image-btn button button-secondary">
|
||||
<span class="dashicons dashicons-format-image" style="vertical-align: middle; margin-right: 5px;"></span>
|
||||
<?php echo esc_html($button_text); ?>
|
||||
</button>
|
||||
<input type="hidden" name="<?php echo esc_attr($field_name); ?>" class="image-id-input" value="">
|
||||
<input type="hidden" name="<?php echo esc_attr($field_name); ?>_url" class="image-url-input" value="">
|
||||
</div>
|
||||
<p class="description" style="margin-top: 5px;">
|
||||
Recommended size: 1200x630 pixels for optimal display across devices.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
// Initialize media upload for this field
|
||||
var fieldContainer = $('.media-upload-field[data-field-name="<?php echo esc_js($field_name); ?>"]');
|
||||
var selectBtn = fieldContainer.find('.select-image-btn');
|
||||
var removeBtn = fieldContainer.find('.remove-image');
|
||||
var imagePreview = fieldContainer.find('.image-preview');
|
||||
var previewImg = fieldContainer.find('.image-preview img');
|
||||
var imageIdInput = fieldContainer.find('.image-id-input');
|
||||
var imageUrlInput = fieldContainer.find('.image-url-input');
|
||||
|
||||
// WordPress media uploader
|
||||
var mediaUploader;
|
||||
|
||||
selectBtn.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// If the uploader object has already been created, reopen the dialog
|
||||
if (mediaUploader) {
|
||||
mediaUploader.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the media frame
|
||||
mediaUploader = wp.media({
|
||||
title: '<?php echo esc_js($button_text); ?>',
|
||||
button: {
|
||||
text: 'Select Image'
|
||||
},
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
}
|
||||
});
|
||||
|
||||
// When an image is selected, run a callback
|
||||
mediaUploader.on('select', function() {
|
||||
var attachment = mediaUploader.state().get('selection').first().toJSON();
|
||||
|
||||
// Update hidden inputs
|
||||
imageIdInput.val(attachment.id);
|
||||
imageUrlInput.val(attachment.url);
|
||||
|
||||
// Update preview
|
||||
previewImg.attr('src', attachment.url);
|
||||
previewImg.attr('alt', attachment.alt || attachment.title || 'Selected image');
|
||||
imagePreview.show();
|
||||
|
||||
// Update button text
|
||||
selectBtn.html('<span class="dashicons dashicons-format-image" style="vertical-align: middle; margin-right: 5px;"></span>Change Image');
|
||||
});
|
||||
|
||||
// Open the uploader dialog
|
||||
mediaUploader.open();
|
||||
});
|
||||
|
||||
// Remove image
|
||||
removeBtn.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Clear inputs
|
||||
imageIdInput.val('');
|
||||
imageUrlInput.val('');
|
||||
|
||||
// Hide preview
|
||||
imagePreview.hide();
|
||||
previewImg.attr('src', '');
|
||||
|
||||
// Reset button text
|
||||
selectBtn.html('<span class="dashicons dashicons-format-image" style="vertical-align: middle; margin-right: 5px;"></span><?php echo esc_js($button_text); ?>');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue