- Created comprehensive field mapping documentation for TEC v5.0.8
- Documented all meta keys, input selectors, and field types
- Built validation tests using correct TEC v5.0.8 selectors
- Verified working selectors through staging environment testing
- Added best practices guide with implementation patterns
- Included JavaScript and PHP code examples
- Documented common issues and solutions
- Added debugging tips and performance optimizations
Test results show successful field discovery and persistence:
- Title, Start Date, End Date, and URL fields verified working
- Cost field may be hidden when Events Tickets is active
- All date/time fields use expected selectors
- Venue and organizer fields use array notation in names
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
552 lines
No EOL
16 KiB
Markdown
552 lines
No EOL
16 KiB
Markdown
# The Events Calendar v5.0.8 Best Practices Guide
|
|
|
|
## Overview
|
|
This guide documents the best practices for working with The Events Calendar (TEC) v5.0.8 and Community Events 5.0.8, based on comprehensive testing and field validation.
|
|
|
|
## Verified Working Configuration
|
|
|
|
### Plugin Versions (Staging Environment)
|
|
- **The Events Calendar**: 6.14.2
|
|
- **The Events Calendar Community Events**: 5.0.8
|
|
- **Events Calendar Pro**: 7.6.3
|
|
- **WordPress**: Latest compatible version
|
|
|
|
## Field Selector Best Practices
|
|
|
|
### 1. Core Fields (Always Available)
|
|
These fields are standard WordPress fields and always work:
|
|
|
|
```javascript
|
|
const coreFields = {
|
|
title: '#title', // Event title
|
|
content: '#content', // Event description
|
|
excerpt: '#excerpt', // Event excerpt
|
|
status: '#post_status' // Post status
|
|
};
|
|
```
|
|
|
|
### 2. Date/Time Fields (Verified Working)
|
|
```javascript
|
|
const dateTimeFields = {
|
|
startDate: '#EventStartDate', // Format: MM/DD/YYYY
|
|
endDate: '#EventEndDate', // Format: MM/DD/YYYY
|
|
startTime: '#EventStartTime', // Format: HH:MM am/pm
|
|
endTime: '#EventEndTime', // Format: HH:MM am/pm
|
|
allDay: '#EventAllDay', // Checkbox
|
|
timezone: 'select[name="EventTimezone"]' // Dropdown
|
|
};
|
|
```
|
|
|
|
### 3. Event Details (Verified Working)
|
|
```javascript
|
|
const eventDetails = {
|
|
eventURL: '#EventURL', // External event website
|
|
showMap: '#EventShowMap', // Checkbox
|
|
showMapLink: '#EventShowMapLink' // Checkbox
|
|
};
|
|
```
|
|
|
|
### 4. Cost Fields (May Be Hidden)
|
|
**Note**: Cost fields may be hidden if Events Tickets plugin is active.
|
|
|
|
```javascript
|
|
const costFields = {
|
|
cost: '#EventCost', // May not be visible
|
|
currencySymbol: '#EventCurrencySymbol',
|
|
currencyCode: '#EventCurrencyCode'
|
|
};
|
|
|
|
// Check visibility before interacting
|
|
async function checkCostField(page) {
|
|
const costField = await page.$('#EventCost');
|
|
if (costField) {
|
|
const isVisible = await costField.isVisible();
|
|
if (!isVisible) {
|
|
console.log('Cost field is hidden - Events Tickets may be active');
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5. Venue Fields (Complex Selectors)
|
|
Venue fields use array notation in their name attributes:
|
|
|
|
```javascript
|
|
const venueFields = {
|
|
venueDropdown: 'select[name="venue[VenueID]"]',
|
|
venueName: 'input[name="venue[Venue]"]',
|
|
venueAddress: 'input[name="venue[Address]"]',
|
|
venueCity: 'input[name="venue[City]"]',
|
|
venueState: 'input[name="venue[State]"]',
|
|
venueZip: 'input[name="venue[Zip]"]',
|
|
venueCountry: 'select[name="venue[Country]"]'
|
|
};
|
|
```
|
|
|
|
### 6. Organizer Fields (Complex Selectors)
|
|
```javascript
|
|
const organizerFields = {
|
|
organizerDropdown: 'select[name="organizer[OrganizerID]"]',
|
|
organizerName: 'input[name="organizer[Organizer]"]',
|
|
organizerEmail: 'input[name="organizer[Email]"]',
|
|
organizerPhone: 'input[name="organizer[Phone]"]',
|
|
organizerWebsite: 'input[name="organizer[Website]"]'
|
|
};
|
|
```
|
|
|
|
## JavaScript Implementation Patterns
|
|
|
|
### Pattern 1: Flexible Field Access
|
|
Always check multiple possible selectors for maximum compatibility:
|
|
|
|
```javascript
|
|
async function getTECField(page, selectors) {
|
|
for (const selector of selectors) {
|
|
const element = await page.$(selector);
|
|
if (element && await element.isVisible()) {
|
|
return element;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Usage
|
|
const startDateField = await getTECField(page, [
|
|
'#EventStartDate',
|
|
'input[name="EventStartDate"]',
|
|
'.tribe-datetime-block input[name*="StartDate"]'
|
|
]);
|
|
```
|
|
|
|
### Pattern 2: Safe Field Updates
|
|
Always verify field exists and is visible before updating:
|
|
|
|
```javascript
|
|
async function updateTECField(page, selector, value) {
|
|
try {
|
|
const element = await page.$(selector);
|
|
if (!element) {
|
|
console.log(`Field not found: ${selector}`);
|
|
return false;
|
|
}
|
|
|
|
const isVisible = await element.isVisible();
|
|
if (!isVisible) {
|
|
console.log(`Field not visible: ${selector}`);
|
|
return false;
|
|
}
|
|
|
|
await element.fill(value);
|
|
return true;
|
|
} catch (error) {
|
|
console.log(`Error updating field: ${error.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pattern 3: Date Format Handling
|
|
TEC accepts multiple date formats. Be flexible:
|
|
|
|
```javascript
|
|
function formatDateForTEC(date) {
|
|
// TEC typically expects MM/DD/YYYY in the UI
|
|
const d = new Date(date);
|
|
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
const day = String(d.getDate()).padStart(2, '0');
|
|
const year = d.getFullYear();
|
|
return `${month}/${day}/${year}`;
|
|
}
|
|
|
|
// For meta updates (database), use YYYY-MM-DD HH:MM:SS
|
|
function formatDateForMeta(date) {
|
|
const d = new Date(date);
|
|
return d.toISOString().slice(0, 19).replace('T', ' ');
|
|
}
|
|
```
|
|
|
|
## PHP Implementation Patterns
|
|
|
|
### Pattern 1: Direct Meta Updates
|
|
When updating events programmatically:
|
|
|
|
```php
|
|
function update_tec_event($event_id, $data) {
|
|
// Update core post data
|
|
wp_update_post([
|
|
'ID' => $event_id,
|
|
'post_title' => $data['title'],
|
|
'post_content' => $data['description']
|
|
]);
|
|
|
|
// Update TEC meta fields
|
|
update_post_meta($event_id, '_EventStartDate', $data['start_date']);
|
|
update_post_meta($event_id, '_EventEndDate', $data['end_date']);
|
|
update_post_meta($event_id, '_EventStartDateUTC', get_gmt_from_date($data['start_date']));
|
|
update_post_meta($event_id, '_EventEndDateUTC', get_gmt_from_date($data['end_date']));
|
|
|
|
// Calculate duration in seconds
|
|
$start = strtotime($data['start_date']);
|
|
$end = strtotime($data['end_date']);
|
|
$duration = $end - $start;
|
|
update_post_meta($event_id, '_EventDuration', $duration);
|
|
|
|
// Update venue if provided
|
|
if (!empty($data['venue_id'])) {
|
|
update_post_meta($event_id, '_EventVenueID', $data['venue_id']);
|
|
}
|
|
|
|
// Update organizer if provided
|
|
if (!empty($data['organizer_id'])) {
|
|
update_post_meta($event_id, '_EventOrganizerID', $data['organizer_id']);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pattern 2: Creating Events with Full Metadata
|
|
```php
|
|
function create_tec_event($data) {
|
|
// Create the event post
|
|
$event_id = wp_insert_post([
|
|
'post_type' => 'tribe_events',
|
|
'post_title' => $data['title'],
|
|
'post_content' => $data['description'],
|
|
'post_status' => 'publish',
|
|
'post_author' => get_current_user_id()
|
|
]);
|
|
|
|
if (!is_wp_error($event_id)) {
|
|
// Add all TEC meta fields
|
|
$meta_fields = [
|
|
'_EventStartDate' => $data['start_date'],
|
|
'_EventEndDate' => $data['end_date'],
|
|
'_EventStartDateUTC' => get_gmt_from_date($data['start_date']),
|
|
'_EventEndDateUTC' => get_gmt_from_date($data['end_date']),
|
|
'_EventCost' => $data['cost'] ?? '',
|
|
'_EventCurrencySymbol' => '$',
|
|
'_EventURL' => $data['url'] ?? '',
|
|
'_EventShowMap' => '1',
|
|
'_EventShowMapLink' => '1',
|
|
'_EventTimezone' => get_option('timezone_string'),
|
|
'_EventTimezoneAbbr' => date('T'),
|
|
'_EventOrigin' => 'events-calendar'
|
|
];
|
|
|
|
foreach ($meta_fields as $key => $value) {
|
|
update_post_meta($event_id, $key, $value);
|
|
}
|
|
}
|
|
|
|
return $event_id;
|
|
}
|
|
```
|
|
|
|
## Testing Best Practices
|
|
|
|
### 1. Always Test Field Visibility
|
|
```javascript
|
|
// Before running tests, check which fields are actually available
|
|
async function auditTECFields(page) {
|
|
const audit = {
|
|
visible: [],
|
|
hidden: [],
|
|
missing: []
|
|
};
|
|
|
|
const allSelectors = {
|
|
...coreFields,
|
|
...dateTimeFields,
|
|
...eventDetails,
|
|
...costFields,
|
|
...venueFields,
|
|
...organizerFields
|
|
};
|
|
|
|
for (const [name, selector] of Object.entries(allSelectors)) {
|
|
const element = await page.$(selector);
|
|
if (!element) {
|
|
audit.missing.push(name);
|
|
} else if (await element.isVisible()) {
|
|
audit.visible.push(name);
|
|
} else {
|
|
audit.hidden.push(name);
|
|
}
|
|
}
|
|
|
|
return audit;
|
|
}
|
|
```
|
|
|
|
### 2. Test Data Persistence
|
|
Always verify that changes persist after save:
|
|
|
|
```javascript
|
|
async function testFieldPersistence(page, selector, testValue) {
|
|
// Set value
|
|
await page.fill(selector, testValue);
|
|
|
|
// Save
|
|
await page.click('#publish');
|
|
await page.waitForSelector('.notice-success');
|
|
|
|
// Reload page
|
|
await page.reload();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check if value persisted
|
|
const currentValue = await page.$eval(selector, el => el.value);
|
|
return currentValue === testValue;
|
|
}
|
|
```
|
|
|
|
### 3. Handle AJAX-Loaded Elements
|
|
Some TEC elements load via AJAX:
|
|
|
|
```javascript
|
|
async function waitForTECElements(page) {
|
|
// Wait for TEC admin scripts to load
|
|
await page.waitForFunction(() => {
|
|
return window.tribe && window.tribe.events;
|
|
}, { timeout: 10000 });
|
|
|
|
// Additional wait for dynamic elements
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
```
|
|
|
|
## Common Issues and Solutions
|
|
|
|
### Issue 1: Date Format Inconsistencies
|
|
**Problem**: Different installations may use different date formats.
|
|
**Solution**: Check WordPress date format setting and adapt:
|
|
|
|
```javascript
|
|
async function getDateFormat(page) {
|
|
return await page.evaluate(() => {
|
|
// Check if TEC provides format
|
|
if (window.tribe_events_calendar && window.tribe_events_calendar.date_format) {
|
|
return window.tribe_events_calendar.date_format;
|
|
}
|
|
// Default to US format
|
|
return 'MM/DD/YYYY';
|
|
});
|
|
}
|
|
```
|
|
|
|
### Issue 2: Venue/Organizer Dropdowns Not Populating
|
|
**Problem**: Select2/Chosen dropdowns may not populate immediately.
|
|
**Solution**: Wait for initialization and trigger manually if needed:
|
|
|
|
```javascript
|
|
async function selectVenue(page, venueId) {
|
|
// Wait for Select2 initialization
|
|
await page.waitForFunction(() => {
|
|
return jQuery && jQuery('select[name="venue[VenueID]"]').data('select2');
|
|
}, { timeout: 5000 }).catch(() => {});
|
|
|
|
// Try Select2 method first
|
|
await page.evaluate((id) => {
|
|
jQuery('select[name="venue[VenueID]"]').val(id).trigger('change');
|
|
}, venueId);
|
|
|
|
// Fallback to standard select
|
|
await page.selectOption('select[name="venue[VenueID]"]', venueId).catch(() => {});
|
|
}
|
|
```
|
|
|
|
### Issue 3: Cost Field Hidden by Events Tickets
|
|
**Problem**: When Events Tickets is active, cost field is managed differently.
|
|
**Solution**: Check for alternative cost input methods:
|
|
|
|
```javascript
|
|
async function setCost(page, amount) {
|
|
// Try standard cost field
|
|
const standardField = await page.$('#EventCost');
|
|
if (standardField && await standardField.isVisible()) {
|
|
await standardField.fill(amount);
|
|
return true;
|
|
}
|
|
|
|
// Check for Events Tickets price field
|
|
const ticketField = await page.$('input[name="_tribe_ticket_price"]');
|
|
if (ticketField && await ticketField.isVisible()) {
|
|
await ticketField.fill(amount);
|
|
return true;
|
|
}
|
|
|
|
console.log('Cost field not available - may need to create ticket');
|
|
return false;
|
|
}
|
|
```
|
|
|
|
## Migration from Older Versions
|
|
|
|
### Key Changes from TEC v4.x to v5.x
|
|
1. **Block Editor Support**: v5.x includes Gutenberg block support
|
|
2. **Updated Meta Keys**: Some meta keys have changed format
|
|
3. **New REST API**: Enhanced REST API endpoints
|
|
4. **Modified Admin UI**: Updated admin interface selectors
|
|
|
|
### Backward Compatibility
|
|
Most v4.x selectors still work in v5.x, but always test:
|
|
|
|
```javascript
|
|
const legacySelectors = {
|
|
// v4.x selectors that may still work
|
|
'event-cost': '.tribe-event-cost input',
|
|
'event-website': '.tribe-event-website input',
|
|
'venue-select': '#saved_venue'
|
|
};
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### 1. Batch Meta Updates
|
|
When updating multiple fields, batch the updates:
|
|
|
|
```php
|
|
function batch_update_event_meta($event_id, $meta_data) {
|
|
global $wpdb;
|
|
|
|
// Begin transaction
|
|
$wpdb->query('START TRANSACTION');
|
|
|
|
try {
|
|
foreach ($meta_data as $key => $value) {
|
|
update_post_meta($event_id, $key, $value);
|
|
}
|
|
|
|
// Commit all changes
|
|
$wpdb->query('COMMIT');
|
|
|
|
// Clear TEC cache
|
|
tribe_cache()->delete('', 'save_post');
|
|
|
|
return true;
|
|
} catch (Exception $e) {
|
|
$wpdb->query('ROLLBACK');
|
|
return false;
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Cache Field Selectors
|
|
Cache discovered selectors for better performance:
|
|
|
|
```javascript
|
|
class TECFieldCache {
|
|
constructor() {
|
|
this.cache = new Map();
|
|
}
|
|
|
|
async getField(page, fieldName, selectors) {
|
|
// Check cache first
|
|
if (this.cache.has(fieldName)) {
|
|
const cached = this.cache.get(fieldName);
|
|
const element = await page.$(cached);
|
|
if (element && await element.isVisible()) {
|
|
return element;
|
|
}
|
|
}
|
|
|
|
// Find and cache working selector
|
|
for (const selector of selectors) {
|
|
const element = await page.$(selector);
|
|
if (element && await element.isVisible()) {
|
|
this.cache.set(fieldName, selector);
|
|
return element;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
### 1. Always Sanitize Input
|
|
```php
|
|
// Sanitize event data before saving
|
|
$clean_data = [
|
|
'title' => sanitize_text_field($data['title']),
|
|
'description' => wp_kses_post($data['description']),
|
|
'start_date' => sanitize_text_field($data['start_date']),
|
|
'cost' => floatval($data['cost']),
|
|
'url' => esc_url_raw($data['url'])
|
|
];
|
|
```
|
|
|
|
### 2. Verify Capabilities
|
|
```php
|
|
// Check user can edit events
|
|
if (!current_user_can('edit_tribe_events')) {
|
|
wp_die('Insufficient permissions');
|
|
}
|
|
```
|
|
|
|
### 3. Validate Nonces
|
|
```php
|
|
// Always verify nonces in AJAX handlers
|
|
if (!wp_verify_nonce($_POST['nonce'], 'tec_event_edit')) {
|
|
wp_die('Security check failed');
|
|
}
|
|
```
|
|
|
|
## Debugging Tips
|
|
|
|
### 1. Enable TEC Debug Mode
|
|
Add to wp-config.php:
|
|
```php
|
|
define('TRIBE_EVENTS_DEBUG', true);
|
|
```
|
|
|
|
### 2. Log Field Discovery
|
|
```javascript
|
|
async function debugFieldDiscovery(page) {
|
|
const results = await page.evaluate(() => {
|
|
const fields = {};
|
|
|
|
// Find all inputs with Event in name/id
|
|
document.querySelectorAll('input[id*="Event"], input[name*="Event"]').forEach(el => {
|
|
fields[el.id || el.name] = {
|
|
type: el.type,
|
|
value: el.value,
|
|
visible: el.offsetParent !== null
|
|
};
|
|
});
|
|
|
|
return fields;
|
|
});
|
|
|
|
console.log('Discovered fields:', JSON.stringify(results, null, 2));
|
|
return results;
|
|
}
|
|
```
|
|
|
|
### 3. Monitor Meta Updates
|
|
```php
|
|
// Hook to log meta updates
|
|
add_action('updated_post_meta', function($meta_id, $object_id, $meta_key, $meta_value) {
|
|
if (get_post_type($object_id) === 'tribe_events') {
|
|
error_log("TEC Meta Update: Post $object_id, Key: $meta_key, Value: " . print_r($meta_value, true));
|
|
}
|
|
}, 10, 4);
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
Working with TEC v5.0.8 requires understanding:
|
|
1. The correct field selectors for each component
|
|
2. How to handle dynamic/AJAX-loaded elements
|
|
3. The proper meta key structure for database operations
|
|
4. Date format handling across different contexts
|
|
5. Compatibility considerations with other Events Calendar plugins
|
|
|
|
By following these best practices, you can reliably interact with TEC events both through the UI and programmatically.
|
|
|
|
## References
|
|
- [TEC Developer Documentation](https://theeventscalendar.com/knowledgebase/)
|
|
- [TEC GitHub Repository](https://github.com/the-events-calendar/the-events-calendar)
|
|
- [WordPress Codex - Custom Post Types](https://codex.wordpress.org/Post_Types)
|
|
- [WordPress Developer - Meta API](https://developer.wordpress.org/reference/functions/update_post_meta/) |