Building a Facebook Application: Part 2

Exclusive offer: get 50% off this eBook here
Learning Facebook Application Development

Learning Facebook Application Development — Save 50%

A step-by-step tutorial for creating custom Facebook applications using the Facebook platform and PHP

$23.99    $12.00
by Dr Mark Alexander Bain Hasin Hayder | July 2009 | Open Source PHP

In the previous part of this article by Dr. Mark Alexander Bain and Hasin Hayder, we covered developing a simple Facebook application and the way to write the Facebook profile. In this part, we will cover Mock AJAX and data storage.

Mock AJAX and your Facebook profile

I'm sure that you've heard of AJAX (Asynchronous JavaScript and XML) with which you can build interactive web pages. Well, Facebook has Mock AJAX, and with this you can create interactive elements within a profile page. Mock AJAX has three attributes that you need to be aware of:

  • clickwriteform: The form to be used to process any data.
  • clickwriteid: The id of a component to be used to display our data.
  • clickwriteurl: The URL of the application that will process the data.

When using Mock AJAX, our application must do two things:

  • Return the output of any processed data (and we can do that by using either echo or print).
  • Define a form with which we'll enter any data, and a div to receive the processed data

Using a form on your profile

Since we want to make our application more interactive, one simple way is to add a form. So, for our first example we can add a function (or in this case a set of functions) to appinclude.php that will create a form containing a simple combo-box:

function country_combo () {
/*You use this function to display a combo-box containing
a list of countries. It's in its own function so that we
can use it in other forms without having to add any
extra code*/
$country_combo = <<<EndOfText
<select name=sel_country>
<option>England</option>
<option>India</option>
</select>
EndOfText;
return $country_combo;
}
function country_form () {
/*Like country_combo-box we can use this form where ever
needed because we've encapsulated it in its own function */
global $appcallbackurl;
$country_form = "<form>";
$country_form .= country_combo ();
$country_form .= <<<EndOfText
<input type="submit"
clickrewriteurl="$appcallbackurl"
clickrewriteid="info_display" value="View Country"
/>
<div id="info_display" style="border-style: solid;
border-color: black;
border-width: 1px; padding: 5px;">
No country selected
</div>
</form>
EndOfText;
return $country_form;
}
function display_simple_form () {
/*This function displays the country form with
a nice subtitle (on the Profile page)*/
global $facebook, $_REQUEST;
#Return any processed data
if (isset($_REQUEST['sel_country'])) {
echo $_REQUEST['sel_country'] . " selected";
exit;
}
#Define the form and the div
$fbml_text = <<<EndOfText
<fb:subtitle>
<fb:name useyou=false uid=$user firstnameonly=true
possessive=true> </fb:name>
Suspect List
</fb:subtitle>
EndOfText;
$fbml_text .= country_form ();
$facebook->api_client->profile_setFBML($fbml_text, $user);
echo $fbml_text;
}

And, of course, you'll need to edit index.php:

display_simple_form ();

You'll notice from the code that we need to create a div with the id info_display, and that this is what we use for the clickrewriteid of the submit button. You'll also notice that we're using $appcallbackurl for the clickrewriteurl ($appcallbackurl is defined in appinclude.php).

Now, it's just a matter of viewing the new FMBL (by clicking on the application URL in the left-navigation panel):

Building a Facebook Application: Part 2

If you select a country, and then click on View Country, you'll see:

Building a Facebook Application: Part 2

I'm sure that you can see where we're going with this. The next stage is to incorporate this form into our Suspect Tracker application. And the great thing now is that because of the functions that we've already added to appinclude.php, this is now a very easy job:

function first_suspect_tracker () {
global $facebook, $_REQUEST;
if (isset($_REQUEST['sel_country'])) {
$friend_details = get_friends_details_
by_country ($_REQUEST['sel_country']);
foreach ($friend_details as $friend) {
$div_text .=
"<fb:name uid=" . $friend['uid'] .
" firstnameonly=false></fb:name>, ";
}
echo $div_text;
exit;
}
$fbml_text .= country_form ();
$facebook->api_client->profile_setFBML($fbml_text, $user);
$facebook->redirect($facebook->get_facebook_url() . '/profile.php');
}

You may also want to change the country_form function, so that the submit button reads View Suspects. And, of course, we'll also need to update index.php. Just to call our new function:

<?php
require_once 'appinclude.php';
first_suspect_tracker ();
?>

