Facebook Events – Exporting Facebook Page Events to Google Calendar

facebookfacebook-eventsgoogle-calendar

We're developing a website for our non-profit book café. We're using Google Calendar for booking meetings and movie showings and lectures and everything else, but we're also using our relatively popular Facebook page to create events. We'd like to minimize double work, so we're looking for a solution that will let us have Google Calendar events created automatically when we create a new event with our Facebook page.

Since so many people are wondering how to sync their own Facebook events to their private calendars, looking for a solution for this problem on the internet proved difficult. Note the difference: usually people want to sync ''events they are invited to'', I want to sync events created through my page. I believe it should be possible using Facebook's Event API somehow, however.


Update 1: Some time has passed without answer. So I've started looking into coding it myself – as I suspected, it is possible to export the relevant information using Facebook's API – using the access token for the page, access the page's events, and then access the individual events for more detailed information. I will post it here as an answer when I'm done.


Update 2: I am well on my way, except that I've hit a snag regarding Facebook Graph API authentication. Let's hope there is a solution.

Best Answer

I've solved this with a little help from my friends and iCalcreator. Code as follows:

<?php
/*
To begin with, you will need an access token. This is complicated stuff - getting a regular one is easy, but we want one that lasts permanently. As described in Scenario 5 here: https://developers.facebook.com/roadmap/offline-access-removal/ , we need to:

1. Get a regular short-lived access token
2. Exchange it for a long-lived access token
3. Using our new access token, get a permanent page access token.

First, create a new Facebook app at https://developers.facebook.com/apps/ . It can be named whatever, it's not going to be really used. Fill in a domain - it doesn't matter which, just take one that shows simple HTML and doesn't do any redirects: I used my own static web page, http://jobjorn.se/ , but http://example.org/ may work too.

As instructed by MPaulo on Stack Overflow here: https://stackoverflow.com/a/11238327/564628 , use the app id - from your newly created app - and visit this page:
https://www.facebook.com/dialog/oauth?client_id=MY_APP_ID&redirect_uri=MY_SITE_URL&scope=manage_pages&response_type=token
Be sure to replace "MY_APP_ID" and "MY_SITE_URL".

Then, exchange your short-lived access token for a long-lived access token by visiting this page:
https://graph.facebook.com/oauth/access_token?client_id=MY_APP_ID&client_secret=MY_APP_SECRET&grant_type=fb_exchange_token&fb_exchange_token=YOUR_ACCESS_TOKEN
Be sure to replace "MY_APP_ID", "MY_APP_SECRET", and "YOUR_ACCESS_TOKEN" (the last being the access token you received from the first page)

Finally, get your page access token here:
https://graph.facebook.com/me/accounts?access_token=YOUR_NEW_ACCESS_TOKEN
Be sure to replace "YOUR_NEW_ACCESS_TOKEN" (the access token you recieved from the second page)

Paste it below. It should be permanent, but due to a bug it might only last two months - see https://developers.facebook.com/bugs/151056591697025 for details. It seems to have been fixed however. You can test it here: https://developers.facebook.com/tools/debug/access_token
*/

// You will need to edit these variables.
$access_token = "";
$page = "";

// We don't want to query the Facebook Graph API over and over, so we'll cache our results. You can force the cache to update by visiting the script and appending "?f=true", otherwise it will only run (rather than display the cache) if the cache is older than 3600 seconds (one hour).
$cache = $page . ".cache";
$f = false;
if($_GET['f'] == "true"){
    $f = true;
}
if(!file_exists($cache) || filemtime($cache) <= time()-3600 || $f){

    // Get and decode the data - your page's event list - from the API
    $graph_url = "https://graph.facebook.com/" . $page . "/events?access_token=" . $access_token;
    $data = file_get_contents($graph_url);
    $data = json_decode($data);

    if(!empty($data->error)){
        echo '<b>$data error</b><br />';
        echo $data->error->type . " error (" . $data->error->code . "/" . $data->error->error_subcode . "): " . $data->error->message;
        exit;
    }

    // Go through the list of events, and get and decode more detailed information on each event.
    foreach ($data->data as $event){
        $event_data = file_get_contents("https://graph.facebook.com/" . $event->id . "?access_token=" . $access_token);
        $event_data = json_decode($event_data);

        if(!empty($event_data->error)){
            echo '<b>$event_data error</b><br />';
            echo $event_data->error->type . " error (" . $event_data->error->code . "/" . $event_data->error->error_subcode . "): " . $event_data->error->message;
            exit;
        }
        // Store it in an array for later use.
        $events[] = $event_data;
    }

    // We're now done fetching the data from Facebook's Graph API. Now we'll have to create an iCal file.

    // This requires the iCalcreator PHP class, which you can downloead from kigkonsult at kigkonsult.se/iCalcreator/index.php
    require_once("icalcreator/iCalcreator.class.php");

    // Create the calendar, set up some basic properties
    $c = new vcalendar(array('unique_id' => $page));

    $c->setProperty('X-WR-CALNAME', $page . ' events');
    $c->setProperty('X-WR-CALDESC', 'Facebook events for ' . $page);
    $c->setProperty('X-WR-TIMEZONE', $events[0]->timezone); // We assume all of the events use the same timezone.

    // Loop through the events, create event components in the calendar
    foreach($events as $key => $event){
        $e[$key] = & $c->newComponent('vevent');

        $e[$key]->setProperty('summary', $event->name);
        $e[$key]->setProperty('dtstart', $event->start_time);
        $e[$key]->setProperty('dtend', $event->end_time);
        $e[$key]->setProperty('description', $event->description . "\n\nhttp://www.facebook.com/events/" . $event->id);
        $e[$key]->setProperty('location', $event->location);
    }

    // Remove the cache if it exists
    if(file_exists($cache)){
        unlink($cache);
    }

    // Open (create) the cache file
    if(!$handle = fopen($cache, 'w')){
        echo "Cannot open output file: " . $cache;
        exit;
    }

    // Write the calendar to the cache
    if(fwrite($handle, $c->createCalendar()) === FALSE){
        echo "Cannot write to output file: " . $cache;
        exit;
    }

    // Close the cache file
    fclose($handle);
}

// Now we've got the calendar in the cache file, either newly generated and stored there just a few lines ago or from earlier. Now we'll just display it.

header("Content-Type: text/calendar; charset=UTF-8");
header("Content-Disposition: filename=" . $page . ".ics");
require($cache);
?>