Views: 5298
Number of votes: 0
Average rating:

Pulling Facebook events into EPiServer

We all want EPiServer and Facebook to play nicely. In theory they should, but the integration suffers where Facebook is a bit ‘person orientated’ where EPiServer is ‘enterprise orientated’. This can make things that should be simple a bit more complex.

For example, one common thing to do would be to create a Facebook page for your organisation. Let’s say it’s “Paper Bag Dating” and your website is www.paperbagdating.com (you can try and click if you like, it doesn’t really exist!). On the Facebook page you create your dating events, and you want them pulled through to your website. Ideally you’d want a custom page provider but if you can’t stretch the bucks then at least you’d want a scheduled job importing the events as pages.

So far so good, but when you use the Graph API to get the events for Paper Bag Dating (more on the Graph API just now), you cannot get the events without being authenticated:

{ 
  "error": { 
    "message": "An access token is required to request this resource.", 
    "type": "OAuthException" 
  } 
}

The reason for this is that while the main Page is public on Facebook, everything on it can only be accessed with an Access Token. That you can only get by being logged on. So where we are at is this:

You can only access Event information on a Facebook page when you are ‘on the Graph’.

So we need to get on the Graph – which is a Facebook term that just means their social network. You can only do this as a person, and so you’re going to need a ‘proxy user’ that has access to the Page you want to pull events from. That would be straightforward, except that there is no way to just ‘log on’ to Facebook via an SDK using a username/password combination. You can only get onto Facebook using an Access Token, and that Access Token you need is tied to a Facebook App. So – yes, you guessed it – we need to create a Facebook App. We’re not actually going to create a full-blown App as such. We just need to create a ‘container’ which we will use to route our requests through to Facebook. You can think of an App as the ‘gateway’ to Facebook.

Thankfully, it’s not hard to create an App. Just go to Facebook’s App creation tool. You’ll need to enter a name for it and a website etc. The only really critical things are the domain and site app url. I would suggest that you put the domain as your site, e.g. www.paperbagdating.com, and the site app url as a subfolder, e.g. www.paperbagdating.com/fb. I will come to why you need those in a moment. One other thing you’ll probably want to do is edit your App settings and turn ‘sandbox mode’ on. That means only you, and other App admins, can see the App. As we are just using this App to get our Access Token, it’s probably a good idea. Remember, we’re only interested in the ‘proxy user’ generating our Access Token anyway.

At this point we need to create our App. Facebook’s Graph API uses oAuth and JSON heavily, and so you can grab libraries for those and cut your own App if you like. I used the Facebook C# SDK on codeplex as it already has those libraries wrapped in, plus some other helpers. It’s an SDK to create Apps, and is fairly lightweight. Note that the documentation isn’t exactly great and the code samples on the linked blog posts are wrong, but the example project on github is a good reference source.

Once you’ve pulled the Facebook SDK, you can create your App. To start, simply create that ‘fb’ folder on your site (or whatever else you’ve chosen) and inside it create an ASPX page (it can be a standard ASP.Net page, it doesn’t need to be an EPiServer template page). The reason I suggested creating a subfolder is because you should lock this folder down using a ‘location’ statement in your web.config. Even though this App is going to be used purely as a ‘gateway’ to Facebook, I personally would like to keep it under wraps.

Now you have your ASPX page, you are ready to authenticate and get your Access Token. The way that Facebook works is to show it’s own dialogs to log you on and grant permissions to the App, and unfortunately because of the way oAuth works, this is unavoidable. What we will therefore do is set a redirect URL so that the control comes back to us at the end. We simply ask Facebook to authenticate us and then return control, as in the sample below, commented ‘stage 1’ (note, my ASPX page is just called ‘app.aspx’). A couple of things to note – the AppId needs to be set to the one that you were given when you created your Facebook App. Also, I’ve added one extended permission, “offline_access”. This means that the access token created won’t timeout any time soon, and the user doesn’t need to be online for me to use this access token. That is the only permission I need to read Events, but you can add other permissions as you need.