This time, we'll see the list of friends in the selected country:

Building a Facebook Application: Part 2

or:

Building a Facebook Application: Part 2

OK, I know what you're thinking, this is fine if all of your friends are in England and India, but what if they're not? And you don't want to enter the list of countries manually, do you? And what happens if someone from a country not in the list becomes your friend? Obviously, the answer to all of these questions is to create the combo-box dynamically.

Creating a dynamic combo-box

I'm sure that from what we've done so far, you can work out how to extract a list of countries from Facebook:

function country_list_sql () {
/*We're going to be using this piece of SQL
quite often so it deserves its own function*/
global $user;
$country_list_sql = <<<EndSQL
SELECT hometown_location.country
FROM user
WHERE uid IN (SELECT uid1
FROM friend
WHERE uid2=$user)
EndSQL;
return $country_list_sql;
}
function full_country_list () {
/*With the SQL in a separate function this
one is very short and simple*/
global $facebook;
$sql = country_list_sql ();
$full_country_list = $facebook->
api_client->fql_query($sql);
print_r ($full_country_list);
}

However, from the output, you can see that there's a problem with the data:

Building a Facebook Application: Part 2

If you look through the contents of the array, you'll notice that some of the countries are listed more than once—you can see this even more clearly if we simulate building the combo-box:

function options_country_list () {
global $facebook;
$sql = country_list_sql ();
$country_list = $facebook->api_client->fql_query($sql);
foreach ($country_list as $country){
echo "option:" . $country['hometown_location']
['country'] ."<br>";
}
}

From which, we'd get the output:

Building a Facebook Application: Part 2

This is obviously not what we want in the combo-box.

Fortunately, we can solve the problem by making use of the array_unique method, and we can also order the list by using the sort function:


function filtered_country_list () {
global $facebook;
$sql = country_list_sql ();
$country_list = $facebook->api_client->
fql_query($sql);
$combo_full = array();
foreach ($country_list as $country){
array_push($combo_full, $country[
'hometown_location']['country']);
}
$combo_list = array_unique($combo_full);
sort($combo_list);
foreach ($combo_list as $combo){
echo "option:" . $combo ."<br>";
}
}

And now, we can produce a usable combo-box:

Building a Facebook Application: Part 2

Once we've added our code to include the dynamic combo-box, we've got the workings for a complete application, and all we have to do is update the country_combo function:

function country_combo () {
/*The function now produces a combo-box
derived from the friends' countries */
global $facebook;
$country_combo = "<select name=sel_country>";
$sql = country_list_sql ();
$country_list = $facebook->api_client->fql_query($sql);
$combo_full = array();
foreach ($country_list as $country){
array_push($combo_full, $country[
'hometown_location']['country']);
}
$combo_list = array_unique($combo_full);
sort($combo_list);
foreach ($combo_list as $combo){
$country_combo .= "<option>" . $combo ."</option>";
}
$country_combo .= "</select>";
return $country_combo;
}

Of course, you'll need to reload the application via the left-hand navigation panel for the result:

Building a Facebook Application: Part 2

Limiting access to the form

You may have spotted a little fly in the ointment at this point. Anyone who can view your profile will also be able to access your form and you may not want that (if they want a form of their own they should install the application!). However, FBML has a number of if (then) else statements, and one of them is <fb:if-is-own-profile>:

<?php
require_once 'appinclude.php';
$fbml_text = <<<EndOfText
<fb:if-is-own-profile>
Hi <fb:name useyou=false uid=$user firstnameonly=true></fb:name>,
welcome to your Facebook Profile page.
<fb:else>
Sorry, but this is not your Facebook Profile page -
it belongs to <fb:name useyou=false uid=$user
firstnameonly=false> </fb:name>,
</fb:else>
</fb:if-is-own-profile>
EndOfText;
$facebook->api_client->profile_setFBML($fbml_text, $user);
echo "Profile updated";
?>

So, in this example, if you were logged on to Facebook, you'd see the following on your profile page:

Building a Facebook Application: Part 2

But anyone else viewing your profile page would see:

Building a Facebook Application: Part 2

And remember that the FBML is cached when you run:

$facebook->api_client->profile_setFBML($fbml_text, $user);

Also, don't forget, it is not dynamic that is it's not run every time that you view your profile page. You couldn't, for example, produce the following for a user called Fred Bloggs:

