Until version 3.6, My Calendar only supported a crude custom templating engine that used curly braces to represent content. You could enter a custom HTML string into the legacy template editor to generate a template.

While it served the purpose for most users, it had a few key weaknesses:

  1. Difficult to sanitize properly.
  2. Required using a mix of standard HTML and custom template tags that do not validate under any standardized syntax.
  3. No support for logical operators.

PHP templating, introduced in version 3.6, uses a standard PHP document to return templates. While it may sound intimidating to write PHP, it is not in fact any more difficult than the custom syntax used previously, and much more powerful.

Why migrate?

With the release of WordPress 7.0, there is a change to the core sanitization function wp_kses_hair() which breaks much of the nonstandard syntax that legacy templates depend on. Specifically, that change normalizes HTML attribute quotes so that they will always be surrounded by double quotes – by My Calendar’s legacy templates placed HTML attributes inside custom template tag attributes, meaning that they could not be double quoted without breaking the template.

You can still use legacy templates – I added some custom workarounds that fix several cases – but they’re much more fragile than they used to be.

Migration steps:

Step 1: Copy your custom template to a text file.

You’ll find the template editor at My Calendar > Design > Templates, under the headings “Core Templates” and “Custom Templates”.

Example showing the core templates and one example custom template.

Core templates are used to replace the default layouts for My Calendar events; Custom Templates are used in shortcodes.

For any core template, you can tell that they are enabled if their ‘Status’ column says Enabled. If you don’t have any custom templates and no core templates are enabled, then you’re done; you don’t have any custom templates to migrate.

Step 2: Turn on PHP Templating

Go to My Calendar > Settings > Display. Find the checkbox “Enable PHP Templating” under “Event Display Fields”.

Check it, and save settings.

Step 3: Migrate your legacy template structure to PHP.

An example legacy template might look like this:

<span class="event-time value-title">{time}{endtime before="<span class='time-separator'> - </span><span class='end-time'>" after="</span>"}</span>
{image before="<div class='mc-event-image'>" after="</div>"}
<div class="sub-details">
	{hcard before="<div class='mc-location'>" after="</div>"}
	{excerpt before="<div class='mc-excerpt'>" after="</div>"}
</div>

This is the default starting template that you’ll get if you enable PHP legacy templates. It has no relationship to the default output, which is controlled in PHP through the settings at My Calendar > Settings > Display.

It consists of HTML, like <span class="event-time value-title"> and My Calendar template tags, like {time}.

Some of the My Calendar template tags have additional parameters, like {endtime before="<span class='time-separator'> = </span><span class='end-time'>" after="</span>"}

Those parameters represent content that will be inserted before or after the output of the {endtime} template tag *if* the tag returns content. It will be omitted if the tag doesn’t output any content. That’s the extent of logic that this templating is capable of.

This same template, generated in a PHP template, looks like this:

<?php
/**
 * Template: Single Event, Grid view.
 *
 * @category Templates
 * @package  My Calendar
 * @author   Joe Dolson
 * @license  GPLv3
 * @link     https://www.joedolson.com/my-calendar/
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

?>
<div class="mc-v2 <?php mc_event_classes( $data->event, 'calendar' ); ?>">

    <span class="event-time value-title">
        <?php
        mc_template_tag( $data, 'time' );
        $endtime = mc_get_template_tag( $data, 'endtime' );
        if ( $endtime ) {
            ?>
            <span class='time-separator'> - </span><span class='end-time'>
            <?php
            mc_template_tag( $data, 'endtime' );
            ?>
            </span>
	<?php
        }
        $image = mc_get_template_tag( $data, 'image' );
        if ( $image ) {
           ?>
           <div class="mc-event-image">
           <?php
           mc_template_tag( $data, 'image' );
           ?>
           </div>
           <?php
        }
        ?>
        <div class="sub-details">
	<?php
        $hcard = mc_get_template_tag( $data, 'hcard' );
        if ( $hcard ) {
           ?>
           <div class="mc-location">
           <?php
           mc_template_tag( $data, 'hcard' );
           ?>
           </div>
	   <?php
        }
        $excerpt = mc_get_template_tag( $data, 'excerpt' );
        if ( $excerpt) {
           ?>
           <div class="mc-excerpt">
           <?php
           mc_template_tag( $data, 'excerpt' );
           ?>
           </div>
		   <?php
        }
	?>
        </div>
</div>

That reproduces the original template exactly. It’s more verbose than the original, but also much more flexible.

This content (originally from the ‘Grid’ core template) would be placed in a file called calendar.php, which you’d place inside your theme directory at /your-theme/mc-templates/event/calendar.php.

Mapping Custom Templates to PHP Templates

  • grid becomes /mc-templates/event/calendar.php
  • list becomes /mc-templates/event/list.php
  • mini becomes /mc-templates/event/mini.php
  • card becomes /mc-templates/event/card.php
  • details becomes /mc-templates/event/single.php
  • Upcoming Events templates become /mc-templates/event/upcoming.php

There isn’t currently a mechanism to use a different template for different upcoming events lists using PHP templates; if this is something you need, please don’t hesitate to comment on the issue at Github!