Facebook will now handle all the authentication, permission granting acceptance etc. and then redirect back to the URL we passed through complete with some extra QueryString parameters, either the Access Token or an error message. You can do what you like with the Access Token. Personally, I just want to display it so that I can copy-paste it where I need it elsewhere. The code-behind therefore looks as follows (I have just one control on my ASPX page, a Literal control called litMessage):

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Web;
   5: using System.Web.UI;
   6: using System.Web.UI.WebControls;
   7: using Facebook;
   8: using System.Text;
   9:  
  10: namespace paperbagdating.fb
  11: {
  12:     public partial class auth : System.Web.UI.Page
  13:     {
  14:         protected void Page_Load(object sender, EventArgs e)
  15:         {
  16:             if(!string.IsNullOrEmpty(Request["error_reason"]))
  17:             {
  18:                 litMessage.Text = Request["error_reason"] + " / " + Request["error"] + " / " + Request["error_description"];
  19:             }
  20:             else if (!string.IsNullOrEmpty(Request["access_token"]))
  21:             {
  22:                 // we are at stage 2
  23:  
  24:                 litMessage.Text = "Access Token (expires in " + Request["expires_in"] + "): " + Request["access_token"];
  25:             }
  26:             else
  27:             {
  28:                 // stage 1
  29:                 string[] extendedPermissions = new[] { "offline_access" };
  30:  
  31:                 var oauth = new FacebookOAuthClient { AppId = "23440901992399932", RedirectUri = new Uri("http://www.paperbagdating.com/fb/app.aspx") };
  32:  
  33:                 var parameters = new Dictionary<string, object>
  34:                     {
  35:                         { "response_type", "token" }
  36:                     };
  37:  
  38:                 if (extendedPermissions != null && extendedPermissions.Length > 0)
  39:                 {
  40:                     var scope = new StringBuilder();
  41:                     scope.Append(string.Join(",", extendedPermissions));
  42:                     parameters["scope"] = scope.ToString();
  43:                 }
  44:  
  45:                 Response.Redirect(oauth.GetLoginUrl(parameters).ToString());
  46:             }
  47:         }
  48:     }
  49: }

We can now use our Access Token wherever we like. I wrote a simple Admin mode plugin and a DDS-compliant settings class to set and store my Access Token. You could do that, or use web.config, whatever you like. To show how we now use our Access Token to pull data, I’ll show you my implementation of the Event import (this is an Entry license so I didn’t have the luxury of custom page providers).

