Saturday, June 12, 2010

Ouray – The Facebook Connect LaTeX editor Chat Room.

I have two main personal projects running right now: Sneffel and Ouray (both named after places in Colorado.) The latter started as my 24-hour attempt at making a better “virtual learning environment” than Blackboard, Moodle, or other software I've seen educational institutions use. Inspired by Twitter and Yammer, I thought a micro-blogging format would do the job, and I put together a quick system that used Facebook Connect to allow a single-click sign on.

Of course, this was all before Open Graph and the other changes to Facebook's API. So naturally, when I went back into the code this week to make some changes, I had to see what was up. All I wanted to do was ask for facebook user's email address (I had not done so when they first signed up) so that I could send them notifications for the Ouray courses they've signed up for. Easy, right? Just set the “perms” field in the FBML, like so:

<fb:login-button perms="email" v="2" size="medium">Connect</fb:login-button>


Well, this is where the trouble starts. First, I was using the old PHP API library, and I wanted to ensure I was using the latest code. Second, as a result, I needed to look up documentation for the new API, and it was a bit fragmented.

The first thing I realized is that I needed to use a new authentication cookie, and to get this cookie, I needed to ensure I was using the latest javascript API. So I replaced the old facebook <script> tags and added the new:



<script src="http://connect.facebook.net/en_US/all.js"></script>
<script>
FB.init({appId: 'MYAPPID', status: true, cookie: true, xfbml: true});
FB.Event.subscribe('auth.sessionChange', function(response) {
if (response.session) {
//login
} else {
//logout
}
}); </script>


At the bottom of my pages as instructed. Now the FB.Event code is key to getting your app to respond to logins. It tells javascript to listen for a facebook event and act upon it with the function passed to it. I found two different events used for logins, “auth.login” and "auth.sessionChange", for seemingly no reason. Now the uses of each seem pretty clear from the names, but it would be nice to see the different responses; e.g., can I use sessionChange to detect a login? Yes I can. Did I find that out from Facebook? No I didn't. (see http://developers.facebook.com/docs/reference/javascript/FB.Event.subscribe for a list of FB events.)

The next step came in trying to access this new Facebook Cookie. Facebook's http://developers.facebook.com/docs/authentication/ provides the PHP function

function get_facebook_cookie($app_id, $application_secret)
{
$args = array();
parse_str(trim($_COOKIE['fbs_' . $app_id], '\\"'), $args);
ksort($args); $payload = '';
foreach ($args as $key => $value)
if ($key != 'sig')
$payload .= $key . '=' . $value;
if (md5($payload . $application_secret) != $args['sig'])
return null;
return $args;
}

Which worked quite well, assuming the first part was done right. From here you could grab the cookie data in an array (say $cookie = get_facebook_cookie...) and get going. The key bit about this cookie is that it carries an “access token” which you needed to pass to the Graph API in order to get the data you requested in the fbLogin perms field. Now the examples you see at http://developers.facebook.com/docs/guides/web#personalization would have you think the key is “oauth_access_token”. But the example at the bottom of http://developers.facebook.com/docs/authentication/ says its “access_token”. I was unfortunate enough to not immediately notice the difference, and was left wondering why my cookie didn't have the “oauth_access_token”. After an hour or so of fighting, I finally realized my var_dump contained an “access_token” and was able to catch the problem. Thus I was finally able to use code like:

$user = json_decode(file_get_contents( 'https://graph.facebook.com/me?access_token=' . $cookie['access_token']));

To get my facebook data. Just one final tip – in case anyone else is trying the same thing – don't use file_get_contents. Use curl. Like this:
$ch = curl_init('https://graph.facebook.com/me?access_token=' . $cookie['access_token']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$fbResponse = curl_exec($ch);
curl_close($ch);
$userDetails = json_decode($fbResponse);

I was also able to finish the LaTeX editor for Ouray (even transparent png outputs!) Thanks to ImageMagick and a great tutorial from http://www.linuxjournal.com/article/7870. Essentially, I just wrote a PHP class to do the batch scripting.

Enjoy!

Monday, June 7, 2010