The Events Calendar is one of the most widely used and popular events plugins for WordPress and the one we recommend to our clients.
Out of the box, it provides a lot of functionality to feature events, and with the add-ons that can be installed, the functionality can be extended to meet your particular needs.
We’ve written a lot about The Events Calendar in the past, from hiding events in WordPress search results to implementing tickets and RSVPs for recurring events to upgrading the calendar to new v2 views.
One request we’ve received from our clients is the ability to check for venue conflicts when adding an event. The Events Calendar itself does not provide any support for this, and although there is a third-party plugin available, it has not been updated or maintained for several years now.
Checking for venue conflicts requires enqueuing your own script in the admin when an Events Calendar event is being added or edited and making an AJAX request to a callback function that checks for conflicts with the current venue and dates selected. Although this can be added as a plugin, for this post, I’ll show you how to add it to your theme.
First, add this function to your theme’s functions.php file. It adds the JavaScript file located inside the JS folder of the theme to the admin when adding/editing an event.
// Add scripts to admin
add_action( 'admin_enqueue_scripts', 'dgtlnk_admin_enqueue_scripts' );
function dgtlnk_admin_enqueue_script($hook) {
global $post_type;
if ( ('post-new.php' != $hook && 'post.php' != $hook ) || 'tribe_events' != $post_type ) {
return;
}
wp_enqueue_script( 'dgtlnk-admin-scripts', get_stylesheet_directory_uri() . '/js/dgtlnk-admin.js', array('jquery'), '1.0', true );
wp_localize_script( 'dgtlnk-admin-scripts', 'venuecheck', array(
'ajax_url' => admin_url( 'admin-ajax.php' )
));
}
In that JavaScript file, add these functions, which add a new table row under the venue selection dropdown with a message to select the event start and end date and a venue before checking for venue conflicts. It also adds a button labeled “Check Now” that, when pressed, will call the venuecheck_checkVenues
function to check for venue conflicts.
jQuery(function($) {
$( document ).ready(function() {
// Add Venue Check table row under Venue dropdown
$("#event_tribe_venue tr.saved-linked-post").after(
"<tr id=\"dgtlnk-venue-check\">
<td class=\"dgtlnk-venue-check--label\">Venue Conflicts: </td>
<td class=\"dgtlnk-venue-check--content\">
<p><strong><em>In order to get correct results, please make sure that the Event Start/End date/time has been added and a Venue has been selected before this button is pressed.</em></strong></p>
<div class=\"dgtlnk-venue-check--content__inner\"></div>
<a class=\"button\" href=\"#\">Check Now</a>
</td>
</tr>"
);
// Call function for venue check when 'Check Now' button is clicked
$('#event_tribe_venue').on('click', '.dgtlnk-venue-check--content a.button', function(e){
e.preventDefault();
venuecheck_checkVenues(
$(this),
venuecheck_getEventDate('Start'),
venuecheck_getEventDate('End'),
$("#post_ID").val(),
$(this).parents('tbody').find("#saved_tribe_venue").select2("data")[0].id
);
});
});
There is also a venuecheck_getEventDate
function, which is used to get the start and end date for the current event and pass it to the venuecheck_checkVenues
function as a parameter. The venuecheck_getEventDate
function is defined in the same JavaScript file.
// Get Start/End date/times for current event
function venuecheck_getEventDate(dateType) {
var eventDate;
var allDay = jQuery("#allDayCheckbox").prop('checked') == true ? true : false;
var date = jQuery('#Event' + dateType + 'Date').datepicker('getDate');
if (allDay) {
if (dateType === 'Start') {
date.setUTCHours(0);
date.setUTCMinutes(0, 0);
}
if (dateType === 'End') {
date.setUTCHours(23);
date.setUTCMinutes(59, 59);
}
} else {
var time = jQuery('#Event' + dateType + 'Time').val();
var meridian = time.slice(-2).toLowerCase();
time = time.slice(0, -2);
var timeSplit = time.split(':');
var hour = parseInt(timeSplit[0]);
var hour = meridian === 'pm' && hour < 12 ? hour + 12 : hour;
var minute = parseInt(timeSplit[1]);
date.setUTCHours(hour);
date.setUTCMinutes(minute);
}
if (dateType === 'Start') {
var eventDate = allDay ? new Date(date).toISOString().substr(0, 19).replace('T', ' ') : new Date(date.getTime()).toISOString().substr(0, 19).replace('T', ' ');
}
if (dateType === 'End') {
var eventDate = allDay ? new Date(date).toISOString().substr(0, 19).replace('T', ' ') : new Date(date.getTime()).toISOString().substr(0, 19).replace('T', ' ');
}
return eventDate;
}
The venuecheck_getEventDate
function accepts a dateType
parameter, which can be either “Start” or “End” and the current event’s start/end date and time, depending on the parameter passed and if the “All Day Event” checkbox is checked.
The venuecheck_checkVenues
function accepts five parameters: the button that is clicked, the current event’s start date and time, the current event’s end date and time, the current post’s ID and the currently selected venue’s ID. It then makes an AJAX request passing these parameters to the callback function.
// Ajax function for Venue check
function venuecheck_checkVenues(currVenuecheckButton, eventDateTimeStart, eventDateTimeEnd, postID, venueID) {
jQuery(currVenuecheckButton).siblings(".dgtlnk-venue-check--content__inner").html('');
jQuery.ajax({
url: venuecheck.ajax_url,
data: {
'action': 'venuecheck_check_venues',
'eventDateTimeStart': eventDateTimeStart,
'eventDateTimeEnd': eventDateTimeEnd,
'postID': parseInt(postID),
'venueID': venueID
},
success: function (conflicts) {
if (typeof conflicts != "undefined") {
if (conflicts.length != 0) {
var conflictMsg = '<table class="eventtable dgtlnk-venue-conflicts"><caption style="color:red;">The currently selected venue has conflicts with the following events on the dates specified.</caption><tr><th>Event Title</th><th>Conflict Dates (YYYY-MM-DD)</th></tr>'
jQuery.each(conflicts, function () {
conflictMsg += '<tr><td><a href="' + jQuery(this)[0]['url'] + '" target="_blank">' + jQuery(this)[0]['title'] + '</a></td><td>';
jQuery.each(jQuery(this)[0]['dates'], function(index, val){
conflictMsg += val + ', ';
});
conflictMsg += '</td></tr>';
});
conflictMsg += '</table';
jQuery(currVenuecheckButton).siblings(".dgtlnk-venue-check--content__inner").html(conflictMsg);
} else {
jQuery(currVenuecheckButton).siblings(".dgtlnk-venue-check--content__inner").html("<p style=\"color:green;\">No Conflicts found.</p>");
}
}
},
dataType: "json",
error: function (errorThrown) {
console.log("ERROR:" + errorThrown);
}
});
}
On a successful AJAX request, this function checks to see if anything is returned by the callback function. If the callback function does return events with venue conflicts, then it outputs a message stating there are conflicts and lists all events that have conflicts with the conflict dates in a table.
If no conflicts are returned, then a “No Conflicts found” message is displayed.
The following function is called by the above venuecheck_checkVenues
ajax function, which should also be added to the functions.php file (along with a helper function).
// Ajax function for Venue Check
function venuecheck_check_venues() {
$venueConflicts = array();
if ( isset($_REQUEST) ) {
$eventDateTimeStart = $_REQUEST['eventDateTimeStart'];
$eventTimeStart = date_format(new DateTime($eventDateTimeStart), 'H:i:s');
$eventDateTimeEnd = $_REQUEST['eventDateTimeEnd'];
$eventTimeEnd = date_format(new DateTime($eventDateTimeEnd), 'H:i:s');
$postID = (int)$_REQUEST['postID'];
$venueID = $_REQUEST['venueID'];
$eventDates = dgtlnk_event_dates($eventDateTimeStart, $eventDateTimeEnd);
$args = array(
'post_type' => 'tribe_events',
'posts_per_page' => -1,
'post__not_in' => array($postID),
'meta_query' => array(
'relation' => 'AND',
array(
'key' => '_EventStartDate',
'value' => $eventDateTimeEnd,
'compare' => '<'
),
array(
'key' => '_EventEndDate',
'value' => $eventDateTimeStart,
'compare' => '>'
)
),
);
if ($venueID != null) {
$args['venue'] = $venueID;
}
$posts = tribe_get_events($args);
foreach($posts as $post){
$postEventDateTimeStart = tribe_get_start_date($post->ID, true, 'Y-m-d H:i:s');
$postEventTimeStart = date_format(new DateTime($postEventDateTimeStart), 'H:i:s');
$postEventDateTimeEnd = tribe_get_end_date($post->ID, true, 'Y-m-d H:i:s');
$postEventTimeEnd = date_format(new DateTime($postEventDateTimeEnd), 'H:i:s');
$postEventDates = dgtlnk_event_dates($postEventDateTimeStart, $postEventDateTimeEnd);
$dateOverlap = array_intersect($eventDates, $postEventDates);
if ($dateOverlap && $postEventTimeStart < $eventTimeEnd && $postEventTimeEnd > $eventTimeStart) {
$conflict = array(
'title' => $post->post_title,
'url' => get_edit_post_link($post->ID),
'dates' => $dateOverlap
);
array_push($venueConflicts, $conflict);
}
}
unset($venueConflicts['']);
echo json_encode($venueConflicts);
}
die();
}
add_action( 'wp_ajax_venuecheck_check_venues', 'venuecheck_check_venues' );
// Helper function for Venue Check to get an array of Event Dates
function dgtlnk_event_dates($eventDateTimeStart, $eventDateTimeEnd) {
$eventDates = array();
$eventDatesPeriod = new DatePeriod(new DateTime($eventDateTimeStart), new DateInterval(‘P1D’), new DateTime($eventDateTimeEnd));
foreach($eventDatesPeriod as $day) {
$day_date = $day->format('Y-m-d');
array_push($eventDates, $day_date);
}
return $eventDates;
}
The helper function in this code returns an array of dates the event will be taking place. In the main venuecheck_check_venues
function, an array is created with dates for the event that is being evaluated against, then the function loops through all events that have the same venue assigned to it and whose start date/time is less than and end date/time is greater than the current events start date/time and end date/time.
It creates an array of dates for each of these events and checks to see if any of these dates are already present in the array that was created for the event that is being evaluated against. If one is present, then there is a conflict for the venue as both events are taking place at the same venue at the same date and time.
This event is pushed to an array containing conflicting events along with its title, admin edit link, and the dates when the conflicts are happening – which is then displayed on the event that is being evaluated against.
In order to style any of the elements for the venue check, another CSS file can be enqueued in the admin alongside the script file.
Adding these scripts and functions provides the extended functionality required to check for venue conflicts in the Events Calendar. If you need help implementing this functionality on your website’s events calendar, then reach out to us and we’d be more than happy to assist you in your web development and digital marketing needs.