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.
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
<!--
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
:
string queryCommand = "select uid " +
" from user
" +
" where has_added_app = 1 " +
" and uid in (select uid2 from friend
where uid1 = " + facebookUserID + ")";
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
{
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:
...
if (GetUserFriends_WithoutApplication(facebookUserID,
out friendList,
out errorInfo)
)
{
if (friendList == String.Empty)
{
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
{
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,
we need to reference the following FBML elements and methods:
<fb:subtitle/>
- defines the subtitle for the profile box
(see more at <fb:subtitle/>
),
<fb:wide/>
- defines content that appears only when profile box is in the wide column of the profile
(see more at <fb:wide/>
),
SetFBML()
method to display Application profile box (see more at Profile.setFBML()
), and
<fb:ref/>
(see more at <fb:ref/>
), and
- Facebook API method
SetRefHandle()
(see more at Fbml.setRefHandle()
)
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:
- Application canvas is being loaded,
- Page_Load() is executed,
- Page_Load() calls
SetFBML()
,
SetFBML()
creates and sets reference handle for subsequent updates using <fb:ref/>
,
- 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;
...
...
string profileFBML = fbmlMaker.BuildInitialApplicationProfileFBML("Application status as of " +
System.DateTime.Now.ToShortTimeString());
FBService.SetFBML(profileFBML, "", null, facebookUserID);
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);
...
}
where
fbmlMaker
is a little helper I have to "deal" with FBML:
public string BuildInitialApplicationProfileFBML(string subTitle)
{
string fbmlProfile = String.Empty;
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;
DataSet dataApplicationUserList = null;
result = GetApplicationUsers(out dataApplicationUserList);
foreach (DataRow dr in dataApplicationUserList.Tables[0].Rows)
{
facebookUserID = dr["facebook_user_id"].ToString();
facebookUserSessionKey = dr["facebook_session_key"].ToString();
this.FBService.SessionKey = facebookUserSessionKey;
this.FBService.UserId = facebookUserID;
string handleUpdate = "announcement_" + facebookUserID;
string userProfileString = "This is wake up call for " +
"<fb:name uid=\"" + facebookUserID + "\" capitalize=\"true\" />";
string updatedProfileFBML = fbmlMaker.BuildUpdatedApplicationProfileFBML(profileTitle,
userProfileString);
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)
{
try
{
string facebookUserID = Request.Form["fb_sig_user"];
}
catch
{
}
}
}
History
Keep a running update of any changes or improvements you've
made here.