Notes // Customizing the Wordpress Events Manager Mini Calendar

Rewrite the small calendar to be cache friendly and limit the number of available months.

The Problem

My brother's website LiveTaos is a great local resource with some fantastic bloggers and the best events calendar in Taos. The problem is that it has been running very slowly. Some users have even seen a variety of 500 errors indicating that the server was under immense load. I did some Apache log forensics and enabled MySQL slow query logging. LiveTaos uses WordPress as the blog platform and the Events Manager plugin to manage the events calendar. I isolated the problem to the Events Manager plugin. There are thousands of events and the queries to retrieve them were stalling MySQL.

While analyzing the Apache logs I noticed that search engines were repeatedly calling urls with a query string beginning with ?ajaxCalendar=... I isolated these calls to the Events Manager sidebar mini calendar. Here is a picture of the small calendar:

The Events Manager Small Calendar

The ?ajaxCalendar calls are generated when the "<<" and ">>" links are clicked. The links have a rel="nofollow" attribute, but a few search engines (including Bing) were not respecting it (grrr!). My first solution was to enable query string caching in WP Rocket's "Advanced Rules". I added ajaxCalendar to the Cache Query Strings section. This worked great, but only on an event-by-event basis, because the query string was appended to the current event url even though it was generating the same calendar for different events. Here is an example of a URL: http://livetaos.com/events/claude-bourbon/?ajaxCalendar=1&mo=5&yr=2018. To enable caching I knew I needed to rewrite the link generation code...

Create a Customizable Copy of the Template File

We don't want to modify the template, so the first step is to copy the calendar-small.php file from the plugins/events-manager/templates/templates folder into the themes/<your_theme>/plugins/events-manager/templates folder. You can now customize the calendar-small.php file.

Rewrite the URLs

To re-build the URLs into a site-wide cache-able form we need to remove the current event slug from the URL. I wanted this to work across the site, so I wrote the code to get the current query string and append that to the base /events/ url.

Add the following to the initial PHP section, right before the ?><table declaration.

// calendar-small.php

$prev_ind = strpos($calendar['links']['previous_url'], "?");
$previous_query_string = substr($calendar['links']['previous_url'], $prev_ind);
$previous_url = "/events/" . $previous_query_string;


$next_ind = strpos($calendar['links']['next_url'], "?");
$next_query_string = substr($calendar['links']['next_url'], $next_ind);
$next_url = "/events/" . $next_query_string;
?>
<table class="em-calendar">
	<thead>

Next, modify the HTML to use the new $previous_url and $next_url variables. Replace $calendar['links']['previous_url'] with $previous_url and $calendar['links']['next_url'] with $next_url:


<thead>
<tr>
    <td>
        <a
                class="em-calnav em-calnav-prev"
                href="<?php echo esc_url($previous_url); ?>"
                rel="nofollow"
        >&lt;&lt;</a
        >
    </td>
    <td class="month_name" colspan="5">
        <?php echo esc_html(date_i18n(get_option('dbem_small_calendar_month_format'), $calendar['month_start'])); ?>
    </td>
    <td>
        <a
                class="em-calnav em-calnav-next"
                href="<?php echo esc_url($next_url); ?>"
                rel="nofollow"
        >&gt;&gt;</a
        >
    </td>
</tr>
</thead>

This modification worked great! The ?ajaxCalendar requests were now being cached on the main /events/ page and single events.

Limiting the Number of Available Months

One thing I noticed while clicking back and forth on the small sidebar calendar was that it was endless. It generated calendars for as far back into the past and as far into the future as I wanted. This seems useful, but old events are stale almost immediately after they occur and the calendar is really only useful for a year into the future because events are constantly being added and changed. From a user perspective an unlimited data-set doesn't make sense, and because search engines were not respecting the rel="nofollow" attribute, it also meant that the server was being taxed for data that wasn't useful in search results. In my Apache forensics I noted that a lot of old events were being accessed by indexers; this explains how they were mining those event addresses.

I added a bit of code to the header PHP section:

$calendar_past_cutoff = strtotime('-6 months');
$should_display_back_arrows = $calendar['month_start'] > $calendar_past_cutoff;

$calendar_future_cutoff = strtotime('+12 months');
$should_display_next_arrows = $calendar['month_start'] < $calendar_future_cutoff;
?>

I then added if statements around the arrow links:


<table class="em-calendar">
    <thead>
    <tr>
        <td>
            <?php if($should_display_back_arrows) {?>
            <a
                    class="em-calnav em-calnav-prev"
                    href="<?php echo esc_url($previous_url); ?>"
                    rel="nofollow"
            >&lt;&lt;</a
            >
            <?php } ?>
        </td>

        <td class="month_name" colspan="5">
            <?php echo esc_html(date_i18n(get_option('dbem_small_calendar_month_format'), $calendar['month_start'])); ?>
        </td>
        <td>
            <?php if($should_display_next_arrows) {?>
            <a
                    class="em-calnav em-calnav-next"
                    href="<?php echo esc_url($next_url); ?>"
                    rel="nofollow"
            >&gt;&gt;</a
            >
            <?php } ?>
        </td>
    </tr>
    </thead>
</table>

Now users can only click back for 6 months and ahead for a year.

Full calendar-small.php Modifications

Here is a code snippet with both the URL change and the month limitation:

<?
// calendar-small.php

$prev_ind = strpos($calendar['links']['previous_url'], "?");
$previous_query_string = substr($calendar['links']['previous_url'], $prev_ind);
$previous_url = "/events/" . $previous_query_string;


$next_ind = strpos($calendar['links']['next_url'], "?");
$next_query_string = substr($calendar['links']['next_url'], $next_ind);
$next_url = "/events/" . $next_query_string;

// Determine if the back arrows should be shown
$calendar_past_cutoff = strtotime('-6 months');
$should_display_back_arrows = $calendar['month_start'] > $calendar_past_cutoff;

$calendar_future_cutoff = strtotime('+12 months');
$should_display_next_arrows = $calendar['month_start'] < $calendar_future_cutoff;
// END MODIFICATIONS (ADDITIONAL CODE BELOW)
?>
<table class="em-calendar">
	<thead>
		<tr>
			<td>
		    	<?php if($should_display_back_arrows) {?>
				<a class="em-calnav em-calnav-prev" href="<?php echo esc_url($previous_url); ?>" rel="nofollow">&lt;&lt;</a>
				<?php } ?>
			</td>

			<td class="month_name" colspan="5">
				<?php echo esc_html(date_i18n(get_option('dbem_small_calendar_month_format'), $calendar['month_start'])); ?>
			</td>
			<td>
		    	<?php if($should_display_next_arrows) {?>
				<a class="em-calnav em-calnav-next" href="<?php echo esc_url($next_url); ?>" rel="nofollow">&gt;&gt;</a>
				<?php } ?>
			</td>
		</tr>
	</thead>
Written by
Published
Email destin{AT}destinmoulton.com if you have any comments or questions.