A Twitter List Powered Fan Page
via
Introduction
Recently, Twitter rolled out a great new feature on their site – lists. You can now create and compile a list of twitter users and make it easier for others to follow all at once.Also, at the same time, they expanded their API to include list management functionality. This allows us to use these new tools to create a widget that flips lists the other way around – a fan page you can put in your sidebar, that allows your visitors to fill in their twitter name and join a specially crafted fan list in your twitter account.
So download the example files and lets start coding!
Step 1 – XHTML
As usual, we start with the XHTML. As the widget is entirely AJAX based, this is the only code that you will need to include directly into your site. The rest is fetched asynchronously.demo.html
<div id="fanPage">
<div class="title">
<a class="fanPageLink" href="http://twitter.com" title="Go to fanpage!"><img src="img/twitter-bird.png" id="twitBird" alt="twitter bird" /></a>
<a class="fanPageLink" href="http://twitter.com" title="Go to fanpage!">Fanpage</a>
</div>
<div class="content">
<div class="fans"><img src="img/loader.gif" alt="loading.." /></div>
</div>
<div class="subscribe">
<a href="#" class="joinFP">Join!</a>
<div class="membersCount">
<a class="fanPageLink" id="counter" href="http://twitter.com" title="Total Fans"></a>
</div>
</div>
</div>
Here we have the main fanPage container DIV, which holds our the widget and inside it we have the title, content and subscribe DIVs.
These are later styled with CSS and populated with data via AJAX. Also notice that we have three links which share a FanPageLink class. Currently they point to the twitter’s main site, but later we are going to edit their href attributes dynamically, and point them to the member page of the list.
Step 2 – CSS
Once we have the markup in place, we can move to the CSS. Here are only presented the rules that are directly used by the widget. You can view all of the code in demo.css in the source archive.demo.css
#fanPage{
/* This is the container that holds the widget */
background-color:#002233;
color:white;
height:300px;
margin:30px auto;
padding:10px;
text-align:left;
width:170px;
}
#fanPage a, #fanPage a:visited{
/* This styles the title and total fans links */
color:white;
text-decoration:none;
}
#fanPage a:hover{
text-decoration:underline;
}
.title{
/* The title on the top */
background-color:#013853;
font-family:&quot;Myriad Pro&quot;,Arial,Helvetica,sans-serif;
font-size:16px;
letter-spacing:1px;
margin:3px 0 10px;
padding:4px 8px;
position:relative;
text-align:right;
text-transform:uppercase;
}
#twitBird{
/* The twitter icon on the top */
left:-10px;
position:absolute;
top:-28px;
}
.content{
/* The div that holds the twitter avatars */
background-color:#eeeeee;
padding:6px;
text-align:left;
height:208px;
position:relative;
color:#333333;
}
#mask{
/* Inserted once you click the green &quot;Join&quot; button */
font-size:10px;
left:0;
padding:10px;
position:absolute;
top:0;
}
#mask label{
display:block;
font-weight:bold;
margin:8px 0 4px;
text-transform:uppercase;
}
#twitterName{
/* The twitter name input box */
background-color:#FCFCFC;
border:1px solid #CCCCCC;
color:#333333;
font-family:Arial,Helvetica,sans-serif;
font-size:12px;
padding:2px;
}
#mask a.greyButton,#mask a.greyButton:visited{
/* The default state of the gray join button */
display:inline-block;
height:19px;
margin-top:10px;
padding:6px 0 0;
text-align:center;
width:70px;
background:url(img/button_gray.png) no-repeat;
color:#222222;
}
#mask a.greyButton:hover{
/* The hover effect on the &quot;Join&quot; button */
background-position:bottom left;
text-decoration:none;
}
div#mask a, div#mask a:hover, div#mask a:visited{
color:#0196e3;
}
#response{
/* The div that holds the response messages in the &quot;Join area&quot; */
margin-top:10px;
font-size:10px;
text-align:center;
}
.subscribe{
position:relative;
}
.membersCount{
/* The total number of fans div */
position:absolute;
right:0;
top:5px;
color:white;
display:block;
font-size:22px;
font-weight:bold;
}
content img{
/* The twitter avatars */
margin:2px;
}
#fanPage, .content, .title{
/* Rounding three elements at once */
-moz-border-radius:4px;
-webkit-border-radius:4px;
border-radius:4px;
}
.a.joinFP, a.joinFP:hover{
/* The green &quot;Join&quot; button */
display:block;
background:url(img/buttons.png) no-repeat;
width:94px;
height:38px;
text-indent:-9999px;
margin:5px 0 0 -4px;
}
.a.joinFP:hover{
/* The hover state of the button */
background-position:bottom left;
}
a img{
border:none;
}
Step 3 – jQuery
As I mentioned earlier, the entire widget is AJAX based. This is actually a necessity, because communication with the twitter API would stall the website otherwise.Here is the main idea behind the code below:
- The page, in which the widget is included is loaded into a visitor’s browser;
- With it, script.js (which holds all of our jQuery code) is executed;
- $(document).ready() is run;
- An AJAX request is initiated, which loads the data from load.php and displays it on success;
- All the links with a fanPageLink class are pointed to the list members page on twitter;
- A click function is bonded to the green join button;
First half of script.js
Later if a click occurs on the green “Join” button, the avatars are faded out and a form appears on their place.$(document).ready(function(){
/* Executed on DOM load */
$.getJSON("load.php",function(data){
/* Loading the widget data */
if(data.error)
{
/* If there is an error, output and exit */
$(".content").html(data.error);
return false;
}
$(".content .fans").html('');
/* Remove the rotating GIF */
$.each(data.members,function(i,val){
/* Loop through all the shown members and add them to the .content DIV */
$(".content .fans").append('<a href="http://twitter.com/'+i+'" target="_blank"><img src="'+val+'" width="48" height="48" title="'+i+'" alt="'+i+'" /></a>');
});
$('#counter').html(data.membersCount);
/* Set the member counter */
$('.fanPageLink').attr('href',data.fanPage+'/members').attr('target','_blank');
/* Set the .fanPageLink-s to point to the profile page */
});
$('.joinFP').click(function(e){
/* IF the green button has been clicked.. */
if($('.content').html().indexOf('id="mask"')!=-1)
{
/* ..and the form is already shown exit */
e.preventDefault();
return false;
}
/* ..in the other case, start a fade out effect */
$(".content .fans").fadeOut("slow",function(){
$('.content').append('<div id="mask">\
To join our fan page, you just have to fill in your name\
<label>Twitter username:</label>\
<input id="twitterName" name="twitter" type="text" size="20" />\
<a href="" class="greyButton" onclick="sendData();return false;">Join!</a> or <a href="#" onclick="cancel();return false;">cancel</a>\
<div id="response"></div>\
</div>');
});
/* Prevent the link from redirecting the page */
e.preventDefault();
});
});
The second half of the code handles the sending of the data to add.php:
Second half of script.js
function sendData()
{
/* This function sends the form via AJAX */
$('#response').html('<img src="img/loader.gif" />');
var twitter = $('#twitterName').val();
if(!twitter.length)
{
$('#response').html('<span style="color:red">Please fill in your twitter username.</span>');
return false;
}
$.ajax({
type: "POST",
url: "add.php",
data: "twitter="+encodeURIComponent(twitter),
/* Sending the filled in twitter name */
success: function(msg){
/* PHP returns 1 on success, and 0 on error */
var status = parseInt(msg);
if(status)
{
$('#response').html('Thank you for being a fan! You will be added in a few minutes. <a href="#" onclick="cancel();return false">Hide this form</a>.');
$('#twitterName').val('');
}
else
$('#response').html('<span style="color:red">There is no such twitter user.</span>');
}
});
}
function cancel()
{
/* Hides the "Join" form */
$('#mask').remove();
$('.content .fans').fadeIn('slow');
}
The sendData function is called if the user clicks on the newly created gray “Join” button below the input field. It also checks the return status of the AJAX request to choose the proper status message.
Also remember that for above code the work, we need to include the jQuery library and script.js into the head section of the document:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="script.js"></script>
Step 4 – PHP
Now that we followed all the code on the front-end, it is now time for the last part of this tutorial – the PHP back-end.PHP has the important task of communicating with the twitter API. This is done via a special extension – CURL. For convenience, I made a special function – curlMe that wraps the CURL code and makes it easier to send requests from other places in the script.
functions.php
Now that we’ve defined those functions, we can use them in any PHP file by just including or requiring functions.php in the script.function error($msg)
{
// Format the error as a JSON object and exit the script:
die('{error:"'.$msg.'"}');
}
function fetchElement($element,$src)
{
// Takes in an XML document as string $src, and returns the required nod value
$match = array();
preg_match_all('/<'.$element.'>(.*)<\/'.$element.'>/u',$src,$match);
// Matching the required property in the xml
return $match[1];
// ..and returning it
}
function curlMe($url,$gp='')
{
// Using CURL to communicate with the Twitter API
global $username,$password;
$cc = curl_init();
curl_setopt($cc, CURLOPT_URL, $url);
if($gp)
{
// If the $gp parameter is set, send it by a POST request:
curl_setopt($cc, CURLOPT_POST, 1);
curl_setopt($cc, CURLOPT_POSTFIELDS, $gp);
}
else
curl_setopt($cc, CURLOPT_GET, 1);
curl_setopt($cc, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($cc, CURLOPT_USERPWD, $username.':'.$password);
curl_setopt($cc, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($cc, CURLOPT_RETURNTRANSFER, 1);
$xml = curl_exec($cc);
curl_close($cc);
return $xml;
}
Adding new fans on the list is done in add.php
add.php
As with any API, there are limits of usage. This is done to prevent abuse of the service and ruining everybody’s day. Twitter enforces a 150 requests per hour rule, which limits how many times we can GET data for the twitter list.require "functions.php";
require "config.php";
if(!$_POST['twitter'])
die('0');
$userXML = curlMe("http://twitter.com/users/show.xml?screen_name=".urlencode($_POST['twitter']));
// Initiating an API request
if(strpos($userXML,'<error>Not found</error>') !== false)
{
// If there is no such user, return an error:
die('0');
}
// fetchElement returns an array, and the list function assigns its first element to $id:
list($id) = fetchElement('id',$userXML);
curlMe('http://api.twitter.com/1/'.$username.'/'.$list.'/members.xml','id='.$id);
echo 1;
This is why I build a simple caching mechanism, that stores the fetched data for 15 minutes after a request is made to the API.
Here is how it works:
- The widget makes an AJAX request to load.php;
- The php script checks to see if a cache file exists;
- If it does, it gets its contents and returns it;
- If it does not, or if the cache is older than 15 minutes, it fetches the data from the API, stores it in the cache file for later use and returns it;
load.php'
require "functions.php";
require "config.php";
$cache_file = 'twitter.cache';
// The cache file
$cache_expire_time = 15*60;
// The cache expires after 15 minutes
$twitterers_shown = 12;
// If you are making changes and want to destroy the cache while testing,
// uncomment the line below:
//$cache_expire_time = 1;
if(!file_exists($cache_file) || time() - filemtime($cache_file) > $cache_expire_time)
{
// If there isn't a cache file, or if it is older than allowed
$xml = curlMe("http://api.twitter.com/1/".$username."/".$list."/members.xml");
//$xml = curlMe("http://api.twitter.com/1/chouka/design/members.xml");
if(strpos($xml,'<error>Not found</error>') !== false)
{
// If there is not such a list, create it automatically:
curlMe('http://api.twitter.com/1/'.$username.'/lists.xml','name='.$list);
}
$usernames = fetchElement('screen_name',$xml);
$avatars = fetchElement('profile_image_url',$xml);
$json = '';
foreach($usernames as $k=>$u)
{
if($k!=0) $json.=', ';
$json.='"'.$u.'":"'.$avatars[$k].'"';
// Generating the json object with a structure: username:avatar_image
if($k>=$twitterers_shown-1) break;
}
// Getting the total number of fans requires an additional API call:
$membersXML = curlMe("http://api.twitter.com/1/".$username."/lists/".$list.".xml");
$membersCount = fetchElement('member_count',$membersXML);
$json = '{members:{'.$json.'}, membersCount:'.$membersCount[0].',fanPage:"http://twitter.com/'.$username.'/'.$list.'"}';
// Save the generated json variable in the cache for later use:
$fp = fopen($cache_file,'w');
if($fp == false)
{
error("Your cache file could not be created! You have to chmod the script directory to 777!");
}
fwrite($fp,$json);
fclose($fp);
}
else
{
$json = file_get_contents($cache_file);
// Fetch the data from the cache file
}
echo $json;
Also you might notice that the API requires you to provide you username and password in order to use it. So if you are planning to run the demo on your own server, be sure to fill in your log-in information in config.php.With this our Twitter List Powered Fan Page is complete!
No comments:
Post a Comment