Title: Facebook Application Development - .NET Software Development - Generate Facebook Content using FBML (part 1) Author: Greg Dubinovskiy Email: siccolo_mobile_management@yahoo.com 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
There are plenty of resources out there that shows how to start developing Facebook application using ASP.NET/.NET environment.
For example Step-by-step Guide to Creating an Application and
Creating a Facebook Application and many others.
But how do you take that "Hello, World" Facebook sample to the next level? Hellooooooo,world? "Don't leave me hanging"....
In this article, I will show (or will try to show) how to build a more sophisticated Facebook application that interacts with Facebook users and with a SQL Server database.
As you may know, Facebook application is "hosted" within Facebook Canvas Page, and canvas page is within Facebook frame (kinda like russian doll house, er?). Facebook application can be FBML or .... just take a look at
Anatomy of a Facebook Application.
(When you're ready to develop your own Facebook Application, don't forget to "create" it first and request Application Key and Application Secret in Facebook at
Facebook Developers Application).
In this Facebook Application, I am using Facebook .NET Toolkit, found at Facebook Developer Toolkit library.
And to use it - simply reference it in your project, for example:
using Facebook;
using Facebook.WebControls;
public partial class _App : CanvasFBMLBasePage
{
...
...
}
CanvasFBMLBasePage
base class
So, let's say I have data in SQL Server database:
and I want to display it in my Facebook Application, for example using GridView control, like this:
How do we go about developing this?
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="app.aspx.cs" Inherits="_App" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server"> <title>Facebook Test Applicatioin:Traveling Babies</title></head>
<!-- <body> -->
<form id="formDataContent" runat="server">
<asp:GridView ID="gridSelectObjects" runat="server" AutoGenerateColumns="true" style="width:80%">
<AlternatingRowStyle BackColor="#f7f7f7" />
</asp:GridView>
</form>
<!-- </body> -->
</html>
body
element is commented. We're using FBML, and it will be hosted inside Facebook canvas, so no body
.
using Facebook;
using Facebook.WebControls;
public partial class _App : CanvasFBMLBasePage
{
//keys for FBTestApplication:
private const string FACEBOOK_API_KEY = "xfxfxdx7xbx7xbxfxaxdxsiccolo";
private const string FACEBOOK_SECRET = "dx8xfx9x8x1xsiccolo2x4x1xfx2xdx";
//db interface:
private FacebookDB m_FacebookDB = null;
new protected void Page_Load(object sender, EventArgs e)
{
base.Api = FACEBOOK_API_KEY;
base.Secret = FACEBOOK_SECRET;
base.Page_Load(sender, e);
string facebookUserID = this.FBService.GetUserInfo().UserId;
try
{
bool result = false;
string connectInfo = Settings.ConnectInfo();
m_FacebookDB = new FacebookDB(facebookUserID, connectInfo);
string applicationUserID = String.Empty;
string applicationUserObjectID = String.Empty;
string objectType = String.Empty;
string objectDescription = String.Empty;
string errorInfo = String.Empty;
result = m_FacebookDB.GetApplicationUserID_withObjectInfo(out applicationUserID,
out applicationUserObjectID,
out objectType,
out objectDescription,
out errorInfo);
if (!result || applicationUserID == String.Empty)
{
this.labelFBDashboard.Text = "Failed to retrieve Application User:" + errorInfo;
return;
}
//show object list in a grid
result = ShowObjectListGrid(applicationUserID, out errorInfo);
}
catch (Exception)
{
throw (new System.Exception "Your mama off the train");
}
}
private bool ShowObjectListGrid(string applicationUserID, out string errorInfo)
{
errorInfo = String.Empty;
try
{
bool result = false;
DataSet objectList = null;// new DataSet("ObjectList");
result = m_FacebookDB.GetObjectList(applicationUserID,
out objectList,
out errorInfo);
// bind the data
this.gridSelectObjects.DataSource = objectList;
this.gridSelectObjects.ShowHeader = false;
//bind
this.gridSelectObjects.DataBind();
return result;
}
catch(Exception ex_show_grid)
{
errorInfo = ex_show_grid.Message;
return false;
}
}
}
FacebookDB
is a middle-layer, to interface with SQL Server database.
fb:dashboard
FBML element (more at fb:dashboard at Facebook Developers Wiki),
fb:create-button
within fb:dashboard
FBML element (more at fb:create-button at Facebook Developers Wiki),
fb:dashboard
FBML element
GridView
element
new protected void Page_Load(object sender, EventArgs e)
{
...
...
//FMBL part:
FBMLMaker fbmlMaker = new FBMLMaker(this.FBService.GetUserInfo());
//set dashboard
this.labelFBDashboard.Text = fbmlMaker.Dashboard();
...
...
}
labelFBDashboard
is <asp:Label ID="labelFBDashboard" runat="server" Text="... fb-dashboard ..." ></asp:Label>
, and
Dashboard()
method:
public string Dashboard()
{
//for FBML generations can also be used StringBuilder();
//http://wiki.developers.facebook.com/index.php/Fb:dashboard
string fbDashboard = "<fb:dashboard>";
fbDashboard += "<fb:action href=\"http://www.siccolo.com/\">Developer Site</fb:action>";
//show greeting as a link..
fbDashboard += "<fb:action href=\"#\" >" + this.Greeting() + "</fb:action>";
//show application help link:
fbDashboard += "<fb:help href=\"http://www.siccolo.com/\">What do I do with this?!</fb:help>";
fbDashboard += "<fb:create-button href=\"http://www.siccolo.com/\">Take me away!</fb:create-button>";
fbDashboard += "</fb:dashboard>";
return fbDashboard;
}
<fbml >
tags right into your aspx page (see bellow using <fb:friend-selector/>
element).
As you may know, you can use FBJS on FBML pages.
So, let's modify grid to add a link on each row:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="app.aspx.cs" Inherits="_App" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server"> <title>Facebook Test Applicatioin:Traveling Babies</title></head>
<!-- <body> -->
<form id="formDataContent" runat="server">
<asp:GridView ID="gridSelectObjects" runat="server" AutoGenerateColumns="false" style="width:80%">
<Columns>
<asp:TemplateField ItemStyle-Width='180px' ItemStyle-Wrap=false>
<ItemTemplate>
<a href="#" onclick="select_object(<%# Eval("object_id")%>, '<%# Eval("type")%>', '<%# Eval("description")%>');return false;"
style="color:Black;background-color:Transparent;border-color:Transparent;border-style:None;">
<img src="http://www.siccolo.com/fbtestappsiccolo/images/select.gif"
border="0"
align="center"
title="select <%# Eval("description")%>"/> select <%# Eval("description")%></a>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<AlternatingRowStyle BackColor="#f7f7f7" />
</asp:GridView>
</form>
<!-- </body> -->
</html>
select_object()
javascript (FBJS) for each row. So, when user clicks on "select..." link, on the grid row, page will display selected row and will allow user to perform some action on selected item:
DIV
element:
<div id="data_selected_content1" style="display:none">
<span class="detached_label">Baby
<span id="spanSelectedObject1" style="font-weight: bold; font-style: italic; font-variant: normal;"></span>
needs:
<asp:DropDownList ID="listObjectAction1" runat="server" CssClass="listElement" ></asp:DropDownList>
...and then off to
<fb:friend-selector idname="textSelectedFriend" />
<asp:Button ID="buttonSend" runat="server" Text="Send" CssClass="inputsubmit" OnClick="buttonSend_Click" />
</span>
</div>
<script>
function select_object(object_id , type, description)
{
Animation(document.getElementById('data_selected_content1')).to('height', 'auto').from('0px').to('width', 'auto').from('0px').to('opacity', 1).from(0).blind().show().go();
document.getElementById("spanSelectedObject1").setTextValue ( description) ;
document.getElementById("buttonSend").setValue ( "Send " + description) ;
}
</script>
Animation()
to show DIV
element - see FBJS/Animation for more information.
As you can see, in order to change SPAN
innerText, I'm using setTextValue()
method, and for a button, I'm using setValue()
.
<fb:friend-selector idname="textSelectedFriend" />
FBML element
(more at Fb:friend-selector)
idname
attribute of <fb:friend-selector>
element - the name of the hidden form element that contains the user ID of the selected friend when form is being submitted, as we will see later on.
<fb:editor>
element (see more at Fb:editor):
<div id="data_selected_content2" >
<fb:editor labelwidth="200">
<fb:editor-custom label="Baby">
<span id="spanSelectedObject2" style="font-weight: bold; font-style: italic; font-variant: normal;"></span>
</fb:editor-custom>
<fb:editor-custom label="Needs">
<asp:DropDownList ID="listObjectAction2" runat="server" CssClass="listElement" ></asp:DropDownList>
</fb:editor-custom>
<fb:editor-buttonset>
<fb:editor-button value="Send to Comrade"/>
</fb:editor-buttonset>
</fb:editor>
</div>
So, at this point we have a nice looking form that presents a list of items, allows user to select a Friend... But, it would be nice if the appliction could pass that information back to us, a?
Do you remember the server-side button buttonSend
I had:
<div id="data_selected_content1" style="display:none">
<span class="detached_label">Baby
<span id="spanSelectedObject1" style="font-weight: bold; font-style: italic; font-variant: normal;"></span>
needs:
<asp:DropDownList ID="listObjectAction1" runat="server" CssClass="listElement" ></asp:DropDownList>
...and then off to
<fb:friend-selector idname="textSelectedFriend" />
<asp:Button ID="buttonSend" runat="server" Text="Send" CssClass="inputsubmit" OnClick="buttonSend_Click" />
</span>
</div>
<script>
function displayError(title, message, context)
{
new Dialog(Dialog.DIALOG_CONTEXTUAL).setContext(context).showMessage(title, message );
}
function check_form(form)
{
if ( form == null) { form = document.getElementById('formDataContent');}
var params=form.serialize();
if (params.textSelectedFriend==null || params.textSelectedFriend=='' )
{
displayError("Error", "Please select a Comrade!" , document.getElementById('data_selected_content1') );
return false;
}
else
{
return true;
}
}
</script>
new protected void Page_Load(object sender, EventArgs e)
{
...
ClientScript.RegisterOnSubmitStatement(this.GetType(), "sudmitformDataContent", "return check_form(this);");
...
}
protected void buttonSend_Click(object sender, EventArgs e)
{
string selectedFriendID = String.Empty;
try
{
selectedFriendID = Page.Request.Form["textSelectedFriend"].ToString();
}
catch
{
//oh-ho, no friend is selected!!!
return;
}
//save selected action and friend to database:
bool result = false;
string errorInfo = String.Empty;
string connectInfo = Settings.ConnectInfo();
string facebookUserID = this.FBService.GetUserInfo().UserId;
m_FacebookDB = new FacebookDB(facebookUserID, connectInfo);
string applicationUserID = this.textApplicationUserID.Value;
if ( applicationUserID == String.Empty)
{
this.labelFBDashboard.Text = "Application User ID is missing!";
return;
}
string applicationUserObjectID = this.textSelectedObject.Value;
if (applicationUserObjectID == String.Empty)
{
this.labelFBDashboard.Text = "Application Object ID is missing!";
return;
}
string selectedActionID = this.listObjectAction1.SelectedValue.ToString();
string expandedUserObjectActionText = String.Empty;
string notificationTextToFriend = String.Empty;
result = m_FacebookDB.DoObjectAction_And_Do_SendToFriend(applicationUserID,
applicationUserObjectID,
selectedActionID,
selectedFriendID,
out notificationTextToFriend,
out errorInfo);
if (!result)
{
this.labelFBDashboard.Text = "Failed to perform selected action!" + errorInfo;
return;
}
}
Page.Request.Form["textSelectedFriend"]
form field, from <fb:friend-selector>
element.
No, we didn't create this element on the page, instead Facebook platform added it "dynamically".
DIALOG_CONTEXTUAL
:
<div id="data_selected_content1" style="display:none">
...
<a href="#" onclick="new Dialog(Dialog.DIALOG_CONTEXTUAL).setContext(this).showMessage('Selected Object', 'This is the object you\'ve selected'); return false;">
<span id="spanSelectedObject1" style="font-weight: bold; font-style: italic; font-variant: normal;"></span></a>
...
</div>
If you need to "insert" a page within your application, FBML canvas - you can use <fb:iframe>
element
(see more at Fb:iframe)
For example, I use <fb:iframe>
element to reference a apsx page to retrieve user IP address - using Request.ServerVariables["REMOTE_ADDR"]
(remeber, that FBML application is within canvas, within Facebook... so you'll get something like 204.15.23.168, which is IP address of Facebook server, not of your user).
Then, based on given IP address I can display various information, for example weather forecast:
In page code, to reference <fb:iframe>
element:
<iframe>
HTML element.
One of the feature of FBML and FBJS - ability to display standard dialogs (see above - Using FBJS Dialogs).
We can use one of standard dialogs to display dynamic content - for example, list of records from SQL Server database:
To do this, first of all I needed to add a link to main GridView (see above - Using <asp:GridView> with FBML):
...
<asp:GridView ID="gridSelectObjects" runat="server" AutoGenerateColumns="false" style="width:80%;text-align:center" align="center">
<Columns>
<asp:TemplateField ItemStyle-Width='180px' ItemStyle-Wrap=false>
<ItemTemplate>
<a href="#" onclick="select_object(<%# Eval("object_id")%>, '<%# Eval("type")%>', '<%# Eval("description")%>');return false;"
style="color:Black;background-color:Transparent;border-color:Transparent;border-style:None;">
<img src="http://www.siccolo.com/fbtestappsiccolo/images/select.gif"
border="0"
align="center"
title="select <%# Eval("description")%>"/> select <%# Eval("description")%></a>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField ItemStyle-Width='180px' ItemStyle-Wrap=false>
<ItemTemplate>
<a href="#" onclick="show_object_info(<%# Eval("object_id")%>);return false;"
style="color:Black;background-color:Transparent;border-color:Transparent;border-style:None;">
show <%# Eval("description")%>'s history</a>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<AlternatingRowStyle BackColor="#f7f7f7" />
</asp:GridView>
...
<script>
function show_object_info(object_id)
{
var ajax = new Ajax();
ajax.responseType = Ajax.FBML;
ajax.ondone = function(data)
{
var dlg = new Dialog();
dlg.showMessage('Object Action History', data );
}
ajax.post("http://www.siccolo.com/fbtestappsiccolo/object_action_history.aspx?id=" + object_id);
}
</script>
object_action_history.aspx
ASP.NET page:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="object_action_history.aspx.cs" Inherits="_ObjectActionHistory" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Object Action History</title>
</head>
<form id="formDataContent" runat="server" >
<div style="text-align:center">
<asp:GridView ID="gridSelectObjects" runat="server" AutoGenerateColumns="false" style="width:95%"> </asp:GridView>
</div>
</form>
</html>
object_action_history.aspx
, when returns from ajax.post()
to ajax.ondone()
gives nothing more, just a "rendered" Grid View.
Then, in javascript (FBJS), on ajax.ondone
, we take that data and "shovel" it down Dialog.showMessage():
<script>
function show_object_info(object_id)
{
//var dlg = new Dialog().showMessage('.. Traveling Babies ..', 'Select a baby and send it to your comrade...');
var ajax = new Ajax();
ajax.responseType = Ajax.FBML;
ajax.ondone = function(data)
{
var dlg = new Dialog();
dlg.showMessage('Object Action History', data );
}
ajax.post("http://www.siccolo.com/fbtestappsiccolo/object_action_history.aspx?id=" + object_id);
}
</script>
So far so good. We have Facebook-alike interface, user selects data, selected data is being saved in the database.
But I forgot that Facebook really is a social network, it's about social interactions...
Let's add a "social interaction" in a form of user notification to our Facebook Application.
Thanks to Facebook Developer Toolkit library,
notifications are very easy to do:
protected void buttonSend_Click(object sender, EventArgs e)
{
string selectedFriendID = String.Empty;
try
{
selectedFriendID = Page.Request.Form["textSelectedFriend"].ToString();
}
catch
{
//oh-ho, no friend is selected!!!
return;
}
...
result = m_FacebookDB.DoObjectAction_And_Do_SendToFriend(applicationUserID,
applicationUserObjectID,
selectedActionID,
selectedFriendID,
out notificationTextToFriend,
out errorInfo);
...
string notification_result = FBService.SendNotification(notificationTextToFriend, selectedFriendID);
...
}
SendNotification()
method and pass notification text and ID of Facebook user.
In this code, ID is of selected Friend.
notificationTextToFriend
notification text is built in the stored procedure, not in the application code.
But you can "compose" notification in the code as well. The result is something like this:
declare @application_path varchar(255)
set @application_path = 'http://apps.facebook.com/fbtestappsiccolo'
declare @notification varchar(8000)
select @notification =
' on [' + convert(varchar(10), action_date, 101) + ' ' +
convert(varchar(8), action_date, 108) + '] ' +
' sent '+
'<a href="' + @application_path + '">' + o.description + '' +
' to you!' +
' <fb:name uid="' + @facebook_user_id + '" /> already ' +
replace( oa.action_performed_text_ex, '<object_description>',o.description )