Skip to content

Instantly share code, notes, and snippets.

@ara4n
Last active February 7, 2018 23:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ara4n/2ee771eb69580125e5b5756872a842be to your computer and use it in GitHub Desktop.
Save ara4n/2ee771eb69580125e5b5756872a842be to your computer and use it in GitHub Desktop.
matrixCal: a saner(?) mapping of iCal into JSON than jCal:

matrixCal: a saner(?) mapping of iCal into JSON than jCal

BEGIN:VCALENDAR
PRODID:-//Apple Inc.//Mac OS X 10.11.6//EN
VERSION:2.0
METHOD:REQUEST
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Europe/London
BEGIN:DAYLIGHT
TZOFFSETFROM:+0000
RRULE:FREQ=3DYEARLY;BYMONTH=3D3;BYDAY=3D-1SU
DTSTART:19810329T010000
TZNAME:BST
TZOFFSETTO:+0100
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0100
RRULE:FREQ=3DYEARLY;BYMONTH=3D10;BYDAY=3D-1SU
DTSTART:19961027T020000
TZNAME:GMT
TZOFFSETTO:+0000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20180207T104720Z
UID:153456CC-A5DA-4090-93A2-89A2355540E2
DTEND;TZID=3DEurope/London:20180207T180000
TRANSP:OPAQUE
X-APPLE-TRAVEL-ADVISORY-BEHAVIOR:AUTOMATIC
SUMMARY:Hiring
DTSTART;TZID=3DEurope/London:20180207T150000
DTSTAMP:20180207T104807Z
ORGANIZER;CN=3DNeil Johnson:mailto:neilj@matrix.org
SEQUENCE:0
ATTENDEE;EMAIL=3Dmatthew@matrix.org;RSVP=3DTRUE;PARTSTAT=3DNEEDS-ACTION=
;CN=3DMatthe
 w Hodgson;CUTYPE=3DINDIVIDUAL:mailto:matthew@matrix.org
CLASS:PUBLIC
END:VEVENT
END:VCALENDAR

-------->

High level rules are:

  • identifiers are case insensitive, but by convention use lowerCamelCase for legibility
  • and so can be trivially converted back to vCard things by uppercasing them
  • certain object types (e.g. VTIMEZONE, VEVENT, RRULE, ORGANIZER, ATTENDEE) have rules on how to map them onto a dict (shown here implicitly).

The whole point is to be able to easily map backwards and forwards between real-world iCal and a much more legible and web-friendly JSON representation (without the pedantry and correctness and ugliness of jCal).

This example probably misses some scenarios where you might need to have an array in the object to handle multiple objects of the same kind (e.g. the attendee section gets this right).

