Christmas Wishes With Facebook Connect

Christmas Wishes With Facebook Connect

Providing a simple way for users to authenticate themselves can turn a web application in a much more personal place. But a registration is a time taking process which visitors prefer to go without.

This is exactly where services such as Facebook Connect step in. They let you dive inside their multimillion user base, skipping that tedious registration process that drives people off. Another benefit is that Connect enables you to communicate user activity back to facebook, which is a great promotional tool.

Today, we will develop a webapp which will allow users to add christmas wishes using facebook connect and share them with their friends. We are using CSS, jQuery, PHP, AJAX and MySQL for data storage.

Feel free to download the tutorial files and continue with step one.

Step 1 – Creating a facebook application

The first step you have to take, is to enable developer functionality for your facebook account.  Providing that you already have a registration, you have to visit the following URL and install the developer application.

After you’ve done that, you’ll need to set up a new application, which will generate a special API key you’ll later have to include in your JavaScript files (more on that in a moment).

For more information on facebook connect, you can watch this great video by the facebook team, and visit the facebook wiki.

Facebook Developers

Facebook Developers

The main idea is that we are going to allow users to authenticate themselves with facebook connect, and store only their unique facebook IDs in the database as their identity. We are later using Connect to dynamically fetch additional details about the person (such as the profile picture and the person’s name) and insert it directly into the page.

Another benefit of using Connect is that the wish can easily be posted to the user’s wall with a link to your web app – a great way to promote your site.

Step 2 – XHTML

Now that we’ve set everything up, we can start off by laying down the XHTML structure of the site.

The main part of the markup is generated at run time by the PHP back-end and inserted into the page at designated places.

index.php

<div id="main">

<h1 class="tutTitle">Christmas Wishes With Facebook Connect</h1>

<div id="wishesContainer">

<?php
// Outputting the wishes to the page:
echo $wishes;
?>

</div>

<?php

// If this is the first page, show the submit form:

if($page==1):
?>

<div id="addWish" class="wish add">
<div class="stage">
<div class="topIcon"></div>
<div class="profile-pic"><fb:profile-pic uid="1" facebook-logo="true" size="s"></fb:profile-pic></div>
<div class="name"></div>
<div id="share" class="body">

<div class="toggleContent">
Connect with facebook to post a wish<br />
<fb:login-button onlogin=""></fb:login-button>
</div>

<div class="toggleContent" style="display:none">
<form action="" method="post" id="wForm">
<p>
<textarea name="wish" id="wish" cols="20" rows="2"></textarea>
<input name="" id="sbutton" type="submit" value="Submit" />
<label for="cb">Post my wish to facebook as well</label>
<input name="cb" id="cb" type="checkbox" value="1" checked="checked" />
<input name="key" id="key" type="hidden" value="<?php echo $_SESSION['key'] ?>" />
</p>
</form>
</div>
</div>
<div class="clear"></div>
</div>
</div>

On line 9 you can see that we output the contents of the $wishes variable on the page. It holds all the markup formed according to the individual entries in the database (covered in detail in Step 4).

Also worth noting are those unfamiliar fb: tags that you’ve probably never seen before.  Those are special tags used by facebook (hence the fb prefix) and are dynamically replaced with real data from facebook’s severs based on the attributes of those tags.

But in order to use those, we have to insert a special xmlns (XML namespace) attribute in the opening <html> tag of our document:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:fb="http://www.facebook.com/2008/fbml">

Also you can see the submit form that allows the visitors to add their Christmas wishes to the webapp. Worth noting is that this form is only shown if the user is on the first (or home) page of the site.

Lets continue with the next step.

Christmas Wishes With Facebook Connect

Christmas Wishes With Facebook Connect

Step 3 – CSS

Functionality is nothing without looks. So lets add in some styles.

All of our styles are positioned positioned in demo.css. I’ve divided the code into three chunks so it is easier to digest:

demo.css – Part 1

body{
	/* Setting default text color, background and a font stack */
	color:#eeeeee;
	font-size:13px;
	background:url(img/flakes.jpg) repeat;
	font-family:Arial, Helvetica, sans-serif;
}

h1.tutTitle{
	/* Hiding the H1 - it is only visble by search engines */
	display:none;
}