Sorry Fred, but this is not Your Facebook Profile page - it belongs to Mark Bain

That said, you are now able to alter what's seen on the screen, according to who is logged on.

Storing data—keeping files on your server

From what we've looked at so far, you already know that you not only have, but need, files stored on your server (the API libraries and your application files). However, there are other instances when it is useful to store files there.

Storing FBML on your server

In all of the examples that we've worked on so far, you've seen how to use FBML mixed into your code. However, you may be wondering if it's possible to separate the two. After all, much of the FBML is static—the only reason that we include it in the code is so that we can produce an output. As well as there may be times when you want to change the FBML, but you don't want to have to change your code every time you do that (working on the principle that the more times you edit the code the more opportunity there is to mess it up).

And, of course, there is a simple solution.

Let's look at a typical form:

<form>
<div id="info_display" style="border-style: solid;
border-color: black;
border-width: 1px; padding: 5px;">
</div>
<input name=input_text>
<input type="submit"
clickrewriteurl="http://213.123.183.16/f8/penguin_pi/"
clickrewriteid="info_display" value="Write Result">
</form>

Rather than enclosing this in $fbml_text = <<<EndOfText ... EndOfText; as we have done before, you can save the FBML into a file on your server, in a subdirectory of your application. For example /www/htdocs/f8/penguin_pi/fbml/form_input_text.fbml.

"Aha" I hear your say, "won't this invalidate the caching of FBML, and cause Facebook to access my server more often than it needs?"

Well, no, it won't. It's just that we need to tell Facebook to update the cache from our FBML file. So, first we need to inform FBML that some external text needs to be included, by making use of the <fb:ref> tag, and then we need to tell Facebook to update the cache by using the fbml_refreshRefUrl method:

function form_from_server () {
global $facebook, $_REQUEST, $appcallbackurl, $user;
$fbml_file = $appcallbackurl . "fbml/form_input_text.fbml";
if (isset($_REQUEST['input_text'])) {
echo $_REQUEST['input_text'];
exit;
}
$fbml_text .= "<fb:ref url='" . $fbml_file . "' />";
$facebook->api_client->profile_setFBML($fbml_text, $user);
$facebook->api_client->fbml_refreshRefUrl($fbml_file);
echo $fbml_text;
}

As far as your users are concerned, there is no difference. They'll just see another form on their profile page:

Building a Facebook Application: Part 2

Even if your users don't appreciate this leap forward, it will make a big difference to your coding—you're now able to isolate any static FBML from your PHP (if you want).

And now, we can turn our attention to one of the key advantages of having your own server—your data.

Storing data on your server

So far, we've concentrated on how to extract data from Facebook and display it on the profile page. You've seen, for example, how to list all of your friends from a given country. However, that's not how Pygoscelis' list would work in reality. In reality, you should be able to select one of your friends and add them to your suspect list. We will, therefore, spend just a little time on looking at creating and using our own data.

We're going to be saving our data in files, and so your first job must be to create a directory in which to save those files. Your new directory needs to be a subdirectory of the one containing your application. So, for example, on my Linux server I would do:

cd /www/htdocs/f8/penguin_pi      

#Move to the application directory

mkdir data

#Create a new directory

chgrp www-data data                         

#Change the group of the directory

chmod g+w data                                 

#Ensure that the group can write to data

Learning Facebook Application Development A step-by-step tutorial for creating custom Facebook applications using the Facebook platform and PHP
Published: May 2008
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

You will, of course, have to change the directory to one on your own server, and you'll need to check which group your server uses for its web access—because, of course, it's going to be your web server that's writing to the files, not you.

Once you've done that, a simple test will check if everything is set up correctly. We can add a function to appinclude.php so that we can create a file in our new directory:

function create_file_on_server ($newfile) {
$file = "data/" . $newfile;
$handle = fopen($file, 'w') or die("can't open file");
fclose($handle);
echo "File written";
}

As always, call the function from index.php:

<?php
require_once 'appinclude.php';
create_file_on_server ('testFile.txt');
?>

Clicking on the left-navigation link should now test your setup. If there's a problem, you'll see something like:

Building a Facebook Application: Part 2

If you do see an error, then check:

  • Have you created the new folder in the correct location?
  • Have you set the permissions correctly on the folder?

When you have managed to set everything up correctly then you'll see:

Building a Facebook Application: Part 2

