3

I want to create an ICS file that people can download to add events to their calendar. I have done this with ExpressionEngine by creating an xml template and just calling it. Below is the code I have so far but I think the problem is that twig doesn't allow php in a template. I'm guessing that means I need to put this in a plugin, but that's beyond my skills. Any help/advice appreciated.

<?php
$ical = "BEGIN:VCALENDAR
X-WR-TIMEZONE:Europe/London
PRODID:-//Orienteering Club//NONSGML v1.0//EN
CALSCALE:GREGORIAN
X-WR-CALNAME:Orienteering Club
VERSION:2.0
{% set events = craft.entries.find({section:'events,activities', eventDate : ">= " ~ now.w3cDate() , order:'eventDate'}) %}
{% if events %}
{% for entry in events %}
BEGIN:VEVENT
UID:{entry_id}
URL;VALUE=URI:{{ entry.url }}
DTSTAMP:{{ entry.eventDate.format('Ymd') }}
DTSTART:{{ entry.eventDate.format('Ymd') }}
DTEND;VALUE=DATE:{{ entry.eventDate.format('Ymd') }}
LOCATION:{{ entry.eventName }}
SUMMARY:Orienteering,nr.{{ entry.map.parts.postal_town }}
END:VEVENT
{% endfor %}
{% endif %}
END:VCALENDAR";

//set correct content-type-header
header('Content-type: text/calendar; charset=utf-8');
header('Content-Disposition: inline; filename=calendar.ics');
echo $ical;
exit;
?>
7

You don't need a plugin. Here's some example code from an old project of mine.

{% spaceless %}

    {% set thisPackage = "" %}

    {% set packageId = craft.request.getSegment(2) %}
    {% if packageId %}
        {% set thisPackage = craft.entries.section('packages').id(packageId).find()[0] %}
    {% endif %}
    {% set craftAdminId = craft.users.admin('1').first().id %}

    {# -- Do some sanity checking, make sure they have perms #}

    {% if thisPackage %}
        {% set authorInfo = craft.users.id(thisPackage.authorId)[0] %}
        {% set thisCurrency = thisPackage.currency.value %}
        {% if authorInfo.id != currentUser.id and craftAdminId != currentUser.id %}
            {% redirect siteUrl %}
        {% endif %}
    {% else %}
            {% redirect siteUrl %}
    {% endif %}

    {% header "Content-Type: text/calendar; charset=utf-8" %}
    {% header "Content-Disposition: attachment; filename=cal.ics" %}
{% endspaceless %}
BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
X-WR-CALNAME:{{ thisPackage.packageTitle | raw | escape }}
PRODID:-//tastystakes/tourneycal//NONSGML v1.0//EN
{% spaceless %}
{% for block in thisPackage.packageTournaments %}
BEGIN:VEVENT
UID:{{ thisPackage.id }}{{ block.id }}{{ authorInfo.email }}
DTSTART:{{ block.date.getTimestamp() | date_modify("+3 hours") | date("Ymd", "UTC") }}T{{ block.date.getTimestamp() | date_modify("+3 hours") | date("Gi", "UTC") }}00Z
DTEND:{{ block.date.getTimestamp() | date_modify("+3 hours") | date_modify("+" ~ block.days ~ " days") | date("Ymd", "UTC") }}T{{ block.date.getTimestamp() | date_modify("+3 hours") | date_modify("+" ~ block.days ~ " days") |date("Gi", "UTC") }}00Z
SUMMARY:{{ block.tourneyName }}
DESCRIPTION:{{ block.tourneyName }}
END:VEVENT
{% endfor %}
END:VCALENDAR
{% endspaceless %}
|improve this answer|||||
  • Thanks, I managed to extract out the key parts I needed and got it working. See my edited code below. – Paul Frost Aug 31 '16 at 15:05
4

Thanks to the starter from khalwat I got this working with the following simplified template code. There do seem to be a few variations on what validates and I got a lot of info from http://icalendar.org/ The GEO field doesn't seem to be used by Google calendar or Apple calendar but I'm guessing some calendars out there can use it.

{% spaceless %}
    {% set events = craft.entries.find({section:'events,activities', eventDate : ">= " ~ now.w3cDate() , order:'eventDate'}) %}
    {% header "Content-Type: text/calendar; charset=utf-8" %}
    {% header "Content-Disposition: attachment; filename=calendar.ics" %}
{% endspaceless %}
BEGIN:VCALENDAR
PRODID:-//{{ siteName }}//NONSGML v1.0//EN
CALSCALE:GREGORIAN
X-WR-CALNAME:{{ siteName }}
VERSION:2.0
{% spaceless %}
    {% for entry in events %}
    BEGIN:VEVENT
    UID:{{ entry.id }}@orienteeringclub.org.uk
    DTSTAMP:{{ entry.eventDate.format('Ymd') }}T120101Z
    DTSTART:{{ entry.eventDate.format('Ymd') }}T120101Z
    DTEND:{{ entry.eventDate.format('Ymd') }}T120101Z
    LOCATION:Near {{ entry.map.parts.postal_town }}
    GEO:{{ entry.map.lat|round(6) }};{{ entry.map.lng|round(6) }}
    SUMMARY:Orienteering, {{ entry.eventName }}
    DESCRIPTION:{{ entry.times|striptags }}
    URL:{{ entry.url }}
    END:VEVENT
    {% endfor %}
    END:VCALENDAR
{% endspaceless %}
|improve this answer|||||
1

Thanks for these examples above. To handle an early event start (i.e. 9am) then the time format works only if you have a leading 0 so H instead of G

date(“Hi", “UTC")
|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.