Create a dynamic iCalendar with blog posts

A small local website used a booking calendar plugin. Those bookings were then copied manually to an online calendar, where the team would have an overview on the bookings. Unfortunately, the booking calendar plugin didn’t have any feature to dynamically show the booking on a calendar app. So I was asked if I could help here. To make this a little more useful for some of you, instead of creating a calendar with entries for this specific booking calendar plugin, I’ll show you how to use the same approach to display all your published and scheduled blog posts.

Installing the dependency

We are going to create a dynamic .ical file. This is a text file, and we could just create the “code” ourselves, we are going to use a library to help us with that task. For the project, I have used the spatie/icalendar-generator library, which has everything I needed (and a lot more). We install it using composer into our plugin folder:

composer require spatie/icalendar-generator

We then have to load the necessary files. The easiest way to do this with packages installed with composer is using the composer autoloader, which we just have to require in our main plugin PHP file:

require_once 'vendor/autoload.php';

Creating a calendar

Now we can start to dynamically create our calendar. I will use the most basic code examples here, but in the documentation of the package you can find more complex examples. So let’s create the $calendar object first:

$calendar = Calendar::create( 'example.com' );

The parameter of the create() function is used as the title, but you can also leave it empty. Once the $calendar object is created, we would query our data for the events. Here we just query for the lasted published and scheduled blog posts:

// Get all blog posts.
$query_args = [
	'post_type'      => 'post',
	'post_status'    => [
		'publish',
		'future',
	],
	'posts_per_page' => - 1,
];

$posts = get_posts( $query_args );

Now that we have our blog posts, we can create individual calendar events for all of them:

// Create an event per blog post.
foreach ( $posts as $post ) {
	$start_date = new DateTime( $post->post_date_gmt, new DateTimeZone( 'UTC' ) );
	$end_date   = ( clone $start_date )->add( new DateInterval( 'PT15M' ) );

	$event = Event::create();
	$event->name( $post->post_title );
	$event->startsAt( $start_date );
	$event->endsAt( $end_date );
	$event->uniqueIdentifier( $post->ID );

	$calendar->event( $event );
}

We are using the GMT/UTC times, so the calendar events can adapt to your local time zone. For the “end date”, we just add 15 minutes. By using the post ID as the unique identifier, we make it easier for calendar applications to update/synchronize the event. Finally, we add the event to the previously created $calendar object.

The last step if sending the output. This can be done with the following lines:

// Print the calendar output.
header( 'Content-Type: text/calendar; charset=utf-8' );
echo $calendar->get();
exit;

Import the iCalendar into your calendar app

Now that we can dynamically create the calendar, we probably want to import (and sync) it into a calendar app. For this, we would need to have a URL we can put into the app. I’ve decided to use a custom REST endpoint, so I’ve wrapped everything in a callback function and registered the endpoint like this:

function blog_posts_calendar_register_rest_route() {
	register_rest_route(
		'blog-posts-calendar/v1',
		'/ical.ics',
		[
			'methods'             => 'GET',
			'callback'            => 'blog_posts_calendar_generate_ical',
			'permission_callback' => '__return_true',
		]
	);
}
add_action( 'rest_api_init', 'blog_posts_calendar_register_rest_route' );

The calendar can then be accessed using this URL: https://example.com/wp-json/blog-posts-calendar/v1/ical.ics

The permission_callback would allow anyone to subscribe to this calendar. If you want to restrict it, you can implement our own login in your own callback here.

Conclusion

Even if a plugin does not offer some features you might need, you can often write some own custom code to make this available. This is usually a lot easier than replacing the whole plugin, which then might lack some other features. In the case of a booking plugin, it would probably also be quite challenging to migrate all the bookings to another plugin.

If you want to test this code on your website, you can find it as a plugin on GitHub.

Posted by

Bernhard is a full time web developer who likes to write WordPress plugins in his free time and is an active member of the WP Meetups in Berlin and Potsdam.

Leave a Reply

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