In door 16 we took care of the fact that we can enter appointments in our calendar, but what do we do if our monkey wants to share an appointment with someone, e.g. as an attachment for an appointment confirmation. We can also realize this by assembling an ics file that contains the data for an appointment. As with the business card that we embedded in the QR code, an ics file also has a specific form, where some data is required and some is optional. But let's take a look at the structure of such a file by exporting an existing appointment from the calendar and opening it in an editor. It is an event that takes place on 24th December 2024 from 17:00 to 23:30 and has the title Christmas Eve Dinner. It takes place in Home, has a further description and a link to a website. And this is what it looks like in the editor:
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//Apple Inc.//macOS 13.4.1//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
BEGIN:DAYLIGHT
DTSTART:19810329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
TZNAME:CEST
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19961027T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
TZNAME:CET
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20241212T205429Z
DESCRIPTION:Prepare food on December 23rd
DTEND;TZID=Europe/Berlin:20241224T233000
DTSTAMP:20241212T205735Z
DTSTART;TZID=Europe/Berlin:20241224T170000
LAST-MODIFIED:20241212T205713Z
LOCATION:Home
SEQUENCE:0
SUMMARY:Christmas Dinner
TRANSP:OPAQUE
UID:809C6AEE-B99E-4DBB-A29F-4223314288A3
URL;VALUE=URI:www.xyz.com
BEGIN:VALARM
ACTION:NONE
TRIGGER;VALUE=DATE-TIME:19760401T005545Z
END:VALARM
END:VEVENT
END:VCALENDAR
Don't panic, you won't need as much information as you see here to create the appointment, but let's take a look at which of it is relevant for us.
The content of the appointment starts with BEGIN:VCALENDAR and ends with END:VCALENDAR, with the information about the appointment in between.
The next line: CALSCALE:GREGORIAN specifies, as you have probably already guessed, the type of calendar. In our case, the Gregorian calendar. Then comes the version. This must be present in every date specification so that it is known how the individual details are to be interpreted. This can simply remain at 2.0.
The PRODID key describes who created the file. In the event you see here, you can see that the calendar app created this event. In our example, we pass the company name afterwards. Then we get down to the details, the data for the actual appointment. Here, our area is always enclosed by BEGIN:VEVENT and END:VEVENT. Such an area must appear at least once in our appointment. Now let's take a look at what's inside this area. Our example appointment is an appointment that has a start and end time. The end time has the key DTEND. We can see that in addition to the character string containing the date and time, the time zone is also specified. In our case, the time that applies in my case, as I live in Germany, is Berlin. If you want to find out more about the individual time zones, you can find this information on the website timezones.de, for example.
Now comes the timestamp and it has a very specific structure. First comes a 4-digit year, then the month, which takes up 2 digits, and the day, which also has 2 digits. This is followed by a T that separates the time from the date. The individual entries for the time are each two characters long and are made up of hours, minutes and seconds. The timestamp therefore has the structure: yyyymmddThhmmss. August 19, 2025 at 9:00 a.m. would therefore receive the following entry: 20250819T090000.
We also have the entry DTSTART in this date. This determines the start time of the event and has the same structure. We see something similar in the time stamps, which tell us when the event was created (CREATED), when it was last modified (LAST-MODIFIED), and when this file was exported or created (DTSTAMP). DTSTAMP is a time specification that must be included in the date. In contrast to the start and end time, we find a Z at the end of the time stamp.
Now we come to a few more useful keys. First, we have the LOCATION key to which we can pass a location or address. Under the DESCRIPTION key, you can describe the appointment further. This content is then displayed as a note for the appointment. You can also use the URL key to specify a URL that belongs to the organizer's or event's website, for example. Another important key is SUMMARY. This describes the title of our event.
The keys do not have to remain in this order, but can be swapped, as we do in our example, so that you can find the order that makes the most sense for you.
We have already created our fields at door 16 and so we only have to create a button in the popover that starts the script that we now want to write. The beginning of our appointment is always the same and we put it together as text. The content of our file will later be in the variable $Content
Set Variable [ $Company ; Value: "Monkey the Elf" ]
Set Variable [ $Start ; Value:
"BEGIN:VCALENDAR¶CALSCALE:GREGORIAN¶VERSION:2.0¶METHOD:PUBLISH¶PRODID:"
& $Company & "¶BEGIN:VEVENT" ]
Set Variable [ $Content ; Value: $Start ]
Now we can enter the title of our calendar entry. This has the key SUMMARY
Set Variable [ $Title ; Value: "¶SUMMARY:" & Data::Appointment_Title & "¶" ]
Set Variable [ $Content ; Value: $Content & $Title ]
The description, the URL and the location of an event are not mandatory, which is why we first check whether these values have been set in our database and only then we add the key with the corresponding value.
If [ Data::Appointment_Notes ≠ "" ]
Set Variable [ $Description ; Value: "DESCRIPTION:" & Data::Appointment_Notes & "¶" ]
Set Variable [ $Content ; Value: $Content & $Description ]
End If
#
If [ Data::Appointment_URL ≠ "" ]
Set Variable [ $URL ; Value: "URL;VALUE=URI:" & Data::Appointment_URL & "¶" ]
Set Variable [ $Content ; Value: $Content & $URL ]
End If
#
If [ Data::Appointment_Location ≠ "" ]
Set Variable [ $Location ; Value: "LOCATION:" & Data::Appointment_Location & "¶" ]
Set Variable [ $Content ; Value: $Content & $Location ]
End If
Now we come to the part that is a bit tricky, because we have to differentiate between an all-day appointment and an appointment with a start and end time. This is because the composition of the time differs slightly here. If the Appointment_All Day field is set to 1, we jump to the TimeAllday script that compiles the all-day time.
If [ Data::Appointment_All Day = 1 ]
Perform Script [ Specified: From list ; "TimeAllday" ; Parameter: ]
We compose the date as we discussed earlier: four characters year, two characters month and two characters day. We get these values from the date field using the corresponding FileMaker functions. But be careful: If we have a day or a month with a value less than 10, the function only returns one character and we have to add the preceding zero.
Set Variable [ $Year ; Value: Year ( Data::Appointment_Start ) ]
#
If [ Length ( Month ( Data::Appointment_Start ) ) ≠ 2 ]
Set Variable [ $Month ; Value: "0" & Month ( Data::Appointment_Start ) ]
Else
Set Variable [ $Month ; Value: Month ( Data::Appointment_Start ) ]
End If
#
If [ Length ( Day ( Data::Appointment_Start ) ) ≠ 2 ]
Set Variable [ $Day ; Value: "0" & Day ( Data::Appointment_Start ) ]
Else
Set Variable [ $Day ; Value: Day ( Data::Appointment_Start ) ]
End If
Set Variable [ $StartDay ; Value: $Year & $Month & $Day ]
Set Variable [ $Beginning ; Value: "DTSTART;VALUE=DATE:" & $StartDay ]
Set Variable [ $Time ; Value: $Beginning & "¶" ]
Now we have to differentiate whether it is an all-day appointment lasting several days or whether it is only one day with an all-day appointment. To do this, we subtract the end date from the start date and obtain the day difference. If we get a value greater than 0 here, it is an all-day event that lasts several days. If we only specify the end date in this case, this is often interpreted incorrectly and, for example, one day too little is displayed for the event in the calendar. For this reason, we would like to specify the DURATION in this case. This is made up as follows: First we write a P and then comes the duration of the event in days or weeks. We can see whether it is days or weeks by the character after the duration. If we enter weeks, there is a W, if we enter days, as in our case, there is a D. You can also combine the times by writing them down one after the other. For example, the duration of 2 weeks and 3 days would be described with this expression: P2W3D.