SOGo, iCal, and FullCalendar

Posted by: thogan 4 years, 2 months ago

I wanted to add a maintenance calendar the site in order to help communicate service outages. I already keep track of pretty much all temporal information in my SOGo calendars. I love integration and hate duplication, so here is how I used the fullcalendar Javascript calendar to display events from SOGo.

In case you do not know what SOGo is, it is an incredible groupware application the provides an IMAP webmail frontend and adds comprehensive calendaring and contact management. Check it out here.

Rendering A Calendar

I did some searching for a simple Javascript calendar renderer. The one I settled on was fullcalendar. The only problem was that it has its own data format. This was not much of a problem though, as I figured it should be fairly simple to write a quick script to transform whatever I could get out of SOGo into a fullcalendar data structure.

Getting The Event Data

The first thing I had to do was turn on public access in SOGo, since sharing calendars with unauthenticated users is disabled by default.

Since this is SOGo, you can go right to the manual and find exactly what you need right away, and the documentation will be complete and correct.

I set the "SOGoEnablePublicAccess" option to true and restarted SOGo, then was able to allow anonymous access to the SiteMaintenance calendar I created.

Allowing Unauthenticated Access To A Single Calendar

1. Right click the calendar to which you wish to allow anonymous access. Select "Sharing" from the context menu.

2. DOUBLE Click the "Public Access" list item. This will open a dialog that allows you to set permissions based on event type as they will apply to unauthenticated users.

3. Click the "Public" drop-down and select the "View All" permission. Click the "Update" button.

Access Method

I tried at first to use the CalDAV URL for the calendar in order to slurp event data from SOGo with a Python script and emit a fullcalendar compatible data structure. Unfortunately the caldav library I found for Python was lacking in documentation and I didn't want to spend too much time on this part.

Luckily, SOGo provides event data in several formats, via CalDAV, as an XML file, or as an iCal file. The iCal library I found for Python was mature and fairly straight forward, so I went with that.

Getting The iCal URL For The Calendar

1. Right click on the calendar for which you want the iCal data and select "Properties" from the context menu

2. From the "Links to this Calendar" tab, copy the "WebDAV ICS URL" to the clipboard and paste it somewhere for later.

Create fullcalendar Data File

In the interest of time, and to protect my mail server from the web server, I decided to just pull the calendar data with wget every five minutes using cron. I could have pulled the event data on every web request with a CGI script, or via numerous other methods, but a < 5 minute delay was acceptable.

I wrote the following Python script to consume the iCal data fetched from SOGo and produce a fullcalendar compatible data structure. The cron job simply writes the output of this script to a static file. Fullcalendar is configured to read this static file from the web server.

ics2fh.py Python Script

#!/usr/bin/env python

import sys
from icalendar import Calendar
from datetime import datetime
import json

if len(sys.argv) < 2:
    print "No ICS file specified"
    exit(1)

icsFile = sys.argv[1]
icsFh = open(icsFile, 'rb')
cal = Calendar.from_ical(icsFh.read())
icsFh.close()

evtList = []

for i in cal.walk():
    if i.name == 'VEVENT':
        newEvt = { 'editable' : False, 'allDay' : False }
        newEvt['title'] = i.get('summary')
        newEvt['start'] = i.get('dtstart').dt.isoformat()
        newEvt['end'] = i.get('dtend').dt.isoformat()
        if i.get('location'):
            newEvt['url'] = i.get('location')
        evtList.append(newEvt)

print json.dumps(evtList)

fullcalendar Javascript

Finally I just added the following snippet of Javascript to render the calendar into my placeholder div using the json file produced by the above script.

$(document).ready(function() {
    var sm = $('#sitemaintenance');
    if (sm) {
        sm.fullCalendar({
            events: '/gendata/sitemaintenance.json'
        });
    }
});
Posted by: thogan