- Update hook names from tribe_* to tec_* prefix for TEC 5.0+ compatibility - Replace non-existent tribe_events_community_submission_before_save with actual tec_events_community_before_save_submission - Replace non-existent tribe_events_community_submission_success with actual tribe_community_event_save_updated - Update method signatures to match correct hook parameters - Maintain WordPress transient caching implementation for performance - Remove JavaScript form override to prevent security conflicts - Add proper debug logging for hook validation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
513 lines
No EOL
20 KiB
JavaScript
513 lines
No EOL
20 KiB
JavaScript
/**
|
|
* HVAC REST API Event Submission System
|
|
* Achieves 100% field control by bypassing TEC Community Events limitations
|
|
* Uses TEC REST API to submit events with ALL WordPress fields including excerpt
|
|
*/
|
|
|
|
(function($) {
|
|
'use strict';
|
|
|
|
const HVACRestEventSubmission = {
|
|
|
|
// REST API endpoints
|
|
apiEndpoint: '/wp-json/tribe/events/v1/events',
|
|
eventId: null, // Will be set if editing
|
|
|
|
/**
|
|
* Initialize the REST API submission system
|
|
*/
|
|
init: function() {
|
|
console.log('[HVAC REST] Initializing REST API Event Submission System');
|
|
|
|
// Check if we're in edit mode
|
|
if (window.hvacEditEventId) {
|
|
this.eventId = window.hvacEditEventId;
|
|
console.log('[HVAC REST] Edit mode - Event ID:', this.eventId);
|
|
}
|
|
|
|
// Enhance existing form or create new submission handler
|
|
this.attachSubmitHandler();
|
|
this.enhanceFormFields();
|
|
|
|
// If editing, load existing excerpt
|
|
if (this.eventId) {
|
|
this.loadExistingExcerpt();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Enhance form with additional fields not supported by TEC frontend
|
|
*/
|
|
enhanceFormFields: function() {
|
|
// Add excerpt field if not present
|
|
if (!$('#event_excerpt').length && $('#tribe-community-events').length) {
|
|
const excerptHTML = `
|
|
<div class="tribe-section tribe-section-excerpt">
|
|
<div class="tribe-section-header">
|
|
<h3>Event Summary</h3>
|
|
<p class="tribe-section-description">
|
|
Brief summary for search results and previews (excerpt)
|
|
</p>
|
|
</div>
|
|
<div class="tribe-section-content">
|
|
<textarea
|
|
id="event_excerpt"
|
|
name="excerpt"
|
|
rows="3"
|
|
class="tribe-common-form-control-text__input"
|
|
placeholder="Enter a brief summary of your event..."
|
|
></textarea>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Insert after description section
|
|
$('.tribe-section-content').first().parent().after(excerptHTML);
|
|
console.log('[HVAC REST] Added excerpt field to form');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Initialize form enhancement without overriding TEC submission
|
|
*
|
|
* Uses WordPress filter hooks for TEC integration instead of JavaScript overrides.
|
|
* This prevents "Security check failed" errors by maintaining TEC's native security flow.
|
|
*/
|
|
attachSubmitHandler: function() {
|
|
console.log('[HVAC REST] TEC form enhancement initialized - using WordPress filter hooks for integration');
|
|
|
|
// Enhance form with additional UI improvements without intercepting submission
|
|
this.enhanceFormUI();
|
|
},
|
|
|
|
/**
|
|
* Enhance form UI without intercepting submission
|
|
*
|
|
* Provides visual feedback and validation while preserving TEC native functionality
|
|
*/
|
|
enhanceFormUI: function() {
|
|
const $form = $('#tribe-community-events form');
|
|
|
|
if (!$form.length) {
|
|
console.log('[HVAC REST] TEC form not found for UI enhancement');
|
|
return;
|
|
}
|
|
|
|
// Add visual feedback for form submission (without prevention)
|
|
$form.on('submit', function() {
|
|
console.log('[HVAC REST] Form submitted - TEC handling natively with WordPress filters');
|
|
});
|
|
|
|
// Enhance excerpt field if present
|
|
const $excerptField = $form.find('[name="excerpt"]');
|
|
if ($excerptField.length) {
|
|
$excerptField.attr('placeholder', 'Brief event description...');
|
|
}
|
|
|
|
// Add form validation helpers (non-blocking)
|
|
this.addFormValidationHelpers($form);
|
|
},
|
|
|
|
/**
|
|
* Add non-blocking form validation helpers
|
|
*/
|
|
addFormValidationHelpers: function($form) {
|
|
// Add visual feedback for required fields
|
|
$form.find('input[required], textarea[required]').on('blur', function() {
|
|
const $field = $(this);
|
|
if (!$field.val().trim()) {
|
|
$field.addClass('hvac-field-warning');
|
|
} else {
|
|
$field.removeClass('hvac-field-warning');
|
|
}
|
|
});
|
|
|
|
// Add CSS for validation styling
|
|
if (!$('style#hvac-form-validation').length) {
|
|
$('<style id="hvac-form-validation">')
|
|
.text('.hvac-field-warning { border-color: #ffa500 !important; background-color: #fff5e6; }')
|
|
.appendTo('head');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Collect all form data including enhanced fields
|
|
*/
|
|
collectFormData: function($form) {
|
|
const data = {
|
|
// Core fields
|
|
title: $form.find('#post_title, input[name="post_title"]').val(),
|
|
description: this.getEditorContent(),
|
|
excerpt: $form.find('#event_excerpt, textarea[name="excerpt"]').val() || '',
|
|
status: 'publish',
|
|
|
|
// Date/Time fields
|
|
start_date: this.formatDateTime(
|
|
$form.find('input[name="EventStartDate"]').val(),
|
|
$form.find('input[name="EventStartTime"]').val()
|
|
),
|
|
end_date: this.formatDateTime(
|
|
$form.find('input[name="EventEndDate"]').val(),
|
|
$form.find('input[name="EventEndTime"]').val()
|
|
),
|
|
all_day: $form.find('#allDayCheckbox').is(':checked'),
|
|
|
|
// Venue data
|
|
venue: this.collectVenueData($form),
|
|
|
|
// Organizer data
|
|
organizer: this.collectOrganizerData($form),
|
|
|
|
// Categories (array of IDs)
|
|
categories: this.collectCategories($form),
|
|
|
|
// Tags (array of names)
|
|
tags: this.collectTags($form),
|
|
|
|
// Featured image
|
|
featured_media: $form.find('input[name="_thumbnail_id"]').val() || 0,
|
|
|
|
// Event cost
|
|
cost: $form.find('#EventCost, input[name="EventCost"]').val() || '',
|
|
|
|
// Event URL
|
|
website: $form.find('#EventURL, input[name="EventURL"]').val() || ''
|
|
};
|
|
|
|
console.log('[HVAC REST] Collected form data:', data);
|
|
return data;
|
|
},
|
|
|
|
/**
|
|
* Get content from TinyMCE or textarea
|
|
*/
|
|
getEditorContent: function() {
|
|
// Try TinyMCE first
|
|
if (typeof tinymce !== 'undefined') {
|
|
const editor = tinymce.get('tcepostcontent') || tinymce.get('post_content');
|
|
if (editor) {
|
|
return editor.getContent();
|
|
}
|
|
}
|
|
|
|
// Fallback to textarea
|
|
return $('#tcepostcontent, #post_content, textarea[name="post_content"]').val() || '';
|
|
},
|
|
|
|
/**
|
|
* Format date and time for REST API
|
|
*/
|
|
formatDateTime: function(date, time) {
|
|
if (!date) return '';
|
|
|
|
// Parse date (MM/DD/YYYY or YYYY-MM-DD)
|
|
let dateObj;
|
|
if (date.includes('/')) {
|
|
const parts = date.split('/');
|
|
dateObj = new Date(parts[2], parts[0] - 1, parts[1]);
|
|
} else {
|
|
dateObj = new Date(date);
|
|
}
|
|
|
|
// Parse time if provided
|
|
if (time) {
|
|
const timeParts = time.match(/(\d+):(\d+)\s*(am|pm)?/i);
|
|
if (timeParts) {
|
|
let hours = parseInt(timeParts[1]);
|
|
const minutes = parseInt(timeParts[2]);
|
|
const meridiem = timeParts[3];
|
|
|
|
if (meridiem) {
|
|
if (meridiem.toLowerCase() === 'pm' && hours !== 12) {
|
|
hours += 12;
|
|
} else if (meridiem.toLowerCase() === 'am' && hours === 12) {
|
|
hours = 0;
|
|
}
|
|
}
|
|
|
|
dateObj.setHours(hours, minutes, 0);
|
|
}
|
|
}
|
|
|
|
// Format as YYYY-MM-DD HH:MM:SS
|
|
return dateObj.toISOString().slice(0, 19).replace('T', ' ');
|
|
},
|
|
|
|
/**
|
|
* Collect venue data
|
|
*/
|
|
collectVenueData: function($form) {
|
|
const venueId = $form.find('#saved_tribe_venue').val();
|
|
|
|
if (venueId && venueId !== '0') {
|
|
return { id: venueId };
|
|
}
|
|
|
|
// New venue data
|
|
return {
|
|
venue: $form.find('input[name="venue[Venue]"]').val(),
|
|
address: $form.find('input[name="venue[Address]"]').val(),
|
|
city: $form.find('input[name="venue[City]"]').val(),
|
|
state_province: $form.find('#StateProvinceText').val(),
|
|
zip: $form.find('#EventZip').val(),
|
|
country: $form.find('#EventCountry').val(),
|
|
phone: $form.find('#EventPhone').val(),
|
|
website: $form.find('#EventWebsite').val()
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Collect organizer data
|
|
*/
|
|
collectOrganizerData: function($form) {
|
|
const organizerId = $form.find('#saved_tribe_organizer').val();
|
|
|
|
if (organizerId && organizerId !== '0') {
|
|
return { id: organizerId };
|
|
}
|
|
|
|
// New organizer data
|
|
return {
|
|
organizer: $form.find('input[name="organizer[Organizer]"]').val(),
|
|
phone: $form.find('#organizer-phone').val(),
|
|
email: $form.find('#organizer-email').val(),
|
|
website: $form.find('#organizer-website').val()
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Collect selected categories
|
|
*/
|
|
collectCategories: function($form) {
|
|
const categories = [];
|
|
|
|
// Checkboxes
|
|
$form.find('input[name="tax_input[tribe_events_cat][]"]:checked').each(function() {
|
|
categories.push($(this).val());
|
|
});
|
|
|
|
// Multi-select
|
|
const selected = $form.find('select[name="tax_input[tribe_events_cat][]"]').val();
|
|
if (selected) {
|
|
categories.push(...(Array.isArray(selected) ? selected : [selected]));
|
|
}
|
|
|
|
return categories;
|
|
},
|
|
|
|
/**
|
|
* Collect tags
|
|
*/
|
|
collectTags: function($form) {
|
|
const tags = [];
|
|
const tagInput = $form.find('input[name="tax_input[post_tag]"], #event-tags').val();
|
|
|
|
if (tagInput) {
|
|
// Split by comma and trim
|
|
return tagInput.split(',').map(tag => tag.trim()).filter(tag => tag);
|
|
}
|
|
|
|
return tags;
|
|
},
|
|
|
|
/**
|
|
* Submit event data via REST API
|
|
*/
|
|
submitViaRestAPI: function(eventData) {
|
|
const self = this;
|
|
|
|
// Show loading state
|
|
this.showLoadingState();
|
|
|
|
// Determine if we're creating or updating
|
|
const isUpdate = this.eventId && this.eventId > 0;
|
|
const requestUrl = isUpdate ?
|
|
this.apiEndpoint + '/' + this.eventId :
|
|
this.apiEndpoint;
|
|
const requestMethod = isUpdate ? 'PUT' : 'POST';
|
|
|
|
console.log('[HVAC REST] ' + (isUpdate ? 'Updating' : 'Creating') + ' event...');
|
|
|
|
// Prepare form data (REST API requires application/x-www-form-urlencoded)
|
|
const formData = new URLSearchParams();
|
|
|
|
// Add all fields
|
|
formData.append('title', eventData.title);
|
|
formData.append('description', eventData.description);
|
|
formData.append('excerpt', eventData.excerpt); // This is the key field!
|
|
formData.append('status', eventData.status);
|
|
formData.append('start_date', eventData.start_date);
|
|
formData.append('end_date', eventData.end_date);
|
|
formData.append('all_day', eventData.all_day ? '1' : '0');
|
|
|
|
// Add optional fields
|
|
if (eventData.cost) formData.append('cost', eventData.cost);
|
|
if (eventData.website) formData.append('website', eventData.website);
|
|
if (eventData.featured_media) formData.append('featured_media', eventData.featured_media);
|
|
|
|
// Add venue data
|
|
if (eventData.venue.id) {
|
|
formData.append('venue[id]', eventData.venue.id);
|
|
} else if (eventData.venue.venue) {
|
|
Object.keys(eventData.venue).forEach(key => {
|
|
formData.append(`venue[${key}]`, eventData.venue[key]);
|
|
});
|
|
}
|
|
|
|
// Add organizer data
|
|
if (eventData.organizer.id) {
|
|
formData.append('organizer[id]', eventData.organizer.id);
|
|
} else if (eventData.organizer.organizer) {
|
|
Object.keys(eventData.organizer).forEach(key => {
|
|
formData.append(`organizer[${key}]`, eventData.organizer[key]);
|
|
});
|
|
}
|
|
|
|
// Add categories
|
|
eventData.categories.forEach(cat => {
|
|
formData.append('categories[]', cat);
|
|
});
|
|
|
|
// Add tags
|
|
eventData.tags.forEach(tag => {
|
|
formData.append('tags[]', tag);
|
|
});
|
|
|
|
// Make REST API request
|
|
$.ajax({
|
|
url: requestUrl,
|
|
method: requestMethod,
|
|
data: formData.toString(),
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'X-WP-Nonce': hvac_ajax.nonce // Use existing nonce
|
|
},
|
|
success: function(response) {
|
|
console.log('[HVAC REST] Event created successfully:', response);
|
|
self.handleSuccess(response);
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error('[HVAC REST] Event creation failed:', error);
|
|
self.handleError(xhr, status, error);
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Show loading state
|
|
*/
|
|
showLoadingState: function() {
|
|
const isUpdate = this.eventId && this.eventId > 0;
|
|
const buttonText = isUpdate ? 'Updating Event...' : 'Creating Event...';
|
|
const loadingText = isUpdate ?
|
|
'Updating your event with all fields...' :
|
|
'Creating your event with all fields...';
|
|
|
|
// Disable submit button
|
|
$('button[type="submit"], input[type="submit"]').prop('disabled', true).text(buttonText);
|
|
|
|
// Show loading indicator
|
|
if (!$('#hvac-rest-loading').length) {
|
|
$('<div id="hvac-rest-loading" class="tribe-common-b1">' + loadingText + '</div>')
|
|
.insertAfter('.tribe-events-community-footer');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handle successful event creation
|
|
*/
|
|
handleSuccess: function(response) {
|
|
const isUpdate = this.eventId && this.eventId > 0;
|
|
const successMessage = isUpdate ?
|
|
'Event updated successfully with 100% field control!' :
|
|
'Event created successfully with 100% field control!';
|
|
|
|
// Show success message
|
|
const successHTML = `
|
|
<div class="tribe-events-notices">
|
|
<div class="tribe-events-notices-success">
|
|
<p>${successMessage}</p>
|
|
<p>Event ID: ${response.id}</p>
|
|
<p><a href="${response.url}">View Event</a></p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
$('#tribe-community-events').prepend(successHTML);
|
|
|
|
// Scroll to top
|
|
$('html, body').animate({ scrollTop: 0 }, 'slow');
|
|
|
|
// Optionally redirect after delay
|
|
setTimeout(function() {
|
|
if (response.url) {
|
|
window.location.href = response.url;
|
|
}
|
|
}, 3000);
|
|
},
|
|
|
|
/**
|
|
* Handle error
|
|
*/
|
|
handleError: function(xhr, status, error) {
|
|
// Show error message
|
|
const errorHTML = `
|
|
<div class="tribe-events-notices">
|
|
<div class="tribe-events-notices-error">
|
|
<p>Error creating event: ${error}</p>
|
|
<p>Please check the form and try again.</p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
$('#tribe-community-events').prepend(errorHTML);
|
|
|
|
// Re-enable submit button
|
|
$('button[type="submit"], input[type="submit"]').prop('disabled', false).text('Submit Event');
|
|
|
|
// Remove loading indicator
|
|
$('#hvac-rest-loading').remove();
|
|
|
|
// Log detailed error for debugging
|
|
console.error('[HVAC REST] Full error response:', xhr.responseJSON);
|
|
},
|
|
|
|
/**
|
|
* Load existing excerpt when editing
|
|
*/
|
|
loadExistingExcerpt: function() {
|
|
const self = this;
|
|
|
|
// Fetch event data from REST API
|
|
$.ajax({
|
|
url: this.apiEndpoint + '/' + this.eventId,
|
|
method: 'GET',
|
|
success: function(response) {
|
|
console.log('[HVAC REST] Loaded event data:', response);
|
|
|
|
// Populate excerpt field if it exists
|
|
if (response.excerpt && response.excerpt.rendered) {
|
|
const excerptField = $('#event_excerpt');
|
|
if (excerptField.length) {
|
|
// Strip HTML tags from rendered excerpt
|
|
const excerptText = $('<div>').html(response.excerpt.rendered).text();
|
|
excerptField.val(excerptText);
|
|
console.log('[HVAC REST] Populated excerpt field');
|
|
}
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error('[HVAC REST] Failed to load event data:', error);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// Initialize when document is ready
|
|
$(document).ready(function() {
|
|
if ($('#tribe-community-events').length > 0) {
|
|
HVACRestEventSubmission.init();
|
|
}
|
|
});
|
|
|
|
})(jQuery); |