Article

Derek Jones's avatar

Downloadable iCalendar Files

by: Derek Jones on: 8/27/2018 | Read in 5 minutes

This tutorial will show you how to create downloadable iCalendar files. With ExpressionEngine, it’s easy-peasy because you can use your well-structured content however you need. You can use the same entries you use for your web site to create shareable .ics iCalendar format files. Here’s how in three easy steps.

  1. Install the “Download Content” plugin.
  2. Create your iCalendar format template.
  3. Build a link to your iCalendar template.

Install the free Download Content plugin

In order to make your ExpressionEngine-generated .ics files be treated like a downloadable file, grab and install the free Download Content plugin.

  • Download the plugin from GitHub.
  • Unzip and upload the enclosed download_content folder to /system/user/addons.
  • Visit the Add-on Manager in your control panel, and click Install for the “Download Content” plugin.

Create your download template

Now we need to create an endpoint that we will link to that allows our visitors to download our .ics file. In this example, I’m creating an iCalendar file for a free concert the local brewpub is putting on. I have created a template named add-to-my-calendar inside my concerts template group.

It uses the entry date and expiration date of the concert, the title, and a custom field called meta_description. Side note: meta_description is part of my standard set of SEO fields that I add to every channel. Repurposing it for the description of the calendar event makes sense. I’m also going to take advantage of a few iCalendar fields to setup a default alarm two hours before the event.

{exp:channel:entries channel='concerts' limit='1' show_future_entries='yes' require_entry='yes'}
	{if no_results}
		{redirect='404'}
	{/if}

{exp:download_content filename='{url_title}.ics'}
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
UID:{url_title}-{entry_date format='%Y%m%d'}@{site_name:url_slug}
DTSTART;TZID={entry_date format='%e:%Y%m%dT%H%i%s'}
DTEND;TZID={expiration_date format='%e:%Y%m%dT%H%i%s'}
SUMMARY:{title}
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR:AUTOMATIC
LOCATION:The BrewPub\n62950 NE 18th St\nBend\, OR 97701
BEGIN:VALARM
X-WR-ALARMUID:{url_title}-{entry_date format='%Y%m%d'}@{site_name:url_slug}
TRIGGER:-PT120M
DESCRIPTION:{meta_description}
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
{/exp:download_content}

{/exp:channel:entries}

Let’s break down the tags:

{exp:channel:entries channel='concerts' show_future_entries='yes' limit='1' require_entry='yes'}
  • show_future_entries= lets us use the Entry Date as the start time of the event. Since we want people to see events that haven’t happened yet, we tell the Channel Entries tag to show future events.
  • limit='1' require_entry='yes' is a common design pattern for single-entry templates. This makes sure we only show one entry, and we don’t want it to display anything if the URL title is invalid. We combine it with:
{if no_results}
    {redirect='404'}
{/if}

If there are no results—an invalid URL, an entry that’s already expired, a closed status, etc.—we will have proper 404 behavior.

The next tag is the Download Content plugin we installed earlier.

{exp:download_content filename='{url_title}.ics'}

Using the entry’s URL title for the filename isn’t a requirement, but it will help the visitor identify the file’s purpose after downloading. It needs an .ics extension so desktop and mobile calendar apps know that the file is for them. This plugin makes sure the output only contains what’s inside the plugin’s tag, and gets treated as a download instead of displaying it on the screen.

iCalendar files are picky about whitespace. This is why the code is not indented like we would normally encourage.

The rest of the contents are made from standard iCalendar file format attributes. You can read more about that file format and its attributes here: iCalendar file format. Here are some notes to help dissect our template decisions:

  • UID: Providing a UID allows the calendar app to change the existing event if it is updated. Otherwise adding a new version will create a new event. The URL title, date, and site name should give us a unique ID. The :url_slug variable modifier replaces spaces with slashes and so forth so we can use the site’s name here.
  • DTSTART/DTEND: The format parameter used here (%e:%Y%m%dT%H%i%s) will output: America/Los_Angeles:20180827T105300
  • We use the entry_date for the start date and the expiration_date for the end date.
  • SUMMARY uses the entry’s title for the event’s name
  • Some calendar apps may not use the DESCRIPTION, but we add it for those that do. Likewise, we add DTSTAMP because Outlook demands it.
  • TRIGGER:-PT120M sets an alarm for 120 minutes (2 hours) before the event
  • X-APPLE-TRAVEL-ADVISORY-BEHAVIOR:AUTOMATIC sets an alarm for Apple clients when the person needs to leave their current location to make it to the event on time.

Create a download link

Now all we need to do is link to our .ics file and we’re good to go! From my concert listing page, I’ll add:

<a href="{url_title_path='concerts/add-to-my-calendar'}">Add this concert to my calendar!</a>

Now when I click the link, it downloads a properly formatted .ics file that I can add to my calendar!

With this knowledge you can create events, todos, journal entries, and any other kind of iCalendar file you can imagine. Use these same principles to output your content in other file formats, too; the sky’s the limit! With ExpressionEngine, you never have to work hard to make your content work hard for you.

Derek Jones's avatar

Derek Jones

Son of a master craftsman. I help make ExpressionEngine. Amateur cyclist and skate-skiier.

Have ideas on how to improve this article? .(JavaScript must be enabled to view this email address) or share your feedback with @ellislab on Twitter.

.(JavaScript must be enabled to view this email address)

ExpressionEngine News!

#eecms, #events, #releases