(N.B. In practice we'd strip the stupid TZ definition and use Olson DB everywhere like Apple do, but for the sake of showing a complete mapping and to demo recurrence rules...)

{
    "prodId": "-//Apple Inc.//Mac OS X 10.11.6//EN",
    "version": "2.0",
    "method": "request",
    "calScale": "gregorian",

    "vTimeZone": { 
        "tzId": "Europe/London",
        "daylight": {
            "tzName": "BST",
            "tzOffsetFrom": "+0000",
            "tzOffsetTo": "+0100",
            "rRule": {
                "freq": "yearly",
                "byMonth": 3,
                "byDay": "-1SU"
            },
            "dtStart": "19810329T010000"
        },
        "standard": {
            "tzName": "GMT",
            "tzOffsetFrom": "+0100",
            "tzOffsetTo": "+0000",
            "rRule": {
                "freq": "yearly",
                "byMonth": 10,
                "byDay": "-1SU"
            },
            "dtStart": "19961027T020000"
        }
    },

    "vEvent": {
        "created": "20180207T104720Z",
        "uid": "153456CC-A5DA-4090-93A2-89A2355540E2",
        "dtEnd": {
            "date": "20180207T180000",
            "tzId": "Europe/London"
        },
        "transp": "opaque",
        "x-apple-travel-advisory-behavior": "automatic",
        "summary": "Hiring",
        "dtStart": {
            "date": "20180207T150000",
            "tzId": "Europe/London"
        },
        "dtStamp": "20180207T104807Z",
        "organizer": {
            "cn": "Neil Johnson",
            "cal-address": "mailto:neilj@matrix.org",
        },
        "sequence": 0,
        "attendee": [
            {
                "email": "matthew@matrix.org",
                "rsvp": "true",
                "partstat": "needs-action",
                "cn": "Matthew Hodgson",
                "cuType": "individual",
                "cal-address": "mailto: matthew@matrix.org"
            }
        ],
        "class": "public"
    }
}

If you strip out all the bloated iCal stuff (and stuff which is redundant given it's implied by the Matrix layer) - i.e. organizer (sender), UID (event_id), method/sequence (it's just an announcement, not an iTIP request), created/dtStamp (origin_server_ts), you get:

{
    "version": "2.0",
    "vEvent": {
        "summary": "Hiring",
        "dtStart": {
            "date": "20180207T150000",
            "tzId": "Europe/London"
        },
        "dtEnd": {
            "date": "20180207T180000",
            "tzId": "Europe/London"
        },
        "attendee": [
            {
                "email": "matthew@matrix.org",
                "rsvp": "true",
                "partstat": "needs-action",
                "cn": "Matthew Hodgson",
                "cal-address": "mailto: matthew@matrix.org"
            }
        ],
    }
}

This compares fairly favourably with the format proposed at: matrix-org/matrix-spec-proposals#1116.

Meanwhile, the human-representation bits of the event (thumbnail, description, body) etc can be handled by 'normal' matrix semantics. (content.body, content.thumbnail etc).

Comparing it with the half-shot's example of:

"content": {
     "title": "Party at Bob's House",
     "start_time": 922838400000,
     "end_time": 922924800000,
     "location_description": "123 Fake Street",
     "location": {
       "geometry": {
         "type": "Point",
         "coordinates": [125.6, 10.1]
       }
     },
     "description": {
       "body": "Party at Bob's house to celebrate the release of a sci-fi film. *Bring beer and popcorn*",
       "formatted_body":  "Party at Bob's house to celebrate the release of a sci-fi film. <i>Bring beer and popcorn</i>",
       "format": "org.matrix.custom.html"
     },
     "thumbnail": {
       "uri": "mxc://localhost/JWEIFJgwEIhweiWJE",
       "width": 256,
       "height": 256,
       "mimetype": "image/jpeg",
       "size": 1024000
     }
},

...and assuming that one followed Option F from https://docs.google.com/document/d/1cLSlMZ0tulRJM4ki-ImQ_z6DSySQgTRafO2m13RtgZo/edit#, the end result could look a bit like...

"content": {
    "m.body": {
        "text/markdown": "Party at Bob's House to celebrate the release of a sci-fi film. *Bring bear and popcorn*",
        "text/html":  "Party at Bob's house to celebrate the release of a sci-fi film. <i>Bring beer and popcorn</i>"
    },
    "m.calendar.request": {
        "version": "2.0",
        "summary": "Party at Bob's house",
        "vEvent": {
            "dtStart": {
                "date": "20180207T150000",
                "tzId": "Europe/London"
            },
            "dtEnd": {
                "date": "20180207T180000",
                "tzId": "Europe/London"
            },
        }
    },
    "m.location": {
        "uri": "geo:37.786971,-122.399677;u=35",
        "description": "123 Fake Street"
    },
    "m.thumbnail": {
       "uri": "mxc://localhost/JWEIFJgwEIhweiWJE",
       "width": 256,
       "height": 256,
       "mimetype": "image/jpeg",
       "size": 1024000
    }
},

...which doesn't seem so bad, relative to the original - and both leverages Matrix nicely as well as making it trivial to bridge back & forth with iCal (and benefiting from all iCal's more advanced stuff).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment