Connecting Facebook with Drupal, the easy way: Part 2

In Part 1: Connecting Facebook with Drupal, the easy way I extolled the wonders of Facebook OAuth and showed off its excellent API with an example of how to map facebook user data to Drupal user objects.

For part 2, I'm going to show you another side of the API. But before we get started, I'd like to give a quick shout-out to Nathan Haug (@quicksketch), author of Facebook OAuth. I'm about to take a lot of text directly from the Nate's well-written README.txt, so don't be confused by the liberally-applied quotation marks.

Writing custom Facebook OAuth Actions

"The Facebook OAuth module provides an API for executing queries against Facebook's vast store of user data." How does this translate into user experience (UX)? In extreme layman's terms, Facebook Oauth allows you to create custom buttons that, when clicked, will 1) grab some good facebook data, and 2) let Drupal interact with it. Sounds like fun, right?

Here are a few examples of things that you could do:

  • Create a post to facebook wall link on certain node types
  • Import a list of a user's facebook friends and expose it to Views (whoa!)
  • Import a user's facebook pictures and ... expose them to the Media module!? (that would be cool)

Hopefully you're starting to see how great this can be! Best of all, you can accomplish all of this without learning Facebook's Javascript SDK! Nate has streamlined the process of asking for facebook permissions, obtaining access tokens, etc. You just need to make a few Drupal hooks and you're off!

A simple example

To create a custom Facebook Oauth action, you'll need to drop a bit of code into a custom module. We'll start by telling Facebook Oauth that we have a new action that it should be aware of. This is done with hook_fboauth_actions().

/**
 * Implements hook_fboauth_actions().
 */
function mymodule_fboauth_actions() {
  // Give each action a unique key, such as "mymodule_photo_import" for a photo
  // import. This function should begin with the name of your module.
  $actions['mymodule_photo_import'] = array(
    // Give you action a human-readable name. This will be used when showing
    // the user a link to start this action.
    'title' => t('Import my Facebook photos'),
 
    // Specify the name of your callback function that contains the import.
    'callback' => 'mymodule_fboauth_action_photo_import',
 
    // Specify permissions you need to do this action. See the Facebook API for
    // a list: http://developers.facebook.com/docs/authentication/permissions/
    'permissions' => array(
      'user_photos', // Gets access to a user's photos.
    ),
 
    // Optionally specify a file that contains your callback function. If you
    // put your callback function in the .module file, this is unnecessary.
    // 'file' => 'mymodule.inc',
 
    // Optionally define a theme function for printing out your link (not
    // including the "theme_" prefix). If you use this option, you must register
    // this function in hook_theme(). If you don't use this option, the link
    // will be output with the theme_fboauth_action() function or the automatic
    // suggestion theme_fboauth_action__[action_name]().
    // 'theme' => 'mymodule_fboauth_action',
  );
  return $actions;
}

Next, you'll need to actually create the mymodule_fboauth_action_photo_import() function specified in the above hook. Until now, we've engaged in what Jeff Eaton might refer to as "Wishful Programming," meaning that we make calls to functions that we wish existed. Let's make this function a reality.

/**
 * Facebook OAuth action callback; Import a user's Facebook photos.
 */
function mymodule_fboauth_action_photo_import($app_id, $access_token) {
  // Query against the Facebook Graph API. See the Facebook API for a list of
  // commands: http://developers.facebook.com/docs/reference/api/
  $result = fboauth_graph_query('me/photos', $access_token);
  foreach ($result->data as $photo) {
    // Do whatever you like with the photos!
  }
 
  // Optionally set a completion or error message.
  drupal_set_message(t('Import complete!'));
 
  // Optionally return a path to which the user will be redirected. If not set
  // the path in the $_REQUEST['destination'] variable will be used. If there
  // is no path at all specified, the user will be redirected to the homepage.
  return 'mymodule/import-complete';
}

"Now to get the user to actually execute this action, you need to link to Facebook so that the user can grant the necessary access. You can do this with the utility function fboauth_action_display(). Our example action was keyed as "mymodule_photo_import", so we would print the link like this:"
 print fboauth_action_display('mymodule_photo_import');

"Now when the user clicks on the output link, they will have the option of granting access to the requested information. If they approve, your callback function will be executed."

What's going on behind the scenes?

Before I go too much further, it may be good for us to go over exactly how Facebook Oauth is doing its magic. The README.txt does a great job of explaining this:

..in order to use this API it is important to understand the basic concepts of OAuth. In short, the user (and only the user) is capable of granting your site access to query information
against Facebook. The user is also only able to do this on Facebook.com, so any requests to query against Facebook must first redirect the user to Facebook where they can grant access. The full workflow looks like this:

  1. The user clicks on a link (such as the Facebook Connect button) that sends the user to Facebook. If the link is requesting permissions that the user has not yet granted, the user is prompted to allow access. After the user has granted access, or if the user granted access previously, the user is redirected back to your site.
  2. When the user is redirected back to your site, Facebook sends along an access "code". Your site then takes this access code and does a server-side request to Facebook's API servers. Facebook's servers return an access "token" to your server. This token is valid for a short amount of time and allows you to access the information to which the user granted you access.
  3. Your site can now execute queries against the user's Facebook information while the token is valid. Because this token only lasts a short amount of (about 6 hours usually), it's safest to always request access from Facebook before every data import session (by having the user click the link), which will renew the existing token or generate a new one.

Whew! Let it me said this really does happen behind the scenes. If the user has already granted the necessary permissions, they don't even see the redirect. Now let's dive back into the code.

Calling your custom actions programmatically

You may be tempted to ask, "But Matt, what if I don't want to require the user to click a button? Can I just fire my facebook action programmatically?" I'm glad you asked, because yes, you can! There's a fairly simple way to accomplish this.

  // Extract the link from a given fboauth action.
  $fb_link = fboauth_action_link_properties('my_custom_action');
 
  // Extract the request url from a given fboauth action link, including the query parameters.
  $fb_query_url = url($fb_link['href'], array('absolute' => TRUE, 'query' => $fb_link['query']));
 
  // Redirect user to facebook for authorization.
  drupal_goto($fb_query_url);

When might you want to do use this method? I had two use cases for this approach:

  • Creating a menu callback that will execute an action.
  • Creating a $form['submit'][] handler that will execute an action.

Pushing data rather than pulling it

Facebook also offers a rich API for sending data back to Facebook. How can we utilize those features? It's not actually that hard. Let's take a look at how we can accomplish this by leveraging some of Facebook Oauth's built-in ability to communicate with Facebook.

The following snippet uses Facebook's Graph API to post to a user's Facebook wall.

function mymodule_fboauth_action_post_to_wall($app_id, $access_token) {
  // Build the data array that we'd like to post to facebook.
  // See https://developers.facebook.com/docs/reference/api/user/#posts for valid array keys.
  $query = array(
    'link' => $awesome_node,
    'picture' => url(drupal_get_path('theme', 'my_theme') . '/images/logo.png', array('absolute' => TRUE)),
    'name' => t('@name loves Drupal', array('@name' => $user->name)),
    'caption' => t('ZOMG'),
    'description' => t('This message was endorsed by Grasmash'),
    'app_id' => variable_get('fboauth_id', ''),
  );
 
  $response = fboauth_graph_query('me/feed', $access_token, $query, 'POST');
  if (isset($response->id)) {
    drupal_set_message(t("You have posted to your facebook wall. That must have been hard. Take a break. Have drink. You're done."));
  }
  else {
    watchdog('mymodule', 'Error executing fboauth action: @error', array('@error' => (isset($reponse->error) ? $response->error : t('Something went horribly wrong'))));
    drupal_set_message(t("Oops! We couldn't post to your facebook wall. Try clicking harder."));
  }
 
  // Optionally redirect user.
  return '<front>';
}

The Encore

The possibilities don't stop there! Facebook OAuth also provides:

  • alter hooks to modify default actions
  • save and pre-save hooks for facebook registrations
  • the ability to hook into the deauthorization process

How might that help you? Well, I used these hooks to integrate Facebook OAuth with the Invite module, such that Drupal can send invites via facebook and then react to fulfilled (facebook) invitations. I'm sure you'll think of great ways to use it too.

That's all folks! I hope these quick posts have been helpful for you. If you liked the article, mention me on twitter or /msg me on IRC — madmatter23.

Drupal Version Compatibility: 

Comments

Thanks for sharing this great info! - This module is awesome, and im looking forward to get my hands dirty creating actions for all kinds of cool things :)

Thanks for this blog post...however, I think the graph_query call returns an object , not an array. So a slight modification to the code may be needed ...ie use $result->data instead of $result['data']. With the latest version of the module atleast on Drupal 7.

Cheers.

I lifted that example of the README.txt file, but didn't battle test it. I've updated the article.

Hi. thanks for this blog. I'm newbie, did not understand very well the lines of your custom code actions programmatically Calling.
I made a function I mymodule_show_photos and insert lines of code there.
command after the call in the hook_menu () this function.
but correct replace 'my_custom_action' by 'mymodule_photo_import'?
or should I create another hook_fboauth_action?

could someone please guide me thanks. :)

I'm not 100% sure that I understand your question, but if you're looking to create a menu callback, then you'll need to create a custom callback function.
The function should:

  1. Load one of your custom actions. That action should return the path that the user will ultimately be redirected to (on your site).
  2. Extract the request URL
  3. Redirect the user to the request URL

The user experience will look like this: 1) visit menu item, 2) redirect to FB for authentication, 3) redirect back to your site. If they user has already granted the required permissions, they should never even know that a redirect is happening.

where is my data prints it's on callback function of anything else

O yes, I can confirm what you said on beginning about other modules including the fridge (post node) one.

I have lost more than one week to try to work with fb, fbconnect & fridge modules (Post nodes to fb) and non of them are trustable.
E.G after 2 days of works to turn around the buggy admin environment of fridge I make it works in test ... and it never works in production !

Thanks then very much also for your example which make us gain a lot of time.

Great tutorial, thanks!
However using the above example I always get empty result {"data":[]}
http request is "GET /me/photos?"
http status message is "OK"

What is the approach for debugging facebook interactions ?

This tool is pretty good: https://developers.facebook.com/tools/explorer/

Hi, thank you for a great tutorial.
I have a problem which i cannot resolve, how do i get a list of a logged in user(with fb module) when a user enters a page?
what i want to achieve is when a user enters a prodcut page, he can see in a block all his friends who bought this product.
i tried the approach you wrote above(fire it without a user action) but i get infinite loop of redirecting if i try put in the "return" value the same page the user come form.
thanks!

Excellent write up, first of all... There's a few out there, but this has been the "simplest" and most concise.

I am intrigued on how you used this for the invite module. Care to share some snippets?

Is it possible to mention someone in a post?
I see a lot of example with @[12345:john do], but using this macro shows it as a plain text in my message body.

First of all, thanks for the great write about Facebook Aouth module.
Im a little frustrating as im keep getting a enoying error, when trying to fire of the my facebook-action from a submit-handler (in a node-type form alter).

I have gone through the few lines of code, but really cant find the error, which says:

The requested page "/fboauth/report_fboauth_action_post_import?code=AQCNB0If1ZFD1yLE654roDDrsdAwzL56eVsX4ZsX0xaoMnFxWfhvNKsGoeDU87y8Jd2DJkIex09I2CToSUUqm3oVPT-gf_jj99sDQsPmZI0VqNs8cMWou1onLiceM8avESLtimmkBJ__9YNwTY6g7NC2bUROE59tRXR8ujHXMXwdRz5AQmQTaP5fB3y2wt9_xdTQU1YN1mp9HJ3sfh40d9F_db1B0c22PRewC8v86hZsU1EIgAaIcmiDkDvQ1PauYwNa5NUqVyl8EaKrRwvW5K3DThdJ-Xv-hOuMfLmDs4zsIMsCLjcAEcCQMsaDr11aXwA" could not be found.

Can you point me in the right direction?

Hi.
Maybe you could help me, as I pretty much copied and pasted your code in my own module, but keep getting a wrong redirect after permissions are granted for facebook.
The thread on DO : https://drupal.org/node/2114241

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.