Answers.Body: <p>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 <a href="http://icalendar.org/" rel="noreferrer">http://icalendar.org/</a>
  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.</p>
  
  <pre><code>{% spaceless %}
      {% set events = craft.entries.find({section:'events,activities', eventDate : "&gt;= " ~ 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 %}
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-08-31T15:04:30.210Z
Answers.LastActivityDate: 2016-08-31T15:04:30.210Z
Answers.UserDisplayName: Paul Frost
Answers.Body: <p>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 <strong>H</strong> instead of <strong>G</strong></p>
  
  <pre><code>date(“Hi", “UTC")
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2017-03-24T06:23:30.440Z
Answers.LastActivityDate: 2017-03-24T06:23:30.440Z
Answers.UserDisplayName: Simon Clayson
Answers.Body: <p>You don't need a plugin.  Here's some example code from an old project of mine.</p>
  
  <pre><code>{% 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 %}
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-08-31T15:05:58.473Z
Answers.Text: Thanks, I managed to extract out the key parts I needed and got it working. See my edited code below.
Answers.UserDisplayName: Paul Frost
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2016-08-31T11:08:21.683Z
Answers.LastActivityDate: 2016-08-31T11:08:21.683Z
Answers.UserDisplayName: andrew.welch
Body: <p>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.</p>
  
  <pre><code>&lt;?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 : "&gt;= " ~ 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;
  ?&gt;
  </code></pre>
  
Tags: <calendar>
Title: How can I create an ICS calendar file
ThreadText: # How can I create an ICS calendar file
  
  (Asked by: username_0 on 2016-08-31)
  
  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;
  ?>
  
  ```
  
  **Tags:** calendar
  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_2 on 2016-08-31. Score: 7 | Accepted answer
  
  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 %}
  
  ```
  
  > **Comments:**
  > **username_0**: Thanks, I managed to extract out the key parts I needed and got it working. See my edited code below.  
  
  ---
  
  ### Answer by username_0 on 2016-08-31. Score: 5
  
  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 %}
  
  ```
  
  
  ---
  
  ### Answer by username_1 on 2017-03-24. Score: 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")
  
  ```
  
  
  ---
  
  