Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Developing Facebook Application with .NET - part 2

0.00/5 (No votes)
22 May 2008 2  
Developing Facebook Application with .NET - part 2 - FBML tabs, setFBML, FB:multi-friend-selector, setRefHandle, PublishAction and much more...

Introduction

Next article in "Facebook Application Development with ASP.NET" series (hopefully last one :) )

Background

See my previous article Facebook Application Development with FBML, FBJS, ASP.NET and C#, or original at Getting started with Facebook Application Development in ASP.NET at Siccolo Development Articles

Short recap of Facebook Application Development in ASP.NET - Part 1 and what's in Facebook Application Development in ASP.NET - Part 2:
how to Setup Facebook Application, and then use FBML in .NET (ASP.NET, C#) - CanvasFBMLBasePage - to create a FBML-based Facebook Application canvas. Part 1 demonstrated using FBJS with FBML and .NET, showing dynamic content using FBJS Dialog, AJAX and ASP.NET (C#), and how to allow user interactions with sending Facebook Notifications in .NET - FBService.SendNotification(), and also how to include an external page using <fb:iframe> with ASP.NET and FBML

in Part 2 of "Facebook Development with .NET" series, more of using visual elements, such as using FBML Tabs and building Invite page with FBML multi friend selector (and using <fb:request-form/>).

Part 2 also shows using FQL in ASP.NET to select user information; and how to update Facebook user profile using Application profile box and FBML - <fb:subtitle/>, <fb:wide/>, SetFBML(), <fb:ref/> and SetRefHandle(). Then, how to create a "cron job" to update Facebook user profile page on regular basis using fbml.setRefHandle() and simple VBS program in Windows Task Scheduler.
Also, how to populate Facebook User mini-feeds with PublishAction() (using Feed.publishActionOfUser) .
And finally using Post Remove URL in Facebook with ASP.NET.

Developing Facebook Application with .NET -
using FBML tabs

Because I set my greatest Facebook Application as FBML - CanvasFBMLBasePage, I can include pretty much any FBML tag, or FBML element in ASP.NET page - In Part 1, I used <fb:dashboard> in ASP.NET. Here a different example to create a Facebook-alike interface - create a "tab"-ed user interface like this:



To create Facebook-like tab'-ed interface I'm using <fb-tabs/> (see more at FB:Tabs) - we can use FBML <fb-tabs/> tag right in ASP page:

 <fb:tabs>
                <fb:tab-item href="http://apps.facebook.com/fbtestappsiccolo/"  title="Home" />
                <fb:tab-item href="http://apps.facebook.com/fbtestappsiccolo/app_users.aspx"  title="Users"  />
                <fb:tab-item href="http://apps.facebook.com/fbtestappsiccolo/app_objects.aspx"  title="Babies" 
selected="true"  />
                <fb:tab-item href="http://apps.facebook.com/fbtestappsiccolo/app_invite.aspx"  title="Invite" 
align="right"/>
 </fb:tabs>
As you can see, in order to create "tab"-ed Facebook-like view, we need to create a group of <fb:tab-item elements nested in <fb:tabs></fb:tabs>. Each <fb:tab-item points to a tab source page using href attribute. To set <fb:tab-item caption - using title attribute. To set if particular tab is selected - using selected attribute. And last, if we need "move" tab all of way to the right - using align attribute: align="right". (see more at Facebook Torah FB:Tabs)
And of' course, if your Facebook "magnum opus" includes more than two pages, you can obviously (or obivously) use <!-- include --> ASP.NET directive. Something along these lines, for example, tabs_header.asp:
 //asp.net page to include tabs:	
 <fb:tabs>
    <fb:tab-item href="url1.aspx"  title="URL#1" 
       <% if ( Request.ServerVariables["SCRIPT_NAME"].ToString().Contains("url1.aspx"))
		{Response.Write ("selected=\"true\");}
	%>
	/>
    <fb:tab-item href=""  title="URL#2"  />
 </fb:tabs>

Building Invite page - using FB:multi-friend-selector

I see quite a few questions on 'net related to "how do I build Invite page, exactly like Facebook has". And the answer is - you don't need to. Facebook platform has it for us - we just need to reuse it.
In order to reference and display Facebook FBML multi friend selection invite tool, I needed to reference <fb:multi-friend-selector element (see more at Fb:multi-friend-selector). But,<fb:multi-friend-selector has to be used within <fb:request-form> (this has a logical reason behind the madness - user needs to submit selection back to server, hence the need of using a form). We can inlcude <fb:multi-friend-selector and <fb:request-form> right in ASP.NET page, as with any other FBML or HTML element. But in my sample application, I built it inside the code (snapshot of app_invite.aspx):

using Facebook;
using Facebook.WebControls;

public partial class _AppInvite : CanvasFBMLBasePage
{
    private const string FACEBOOK_API_KEY = "0m0y0v0e0r0y1s1e1c2r3e4t9key";
    private const string FACEBOOK_SECRET = "0evenmoresecretsekret1";
    
    new protected void Page_Load(object sender, EventArgs e)
    {
        base.Api = FACEBOOK_API_KEY;
        base.Secret = FACEBOOK_SECRET;
        this.FBService.ApplicationKey = FACEBOOK_API_KEY;
        this.FBService.Secret = FACEBOOK_SECRET;

        base.Page_Load(sender, e);

        string errorInfo = String.Empty;

        ShowApplicationInvite(this.FBService.UserId);
    }

    private bool ShowApplicationInvite(string facebookUserID)
    {

                string inviteContent = "<fb:name uid='" + facebookUserID + "' firstnameonly='true' " +
" shownetwork='false' />" +
       " Wants You to join 'Traveling Babies'; pass Babies Around!" +
       "<fb:req-choice url='http://www.facebook.com/add.php?api_key=" + FACEBOOK_API_KEY + "' " +
       " label='Pass Babies Around with Traveling Babies!' />";

                inviteContent = Server.HtmlEncode(inviteContent);

                string inviteForm = "<fb:request-form type=\"Traveling Babies\" invite=\"true\" method=\"POST\" "+
                                    " action=\"http://apps.facebook.com/fbtestappsiccolo/\" "+     
                                    " content=\"" + inviteContent + "\">"  ;

		inviteForm += "<fb:multi-friend-selector actiontext=\"Give your Friend a Gift of Baby\""+
 showborder=\"true\" rows=\"3\" "+
                                    "/>";

                inviteForm += "</fb:request-form>";

                this.divFriendSelector.InnerHtml = inviteForm;
    }
}	
Let me try to explain the above code.
    First, <fb:request-form> element (more at: Fb:request-form)
  • type attribute - just a caption that is displayed on the invite form
  • invite attribute - = true - yes, I'm sending the invitation
  • action attribute - user is taken to that page after <fb:request-form> is submitted
  • content attribute - FBML code that is displayed on the invite form. content attribute also includes <fb:req-choice> element. (see more at Fb:req-choice)


As you can see from the above example, I'm including <fb:name/> element in content FBML, to create a more personalized invitation.

    Second, <fb:multi-friend-selector/> element (more at: Fb:multi-friend-selector)
  • actiontext attribute - caption text that is displayed on the invite selection
  • showborder attribute - border anyone?
  • rows attribute - number of rows of friends to show in the <fb:multi-friend-selector/> element.



With no complex coding, we can include the same Facebook Invite form everybody is talking about. And user's Friend will receive a request invite, like this:


Using the same approach you can also include <FB:friend-selector/> (see FB:friend-selector example in Part 1), or <fb:multi-friend-input/>(see at Fb:multi-friend-input)

using Facebook Query Language (FQL) - building query for Invite page

In the previous section, we saw how to include FBML Invite "constructor" to show Invite page. But, if noticed, "as-is" version above shows all of User's Friends.
If you are to provide a way for a user to invite his/her Friends to use your "supreme application", you don't need to display all of the Friends - instead, just the ones who haven't installed your application (how could they not?!). Bottom line is, we need to find who already installed (added) application, and then exlude from the Invite list.

To find such information, we can use GetFriendsAppUsers() method (too easy!) from Facebook Developer Toolkit library, or we can run a select query against Facebook data using Facebook Query Language - FQL.

* side note: I don't know how to find all the users of a particular application via Facebook API - friends and not-so-friends. But curious to find out...

Anyhow, to select and get a list of user ID's of Friends who installed the application already, we need to reference two Facebook tables User and Friend:

where facebookUserID points to Facebook User ID of the current user using your application.

In this query, has_added_app = 1 - "where" clause part - tells Facebook that we only need users who installed this application (so, if I need to find Friends who hasn't added my application, I need to use has_added_app = 0 in "where" clause).
And uid in (select uid2 from friend where uid1 .... part - that we only need Friends of a given user (see more at
FQL "Books Online", and FQL Tables).
In the above query we only select uid field, because that's only one we need to pass into "#fbml_multi_friend_select">fb:multi-friend-selector element. But we could extract more information using Facebook FQL - such as name, picture, interests etc (see full list at Facebook User table (FQL)). By the way you can test your query command against Facebook FQL engine and see data returned at Facebook Test console:



Back to using Facebook Query Language and ASP.NET/C#.
To actually execute FQL query and retrieve data back - for example get Dataset containing list of Friends using your application:
public bool GetApplicationUserFriends(string facebookUserID, 
                                        out DataSet queryResults, 
                                        out string errorInfo)
    {
        errorInfo = String.Empty;
        queryResults = null;

        try
        {
            //Facebook [User] table:
            string queryCommand = "select uid " +
                                   " from user " +
                                   " where has_added_app = 1 " +
                                   " and uid in (select uid2 from friend where uid1 = " + facebookUserID + ")";

            bool result = ExecuteQuery(queryCommand, out queryResults, out errorInfo);

            if ( result && queryResults.Tables.Count > 0)
            {
                if (queryResults.Tables["user"] != null)
                {  return true; }
            }

            return result;
        }

        catch (Exception ex_get_user_app_friend)
        {
            errorInfo =  "Failed to GetApplicationUserFriends(dataset):" + ex_get_user_app_friend.Message;
            return false;
        }
    }
Where ExecuteQuery() method calls DirectFQLQuery() of Facebook Developer Toolkit library:
public bool ExecuteQuery(string queryCommand, out DataSet queryResults, out string errorInfo)
    {
        errorInfo = String.Empty;
        queryResults = null;

        try
        {
            string xmlDataReturned = m_FacebookService.DirectFQLQuery(queryCommand);

            queryResults = new DataSet();
            System.IO.StringReader xmlReader = new StringReader(xmlDataReturned);
            queryResults.ReadXml(xmlReader);

            return true;
        }
        
        catch(Exception ex_exec_query)
        {
            errorInfo = "Failed to exec [" + queryCommand + "]:" + ex_exec_query.Message;
            return false;
        }
    }
Using FQL, and two above methods with DirectFQLQuery(), I can display list of users already who installed my application:


Same list can be used for <fb:multi-friend-selector/> element - to exclude Friends who installed application already. For this, we need to reference exclude_ids attribute of <fb:multi-friend-selector/> element:
using Facebook;
using Facebook.WebControls;

public partial class _AppInvite : CanvasFBMLBasePage
{
    private bool ShowApplicationInvite(string facebookUserID)
    {

                string inviteContent = "<fb:name uid='" + facebookUserID + "' firstnameonly='true'  " +
  " shownetwork='false' />" +
         " Wants You to join 'Traveling Babies'; pass Babies Around!" +
         "<fb:req-choice url='http://www.facebook.com/add.php?api_key=" + FACEBOOK_API_KEY + "' " +
         " label='Pass Babies Around with Traveling Babies!' />";

                inviteContent = Server.HtmlEncode(inviteContent);

                string inviteForm = "<fb:request-form type=\"Traveling Babies\" invite=\"true\" method=\"POST\" "+
                                    " action=\"http://apps.facebook.com/fbtestappsiccolo/\" "+     
                                    " content=\"" + inviteContent + "\">"  ;

		inviteForm += "<fb:multi-friend-selector actiontext=\"Give your Friend a Gift of Baby\"  "+
  " showborder=\"true\" rows=\"3\" "+
				" exclude_ids=\"" + friendList + "\" "  +	
                                    "/>";

                inviteForm += "</fb:request-form>";

                this.divFriendSelector.InnerHtml = inviteForm;
    }
}	
where friendList variable contains list of Friend ID's extracted using the above query and DirectFQLQuery() method
(also, in your application, you may want to check if given user has any friends, or any friends without the application - may be you don't need to show Invite form, just tell the user to find more friends first:

	...	
	//do we have any friends without babies?
        if (GetUserFriends_WithoutApplication(facebookUserID,
                                                        out friendList,
                                                        out  errorInfo)
                )
            {
         if (friendList == String.Empty)
         {
                    //no friends without
         this.divFriendSelector.InnerText = "Yahooooooooooooo! All of your friends are with baby!Get a life!";
                    return true;
                }

            }
        else
            {
                return false;
            }
	...
where GetUserFriends_WithoutApplication() is something like this:
public bool GetUserFriends_WithoutApplication(string facebookUserID, 
                                                    out DataSet queryResults, 
                                                    out string errorInfo)
    {
        errorInfo = String.Empty;
        queryResults = null;

        try
        {
            //Facebook [User] table:
            string queryCommand = "select uid " +
                                   " from user " +
                                   " where has_added_app = 0 " +
                                   " and uid in (select uid2 from friend where uid1 = " + facebookUserID + ")";

            bool result = ExecuteQuery(queryCommand, out queryResults, out errorInfo);

            if (result && queryResults.Tables.Count > 0)
            {
                if (queryResults.Tables["user"] != null)
                { return true; }
            }

            return result;
        }

        catch (Exception ex_get_user_app_friend)
        {
            errorInfo = ex_get_user_app_friend.Message;
            return false;
        }
    }
So, instead of using has_added_app = 1, I used has_added_app = 0 to get list of users who are Friends but don't have application added.

using FBML in ASP.NET to update/show Application Profile on User Profile Page

So far we have managed to display very-very important information on application canvas page. Usually, though, in addition to show information on application canvas page, we also need to let user know what's going on via User Profile page - using Application Profile box, mini-feeds and/or news/story feeds (see also - Changing profile content at Facebook Developers Wiki).

Let's see how we can update User Profile page and populate Application Profile box using FBML elements:

In order to display and/or update application information on User Profile page,

To display Application profile information on User Profile page, we need to call SetFBML() method (see more at Profile.setFBML()), but we can only use setFBML() method when our application is being called, i.e. when canvas page is being loaded.
FBML Application has no knowing of When user loads his/her User Profile page, so in order to let Facebook know that we need to update information on User Profile page we can use SetRefHandle() method for subsequent updates.
    For this, sequence of events is:
  1. Application canvas is being loaded,
  2. Page_Load() is executed,
  3. Page_Load() calls SetFBML(),
  4. SetFBML() creates and sets reference handle for subsequent updates using <fb:ref/>,
  5. and then, when update is required, we just need to call SetRefHandle() and pass reference handle created in #4.


So, let's see how it can be done in ASP.NET and C#.

First, Page_Load() calls SetFBML() to create and set reference handle:
	...
	using Facebook;
	using Facebook.WebControls;
	...
	public partial class _App : CanvasFBMLBasePage
	{
		...
		...
		base.Api = FACEBOOK_API_KEY;
        	base.Secret = FACEBOOK_SECRET;
        	base.Page_Load(sender, e);
		...
		...
		string facebookUserID = this.FBService.GetUserInfo().UserId;
		...
		...

		//set initial profile - empty profile with reference handle only:
		//show in wide
                string profileFBML = fbmlMaker.BuildInitialApplicationProfileFBML("Application status as of " + 
System.DateTime.Now.ToShortTimeString());
                FBService.SetFBML(profileFBML, "", null, facebookUserID);

		//and send complete information via reference handle:
		profileFBML =
 fbmlMaker.BuildUpdatedApplicationProfileFBML("Application status as of " + 
System.DateTime.Now.ToLongTimeString(), 
"...some user information here...");
                string handleUpdate = "announcement_" + facebookUserID;
                FBService.SetRefHandle(handleUpdate, "<fb:wide/>");
                FBService.SetRefHandle(handleUpdate, profileFBML);
		//done with profile

		...
	}
where fbmlMaker is a little helper I have to "deal" with FBML:
public string BuildInitialApplicationProfileFBML(string subTitle)
    {
        string fbmlProfile = String.Empty;
        //attach ref handle:
        fbmlProfile += "<fb:ref handle=\"announcement_" + m_FacebookUserID + "\">";

        fbmlProfile += "<fb:subtitle>";
        fbmlProfile += subTitle;
        fbmlProfile += "</fb:subtitle>";

        return fbmlProfile;
    }

public string BuildUpdatedApplicationProfileFBML(string subTitle, string profileInfo)
    {
        string fbmlProfile = String.Empty;

        fbmlProfile += "<fb:subtitle>";
        fbmlProfile += subTitle;
        fbmlProfile += "</fb:subtitle>";

        fbmlProfile += "<fb:wide>";
        fbmlProfile += profileInfo;
        fbmlProfile += "</fb:wide>";

        return fbmlProfile;
    }

So, as a reference handle, I'm using "announcement_<id of facebook user>" expression. And you can assign same reference handle to a group of users. In that case, calls to SetRefHandle(handleUpdate, profileFBML) will affect all users with that particular reference handle.

For example, when a certain event occured in the application, application can tell Facebook to update user profile:
protected void buttonSend_Click(object sender, EventArgs e)
	{
		...
		string handleUpdate = "announcement_" + facebookUserID;
		string updatedUserProfile =
 fbmlMaker.BuildUpdatedApplicationProfileFBML("Updated Application status as of " + 
System.DateTime.Now.ToLongTimeString(), 
											"Something just happened!");
		
		FBService.SetRefHandle(handleUpdate, updatedUserProfile);
		...
	}
	
Or, you can schedule updates to be ran with Task scheduler and simple vbs (VB script) file.
For this, create a simple ASP.NET page (for example app_update_profile.aspx):
using Facebook;
using Facebook.WebControls;

public partial class _AppUpdateProfile :   CanvasFBMLBasePage
{
    
    	new protected void Page_Load(object sender, EventArgs e)
    	{
		base.Api = FACEBOOK_API_KEY;
        	base.Secret = FACEBOOK_SECRET;

        	this.FBService.ApplicationKey = FACEBOOK_API_KEY;
	        this.FBService.Secret = FACEBOOK_SECRET;

		// get list of Facebook User IDs from....database, for example, something like this:
		DataSet dataApplicationUserList = null;
            	result = GetApplicationUsers(out dataApplicationUserList);

		//for every known Facebook User - update thier profile:
		foreach (DataRow dr in dataApplicationUserList.Tables[0].Rows)
            	{
			//read Facebook User ID and user session from database
			facebookUserID = dr["facebook_user_id"].ToString();
                	facebookUserSessionKey = dr["facebook_session_key"].ToString();

			//set FBService, so it looks like that user:
			this.FBService.SessionKey = facebookUserSessionKey;
                	this.FBService.UserId = facebookUserID;
				
			//set reference handle:
			string handleUpdate = "announcement_" + facebookUserID;
			
			//construct updated profile:
			string userProfileString = "This is wake up call for " +
					"<fb:name uid=\"" + facebookUserID + "\" capitalize=\"true\" />";

			string updatedProfileFBML = fbmlMaker.BuildUpdatedApplicationProfileFBML(profileTitle, 
userProfileString);			

			//and send updated profile to Facebook:
			this.FBService.SetRefHandle(handleUpdate, updatedProfileFBML);
		}
	}
Then, we just need to schedule this ASP.NET page to execute in Windows Task Scheduler.
For example, using such VBS file:
args = WScript.Arguments.Count

URL = "http://www.siccolo.com/articles/app_update_profile.aspx"

Set WshShell = WScript.CreateObject("WScript.Shell")

Set http = CreateObject("Microsoft.XmlHttp")
http.open "GET", URL, FALSE
http.send ""
WScript.Echo http.responseText

set WshShell = nothing
set http = nothing

using FBML and Facebook API in ASP.NET to create mini-feed

Now if you really want to "flood" User Profile page, you can post (create) entries under user's mini-feeds:



To do this, Facebook API has a method Feed.publishActionOfUser (see more at
Feed.publishActionOfUser()), and in Facebook Developer Toolkit library, we need to reference PublishAction():

using Facebook;
using Facebook.WebControls;

public partial class _AppUpdateProfile :   CanvasFBMLBasePage
{
	...
	...
	protected void buttonSend_Click(object sender, EventArgs e)
	{
		...
		string updatedFeed = "... Updated Information ...";
		System.Collections.ObjectModel.Collection images = new Collection();
		images.Add(new Facebook.Entity.PublishImage("http://www.siccolo.com/fbtestappsiccolo/images/minifeed.jpg?11",
 "http://apps.facebook.com/fbtestappsiccolo"));
		string publish = FBService.PublishAction("Application update:", updatedFeed, images);
		...
	}
	...
	...
	   
}    	
As you can see in the above code, you can also include image to be displayed next to a mini-feed entry. That image can be used as a link to, for example, your facebook application:



And, as with updating profile, you can also schedule mini-feed entry publishing process using Windows Task scheduler (see example above fbml.setRefHandle() and simple VBS program in Windows Task Scheduler).

Handle Facebook Application Uninstall - using Post Remove URL

As with any application, users have a tendency to not only install applications, but also un-install them (bloody bustards, how could they?!).
And Facebook platform allows a developer to keep track of application removal occurrences (ha, if you want to send mafia after these users), by using Post Remove URL field under Facebook Developer Application settings:



As you can see, you just need to reference a ASP.NET page (or ASP page) that would handle POST request from Facebook, i.e. Facebook will send a POST request to this URL when a user uninstalls the application (see more at
Post-Remove URL).
And to process Facebook Application remove request:

public partial class app_remove : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //facebook passes user_id
        try
        {
		string facebookUserID = Request.Form["fb_sig_user"];
         	//having ID of Facebook User we can update database for example,
		//or call and send them pizza...
         
        }
        catch //(Exception ex_remove_app)
        {
            //what do we do with the error?
        }
    }
}

History

Keep a running update of any changes or improvements you've made here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here