Title: Facebook Application Development - .NET Software Development - Generate Facebook Content using FBML (part 2) Author: Greg Dubinovskiy Email: [email protected] Environment: c# .NET Keywords: Facebook Application Development - .NET Software Development - Generate Facebook Content using FBML Level: Intermediate Description: Facebook Application Development - .NET Software Development - Generate Facebook Content using FBML Section Miscellaneous SubSection General
In Part 1 of
Facebook Application Development with .NET, we saw
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 of these Facebook Development series 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, we will see 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>
<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)
<!-- 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>
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;
}
}
<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)
<fb:name/>
element in content
FBML,
to create a more personalized invitation.
<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.
<FB:friend-selector/>
(see FB:friend-selector example in Part 1),
or <fb:multi-friend-input/>
(see at Fb:multi-friend-input)
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 + ")";
facebookUserID
points to Facebook User ID of the current user using your application.
In this query, has_added_app = 1
"where" 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
part - that we only need Friends of a given user
(see more at FQL "Books Online", and
FQL Tables).
friend
where uid1 ....
In the above query we only select uid
field, because that's only one we need to pass into
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;
}
}
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;
}
}
DirectFQLQuery()
,
I can display list of users already who installed my application:
<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;
}
}
friendList
variable contains list of Friend ID's extracted using the above query and DirectFQLQuery()
method
...
//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;
}
...
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;
}
}
has_added_app = 1
, I used has_added_app = 0
to get list of users who are Friends but don't have application added.
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:
To display Application profile information on User Profile page, we need to call <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
SetRefHandle()
(see more at Fbml.setRefHandle()
)
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.
SetFBML()
,
SetFBML()
creates and sets reference handle for subsequent updates using <fb:ref/>
,
SetRefHandle()
and pass reference handle created in #4.
...
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
...
}
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;
}
SetRefHandle(handleUpdate, profileFBML)
will affect all users with that particular reference handle.
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);
...
}
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);
}
}
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
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);
...
}
...
...
}
fbml.setRefHandle()
and simple VBS program in Windows Task Scheduler).
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?
}
}
}