#main{
	/* The main container */
	margin:15px auto;
	text-align:center;
	width:865px;
	position:relative;
	display:none;
}

a, a:visited {
	color:#0196e3;
	text-decoration:none;
	outline:none;
}

a:hover{
	text-decoration:underline;
}

#header{
	width:100%;
	height:160px;

	/* Setting the background of the header to show in the center of the page
	   and blend seemlessly with the background color: */

	background:url(img/header_bg.jpg) no-repeat #ad0303 center bottom;
	border-bottom:1px solid #d84b4e;
	text-align:center;
	margin-bottom:60px;

	/* Relative positioning, so that the #backToTut div is positioned accordingly */
	position:relative;
}

#backToTut{
	/* Positioning the "Back to the tut" link */
	position:absolute;
	right:30px;
	top:40px;
}

#logo{
	width:880px;
	text-align:left;
	margin:0 auto;
}

.wish{
	/* A common class, shared by all .wish divs */
	padding:10px;
	text-align:left;
	width:600px;
	margin:0 auto 45px;
	position:relative;
}

/* Individual CSS rules for each color: */

.wish.green{
	background:url(img/green_bg.jpg);
	border:1px solid #3ccd1b;
}

.wish.red{
	background:url(img/red_bg.jpg);
	border:1px solid #e9403c;
}

.wish.add{
	background:url(img/blue_bg.jpg);
	border:1px solid #2976BB;
}

The site has a basic structure with a header on the top (the div with an id of header), which takes the full width of the page and the main container, which holds all the content of the site and is centered on the page.

The header’s background is an image with a fixed width (aren’t they all) which fades gracefully to the background color of this same div. This way no matter how wide the visitor’s screen is, they are guaranteed to see a uniform heading section of the site.

You can also see that we’ve defined a general .wish class, which we build on by defining custom backgrounds for the different color classes.

demo.css – Part 2

.profile-pic{
	float:left;
	margin-right:10px;
}

div div.profile-pic img{
	/* Re-designing the facebook avatars */
	border:2px solid white;

	/* CSS3 Box Shadow */
	-moz-box-shadow:0 0 1px #666666;
	-webkit-box-shadow:0 0 1px #666666;
	box-shadow:0 0 1px #666666;
}

a.fbconnect_login_button {
	/* The facebook button */
	display:block;
	margin-top:7px;
}

.stage{
	/* The .stage divs, which hold the text of the wish */
	background:url(img/bg_gwg.png) repeat-y  center top #eeeeee;
	border:3px solid white;
	padding:8px;

	/* CSS3 Box Shadow */
	-moz-box-shadow:0 0 3px #666666;
	-webkit-box-shadow:0 0 3px #666666;
	box-shadow:0 0 3px #666666;

	/* Fixing the peekaboo bug in IE by triggering the
	   hasLayout mechanism with the zoom property */
	zoom:1;
}

.topIcon{
	/* The icons at the top */
	width:128px;
	height:120px;
	position:absolute;
	top:-40px;
	right:-40px;
}

.wish.red .topIcon{
	background:url(img/mistletoe.png) no-repeat;
}

.wish.green .topIcon{
	background:url(img/jingle.png) no-repeat;
}

.name{
	color:#888888;
	margin-left:114px;
}

.body{
	/* Text of the wish */
	color:#555555;
	font-size:20px;
	padding-top:6px;
	margin-left:114px;
}

#addWish .name{
	/* Initially hiding the name field of the comment form */
	display:none;
}

#cb{
	/* The checkbox */
	position:relative;
	top:2px
}

#sbutton{
	/* The submit button */
	background:#155C9C;
	border:1px solid #093F6F;
	color:white;
	cursor:pointer;
	font-family:Arial,Verdana,Sans-serif;
	font-size:12px;
	padding:3px;
}

There is one aspect of Facebook Connect that might surprise you at first – it automatically includes a stylesheet in your page that might overwrite the styles you’ve set up for things such as the user avatars and names.

For this purpose you can use the !important modifier next to your rules (font-size:12px !important;) or do what I’ve done – provide more elements in the description of the classes, which adds more weight to your rules and overrides those of facebook ( div div.profile-pic img instead of just .profile-pic img, line 6).

demo.css – Part 3