I wrote a scheduled job that pulls all the events for my dating page and checks to see if they are already existing under my ‘Events’ page on EPiServer. If they aren’t, it creates them. This is fairly dirty code and I’m not using Page Type Builder here, but it will give you an idea of how it works. Just a few points to note first:

  • The Graph API, despite the fact that Facebook say that they are deprecating the old REST API, is still pretty much REST based insofar that it’s HTTP verbs with the addition of parameters. You pass through what you want and get JSON objects back. A very useful tool is the Graph API Explorer to work out what calls you need. In this case, I am using ‘PaperBagDating/events’.
  • As with all JSON calls, they are loosely typed so you need to refer to the Facebook Graph API to make sure your casting/parsing matches what you need.
  • You’ll notice that I’m passing through my Access Token and accessing the API, even though it is nothing to do with that App I created any more. This is fine – as far as Facebook is now concerned, it is that App that is doing this stuff (the Access Token is linked to my Facebook user and the App).
  • The ‘FacebookSettings’ class is just my simple DDS-compliant class for persisting the settings
   1: using System;
   2: using EPiServer.Core;
   3: using Facebook;
   4: using EPiServer.Data.Dynamic;
   5: using System.Linq;
   6: using System.Collections.Generic;
   7: using EPiServer.PlugIn;
   8:  
   9: namespace paperbagdating.Plugins.Admin.FacebookEventImport
  10: {
  11:     [ScheduledPlugIn(
  12:     DisplayName = "Facebook Event Import",
  13:     Description = "A scheduled job for importing Facebook events to the calendar",
  14:     SortIndex = 20)]
  15:     public class FacebookEventImport
  16:     {
  17:         public static string Execute()
  18:         {
  19:             string accessToken = null;
  20:             string pageID = null;
  21:  
  22:             var store = DynamicDataStoreFactory.Instance.CreateStore(typeof(FacebookSettings));
  23:  
  24:             if (store.Items<FacebookSettings>() != null)
  25:             {
  26:                 if (store.Items<FacebookSettings>().Count<FacebookSettings>() == 1)
  27:                 {
  28:                     var settings = store.Items<FacebookSettings>().First<FacebookSettings>();
  29:  
  30:                     accessToken = settings.AccessToken;
  31:                     pageID = settings.PageId;
  32:                 }
  33:             }
  34:  
  35:             if (accessToken == null) throw new Exception("You need to set the Access Token first (use the plugin)");
  36:             if (pageID == null) throw new Exception("You need to set the Events page ID first (use the plugin)");
  37:  
  38:             var fb = new FacebookClient(accessToken);
  39:  
  40:             JsonObject events = (JsonObject)fb.Get("PaperBagDating/events");
  41:  
  42:             PageDataCollection eventPages = EPiServer.DataFactory.Instance.GetChildren(new PageReference(int.Parse(pageID)));
  43:  
  44:             IList<object> data = (IList<object>)events["data"];
  45:  
  46:             int existing = 0;
  47:             int added = 0;
  48:  
  49:             foreach (IDictionary<string, object> item in data)
  50:             {
  51:                 string eventID = item["id"] as string;
  52:  
  53:                 var query = from page in eventPages where (string)page["FacebookEventID"] == eventID select page;
  54:  
  55:                 if (query.Count<PageData>() >= 1) existing++;
  56:                 else
  57:                 {
  58:                     PageData myPage = EPiServer.DataFactory.Instance.GetDefaultPageData(new PageReference(pageID),
  59:                       "[PaperBagDating] Event");
  60:  
  61:                     myPage.PageName = item["name"] as string;
  62:                     myPage.URLSegment = EPiServer.Web.UrlSegment.CreateUrlSegment(myPage);
  63:  
  64:                     myPage.Property["Time"].Value = DateTime.Parse(item["start_time"] as string);
  65:                     myPage.Property["FacebookEventID"].Value = eventID;
  66:  
  67:                     EPiServer.DataFactory.Instance.Save(myPage, EPiServer.DataAccess.SaveAction.Publish, EPiServer.Security.AccessLevel.NoAccess);
  68:  
  69:                     added++;
  70:                 }
  71:             }
  72:  
  73:             return "Events found: " + existing.ToString() + ", added: " + added.ToString();
  74:         }
  75:     }
  76: }

We are now done. The scheduled job will run and, using the Access Token we generated with our Facebook App, will pull in all the events from the PaperBagDating page and push them into EPiServer. This could be expanded to much more, naturally – like RSVPs, updates to existing events and all the rest, but it shows the concept well enough and I prefer to keep things simple anyway. I have this scheduled job running every five minutes, and here is an example of the output:

 

image

 

I’m toying with the idea of expanding this, maybe cleaning it up and putting it on epicode. I can see how this technique could be used to integrate lots more between Facebook Pages and an EPiServer site. If anyone has any other ideas, or knows a far easier way to do this, please do let me have your feedback.

Sep 27, 2011

Hampus Persson
(By Hampus Persson, 10/7/2011 3:25:54 PM)

Nice one!

Steve Celius once pointed to me this article, which helped loads to get a permanent access token.

http://www.jamescrowley.co.uk/2011/02/03/posting-to-facebook-page-using-c-sdk-from-offline-app/

Please login to comment.