Roll Your Own Events Calendar

You can use WordPress and the Events Manager plugin by Marcus Sykes along with the Head, Footer and Post Injections plugin from Satallo to easily encode your event data for google’s web crawling robots, which can improve event SEO and visibility. Below, I’ll review how this works for a local restaurant that hosts weekly events, and how you can get this going on your library’s website. Although this walk-through will focus on a building a calendar for a library with two branches as an example, most of the stuff could be used for any business or organization that wants to make their events more visible. I’m by no means a pro at this, so most people familiar with WP can probably follow along, but if you’re a black belt, you can probably find efficiencies that I’ve missed.

Everything Your Heart Desires

Sometimes, you can get the off-the-shelf events calendars from Demco or Eventkeeper to look okay, but there’s usually some crucial functionality for creating events or displaying them that’s just not available to you as a buyer and end user of their product. My breaking point with some calendar software came when I found Daryl Hall’s local venue’s concerts showing up when I googled my library’s town. Not only were his concerts there, but there were shows from some aquarium a couple towns away in the list of local events, too. What were not there were the kick-ass story times or early literacy programs my library was putting on, or our hugely popular volunteer groups for teens, or our computer classes, or any of the awesome stuff my library was doing. I can’t go for that, and after some digging, I found Daryl’s secret. It wasn’t private eyes, or maneaters, or even a rich girl that got those events into my browser results; it was just some of google’s structured data markup. Yes, all I needed was a little bit of code, and you make my dreams… sorry, I’m done with the Hall & Oates for now.  The point is you can use WordPress and some free plugins to organize some of the event info you’re already producing into a format that google’s web crawling robots can translate into events, and have your events show up when people search for things to do in your town. The code below is from an event at a restaurant that does weekly shows and happenings.



The markup above from the <head> of the salsa dancing event on September 20 will let google crawl the page, and display the title, image, cost, start time and date, performer, and location info for that event. You can see what this looks like for the end user in the screenshot below. As of this writing, google’s support and presentation of this kind of event info is limited to mobile search, so you won’t get the same results searching for events on your desktop computer. While it’s a bummer that this code won’t push our results into all realms of googledom, you can take some solace in the fact that the internet is used mostly from mobile devices, and google will probably bring this functionality to desktop search in the future.



Okay, I’m in, let’s do it

So, how do you get the code into your event pages? How do you organize the data you have into the neat boxes google is looking for? For a super-simple, turnkey plugin solution you can use The Events Calendar by Modern Tribe. This calendar plugin will let you create, manage, and display your events like most calendar plugins, but it will also organize and present some of your event data according to the Event Schema google is looking for to produce a very basic version of the code you see above. The Events Calendar plugin from Modern Tribe might be totally adequate for many libraries and other venues; they offer both free and paid versions, both of which have a lot of fun design features you can play around with along with the SEO event data functionality we’ve been talking about. But your control of what is presented to google and many other settings are limited.

If you want more control over how your calendar looks and behaves, and what info it presents to google about your events, you can use the Events Manager plugin by Marcus Sykes, along with the Head, Footer and Post Injections plugin from Satallo, to easily create, organize, and display events for humans and for google.

EM has sooo many settings and shortcodes available to users right out of the box, so you can really easily get your calendars and events to look and behave however you want. A lot of these features are pretty evident when you play around making events and formatting pages, and you can find a lot of help on their documentation pages, too. So, I’ll let you figure that stuff out, and we’ll go back to marking up our events for google. Here’s my example calendar, and you can look at the events and markup there. Below is the framework for the code we want to inject into the <head> of each event page. It uses a couple WordPress hooks and functions and some php to grab some of the info in the WP and Events Manager databases and shove it into the appropriate slots we’ve made for presenting that info to google.



We could put this code or something like it directly into some of our theme or plugin templates, but The Header Footer plugin makes getting it into our pages more forgiving and less susceptible to being wiped out by updates. In the code above, we check to see if the page being loaded is an event with the is_singular function, and giving it an “event” argument. So, if the page is an event, we run all the code, if not, we won’t bother.

Some of the info presented is standard event data that comes stock with any event you’ll add to your calendar in Events Manager, like date and time, event title, description, location, relevant URL for more info. But things like event type or performer info are pieces of data that have been added as attributes to each event, which are similar to custom fields in WP. Event attributes can be added to Events Manager in the WP Admin area under Events->Settings->General->Event Settings ->Event Attributes, and should use the following format for open ended fields where users can put anything: #_ATT{Attribute Name} . Or you can use a pipe to separate options for a drop down box attribute field to limit users to selecting predetermined attributes: #_ATT{Attribute Name}{Choice 1|Choice 2}. If you need help, the the Settings and Help area of EM can get you pretty far, and they have some stuff laid out on their documentation pages. Let’s go over the pieces of the code above, and you can get an idea of where stuff is coming from, and maybe learn a little bit about the guts of your website.