#wish{
	/* The textarea */
	border:1px solid #AAAAAA;
	color:#606060;
	font-family:Arial,Verdana,Sans-serif;
	font-size:20px;
	padding:3px;
	width:400px;
	display:block;
}

label{
	font-size:10px;
}

a img{
	border:none;
}

/* Styling the pagination at the bottom of the page: */

.pagination{
	font-size:10px;
	margin:-15px auto 10px;
	text-transform:capitalize;
	width:620px;
}

.pagination a,.pagination a:visited,.pagination span{
	background-color:#F2F2F2;
	border:1px solid white;
	color:#545454;
	display:inline;
	float:left;
	margin:3px 5px 0 0;
	padding:4px 6px;
	text-decoration:none;
}

.pagination a:hover{
	background-color:#eaf7ff;
	color:#444444;
}

.pagination span{
	background-color:#BBBBBB;
	border:1px solid #CCCCCC;
	color:#666666;
}

/* The clearfix hack */

.clear{
	clear:both;
}

p{
	/* The tut info on the bottom of the page */
	padding:10px;
	text-align:center;
}

In the third part of the code, we style the form elements and the pagination, displayed on the bottom of the page.

The Wish Container

The Wish Container

Step 4 – PHP

As I mentioned in step one, PHP generates all of the christmas wishes XHTML markup dynamically, after fist selecting them from the database. Lets see how this works.

index.php

// Error reporting

error_reporting(E_ALL^E_NOTICE);

// Including additional files
require "connect.php";
require "functions.php";

// If there is no key, create a new one
if(!$_SESSION['key'])
{
	// Making a new unique key:
	$key = md5(time().$_SERVER['REMOTE_ADDR']);
	// Saving it to the session. Will be used in post.php;
	$_SESSION['key'] = $key;
}

// The total number of wishes. Used in the pagination:
list($total)=mysql_fetch_array(mysql_query("SELECT COUNT(*) FROM christmas_wishes"));

$results_per_page=5;
$page=1;
if((int)$_GET['page']>1) $page = (int)$_GET['page'];

$start=($page-1)*$results_per_page;

// Selecting the wishes from the database. Using te LIMIT cause:
$res = mysql_query("SELECT * FROM christmas_wishes ORDER BY id DESC LIMIT ".$start.",".$results_per_page);

$wishes='';
$c = '';
$i=0;

while($row=mysql_fetch_assoc($res))
{
	$c='red';
	if($i++%2 == 0) $c='green';
	$wishes .= '
		<div class="wish '.$c.'">
		<div class="stage">
		<div class="topIcon"></div>
		<div class="profile-pic"><fb:profile-pic uid="'.$row["facebook_id"].'" facebook-logo="true" size="s"></fb:profile-pic></div>
		<div class="name"><fb:name uid="'.$row["facebook_id"].'" useyou="false"></fb:name>&nbsp;wished</div>
		<div class="body">'.htmlspecialchars($row["wish"]).'</div>
		<div class="clear"></div>
		</div>
		</div>
	';
}

First we generate a special key which is used to check whether the form has been submitted properly and put it add it as a session variable.

As you can see from the XHTML section of the tut, this key is also inserted as a hidden field inside of the form and is later submitted back to the server along the textual body and other data.

It is then checked whether it corresponds with the key in the session. If it does not, this means that the form was submitted without visiting index.php first (a sure sign of a spam bot).

This technique will also prevent double posting, because the session key is unset after a successful insert in the database.

The form is submitted via AJAX, which sends the data to post.php, which is given below:

ajax/post.php

// Error reporting

error_reporting(E_ALL^E_NOTICE);
require "../connect.php";
require "../functions.php";

if(!$_POST['uid'] || !$_POST['wish'] || !$_POST['key']) die('0');

// Checking to see whether the key is in place:
if(!$_SESSION['key'] || $_SESSION['key']!=$_POST['key']) die('0');

$uid = (int)$_POST['uid'];
if(!$uid) die(err('Please login to facebook first!'));

// Escaping the string and stripping the HTML:
$wish = mysql_real_escape_string(strip_tags($_POST['wish']),$link);

if(mblen($wish)<4) die(err('Your wish is too short!'));