And, of course, you should also have an empty file (testFile.txt) saved in your data directory.

We can then quickly add functions to appinclude.php that can maintain our data files. And the first thing to do is to define a file name for each user's suspect file:

$suspect_file = 'data/' . $user . '_suspects.txt';

Having done that, we can add a function that will obtain an array of suspect ids from the suspect file (or create the suspect file if it doesn't exist):

function get_suspect_list () {
/*If the suspect file exists then load the
suspect list, if not then create the file*/
global $suspect_file;
if (file_exists($suspect_file)) {
if (filesize($suspect_file) > 0) {
$fh = fopen($suspect_file, 'r')
or die("can't open file " . $suspect_file);
$suspect_input = fread($fh, filesize
($suspect_file));
$get_suspect_list = split(" ", $suspect_input);
fclose($fh);
}
} else {
$fh = fopen($suspect_file, 'w')
or die("can't open file " . $suspect_file);
fclose($fh);
$get_suspect_list = array();
}
return $get_suspect_list;
}

Next, we'll create a function for adding new suspects:

function add_suspect ($nid, $suspect_list) {
/*With this function we add any new suspects to
the suspect file, and return an array containing
the new list of suspects*/
global $suspect_file;
if (! in_array($nid, $suspect_list)) {
$fh = fopen($suspect_file, 'a') or die
("can't open file" . $suspect_file);
fwrite($fh, $nid . " " );
fclose($fh);
array_push($suspect_list, $nid);
}
return $suspect_list;
}

Then, we'll need a function for displaying the list of suspects (just to the application page for the moment—not the profile page):

function display_suspect_list ($suspect_list) {
/*Display the new suspect list*/
foreach ($suspect_list as $suspect) {
if ($suspect) {
echo "<fb:profile-pic uid=$suspect></fb:profile-pic>";
echo
"<fb:name useyou=false uid=$suspect firstnameonly=false>
</fb:name>";
echo "<br>";
}
}
}

And, of course, we'll need a function for displaying the form that we'll use for entering any data:

function add_suspect_form () {
/*Create the form for entering data*/
$suspect_form = <<<EndOfText
<hr>
<form method=post>
<fb:friend-selector />
<input type=hidden name=add_new_suspect value=true />
<input type=submit value="Add to List of Suspects" />
</form>
EndOfText;
return $suspect_form;
}

Now, we can pull all of the functions together into a single function that we can call from index.php:

function better_suspect_tracker () {
global $_REQUEST;
$suspect_list = get_suspect_list ();
/*If a new suspect id has been passed through then
add it to the suspect file and the suspect list array
(if add_new_suspect is passed as well)*/
if ((isset($_REQUEST['friend_selector_id']))
&&(isset($_REQUEST['add_new_suspect']))) {
$suspect_list = add_suspect ($_REQUEST[
'friend_selector_id'],$suspect_list);
}
display_suspect_list ($suspect_list);
$text = add_suspect_form ();
echo $text;
}

So, once we've updated index.php:

<?php
require_once 'appinclude.php';
better_suspect_tracker ();

Then, our end result is an application that displays a list of suspects, and allows us to add new ones:

Building a Facebook Application: Part 2

And if we look on the server, we can see the contents of our new file:

cat /www/htdocs/f8/penguin_pi/data/614902533_suspects.txt
517311956 286603790 521240370
?>

Obviously, the next thing that we want to do is add another function so that we can remove suspects as well. For this, you'll need a form definition function:

function remove_suspect_form () {
/*Create the form for removing suspects from the list*/
$remove_suspect_form = <<<EndOfText
<hr>
<form method=post>
<fb:friend-selector />
<input type=hidden name=remove_suspect value=true />
<input type=submit value="Remove From List of Suspects" />
</form>
EndOfText;
return $remove_suspect_form;
}

And it's worth noting (if you haven't done so already) that we're using hidden input boxes (remove_suspect value and add_new_suspect) to tell the application which operation we want to carry out.

So, having added a form to select the data, we now need a function to remove the suspect:

function remove_suspect ($oid, $suspect_list) {
/*This function removes the inputted id and removes
it from the array (by making use of array_diff)
and from the suspect file*/
global $suspect_file;
if (in_array($oid, $suspect_list)) {
$suspect_list = array_diff($suspect_list, array($oid));
$fh = fopen($suspect_file, 'w')
or die("can't open file" . $suspect_file);
fwrite($fh, implode(" ",$suspect_list ));
fclose($fh);
}
return $suspect_list;
}

