Web Analytics

The Events Calendar: Checking for Venue Conflicts

by Utsab Karki, Senior WordPress Developer

Photo by Claudio Schwarz via Unsplash

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.

Avatar photo
About Utsab Karki

Utsab Karki is the Senior WordPress Developer at Digital Ink. He builds new websites, manages functionality requests and changes, and makes clients' sites run better. Digital Ink tells stories for forward-thinking businesses, mission-driven organizations, and marketing and technology agencies in need of a creative and digital partner.

Other Stories You May Like

What’s your story?

Let’s share it with the world.

Let’s Do This

Close Window
All the Cool Kids are Doing it

Sign Up for the Digital Ink Newsletter

All the cool kids are doing it.