/* Inserting a new record in the christmas_wishes DB: */
mysql_query('	INSERT INTO christmas_wishes (facebook_id,wish)
			VALUES ('.$uid.',"'.$wish.'")');

if(mysql_affected_rows($link)==1)
{
	echo '{status:1}';
	unset($_SESSION['key']);
}
else echo err('Something went wrong. Try again!'.mysql_error($link));

It is pretty straightforward. The variables are validated, the data is escaped and inserted in the database. A JSON object is returned which has a status and text property (the latter is initialized only if there is an error and contains the error message).

There is another PHP file – functions.php, which will not be discussed here, but is worth taking a look. Now lets take a look at jQuery.

The Submit Form

The Submit Form

Step 5 – jQuery

Before being able to use the jQuery library, we have to include it into the page.

index.php

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php"></script>
<script type="text/javascript" src="jquery.scrollTo-min.js"></script>

<script type="text/javascript" src="script.js"></script>

I included jQuery directly from Google’s AJAX CDN – this enables us to have the benefits of their huge infrastructure and the likely possibility that the library is already cached in the visitors browser.

The next scripts are the facebook connect library, the jQuery scrollTo plugin (which you’ll see in action in a moment) and our own script file.

All of our JavaScript code is located inside script.js. I’ve split the code in two parts:

script.js – Part 1

$(function(){

	/* This code is run on page load */

	/* Showing the wishes, hidden by the CSS. */
	$('#main').show();

	/* Listening for the submit event on the form */
	$('#wForm').submit(function(e){
		addWish();
		e.preventDefault();
	});

	function init(){
		/* Run on page load if the user is logged into facebook */
		$('#share .toggleContent').toggle();

		$('#addWish .name').html('Hi, <fb:name uid="loggedinuser" useyou="false" firstnameonly="true"></fb:name>, add your wish below:').show();
		$('#addWish .profile-pic').children().attr('uid',FB.Connect.get_loggedInUser());
		FB.XFBML.Host.parseDomTree();
	}

	/* Initializing facebook connect */
	FB.init("bcf260ae02d453654be01ec11e2d0aa0","xd_receiver.htm",{"ifUserConnected" : init});

In this first part of the script, we set the code to be run after the DOM has been completely loaded (and all page elements are accessible).

We later define a init() function that displays the submit form and initialize facebook connect (notice the ifUserConnected property – the init() functon is run on page load if the user has logged in previously).

The first parameter of the FB.init function is the API key of the app we created earlier.

script.js – Part 2

var commencing = false;

function addWish()
{
	/* If there is another AJAX process already active stop the function: */
	if(commencing) return false;

	/* Stripping HTML tags */
	var wish = $('#wish').val().replace(/<[^>]*>/g,'');

	if(wish.length<3) { alert('Your wish is too short or empty!'); return false; }
	if(!FB.Connect.get_loggedInUser()) { alert('You are not logged in!'); return false; }

	commencing=true;

	/* Sending the data to post.php */
	$.post('ajax/post.php',{uid:FB.Connect.get_loggedInUser(),wish:wish,key:$('#key').val()},function(msg){

		/* This function is run if the data was successfully sent */
		commencing=false;

		if(msg.status)
		{
			/* If there were no errors */
			if($('#cb').attr('checked'))
			{
				var attachment = {
					name:'Christmas Wishes',
					href:'http://christmas-wishes.tutorialzine.com/',
					description:'What do you wish for this christmas?'
				};

				var action_link='';

				setTimeout(function(){
					FB.Connect.streamPublish(wish, attachment, action_link);
				},1000);
			}

			/* Insert a new wish by duplicating and changing the first wish currently on the page */
			var el = $('.wish').eq(0).clone()

			if(el.hasClass('green'))
			{
				el.addClass('red').removeClass('green');
			}
			else el.addClass('green').removeClass('red');

			el.find('.body').html(wish);
			el.hide().html(el.html().replace(/uid=\"\d+\"/g,'uid="'+FB.Connect.get_loggedInUser()+'"'));
			$('#wishesContainer').prepend(el);

			/* Parsing the fb tags: */
			FB.XFBML.Host.parseDomElement(el.get(0));

			el.fadeIn('slow');

			/* Scrolling the page to show the newly added wish into view: */
			$.scrollTo(el,800);
			$('#share').html('Thank you!');
		}
		else
		{
			alert('There was an error trying to add your comment!\nPlease try again in a few moments.');
			return false;
		}
	},'json')
}
});

The second part of the code holds the addWish() function which posts the data via AJAX.

You can see that I’ve used a variable called commencing to figure out whether there is an active transfer in the progress, which allows only one AJAX call at once (and thus preventing unnecessary double-posting caused by multiple clicks on the submit button).

Also, you can see on line 39 that I’ve used the jQuery ScrollTo plugin to scroll the window smoothly so that the newly inserted element is in view.

Step 6 – MySQL

In order to make this work on your own server, you’ll need to create the christmas_wishes MySQL table by running tables.sql, which is included in the download archive.

You’ll have to change your MySQL credentials in connect.php and replace the API key used in script.js with your own.

With this our Christmas Wishes webapp is complete!

Conclusion

Today we created a fully blown webapp which implemented facebook connect. You are free to build upon this source code and use it in your own personal or commercial projects.
Have a merry Christmas and a happy New Year!

What do you think? How would you improve it this code?

by Martin Angelov

Martin is a web developer with an eye for design from Bulgaria. He founded Tutorialzine in 2009 and publishes new tutorials weekly.

Tutorials

22 Comments

  1. Xesbeth says:

    This is really awesome! Thanks for sharing!

  2. Merry Christmas awesome app, I'm going to do some brainstorming on this for a project. More to come in an email.

  3. Tomas says:

    Great tutorial, I like it, thanks.

  4. Wow such a great tut. Thanks for the quality info!

  5. Anassin says:

    This is a great tutorial!

  6. flAme says:

    Great tut!
    I think that there is small bug. I can see more user's (facebook) images when i am connected to facebook. Especially in page number one (1).
    I am not friend to any of those users.

    Why do you think this is happened?

  7. Martin says:

    Thanks for the comments!

    @ flAme

    In fact it was a bug. I'd put a uid="1" as a default attribute for unauthorized users inside of the submit form, presuming that facebook would just ignore it, but instead it tries to fetch a user with an ID of 1 and its API crashes.

    I replaced it with uid="loggedinuser", which gets properly ignored if the user is not logged in.

    I also updated the download archive with the fix.

    There are still some users who are not displayed, but this is due to their privacy settings.

  8. Bisculis says:

    Hi,

    I have a little problem... :-(

    On my site, when i write something like "It's" i got this "it\'s".

    How to delete the \ ?

  9. Martin says:

    @ Bisculis

    You can fix this by adding the stripslashes PHP function on line 44 of the PHP section of the tut:

    htmlspecialchars($row["wish"])

    Replace it with:

    htmlspecialchars(stripslashes($row["wish"]))

  10. Bisculis says:

    Thanks a lot Martin for your fast reply!
    It's working :-)

    Thanks again for your good work!

  11. Bisculis says:

    Someone injected a fake profile. How i can block that?

    The Facebook id was wrong.

  12. flAme says:

    Thank you Martin for the reply! It is working realy good! I think i will try to improve this :)
    Much much appreciate for the knowledge you gave us.

  13. Evan says:

    Hmm, awesome tutorial! Question though - what if I wanted to give them the ability to post a wish or a picture? How about comments?

  14. martin says:

    why its always showing this notification?

    There was an error trying to add your comment!
    Please try again in a few moments.

    but if i refresh, the comment still added into my database, and showed on the page. but without the image of sender.

    can someone help me???

  15. Hanan Ali says:

    Thanks alot for such a nice sharing :)

  16. Joe says:

    How can I add the ability to let people to optionally add comments without signing into facebook?

  17. hi,

    This is great tutorials. Very helpful

    Thanks :)

  18. sager says:

    It's not work on Internet Explorer 9.

    1. sager says:

      Connect button is hidden.

  19. Adrian says:

    My app is not asking for user basic information, instead of displaying a allow or deny button actions it displays a login and a cancel button. Could anyone please guide me on how to configure the app in order to make it request the user basic information?

  20. Adrian says:

    Hi, this app does not work anymore. When i enter the first page i see the "Connect with facebook to post a wish" button and that is all. I can't press it or anything else...

Add Comment

Add a Reply

HTML is escaped automatically. Surround code blocks with <pre></pre> for readability.
Perks:   **bold**   __italics__   [some text](http://example.com) for links