A good primer on the stuff google wants to see is here. You can also google events in your area, and look at the source code for those events to get familiar with it, too. If they came up in your search, the events will have structured data info. You can search the source code for “schema,” and you’ll probably see it there.


Getting Started

This stuff comes in right after the if is_singular check mentioned earlier, and it tells our browser that we’ll be using ld+json code, the recommended format for presenting this kind of info along with terms from The first piece of info that we’ll grab that’s actually specific to this event is the Event Type. lists all the currently supported event types here. When I was putting together my example calendar, I put in all these event types as a drop down box event attribute called Event Type in EM, so that any known event type could be used for events, but only those known event types. You could also probably just use the generic type “Event,” and get away with it. EM stores events as custom post types, so we can use a lot of WP functions meant for posts to grab the info we need from our events. In this case, the Events Type attribute info, and any attribute info, will be in WP’s postmeta table, so we use WP’s get_post_meta function to grab it and return a single value. The get_the_ID function will grab the Post Id for whatever post / event is being displayed, which we give to the get_post_meta function, so it knows what to look up in our postmeta table. We use echo to write out what we find using the functions after it, and we should get the Event Type that we chose as a value for that attribute for that event when we created it.


Info for the Event

Now we bring in the name or title of the event, which is really just the title of a post, so we can use WP’s the_title function. It has an echo built into it, so we don’t have to echo it. Next we’ll echo get_the_excerpt, which will give us the description of our event up to 55 words. If you’re creating events in EM, you’ll notice that it uses Featured Images to add images to events. This is good, because google and everyone else wants to see an image, and we can grab and present the url of our image for our event easily with the the_post_thumbnail_url function. The URL for the actual event would be helpful for people who might want to go to it or learn more about it, of course, so we echo the get_permalink function to bring that in. And then we need the start and end times for our event. Computers have a ton of ways for displaying the same date and time info, so make sure you’re giving it to google how it wants to see it. You can check any events you make up by giving the URL of the event to google’s Structured Data Testing Tool, which will also help you troubleshoot any errors you have. The way EM stores the info in the postmeta table seems to work for me here. You’ll see we’re using the same get_post_meta trick we used earlier for the Event Type to pull in the event start and end dates and times. Sticking the time right after the date effectively concatenates the two pieces of info together to create the startDate and endDate info in a format google is happy with. As we’ll see later, you could also use a SQL query to get start and end times directly from the EM database, but this info from the postmeta table will work, too. If you don’t have an end date and time for events, google says it’s okay to omit it.


Where is it?

So we took care of a lot of the temporal details and specifics of the event in the first part, now we’ll dig into where it’s happening. If you have a single library building, you could just set this info as static text; the info we’re looking for here, like street address and phone number, is not likely to change very often. But to use EM, you’re required to set up at least one location, which includes providing most of this info, so I’ll go over how we can grab these details from that database.

To look in the location database, we’ll need to know what the Location Id is for the location associated with the event we’re looking at, so right after we start the location info area, we declare a $locationId variable to use in a couple queries and functions later. This info will be found (a-gain) in the WP postmeta table, so we’ll use get_the_ID to get the Post Id, then we’ll use get_post_meta to look up the _location_id associated with the post, and store that as the $locationId variable. On the next line, there’s some static text to let google or whoever know we’re talking about a Place. Then we want to get the name of our location, which is easily set in EM’s WP Admin area under Events -> Locations. We’ll make a variable $locationName, and use $wpdb->get_var to look into our less common WP databases. We’ll look up the column called location_address in our em_locations database for the row that matches our $locationId variable created before. The locations db will have as many rows as you have locations in EM, each location will have it’s own row, and columns with data for all the info you provide for that location in EM. The stuff between the single quotes is SQL to query the database and the periods (.) concatenate the php to the SQL. The $wpdb->prefix part makes it so you don’t have to know what your WP database prefixes are – WP supplies if for you – which is nice, because these prefixes are different on most sites, and it means this same code will work on my site and yours, and you don’t have to go all the way to your cPanel to figure it out. So, after all that line of code, the $locationName variable at the front of the line has been set to whatever is found in the EM locations database for the location where this event is being held. The we just use echo and $locationName to write out whatever we found. We’ll do all this for the next couple lines of info we need, too; we just have to swap out the column name for whatever piece of info we want to find, like ZIP code or town, and give it a new variable.