It's then just a matter of updating the better_suspect_tracker function to make use of this new code:

/*If a suspect id has been passed through to the 
application with the remove_suspect parameter
then delete the suspect from the list and
update the file*/
if ((isset($_REQUEST['friend_selector_id']))
&&(isset($_REQUEST['remove_suspect']))) {
$suspect_list = remove_suspect ($_REQUEST[
'friend_selector_id'],
$suspect_list);
}

And just one last thing, at the moment the profile file will not show the results of your changes (to be precise it will show the result of the last profile_setFBML that you carried out). So, we need just one more function:

function suspect_tracker_to_profile ($suspect_list) {
global $facebook, $user;
$fbml_text .= <<<EndOfText
<fb:subtitle><fb:name useyou=false uid=$user firstnameonly=true
possessive=true></fb:name> Suspect List
<fb:action href=http://apps.facebook.com/penguin_pi/>Edit</fb:action>
</fb:subtitle>
<fb:user-table cols=4>
EndOfText;
foreach ($suspect_list as $suspect) {
if ($suspect) {
$fbml_text .= "<fb:user-item uid=" . $suspect . " />";
}
}
$fbml_text .= "</fb:user-table>";
$facebook->api_client->profile_setFBML($fbml_text, $user);
}

Of, course you'll need to edit the better_suspect_tracker function so that it calls this code, and now you'll be able to see the result on your profile:

Building a Facebook Application: Part 2

You'll also notice (just to make this a really professional application) that we've added a link from the profile part of the application to the main application by adding:

<fb:action href=http://apps.facebook.com/penguin_pi/>Edit</fb:action>

into the <fb:subtitle></fb:subtitle> portion of the FMBL.

And there you have it—a simple, but effective, Facebook Application.

Summary

This article has been all about Mock AJAX with which you can create interactive elements within a profile page and storing files on your server.

Learning Facebook Application Development A step-by-step tutorial for creating custom Facebook applications using the Facebook platform and PHP
Published: May 2008
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Dr Mark Alexander Bain

Dr. Mark Alexander Bain first started customizing CRM systems back in the mid '90s when he was team leader for Vodafone's Cascade project – the team took the 'out-of-the-box' Clarify CRM and turned it into a radio base station planning application, complete with a workflow engine for passing jobs between the different departments involved in the planning, building, and implementation of a radio network. Since then he's lectured at the University of Central Lancashire, and currently Mark writes articles on all things Linux and Open Source for Linux Format, Newsforge.com and Linux Journal. SugarCRM customization, therefore, seems the obvious choice for this, his second book, since it combines Mark's knowledge of working with commercial CRMs and the Open Source philosophy. Mark works from his home on the edge of the Lake District in the UK, where he lives with his wife, two dogs and two cats, and gets the odd visit from his sons – Michael and Simon.

Hasin Hayder

Hasin Hayder graduated in Civil Engineering from the Rajshahi University of Engineering and Technology (RUET) in Bangladesh. He is a Zend-certified Engineer and expert in developing localized applications. He is currently working as a Technical Director in Trippert Labs and managing the local branch in Bangladesh. Beside his full time job, Hasin writes his blog at http://hasin.wordpress.com, writes article in different websites and maintains his open source framework Orchid at http://orchid.phpxperts.net. Hasin lives in Bangladesh with his wife Ayesha and his son, Afif.

Books From Packt

Drools JBoss Rules 5.0 Developer's Guide
Drools JBoss Rules 5.0 Developer's Guide

Plone 3 Theming
Plone 3 Theming

WordPress 2.7 Cookbook
WordPress 2.7 Cookbook

Building Enterprise Ready Telephony Systems with sipXecs 4.0
Building Enterprise Ready Telephony Systems with sipXecs 4.0

PHP and script.aculo.us Web 2.0 Application Interfaces
PHP and script.aculo.us Web 2.0 Application Interfaces

Expert Cube Development with Microsoft SQL Server 2008 Analysis Services
Expert Cube Development with Microsoft SQL Server 2008 Analysis Services

WordPress 2.7 Complete
    WordPress 2.7 Complete

Drupal 5 Views Recipes
Drupal 5 Views Recipes

 

 

 

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
u
1
R
4
E
Y
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software