{{- /* Calendar link for one date of a tour, shown as a calendar icon: an .ics file (iCalendar), which Apple Calendar, Outlook, Thunderbird and most other calendar apps can open. On Android, assets/js/calendar.js turns it into a link that adds the date in Google Calendar. Meant for the list of dates on a tour page: * Tag 1 (27.12.) **17:30 Uhr** {{< calendar date="2026-12-27" time="17:30" duration="210" >}} Params: date (YYYY-MM-DD), time (HH:MM, local time in Hamburg), duration in minutes (optional, else tours..duration in data/schedule/.yaml), title (optional, else the title of this date in the schedule data, else the page title). */ -}} {{- $page := .Page -}} {{- $lang := $page.Language.Name -}} {{- $other := cond (eq $lang "de") "en" "de" -}} {{- $event := $page.Section -}} {{- $slug := $page.File.ContentBaseName -}} {{- $date := .Get "date" -}} {{- $time := .Get "time" -}} {{- if not (and (findRE `^\d{4}-\d{2}-\d{2}$` $date) (findRE `^\d{2}:\d{2}$` $time)) -}} {{- errorf "calendar: date must be YYYY-MM-DD and time HH:MM, got %q %q: %s" $date $time .Position -}} {{- end -}} {{- $data := dict -}} {{- with hugo.Data.schedule }}{{ with index . $event }}{{ $data = . }}{{ end }}{{ end -}} {{- $tourData := dict -}} {{- with $data.tours }}{{ with index . $slug }}{{ $tourData = . }}{{ end }}{{ end -}} {{- /* This date in the schedule, for its title. */ -}} {{- $slot := dict -}} {{- range $data.days -}} {{- if eq .date $date -}} {{- range .tours }}{{ if and (eq .tour $slug) (eq .time $time) }}{{ $slot = . }}{{ end }}{{ end -}} {{- end -}} {{- end -}} {{- $title := .Get "title" -}} {{- if not $title }}{{ with $slot.title }}{{ $title = index . $lang | default (index . $other) }}{{ end }}{{ end -}} {{- $title = $title | default $page.Title -}} {{- $minutes := int (.Get "duration" | default ($tourData.duration | default 0)) -}} {{- if not $minutes -}} {{- warnidf "warning-calendar-duration" "calendar: no duration for %s on %s %s, the calendar entry has no end time: %s" $slug $date $time .Position -}} {{- end -}} {{- $start := time.AsTime (printf "%sT%s:00" $date $time) "Europe/Berlin" -}} {{- $end := $start.Add (time.ParseDuration (printf "%dm" $minutes)) -}} {{- $utc := "20060102T150405Z" -}} {{- /* Everyone meets at the Hackertours desk, which is where the routes on the map start. */ -}} {{- $location := i18n "calendar_meeting_point" -}} {{- with $data.map }}{{ with .origin }}{{ with .name -}} {{- $location = printf "%s, %s" $location (index . $lang | default (index . $other)) -}} {{- end }}{{ end }}{{ end -}} {{- /* The front matter summary: .Summary would render this page again from inside its own shortcode. */ -}} {{- $summary := $page.Params.summary | default $page.Description | default "" -}} {{- $description := printf "%s\n\n%s" ($page.RenderString $summary | plainify | htmlUnescape | strings.TrimSpace) $page.Permalink -}} {{- $ics := printf "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//CCC Hansestadt Hamburg//Hackertours//%s\r\nCALSCALE:GREGORIAN\r\nMETHOD:PUBLISH\r\nBEGIN:VEVENT\r\n" (upper $lang) -}} {{- $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "UID" "value" (printf "%s-%s-%s-%s@%s" $event $slug $date (replace $time ":" "") (urls.Parse site.BaseURL).Host))) -}} {{- $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "DTSTAMP" "value" ($page.Lastmod.UTC.Format $utc))) -}} {{- $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "DTSTART" "value" ($start.UTC.Format $utc))) -}} {{- if $minutes }}{{ $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "DTEND" "value" ($end.UTC.Format $utc))) }}{{ end -}} {{- $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "SUMMARY" "value" $title "text" true)) -}} {{- $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "LOCATION" "value" $location "text" true)) -}} {{- with $data.map }}{{ with .origin }}{{ with .geo -}} {{- $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "GEO" "value" (printf "%v;%v" .lat .lon))) -}} {{- end }}{{ end }}{{ end -}} {{- $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "DESCRIPTION" "value" $description "text" true)) -}} {{- $ics = printf "%s%s" $ics (partial "ics-line.html" (dict "name" "URL" "value" $page.Permalink)) -}} {{- $ics = printf "%sEND:VEVENT\r\nEND:VCALENDAR\r\n" $ics -}} {{- $file := printf "%s-%s-%s.ics" $slug $date (replace $time ":" "") -}} {{- $icsFile := resources.FromString (path.Join (strings.TrimPrefix "/" $page.RelPermalink) $file) $ics -}} {{- /* The icon is the same for every date, so the link name for screen readers includes the date. */ -}} {{- $when := printf "%s %s" ($start | time.Format "Mon 2.1.") $time -}} {{- /* Android's Google Calendar app can't open .ics files; there, calendar.js uses this link instead. */ -}} {{- $google := printf "https://calendar.google.com/calendar/render?%s" (querify "action" "TEMPLATE" "text" $title "dates" (printf "%s/%s" ($start.UTC.Format $utc) ($end.UTC.Format $utc)) "details" $description "location" $location "ctz" "Europe/Berlin" ) -}} {{ partial "fa-icon.html" "regular/calendar-plus" }}{{ i18n "calendar_ics" }} {{ $when }} {{- /* chomp */ -}}