The telephone info was a little tricky, because EM doesn’t store this info out of the box. I added it as a Location attribute, which is similar to creating an event attribute, but only shows the attributes on the WP admin pages for editing locations. So, whatever, right? Just grab the info from the postmeta table. I know, but the postmeta id isn’t the same as the location id, so you have to use a query to find out the Post Id of the location in the em_locations database, and then use that to look up the location’s Post Id in the postmeta table wit the get_post_meta function. It’s the same stuff we’ve been doing, it just takes an extra step, at least with my limited abilities. But it gets us there, and returns the info we want, and we only have to do it once. The last line of info for the location is the URL. Here I’ve set it to the home_url of the whole site. If you wanted to use a specific, different URL for different branch locations, you could do it as an Attribute, like we did the phone number. This location code should work if you have 1 or 100 locations so long as you’ve set your events up in EM in a normal way. There’s probably some less labor-intensive ways to code this with loops and arrays, but it works. You may want to present business hours, or specify that the place you’re talking about is a library; these are things currently supported by schema. Again, you could do this with attributes, or static text if you have one location. We’re almost done. Just a couple more pieces, and our events will be out there!



The piece above is conditional, and we’ll display it if our event has some data in an attribute called Artist Name. Performer info is event info people might be interested in, and google can look for and present. In the first example, for the restaurant, the performer is the Dojo Dance Company, who puts on the event. At the library, you could use it if you had a special event with a band or some other outside performer. I made it conditional, because a lot of times libraries will have events like Story Time, which don’t really require performer info, and it’d be better not to show an empty field for Performer. Artist Name, and Artist Website are custom attributes that I created in EM as described earlier.

Like we did with the location, we’ll first make up a $performer variable and look in the postmeta table to see what’s there for this event. Unlike the $locationId variable, our $performer variable could be empty. So after we grab the variable, we use an if statement to see if there’s anything there, and to execute some code if there is. If there is no Artist Name attribute for this event, then the $performer variable will be empty, and the code after the { will not run. If we do finds something for Artist Name, we just run some the get_post_meta function like we did before to grab and echo the relevant attribute info. We also close the if statement after everything.


Pricing Info

Last piece! The Offers property gathers up all the ticketing and price info for the event. For libraries, we can use the EM booking / ticketing system for event registration even if all our events are free.  @type will be the same always, and currency won’t change, so those are static text. Ticketing info is kept in a different database than our location and postmeta tables, so we’ll need to get the right kind of ID to look in there. Event Ids are different than Post Ids, so we’ll make up an $eventId variable to hold our Event Id for use in querying our tickets database. We’ll grab our Post Id with the get_the_ID function, and look up our Post ID in our em_events database, and grab the Event Id from the event_id column.

When we have that, we can look up the ticket_price in the em_tickets database as we were doing for locations, using the Event Id this time, and a $ticketPrice variable to hold what we find. If an event doesn’t have registration, or a ticket price set, it’s probably free, so we’ll use our if statement again to see if we find anything for $ticketPrice. If we do, we’ll echo what we find with a float to remove a bunch of excess zeroes at the end of the price. If we don’t find anything for $ticketPrice, we’ll echo 0 (zero), because it’s probably a good ol’ free library event that in this case does not require registration, so a ticket hasn’t been made up for the event. You could also add an elseif in here to check for a custom attribute with price info added to an event, and show that if you find it.

Next we’ll check for a ticket start date to provide to google for validFrom. This info tells people when tickets or an item will be on sale, and EM supports this out of the box. This could be useful for library events, because I know some libraries will make up a calendar, but wait to open registration at a certain point in the future. It might just be getting brownie points from google to do this, because I don’ think this is totally necessary, but if we have the info, why not get a pat on the head from the search gods? So, on the next line we grab the ticket_start info and sets it to the $ticketStart variable. Then, we check to see if there’s anything in that variable, and show the relevant ticketStart info if there is. If there’s nothing there, we skip over this. There’s nothing new here in terms of mechanics, we’ve done this before for other info, the variables are just different. The last line is the URL for the event registration. The easiest way to do this would be to show the Booking / Ticketing info right on the page of the event using EM’s shortcodes, and provide the link to the event by echoing the get_permalink function.


That’s it!

Aaaand you’re done. As I said before, this is pretty basic-looking, and probably ugly to a lot of folks. But it works, and I’m hoping some of the Mad Max, stripped down vibe gives it some transparency for the intermediate folks. You should also be able to just drop this into  your own website in the <HEAD> PAGE SECTION INJECTION box in the WP Admin area of your Head, Footer Post Injection Plugin (found under Settings -> Header and Footer). I’m really loving the Events Manger plugin, and all the control that you can easily get from it, and I think you’ll find the same if you play around with it. One of my favorite features is the customizable RSS feed, which makes newsletters a breeze (write-up coming soon). Another cool feature is that you can accept event submissions from people outside your organization. I haven’t been able to get the calendar to check for event conflicts, which can be important in larger places, but I’m working on it. Let me know if you have any questions!


My Test Calendar

WP Events Manager Plugin

Head, Footer Post Injections Plugin

WP Codex

Event Schema

Google Structured Data Events Overview

Google Structured Data Testing Tool

Leave a Reply

Your email address will not be published. Required fields are marked *