Jabberwocky

snicker-snack!

Status Update With Rails and Facebooker

| Comments

Update Please consider this article as out of date. Facebook’s API and the Facebooker gem both have evolved since then, and I’ve decided to stay away from Facebook applications, so I can’t update it …

This feature has had me cursing quite a bit. After the breeze that are Twitter updates, and the usual pieces of cake that are other APIs, it appears that Facebook has its own microcosm of requirements, causing me to cry out ‘i only want to update the status, godd****’ in despair more than once – while the documentation doesn’t totally suck, it’s far from complete. And I don’t even like Facebook.

The problem is that Facebook has an involved permission framework, with its own workflow. I ended up installing the Facebooker plugin, which is usually used for anything Rails Facebook.

First off, you need to declare your app with Facebook. This is done with the Developer Application (Settings – Application Settings – Developer). Once you create an application, you receive an API key and a secret (needed for authentication). These have to be pasted in the facebooker.yml configurations.

After some tribulations, i ended up using the Facebook Connect infrastructure. For this, you first need to generate the pages used to do the connect:

script/generate xd_receiver

You can test the connect by adding the following in the body of a view (assuming you use jQuery, otherwise you include Prototype in your header and remove the :js => :jquery bit):

1
2
3
= fb_connect_javascript_tag
= init_fb_connect "XFBML", :js => :jquery
= fb_login_button

and in your corresponding controller:

1
2
before_filter :set_facebook_session
helper_method :facebook_session

Since the idea is that you want your app to post to the feed not only now, but also later, you need to store the connection data, once connected. So you need to either make a model for this, or to include the necessary data in your user model. Let’s assume in this post that i make a model FacebookUser. The model, in any case, needs to contain following data

1
2
3
t.integer :facebook_id, :limit => 20, :null => false
t.string :session_key
t.timestamps

A way to save this is to make sure that on return from facebook (ie when the session is set) the session and user id data are saved away. In the model, let’s first make a convenient method (this is copied pretty much from the pragmatic programmer’s facebook booklet)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def self.for(facebook_id,user,facebook_session=nil)
  returning FacebookUser.find_or_create_by_facebook_id(facebook_id) do |fb_user|
    fb_user.update_attributes(:user_id => user.id)
    unless facebook_session.nil?
      fb_user.store_session(facebook_session.session_key)
    end
  end
end

def store_session(session_key)
  if self.session_key != session_key
    update_attribute(:session_key,session_key)
  end
end

Then in the facebook developer application, and the return url of connect needs to be set to the url you want used. Say: http://www.mydomain.com/facebook which you then declare in your routes.

When this URL is accessed, make sure the following is called, to store your facebook data away.

1
self.fb_user = FacebookUser.for(facebook_session.user.to_i,current_user,facebook_session)

and in corresponding view, show the user the fact that now, he’s logged in, testing on the presence of the facebook_session (if facebook_session).

Test. But, unfortunately, that’s not the only requirement: updating the status is an ‘extended permission’ in facebook, that is, you need to ask the user pretty please. How ? Well, in the same action of the controller, you can add this little bit of code:

1
@permission_dialog = !status_updates_allowed?

and this method corresponds to an FQL query (yes, seriously, they’ve got a query language, too)

1
2
3
4
5
6
7
8
def status_updates_allowed?
  res = facebook_session.fql_query("select status_update from permissions where uid == #{facebook_session.user.uid}")
  if res.join =~ /status_update1/
    return true
  else
    return false
  end
end

This basically asks whether the permission was already granted or not. Then, include the following in your view:

1
2
3
4
5
6
7
<%- if @permission_dialog %>
<script type="text/javascript">
  FB.ensureInit(function(){
    FB.Connect.showPermissionDialog('status_update', function(accepted) { window.location.reload(); } );
  });
</script>
<% end %>

This makes sure that when the action is carried out, a second dialog box is shown to the user, asking it to grant status_update to your app. It’s not shown if the permission was already granted. I added some conditional bits in my view, too, to tell the user what’s going on.

When this is done, you should have enough to work with. I added a variation of the above to allow the user to update his facebook data, change to a different facebook user, or simply log out and remove his data completely.

Anyway: now you have the data you need to make status updates. At appropriate moments, recreate the facebook session, and do the update, as follows: recreate session: add in your model:

1
2
3
4
5
6
7
def facebook_session
  @facebook_session ||=
  returning Facebooker::Session.create do |session|
    # facebook sessions are only good for 1 hour
    session.secure_with!(session_key, facebook_id, 1.hour.from_now)
  end
end

and in your application, where you need the update:

1
2
facebook_session = fb_user.facebook_session
facebook_session.user.set_status(status)

There, that’s it ! Your application will now be able to update/pollute the user’s status at will ! Yay.

Comments