Tying It All Together: Speed Dial Application in facebook Complete information on it !

Tying It All Together: Speed Dial Application in facebook


 Speed Dial Application

If you build it, they will come. That strategy may have worked for Ray Kinsella in Field of Dreams, but it is not a practical solution for Facebook application developers. For better or worse, you cannot simply develop a killer app and expect people to gravitate toward it each time they come to Facebook. The constant fear of all app developers is that users install their app, place it on their sidebar, and then quickly forget about it and never launch it again. Because Facebook is a social environment in which users are always interacting with one another, people are constantly in each other’s “business” — a status message update, a new friend, a new app installed, and so on. Therefore, one of the keys to any app that is able to achieve significant viral growth is leveraging the social network to build user loyalty and spread the news to non-users of the app. In this chapter, you discover how to communicate effectively with users and their friends through news stories and notifications. 17_277959-ch11.qxp 5/5/08 11:28 AM Page 235 Before beginning this chapter, you may find it helpful to examine your application and identify specific actions that users perform that are “story worthy.” Then, as you read through the chapter, you can determine the appropriate publishing method for that particular user action. Publishing a News Feed Story to Current Users The feed.publishStoryToUser API call can be used to publish a News Feed story to current users. (See Chapter 1 for more info on the News Feed.) The method is called in PHP using the following syntax: feed_publishStoryToUser($title, $body, $image_1, $image_1_link, $image_2, $image_2_link, $image_3, $image_3_link, $image_4, $image_4_link) $title is required, but the remaining parameters are optional. Here’s more information on each of them: The $title parameter declares the title of the News Feed story. The title length must be 60 characters or fewer of visible text. You can include one a link in the title, but no more than one. The $body parameter specifies content for the story and can be a maximum of 200 visible characters. You can include a links and old-style b and i tags. The image-related attributes allow you to define up to four 75x75 pixel image links for your story. The image source can either be a Facebook PID (photo ID) or the URL of an image you have granted Facebook permission to cache. The feed.publishStoryToUser returns a 1 when successful or 0 when an error has occurred. There are a couple of usage notes when using feed.publishStoryToUser: As Table 11-1 shows, you can only call this method once every 12 hours for a given user. However, for development and testing purposes, there are no restrictions to publishing to your own News Feed. Your story is not guaranteed to show up in the user’s News Feed. Based on Facebook’s algorithms, its appearance is related to the number of competing stories and the “relative quality” of the news stories. 236 Part III: Developing Facebook Applications 17_277959-ch11.qxp 5/5/08 11:28 AM Page 236 Table 11-1 Publishing Restrictions Method Restriction feed.publishStoryToUser 1 story per user in a 12-hour period (unlimited to developer’s user ID). feed.publishActionOfUser 10 stories per user in a rolling 48-hour window. (Developers are limited to the same restriction.) feed.publishTemplatizedAction 10 stories per user or page in a rolling 48- hour window. (Developers are limited to the same restriction.) notifications.send 40 notifications (non-invitation) to a user’s notifications page per day; 20 invites per user in a day. Here’s a PHP-based example using feed.publishStoryToUser: $title = ‘Your Buried Treasure Dip recipe has been approved and is ready for sharing’; $story = ‘We just wanted to let you know that Chef Cosma Crammer loved your recipe so much that he has stolen your idea and called it his own for his upcoming book Best Recipes I Never Knew.’; $image1 = ‘http://richwagnerwords.com/facebook/rimages/14231.jpg’; $image1_link = ‘http://apps.facebook.com/dummies/recipes?14231’; $success = $facebook->api_client->feed_publishStoryToUser($title, $story, $image1, $image1_link); The $title contains the one permissible a link, whereas the $story uses an i tag (em tags are not allowed). One image link is specified. Figure 11-1 shows the story after it is published in the News Feed. Figure 11-1: Publishing a story to the current user. Chapter 11: Communicating with the News Feed and Notifications 237 17_277959-ch11.qxp 5/5/08 11:28 AM Page 237 Here’s a second example in which a PID is used in place of an image URL: $result = $facebook->api_client->feed_publishStoryToUser(‘You did something newsworthy’, ‘You performed an amazing feat of human strength and endurance today.’, 2856699047694594574, ‘http://www.facebook.com/profile.php?id=665127078’); if ( $result[0] == 1 ) { echo “News Feed story was successfully published. Let’s celebrate.”; } else { echo “Unable to publish News Feed story. Isn’t that awful news?”; } Publishing Actions to a User’s Mini-Feed and Friends’ News Feed The Mini-Feed contains a summary of the actions of a Facebook user, whereas the News Feed presents action-based stories about a Facebook user and friends of the user. However, you may often want to publish to a user’s Mini-Feed of his or her recent activity, while letting app-installed friends of the user know as well through a News Feed story. You can use the feed. publishActionOfUser to do both at the same time. The syntax and parameters for the method are nearly identical to that of the feed.publishStoryToUser call described earlier in the chapter in the “Publishing a News Feed Story to Current Users” section. In PHP, the syntax looks like this: $facebook->api_client->feed_publishActionOfUser($title, $body, $image_1, $image_1_link, $image_2, $image_2_link, $image_3, $image_3_link, $image_4, $image_4_link) The $title parameter is required, whereas others are optional. Although the parameters are the same as with feed.publishStoryToUser, there are some differences to the content you are permitted to place inside of them. Take a look at the details: The $title parameter declares the title of the story. Excluding markup tags, the title length must be 60 characters or fewer. You are limited to the FBML tags permitted. Specifically, you can include one a link, one fb:userlink (with the user ID of the person performing the action), and one or more fb:name tags. 238 Part III: Developing Facebook Applications 17_277959-ch11.qxp 5/5/08 11:28 AM Page 238 If no fb:userlink tag is contained in your $title content, Facebook automatically adds it to the start of the title. The $body parameter specifies content for the story and can be a maximum of 200 visible characters. You can include a links, fb:userlink and fb:name FBML tags, and the old-school b and i formatting tags. The image-related attributes allow you to define up to four 75x75 pixel image links for your story. The image source can either be a Facebook PID (photo ID) or the URL of an image you have granted Facebook permission to cache. When using feed.publishActionOfUser, remember the following: You can call this method up to ten times for a given user within a 48- hour window. As a developer, you have the same restriction. Although your story appears in the Mini-Feed of the user, it is not guaranteed to show up in his or her friends’ News Feed. Based on Facebook’s algorithms, its appearance is related to the number of competing stories and the quality of the stories relative to each other. The feed.publishActionOfUser returns a 1 when successful or 0 when an error has occurred. Consider the following PHP example: $title = ‘ is sharing Crazy Louie\’s Potato Chip Dip recipe with and ’; $story



 = ‘We just wanted to let you know that, with the blessings of Master Chef Cosmoo Crammer, is now willing to share his beloved recipes on fbRecipe. Click here to share your recipes with Cosmoo too.’; $image1 = ‘http://richwagnerwords.com/facebook/rimages/14231.jpg’; $image1_link = ‘http://apps.facebook.com/dummies/recipes?14231’; $result = $facebook->api_client->feed_publishActionOfUser($title, $story, $image1, $image1_link); if ( $result[0] == 1 ) { echo “Your story was successfully published. Let’s celebrate.”; } else { echo “Unable to publish story. Isn’t that awful news?”; } Chapter 11: Communicating with the News Feed and Notifications 239 17_277959-ch11.qxp 5/5/08 11:28 AM Page 239 This example uses fb:userlink and fb:name tags in the $title parameter, connecting the user with app friends who were the recipients of his action. Figure 11-2 shows the story as published on the user’s Mini-Feed. Facebook is expected to phase out feed.publishActionOfUser over time, so I recommend that you use feed.publishTemplatizedAction instead. This method is discussed more fully in the next few sections. Rolling Up Your Sleeves: Publishing Templatized Actions If feed.publishActionOfUser is a relatively straightforward way to publish a story to the Mini-Feed and News Feed, think of feed.publish TemplatizedAction as its “industrial strength” counterpart. It is more complicated to assemble what Facebook calls a “templatized action,” but it is also a more powerful way to publish. Here’s why: Story aggregation. feed.publishTemplatizedAction enables two or more stories published to be aggregated, which increases the chances of it being displayed in News Feeds. In other words, if two users have the same exact story published for each of them, aggregation combines them up into one story that includes both users in the story. Publish to non-app users. This method allows you to optionally publish to the News Feeds of all friends of the user, not just those who have installed your app. (The feed.publishActionOfUser call, in contrast, only publishes to app user friends.) You can also publish to Pages as well. Token support. This method supports variables known as tokens, which enable you to more compose more flexible messages. I show you how these work in the section, “Working with tokens,” later in this chapter. Figure 11-2: Mini-Feed story just published. 240 Part III: Developing Facebook Applications 17_277959-ch11.qxp 5/5/08 11:28 AM Page 240 Exploring the template parameters Check out the PHP version of the call: $facebook->api_client->feed_publishTemplatizedAction($title_template, $title_data, $body_template, $body_data, $body_general, $image_1, $image_1_link, $image_2, $image_2, $image_3, $image_3_link, $image_4, $image_4_link, $target_ids, $page_actor_id) This method allows you to publish a Mini-Feed story to the logged-in user (or to a Facebook Page) and simultaneously publish a News Feed story to friends of the user about the action. The method has several parameters. $title_template is the only one required. Others are optional. They are $title_template contains the FBML markup (60 visible characters or fewer) that is used as the title for the story. You can use fb:name, fb:pronoun, fb:if-multiple-actors, and a links tags, but no other markup tags are allowed. An important distinction of feed.publishTemplatizedAction is the ability to use tokens (variables that are enclosed in squiggly brackets): • The {actor} token, which refers to the current user or Facebook page, must be included in the title. • The {target} token can be used to specify additional user(s) related to the story. The $target_ids parameter supplies the user IDs. • You can also add your own tokens and then specify corresponding values using the $title_data parameter. Developers are permitted to publish stories only in which the {actor} plays an active role in the activity. For example, Rich added a new recipe is acceptable, but Rich has a free recipe box waiting for him is not. $title_data is used to declare a JSON associative array that corresponds to the tokens you defined in the $title_template. (However, the array cannot substitute for {actor} and {target}.) $body_template contains the markup for the story’s body. The displayed content must be 200 characters or less. Permissible tags include Chapter 11: Communicating with the News Feed and Notifications 241 17_277959-ch11.qxp 5/5/08 11:28 AM Page 241 fb:userlink, fb:name, fb:pronoun, fb:if-multiple-actors, a, b, and i. Once again, you can use tokens. $body_data, much like $title_data, contains a display a JSON associative array that you wish to have substituted into $body_template. $body_general provides additional markup to be displayed in the story’s body. The difference between this parameter and $body_template is that $body_general does not aggregate. Therefore, because this content does not need to match another story for aggregation to occur, this parameter is the spot in which you can place unique content for a given user. The $image_x and $image_x_link parameters provides a way to enter up to four image links. $target_ids is a comma-delimited list of user IDs that are directly linked to the story. (This parameter is required if you use {target} in $title_template or $body_template.) $page_actor_id is used to optionally specify a page ID when publishing to a Facebook page that has the app installed. In order for two or more stories to aggregate, the following pieces of the story must be identical: $title_template $title_data $body_template $body_data $target_ids When stories are aggregated, {actor} takes on the names of all of the users in the stories. You are limited to calling feed.publishTemplatizedAction ten times for each user or Page in a rolling 48-hour window. Working with tokens One of the powerful aspects of feed.publishTemplatizedAction is the ability to use tokens, making it easy for you to create content templates that are easy to use from story to story. For example: 242 Part III: Developing Facebook Applications 17_277959-ch11.qxp 5/5/08 11:28 AM Page 242 {actor} added a new recipe. {actor} is throwing a pie at {target}. {actor} made a cake for {target}. The one potential gotcha that can arise when stories are aggregated, however, is subject-verb agreement. For example, suppose the following two stories are combined into a single news item: Jordan is throwing pie at Josh. Jared is throwing a pie at Josh. With the template defined above, the following aggregated title would be generated: Jordan and Jared is throwing a pie at Josh. To the rescue, the fb:if-multiple-actors allows you to specify alternate content when multiple actors are used in an aggregated story. Here’s how the template would look: {actor} areis throwing a pie at {target}. Now, when aggregated, the template displays grammar that would make any English teacher proud: Jordan and Jared are throwing a pie at Josh. The {actor} and {target} tokens are reserved. The {actor} receives its value from the current session ID (current user) or the $page_actor_id (if the actor is a Facebook Page). The {target} token receives its value from the $target_ids parameter. However, consider the following template: {actor} made a {recipe} for {holiday}. The {recipe} and {holiday} tokens are variables that I am defining specifically for use with my news story. The $title_data and $body_data parameters are used for passing this data to Facebook. The values for these two parameters must be in the form of a JSON-encoded associative array: {“recipe”:”California Grits”,”holiday”:”Festivus”} In PHP, you can define using the json_encode() function as $title_data = json_encode(array( ‘recipe’ => ‘California Grits’, ‘holiday’ => ‘Festivus’, )); Chapter 11: Communicating with the News Feed and Notifications 243 17_277959-ch11.qxp 5/5/08 11:28 AM Page 243 Exploring the fbRecipe template Consider a full example of feed.publishTemplatizedAction using the sample fbRecipe app. I’ll begin by defining the template title, using tokens: $title_template = ‘{actor} areis sharing {recipe} with {target} for {holiday}’; The fb:if-multiple-actors tag is included to ensure subject-verb agreement, regardless of the number of actors in an aggregated story. The {actor} is always replaced by the current user, whereas the {target} is specified with the $target_ids parameter. I include two user IDs in the comma-delimited list: $target_ids = ‘530377451,685578792’; The data represented by the {recipe} and {holiday} tokens are defined in the $title_data parameter: $title_data = json_encode(array( ‘recipe’ => ‘Crazy Louie\’s Potato Chip Dip recipe’, ‘holiday’ => ‘New Year\’s Eve’ )); The $body_template provides a full description of the story: $body_template = ‘We just wanted to let you know that, with the blessings of Master Chef Cosmoo Crammer, {actor} areis sharing his beloved recipes on fbRecipe. Click here to join fbRecipe and share your recipes with the Great Cosmoo too.’; One image link is defined: $image1 = ‘http://richwagnerwords.com/facebook/rimages/14231.jpg’; $image1_link = ‘http://apps.facebook.com/dummies/recipes?14231’; The API call is then made using each of the defined parameters: $result = $facebook->api_client->feed_publishTemplatizedAction($title_template, $title_data, $body_template, ‘’, ‘’, $image_1, $image_1_link, null, null, null, null, null, null, $target_ids, null); 244 Part III: Developing Facebook Applications 17_277959-ch11.qxp 5/5/08 11:28 AM Page 244 Here’s the complete listing for this example: $title_template = ‘{actor} is sharing {recipe} with {target} for {holiday}’; $title_data = json_encode(array( ‘recipe’ => ‘Crazy Louie\’s Potato Chip Dip recipe’, ‘holiday’ => ‘New Year\’s Eve’ )); $body_template = ‘We just wanted to let you know that, with the blessings of Master Chef Cosmoo Crammer, {actor} areis sharing his beloved recipes on fbRecipe. Click here to join fbRecipe and share your recipes with the Great Cosmoo too.’; $image1 = ‘http://richwagnerwords.com/facebook/rimages/14231.jpg’; $image1_link = ‘http://apps.facebook.com/dummies/recipes?14231’; $target_ids = ‘530377451,685578792’; $result = $facebook->api_client->feed_publishTemplatizedAction($title_template, $title_data, $body_template, ‘’, ‘’, $image_1, $image_1_link, null, null, null, null, null, null, $target_ids, null); if ( $result[0] == 1 ) { echo “Your story was successfully published. Let’s celebrate.”; } else { echo “Unable to publish story. Isn’t that awful news?”; } Figure 11-3 shows the story when it shows up in the Mini-Feed. Registering your story template Facebook also allows you to register a news story template. When you register, your stories become eligible for being distributed to non-users of your application. In addition, registered story templates are monitored by Facebook so they can measure how engaged users are in your stories (through the thumb’s up and x buttons). You can register up to ten story templates for each application. Figure 11-3: Template story. Chapter 11: Communicating with the News Feed and Notifications 245 17_277959-ch11.qxp 5/5/08 11:28 AM Page 245 To register a news story template, go to the My Applications page (www. facebook.com/developers/apps.php). In your application listing, click the Feed Templates link. (See Figure 11-4.) On the following page, click the Register New Template link. The Register Feed Story Templates page is displayed (see Figure 11-5). Figure 11-5: Defining the story template to be registered. Feed Templates link Figure 11-4: Begin here to register your news story templates. 246 Part III: Developing Facebook Applications 17_277959-ch11.qxp 5/5/08 11:28 AM Page 246 Enter the content in the Title Template and Body Template boxes. If you would like to have your news story considered for publication to non-users of your app, be sure to click the check box near the bottom of this page. Click Submit. Sending Notifications Facebook provides a way to notify users of your application or friends of theirs through its notifications.send API call. The function in PHP looks like this: $facebook->api_client->notifications_send($to_ids, $notification) The $to_ids parameter is a comma-delimited of user IDs. The users must be users of the application or friends of the current user. The $notification parameter is the FBML content to be displayed. When you send a message to other users, the current user’s name is added to the start of the notification. If you just send to the current user, the name is not added. Here’s how to send a notification to the current user: $facebook->api_client->notifications_send( null, ‘Your new recipe is ready for viewing.’); Chapter 11: Communicating with the News Feed and Notifications 247 Tips for getting published Like it or not, the stories you work so hard to compose are not guaranteed to be displayed on the News Feed of your user’s friends. Your chances of success depend on a top-secret Facebook algorithm. However, to increase your chances, consider these tips: Register your story template with Facebook. Only stories that are published using registered templates have a chance at being displayed to friends who do not have your application installed. When you add a link to your story, you should not take the user to a page that requires a login. A login prompt usually discourages users from following through on the link and may cause them to click the dreaded X (Close) button. Make sure your content is worthwhile to users and not just NFN — News Feed Noise. 17_277959-ch11.qxp 5/5/08 11:28 AM Page 247 Figure 11-6 shows the results in the Notifications list. Here’s how to send a notification to a list of others: $facebook->api_client->notifications_send(array(530377451,685578792, 665127078), ‘: My new recipe is available for viewing.’); Figure 11-7 shows the result in the current user’s notification list, including a notification that a message was sent on the user’s behalf. Figure 11-8 shows the message in the friend’s list.
In This Chapter Scoping out the application Using FBML and the Facebook API inside PHP Designing the canvas page UI Rendering the profile box content Publishing notifications and News Feed stories Throughout this book, I progressively explore the various technologies in which you need to become proficient if you are going to build applications for the Facebook Platform. However, now it is time for me to roll up my sleeves and walk through an entire application, tying together a MySQL database, PHP app server, and the Facebook Platform. The app explored in the case study in this chapter is called Speed Dial. It is inspired by Seinfeld’s famous Speed Dial episode, in which Jerry notices that he is on his girlfriend Valerie’s Speed Dial and begins to obsess over his position on it. Speed Dial allows users to add their friends to their own profile’s Speed Dial, creating a “relationship barometer,” to borrow the words of Jerry. Coming Up with a Basic Vision Several profile-based third-party apps on Facebook extend the functionality offered by the basic Friends profile box. In watching an old rerun of the Speed Dial episode Seinfeld one day, however, I decided that it would be a fun idea to take this “relationship barometer” and transform it into a social networking app. 18_277959-ch12.qxp 5/5/08 11:28 AM Page 249 250 Part III: Developing Facebook Applications I envisioned the primary user interaction with Speed Dial as a profile box. The UI would be a square grid of ranked friends that visually looked like buttons on a phone. Most of the fun of the app is simply the ranking of friends on the dial, but I wanted to add some basic functionality, including the ability to jump to the friend’s profile, to poke the friend, and to go to the friend’s wall. In a further nod to the television episode, I wanted to add a Poison Control spot in which a user could place a secret friend. (If you’ve seen the Seinfeld episode, you will recall that the stepmom of Jerry’s girlfriend places Jerry under the Poison Control Speed Dial button.) A Poison Control image (the gagging green face of Mr. Yuk) would take the place of the user, but still link to the secret friend. The app’s canvas page would be kept as simple as possible and be used simply for configuring the Speed Dial itself, such as adding, reordering, and removing friends. It would also have a space for inviting friends. Perhaps the biggest decision I had to make at the design stage was trying to determine whether to use an FBML or iframe page. I wanted to use FBML to leverage the tight Facebook integration that you get with FBML to speed and simplify development. However, I envisioned a drag-and-drop interface for configuring the Speed Dial and knew that I’d have to go with an iframe solution if I went that direction. I conducted a drag-and-drop proof of concept during the design stage, but felt like the UI that I came up with was not all that much easier to use than a forms-based solution that I could implement using FBML. Therefore, in the end, I ended up opting for FBML. At the design stage, make sure you fully think through the FBML versus iframe decision. The decision you make has major implications for how your application turns out, or at least how it is implemented. Setting Up Speed Dial in Facebook After you have the basic design goals and objectives determined, your next step in any Facebook application is setting up the application inside of the My Application area at www.facebook.com/developers/apps.php. If you have worked through this book in order, you know by now that most of these options are fairly straightforward. Perhaps the key decision to make in setting up your application is deciding on the Canvas Page URL. I tried for http://apps.facebook.com/speeddial, but it was already taken — evidently another Facebook developer had a similar idea! So, I opted for the hyphenated solution instead: http://apps. facebook.com/speed-dial. 18_277959-ch12.qxp 5/5/08 11:28 AM Page 250 Figure 12-1 shows the base options for Speed Dial. In the Installation Options section are a few noteworthy options to speak of: Although it makes more sense for users to add Speed Dial to their profile instead of a Facebook page, there is no reason technically why a page could not add it, so I enabled the option for both users and pages. The Post-Add URL should be an apps.facebook.com URL, not my own domain. Because the profile box content is the heart of the app and will be rendered immediately when the user first views Speed Dial, I choose not to add Default FBML. However, I have only one default profile action (Edit My Speed Dial) for version 1.0, so I am going to add the FBML code here. I specify Narrow as the Default Profile Box Column. For Speed Dial, this setting is equivalent. I decided during the design stage that the “phone dial” interface should be rendered only in narrow mode, not wide. The Developer Mode box should be checked during the coding and testing stage. Figure 12-1: Base options for Speed Dial. Chapter 12: Tying It All Together: Speed Dial Application 251 18_277959-ch12.qxp 5/5/08 11:28 AM Page 251 Figure 12-2 displays the Installation options. The Integration Points section is used to connect various optional parts of the application to Facebook. The only integration point I am concerned about at this point is the Side Nav URL, as shown in Figure 12-3. This URL must be the canvas page URL (apps.facebook.com/myapp), not a separate domain. Figure 12-3: Integration points options for Speed Dial. Figure 12-2: Installation options for Speed Dial. 252 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 252 After I click Save, I am brought back to the main My Applications window, shown in Figure 12-4. From there, the API key and secret key are displayed. I will need that info when I begin to code my application. Creating the Speed Dial Database As I mention at the start of the chapter, Speed Dial is a PHP-based application that uses a MySQL backend database. The data the app needs to store is quite basic, consisting of four fields in a table named speeddial. The fields are as follows: User ID of the current user (bigint). Friend ID of the user assigned to the Speed Dial (bigint). Ranking of the user (value between 1-12) (tinyint). Thumbnail, which indicates the thumbnail image to display. For now, 0 indicates the default user picture and 1 indicates the Poison Control image. A future version may add additional thumbnails (tinyint). Figure 12-4: Speed Dial shown in the My Applications page. Chapter 12: Tying It All Together: Speed Dial Application 253 18_277959-ch12.qxp 5/5/08 11:28 AM Page 253 The SQL for creating the database table is shown below: CREATE TABLE speeddial ( uid bigint(20) NOT NULL default ‘0’, fid bigint(20) NOT NULL, ranking tinyint(4) NOT NULL, thumbnail tinyint(4) NOT NULL default ‘0’, PRIMARY KEY (uid,fid) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; Structuring the PHP Source Code In structuring the source code for Speed Dial, I divided it up into several different source files, as follows: appinclude.php contains global variable declarations and the basic Facebook login routine. db.php defines all of the MySQL database routines. display.php defines all of the main routines associated with the canvas page. index.php serves as the main application file. invite.php contains the routines used for the Invite Friends module. profile.php defines all of the functions for rendering the profile box. publish.php contains routines for sending notifications or publishing News Feed stories. By dividing up the source code across several files by app module, the source code becomes easy to navigate and make sense of. Figure 12-5 shows the basic architecture of Speed Dial. Setting Up the Canvas Page The profile box may be intended as the primary UI of Speed Dial, but because of the architecture of the Facebook Platform, an app’s profile box can’t drive the application itself. The FBML content for the profile box, after all, is cached by Facebook. It must be generated by Speed Dial elsewhere. As a result, the Speed Dial’s canvas page becomes the primary driver of all application activity. 254 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 254 Speed Dial’s canvas page is the index.php source file. As the first step in coding, I’ll add includes to the other app files: get_loggedin_user(); $is_logged_out = !$user; // Exception handler for invalid session_keys try { // If app is not added, then attempt to add if (!$facebook->api_client->users_isAppAdded()) { $facebook->redirect($facebook->get_add_url()); } } catch (Exception $ex) { // Clear out cookies for app and redirect user to a login prompt $facebook->set_user(null, null); $facebook->redirect($APP_CALLBACK_URL); } ?> Building the Canvas Page The user interface for the canvas page needs to be able to enable the user to perform the following activities: Look up a friend and add him to a specific spot on the Speed Dial. Remove a friend or all friends from the Speed Dial. Assign a friend the Poison Control thumbnail. Preview the Speed Dial. Invite friends to join Speed Dial. 256 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 256 Constructing the page header There is a logical division for all of the activities from the previous list — configure and invite modes. As a result, a two-tab navigation scheme was most appropriate using fb:tabs. I also added an fb:dashboard to display the app name and icon, even though I don’t have immediate use for fb:action links at this time. The render_canvas_header() function, placed inside of display.php, is used to render the header. The $invite parameter indicates which tab is selected: function render_canvas_header($invite) { global $APP_ROOT_URL; echo ‘’; echo ‘’; echo ‘’; if (!$invite) { echo ‘’; echo ‘’; } else { echo ‘’; echo ‘’; } echo ‘’; echo ‘
’; } The index.php file calls this routine as its first step in generating the UI for the page: render_canvas_header(false); I then use some FBML to provide a user-specific greeting as a header in index.php:

Welcome to Speed Dial, the Relationship Barometer


Chapter 12: Tying It All Together: Speed Dial Application 257 18_277959-ch12.qxp 5/5/08 11:28 AM Page 257 Adding a friend The interface for adding a friend to the Speed Dial needs to have three controls — a friend picker, a placement selector, and a submit button. Facebook has different friend picker controls available through FBML, but unfortunately, the most visual one, fb:multi-friend-input, can be used only inside an fb:request-form. I could have created my own control from scratch, but for now I’m content with the fb:friend-selector element. This element provides a predictive friend selector input that a user can quickly use to find a friend. This control has two disadvantages, however. First, a user can’t browse through all friends, but needs to know the friend’s name or the start of the name first. Second, the friend’s thumbnail picture is not displayed like it is in fb:multi-friend-input. Nonetheless, given the simplicity of the FBML tag and its UI cleanliness, I opted for fb:friendselector in spite of its drawbacks. I then use a normal select element for obtaining the placement value. Here’s the form code in index.php:

Add a Friend

First, select a friend:

Then pick your friend’s placement on the speed dial:
Note that I need to account for the asterisk and pound sign keys of the Speed Dial. For simplicity, I simply assign a value of 1-12. The input is assigned a CSS class of inputbutton, which I define later to emulate a blue Facebook submit button. 258 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 258 The action parameter in the form calls itself (the index.php file). Therefore, I need to add a handler for the form at the top of the source file. Here’s the code: if (isset($_POST[‘friend_picked’])) { $fid = $_POST[‘friend_picked’]; $place = $_POST[‘placement’]; add_dial_friend($user, $fid, $place, 0); } This handler assigns the form values to variables and uses them in calling a database routine called add_dial_friend(), which is in the db.php file. This routine calls a basic routine that gets a database connection. Both of these functions are shown below: function get_db_conn() { $conn = mysql_connect($GLOBALS[‘DB_IP’], $GLOBALS[‘DB_USER’], $GLOBALS[‘DB_PASS’]); mysql_select_db($GLOBALS[‘DB_NAME’], $conn); return $conn; } function add_dial_friend($uid, $fid, $ranking, $thumbnail) { $conn = get_db_conn(); $query = “DELETE FROM speeddial WHERE uid=$uid AND ‘ranking’=$ranking”; mysql_query($query, $conn); $query = “INSERT INTO ‘richwagn_speeddial’.’speeddial’ (‘uid’, ‘fid’, ‘ranking’, ‘thumbnail’) VALUES (‘$uid’, ‘$fid’, ‘$ranking’, ‘$thumbnail’)”; mysql_query($query, $conn); } Getting a list of dial friends Each time the index.php file loads, it makes a call to a database routine to get the list of friends currently on the Speed Dial. This array, called $dial_friends, is used in several parts of the app. Here’s the call: $dial_friends = get_dial_friends($user); The get_dial_friends() function inside of db.php makes a SQL SELECT call and returns the result set back as an array: function get_dial_friends($uid) { $conn = get_db_conn(); $query = “SELECT uid, fid, ranking, thumbnail FROM speeddial WHERE uid=$uid ORDER BY ranking”; Chapter 12: Tying It All Together: Speed Dial Application 259 18_277959-ch12.qxp 5/5/08 11:28 AM Page 259 $result = mysql_query($query, $conn); $dial_friends = array(); while ($row = mysql_fetch_assoc($result)) { $dial_friends[] = $row; } return $dial_friends; } Previewing the Speed Dial The canvas page (index.php) also needs to have a preview for the Speed Dial. This previewer needs to be able to show what the actual Speed Dial will look like in the profile box. However, it also provides links that enable a user to remove a friend or else assign the Poison Control designation to a friend. Here’s the basic HTML added to index.php that is used to display the Speed Dial grid:
As you can see, I am going to render each of the cells using a routine called render_cell() that I add to display.php. Check out the routine before I walk you through it: function render_cell($index) { global $dial_friends; global $APP_ROOT_URL; global $APP_CALLBACK_URL; $total_friends = count($dial_friends); 260 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 260 for ($i=0; $i<=$total_friends; $i++) { $match = false; if ( $dial_friends[$i][‘ranking’] == $index ) { $match = true; $f_index = $i; break; } } echo “”; echo “
”; if ( !$match ) { echo “”; echo “(None)
”; } else { if ( $dial_friends[$f_index][‘thumbnail’] == 1 ) { echo “”; } else { echo “”; } echo “
”; echo “”; echo “”; } echo “”; } The for loop at the start of the function is used to find the specific friend whose ranking matches the table cell. If no match is found, the function renders the cell contents as an unassigned spot. Otherwise, the friend’s thumbnail image is added. (However, if the friend has the Poison Control designation, the toxic.jpg image is used instead.) The final two echo statements render two icon links: One calls a remove friend command and the other calls the Poison Control action. Notice that the actionspecific values are added to the HREF attributes for these links. The index.php file needs to add handlers for these parameters being passed to it. I cover that in the “Processing user actions” section later in this chapter. Chapter 12: Tying It All Together: Speed Dial Application 261 18_277959-ch12.qxp 5/5/08 11:28 AM Page 261 Resetting the Speed Dial Although most of the friend activity of the Speed Dial can be added by the add friend form (see the “Adding a friend” section earlier in this chapter) and by the remove and Poison Control links in the previewer (see the previous section, “Previewing the Speed Dial”), there are a couple additional functions that should be added — the ability to clear the entire Speed Dial, and the ability to unassign the Poison Control designation. These can be accomplished using links on the canvas page (index.php) that specify the action as part of the URL, as follows: Unassign the Poison Control icon for now.
Remove all your friends and start over. No soup for anyone!
Processing user actions Although the add friend form has its own handler already discussed earlier in the “Adding a friend” section, the action links called in the canvas page need to have a separate handler at the top of the index.php page. Here’s the code: // Handlers for action links $action = $_REQUEST[‘action’]; $fid_actor = (int) $_REQUEST[‘fid’]; $place = (int) $_REQUEST[‘place’]; // Delete friend if ( $action == ‘delete’ ) { remove_dial_friend($user, $fid_actor); } // Add as Poison Control elseif ( $action == ‘pc’ ) { clear_thumbnail($user); remove_dial_friend($user, $fid_actor); add_dial_friend($user, $fid_actor, $place, 1); } // Clear all friends elseif ( $action == ‘clearall’ ) { clear_dial($user); } // Clear Poison Control icon elseif ( $action == ‘clearpc’ ) { clear_thumbnail($user); } 262 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 262 When the delete action is called, a remove_dial_friend() function in db.php is executed: function remove_dial_friend($uid, $fid) { $conn = get_db_conn(); $query = “DELETE FROM speeddial WHERE ‘uid’=’$uid’ AND ‘fid’=’$fid’”; mysql_query($query, $conn); } When the pc action is called to add the Poison Control designation, the clear_thumbnail() function in db.php is first called, which resets the thumbnail values for the user’s Speed Dial: function clear_thumbnail($uid) { $conn = get_db_conn(); $query = “UPDATE speeddial SET ‘thumbnail’=0 WHERE ‘thumbnail’=1 AND ‘uid’=’$uid’”; mysql_query($query, $conn); } Back in index.php, the friend in question is then re-added with the updated thumbnail value: clear_thumbnail($user); add_dial_friend($user, $fid_actor, $place, 1); When the clearall action is called, the clear_dial() function from db.php is called, which removes all of the friends of the user from the speeddial database: function clear_dial($uid) { $conn = get_db_conn(); $query = “DELETE FROM speeddial WHERE ‘uid’=$uid “; mysql_query($query, $conn); } Finally, when the clearpc action is called, the clear_thumbnail() routine is called. Assembling the canvas page UI So far, I’ve coded all of the core functionality for the canvas page, but need to assemble it in usable manner. In considering the different UI options for the main canvas page, a two-column list style seemed to work best (see wiki.developers.facebook.com/index.php/Facebook_Styles for more details). According to the two-column list style, I enclosed all of these controls inside of a table. Here’s the code in index.php: Chapter 12: Tying It All Together: Speed Dial Application 263 18_277959-ch12.qxp 5/5/08 11:28 AM Page 263

Welcome to Speed Dial, the Relationship Barometer


Barometerize Your Friends

Your Speed Dial

Add a Friend

First, select a friend:

Then pick your friend’s placement on the speed dial:


Pick your Special Friend

Click the button under your friend’s picture to assign a friend to the coveted Poison Control spot.
Unassign the Poison Control icon for now.
264 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 264

Remove a Friend

Click the button under your friend’s picture to delete him or her from your speed dial.
Remove all your friends and start over. No soup for anyone!
See Chapter 10 for more details on the two-column list style. Chapter 12: Tying It All Together: Speed Dial Application 265 18_277959-ch12.qxp 5/5/08 11:28 AM Page 265 Styling the UI Several CSS styles are used to format these elements during rendering. The Speed Dial previewer table uses the following styles to resemble a phone dial: .speeddial { margin:0 0; left:0px;} .speeddial td { width: 65px; text-align:center; vertical-align: top; border: 1px solid #d8dfea; padding:2px; } The Add button is assigned the inputbutton class to emulate a Facebook button. .inputbutton { border-style: solid; border-top-width: 1px; border-left-width: 1px; border-bottom-width: 1px; border-right-width: 1px; border-top-color: #D9DFEA; border-left-color: #D9DFEA; border-bottom-color: #0e1f5b; border-right-color: #0e1f5b; background-color: #3b5998; color: #ffffff; font-size: 11px; font-family: “Lucida Grande”, Tahoma, Verdana, Arial, sans-serif; padding: 2px 15px 3px 15px; text-align: center; } The two-column list uses a set of styles to define its look and feel: .lists th { background: #6d84b4; text-align: left; padding: 5px 10px; } .lists .spacer { background: none; border: none; padding: 0px; margin: 0px; width: 10px; } 266 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 266 .lists th h4 { float: left; color: white; } .lists th a { float: right; font-weight: normal; color: #d9dfea; } .lists th a:hover { color: white; } .lists td { margin:0px 10px; padding:0px; vertical-align:top; width:306px; } .lists td h4 { text-align:left; background-color: #e7e7e7; border: 1px solid #d8dfea; padding:2px; color: #333333; padding-top: 3px; padding-right: 10px; padding-bottom: 3px; padding-left: 10px; font-weight: bold; font-size: 11px; margin-top: 10px; margin-right: 0pt; margin-bottom: 0pt; margin-left: 0pt; } .lists .list { background:white none repeat scroll 0%; border-color:-moz-use-text-color #BBBBBB; border-color: #d8dfea; border-style:none solid solid; border-width:medium 1px 1px; } .lists .list .list_item { border-top:1px solid #E5E5E5; padding: 10px; } .lists .list .list_item.first { border-top: none; } .clearfix:after { content: “.”; display: block; clear: both; visibility: hidden; line-height: 0; height: 0; } .clearfix { display: inline-block; } html[xmlns] .clearfix { display: block; } * html .clearfix { height: 1%; } Chapter 12: Tying It All Together: Speed Dial Application 267 18_277959-ch12.qxp 5/5/08 11:28 AM Page 267 Adding a random quote display As a finishing touch to the canvas page UI, I would like to display a Speed Dial–related quote just under the page header. However, instead of a single quote, I’d like to display a random quote. To do so inside a Facebook app, I can take advantage of the fb:random tag, which renders content by randomly choosing a fb:random-option item based on the weights you provide. Here’s the function in display.php that returns a random quote: function get_random_quote() { $fbml .= “”; $fbml .= “\”You know uh, Valerie, I uh, couldn’t help but notice that I’m on your speed dial.\” - Jerry”; $fbml .= “George: \”So you’re on the speed dial?\” Jerry: \”After two dates!\””; $fbml .= “\”Wha! You know, it’s a pain to change that. You gotta lift up that plastic thing with a pen.\” - George”; $fbml .= “\”I had like a so-so date with Valerie, now I’m number nine on the speed dial.\” - Jerry”; $fbml .= “\”Yeah, this speed dial’s like a relationship barometer.\” - Jerry ”; $fbml .= “\”It’s taken me thirteen years to climb up to the top of that speed dial, and I don’t intend to lose my spot to you.\” - Mrs. Hamilton”; $fbml .= “\”You know, in the year two-thousand, we’ll all be on speed dial. You’ll just have to think of a person, they’ll be talking to you.\” - Kramer”; $fbml .= “\”Uuh, I can’t believe she did this again. That’s it! She’s off the speed dial completely!\” - Valerie”; $fbml .= “\”Wow, poison control? That’s even higher than number one!\” - Jerry”; $fbml .= “\”I can’t help thinking that maybe there’s someone in your life who deserves [the number one spot] more. Someone you’ve known, you know, more than a week.\” - Jerry”; $fbml .= “\”My stepmother got to you, didn’t she?...Uuh, I can’t believe she did this again. That’s it! She’s off the speed dial completely!\” - Valerie”; $fbml .= “\”Why don’t I put you on my speeddial?\” - Mrs. Hamilton”; $fbml .= “”; return $fbml; } 268 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 268 Some of the quotes are weighted higher than others based on the weight attribute of the fb:random-option. In the index.php, the following code is provided to display the random quote:
Facebook uses graphical quotation marks when it displays quotes in certain parts of its UI (such as the Events app). I am going to emulate that through the following styles, both of which call the Facebook quote mark image as a background-image: .q_owner { background-color: transparent; background-image: url(http://www.facebook.com/images/start_quote_small.gif); background-repeat: no-repeat; padding-top: 0px; padding-right: 0px; padding-bottom: 5px; padding-left: 18px; width: 400px; } .q_owner span.q { color: #555555; background-color: transparent; background-image: url(http://www.facebook.com/images/end_quote_small.gif); background-repeat: no-repeat; background-position: center right; padding-top: 2px; padding-right: 16px; padding-bottom: 2px; padding-left: 0px; } As you can see from the fb:random declaration, the quotes themselves contain quotation marks. A str_replace() call is used to remove these quotes in favor of the graphical ones. (I use the character-based quotation marks in the profile box later.) Chapter 12: Tying It All Together: Speed Dial Application 269 18_277959-ch12.qxp 5/5/08 11:28 AM Page 269 Adding a page footer The final part of the canvas page is to add a standard footer. I do this with a routine contained in display.php: function render_canvas_footer() { echo ‘
Speed Dial v.0.1. Beta. Please post your feedback here.
’; echo ‘Learn how to build Speed Dial in Building Facebook Applications For Dummies.
’; echo ‘’; } Figure 12-6 shows the completed canvas page. Figure 12-6: Fully assembled Speed Dial canvas page. 270 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 270 Setting the Profile Box Content Although the canvas page is fully operational, the real intent of Speed Dial is to display the Speed Dial on the user’s profile. Therefore, the next step is to take the information gathered in the canvas page and render it in the profile box. The actual content that I want to display in the profile box is similar, but not identical, to the previewer shown in the canvas page. Additionally, the way in which the canvas page and profile box content are both rendered is different. For the canvas page, I render the FBML either using literal FBML markup or through the PHP echo command. However, with the profile box, I need to pass an FBML string to the profile.setFBML() API call and then Facebook does the rendering. The get_profile_fbml() function compiles the FBML content for use with the profile. An fb:narrow element contains the Speed Dial itself and a random quote. I don’t want to display the Speed Dial inside fb:wide, so I add a quote and provide instructions to move it to the narrow column. Here’s the code in profile.php: function get_profile_fbml() { $fbml .= “”; $fbml .= “”; $fbml .= “”; $fbml .= “”; $fbml .= get_cell_contents(1); $fbml .= get_cell_contents(2); $fbml .= get_cell_contents(3); $fbml .= “”; $fbml .= “”; $fbml .= get_cell_contents(4); $fbml .= get_cell_contents(5); $fbml .= get_cell_contents(6); $fbml .= “”; $fbml .= “”; $fbml .= get_cell_contents(7); $fbml .= get_cell_contents(8); $fbml .= get_cell_contents(9); $fbml .= “”; $fbml .= “”; $fbml .= get_cell_contents(10); $fbml .= get_cell_contents(11); $fbml .= get_cell_contents(12); $fbml .= “”; $fbml .= “
” ; Chapter 12: Tying It All Together: Speed Dial Application 271 18_277959-ch12.qxp 5/5/08 11:28 AM Page 271 $fbml .= “

Random Speed Dial Quote:
”; $fbml .= “” . get_random_quote() . “
”; $fbml .= “”; $fbml .= “”; $fbml .= “
Random Speed Dial Quote:
”; $fbml .= “
” . get_random_quote() . “


”; $fbml .= “
Move profile box to narrow column to view speed dial.
”; $fbml .= “”; return $fbml; } The get_cell_contents() function in profile.php is similar to the canvas page’s render_cell() function, but specific to the needs of the profile box: function get_cell_contents($index) { global $dial_friends; global $APP_ROOT_URL; global $APP_CALLBACK_URL; $total_friends = count($dial_friends); for ($i=0; $i<=$total_friends; $i++) { $match = false; if ( $dial_friends[$i][‘ranking’] == $index ) { $match = true; $f_index = $i; break; } } $fbml .= “”; $fbml .= “
”; if ( !$match ) { $fbml .= “”; $fbml .= “(None)
”; } else { if ( $dial_friends[$f_index][‘thumbnail’] == 1 ) { $fbml .= “”; $fbml .= “Poison Control
”; } else { $fbml .= “”; 272 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 272 $fbml .= “
”; } $fbml .= “”; $fbml .= “
”; } $fbml .= “”; return $fbml; } Underneath the thumbnail of the friend, a poke and wall action links are provided. All of the content is returned to the calling get_profile_fbml() function as a string. The get_profile_fbml() and get_cell_contents() functions do the work of assembling the profile box content. The render_profile_box() in display.php is the one that actually calls the profile.setFBML function: function render_profile_box() { global $facebook; $fbml = get_profile_fbml(); $facebook->api_client->profile_setFBML(null, null, $fbml); } One of the key questions for any app is when to update the profile box. For some apps, it makes sense to have a chron program running that updates the profile box of a user based on time. However, with Speed Dial, it makes the most sense to update the profile box anytime a user makes a change to his Speed Dial. I could call render_profile_box() when index.php loads, but that is not as efficient as adding it to the data-changing handlers in index.php: if (isset($_POST[‘friend_picked’])) { $fid = $_POST[‘friend_picked’]; $place = $_POST[‘placement’]; add_dial_friend($user, $fid, $place, 0); render_profile_box(); } // Handlers for action links $action = $_REQUEST[‘action’]; $fid_actor = (int) $_REQUEST[‘fid’]; $place = (int) $_REQUEST[‘place’]; Chapter 12: Tying It All Together: Speed Dial Application 273 18_277959-ch12.qxp 5/5/08 11:28 AM Page 273 // Delete friend if ( $action == ‘delete’ ) { remove_dial_friend($user, $fid_actor); render_profile_box(); } // Add as Poison Control elseif ( $action == ‘pc’ ) { clear_thumbnail($user); add_dial_friend($user, $fid_actor, $place, 1); render_profile_box(); } // Clear all friends elseif ( $action == ‘clearall’ ) { clear_dial($user); render_profile_box(); } // Clear Poison Control icon elseif ( $action == ‘clearpc’ ) { clear_thumbnail($user); render_profile_box(); } Figures 12-7 and 12-8 show the profile box in both the narrow and wide columns of the profile page. Figure 12-7: Speed Dial in the profile page. 274 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 274 Sending Notifications and Publishing a News Feed Story Some of the functionality of Speed Dial lends itself to notifications and News Feed stories. The trick is determining what sort of communication is appropriate for a given user action. I decided that I would send a notification to a friend when she is added to a user’s Speed Dial or is assigned the Poison Control designation. Here’s the notify_friend() function, contained in publish.php: function notify_friend($fid, $action, $ranking) { global $facebook; global $user; if ($action == ‘add’) { $fbml = ‘added you to spot $ranking on speed dial.’; } else if ($action == ‘pc’ ) { $fbml = ‘secretly placed you under the Poison Control label on speed dial.’; } try { $facebook->api_client->notifications_send($fid, $fbml); } catch (Exception $ex) { // maybe do something } } Because Facebook places a limit to the number of notifications an app can send a day, the notifications.send() call is wrapped inside of a try/catch block. Figure 12-8: Speed Dial is not designed for the wide column. Chapter 12: Tying It All Together: Speed Dial Application 275 18_277959-ch12.qxp 5/5/08 11:28 AM Page 275 I also set up a templatized action in publish.php by calling feed.publish TemplatizedAction() based on the action that the user performs: // Publish a news story related to a Speed Dial event function publish_news_story( $target_id, $action, $ranking) { global $facebook; global $APP_ROOT_URL; if ($action == ‘add’) { $title_template = ‘{actor} added {target} to spot {ranking} on speed dial.’; $title_data = json_encode(array( ‘ranking’ => $ranking, )); } else if ($action == ‘delete’ ) { $title_template = ‘{actor} removed {target} from speed dial.’; $title_data = null; } else if ($action == ‘pc’ ) { $title_template = ‘{actor} secretly placed {target} under the Poison Control label on speed dial.’; $title_data = null; } else if ($action == ‘clearall’ ) { $title_template = ‘{actor} cleared all friends from speed dial.’; $title_data = null; } else if ($action == ‘clearpc’ ) { $title_template = ‘{actor} unassigned the coveted Poison Control label from speed dial.’; $title_data = null; } // Wrap in an exception handler in case the max is reached try { $facebook->api_client->feed_publishTemplatizedAction($title_template, $title_data, $body_template, ‘’, ‘’, null, null, null, null, null, null, null, null, $target_id, null); } catch (Exception $ex) { // maybe do something } } 276 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 276 However, the act of publishing News Feed stories is something that you probably want to massage and tweak based on how the people are using your application. For example, after coding the above routine, I found that publishing for each of these events was simply too much. Therefore, for now, I decided to publish a News Feed story only when the Poison Control designation is assigned. These two functions are called in the data-changing handlers of index.php: if (isset($_POST[‘friend_picked’])) { $fid = $_POST[‘friend_picked’]; $place = $_POST[‘placement’]; add_dial_friend($user, $fid, $place, 0); render_profile_box(); notify_friend($fid, ‘add’, $place); } // Handlers for action links $action = $_REQUEST[‘action’]; $fid_actor = (int) $_REQUEST[‘fid’]; $place = (int) $_REQUEST[‘place’]; // Delete friend if ( $action == ‘delete’ ) { remove_dial_friend($user, $fid_actor); render_profile_box(); } // Add as Poison Control elseif ( $action == ‘pc’ ) { clear_thumbnail($user); add_dial_friend($user, $fid_actor, $place, 1); render_profile_box(); publish_news_story( $fid_actor, $action, $place); notify_friend($fid_actor, $action, $place); } // Clear all friends elseif ( $action == ‘clearall’ ) { clear_dial($user); render_profile_box(); } // Clear Poison Control icon elseif ( $action == ‘clearpc’ ) { clear_thumbnail($user); render_profile_box(); } Facebook app development is iterative. Therefore, I make changes to the story publishing triggers as time goes on. Chapter 12: Tying It All Together: Speed Dial Application 277 18_277959-ch12.qxp 5/5/08 11:28 AM Page 277 Adding an Invitation Page The final component of the Speed Dial application is a page for inviting users. This page is displayed when a user clicks the Invite Users tab. The fb: request-form tag is ideal for quickly constructing an invitation form. However, before doing so, I need to gather a list of current users of Speed Dial who are friends of the user and exclude these people from the invitation form. To do so, I slip into FQL mode and use a query in invite.php: $result_set = $facebook->api_client->fql_query(“SELECT uid FROM user WHERE has_added_app=1 and uid IN (SELECT uid2 FROM friend WHERE uid1 = $user)”); $exclude_list = “”; // Build a delimited list of users... if ($result_set) { for ( $i = 0; $i < count($result_set); $i++ ) { if ( $exclude_list != “” ) $exclude_list .= “,”; $exclude_list .= $result_set[$i][“uid”]; } } I then use some boilerplate code in invite.php to do the rest, only changing the content of the invitation message and the fb:request-form attribute values: $sNextUrl = urlencode(“&refuid=”.$user); $invfbml = << invites you to add Speed Dial, the most Seinfeld-like app on Facebook. FBML; ?> ” invite=”true”> ”> Figure 12-9 shows the invitation form in action. 278 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 278 Prepping the About Page The final UI portion of the Speed Dial app to take care of is the About page, as shown in Figure 12-10. As I discuss in Chapter 15, add an app logo and descriptive information. The About page is an important component of making your application successful. Figure 12-9: Invitation form. Chapter 12: Tying It All Together: Speed Dial Application 279 18_277959-ch12.qxp 5/5/08 11:28 AM Page 279 Exploring the Full Source Files Listings 12-1 through 12-7 show the full source code for the Speed Dial application. Listing 12-1: index.php

Welcome to Speed Dial, the Relationship Barometer



284 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 284 (continued) Chapter 12: Tying It All Together: Speed Dial Application 285 18_277959-ch12.qxp 5/5/08 11:28 AM Page 285

Barometerize Your Friends

Your Speed Dial

Add a Friend

First, select a friend:

Then pick your friend’s placement on the speed dial:


Pick your Special Friend

Click the button under your friend’s picture to assign a friend to the coveted Poison Control spot.
Unassign the Poison Control icon for now.

Remove a Friend

Click the button under your friend’s picture to delete him or her from your speed dial.
Remove all your friends and start over. No soup for anyone!
Listing 12-2: appinclude.php get_loggedin_user(); $is_logged_out = !$user; // Exception handler for invalid session_keys try { // If app is not added, then attempt to add if (!$facebook->api_client->users_isAppAdded()) { $facebook->redirect($facebook->get_add_url()); } } catch (Exception $ex) { // Clear out cookies for app and redirect user to a login prompt $facebook->set_user(null, null); $facebook->redirect($APP_CALLBACK_URL); } ?> Listing 12-3: display.php ”; $fbml .= “\”You know uh, Valerie, I uh, couldn’t help but notice that I’m on your speed dial.\” - Jerry”; $fbml .= “George: \”So you’re on the speed dial?\” Jerry: \”After two dates!\””; $fbml .= “\”Wha! You know, it’s a pain to change that. You gotta lift up that plastic thing with a pen.\” - George”; $fbml .= “\”I had like a so-so date with Valerie, now I’m number nine on the speed dial.\” - Jerry”; $fbml .= “\”Yeah, this speed dial’s like a relationship barometer.\” - Jerry ”; $fbml .= “\”It’s taken me thirteen years to climb up to the top of that speed dial, and I don’t intend to lose my spot to you.\” - Mrs. Hamilton”; $fbml .= “\”You know, in the year two-thousand, we’ll all be on speed dial. You’ll just have to think of a person, they’ll be talking to you.\” - Kramer”; $fbml .= “\”Uuh, I can’t believe she did this again. That’s it! She’s off the speed dial completely!\” - Valerie”; $fbml .= “\”Wow, poison control? That’s even higher than number one!\” - Jerry”; $fbml .= “\”I can’t help thinking that maybe there’s someone in your life who deserves [the number one spot] more. Someone you’ve known, you know, more than a week.\” - Jerry”; $fbml .= “\”My stepmother got to you, didn’t she?...Uuh, I can’t believe she did this again. That’s it! She’s off the speed dial completely!\” - Valerie”; $fbml .= “\”Why don’t I put you on my speeddial?\” - Mrs. Hamilton”; $fbml .= “”; return $fbml; } // Renders the canvas header for the main page and invite page function render_canvas_header($invite) { global $APP_ROOT_URL; echo ‘’; echo ‘’; echo ‘’; if (!$invite) { 288 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 288 echo ‘’; echo ‘’; echo ‘’; } else { echo ‘’; echo ‘’; } echo ‘’; echo ‘
’; } // Renders the footer function render_canvas_footer() { echo ‘
Speed Dial v.0.1. Early beta, so please post bugs here.
’; echo ‘Learn how to build Speed Dial in Building Facebook Applications For Dummies.
’; echo ‘
’; } // Renders a Speed Dial cell for the canvas previewer (not for the profile box) function render_cell($index) { global $dial_friends; global $APP_ROOT_URL; global $APP_CALLBACK_URL; $total_friends = count($dial_friends); for ($i=0; $i<=$total_friends; $i++) { $match = false; if ( $dial_friends[$i][‘ranking’] == $index ) { $match = true; $f_index = $i; break; } } echo “”; echo “
”; if ( !$match ) { echo “”; echo “(None)
”; } (continued) Chapter 12: Tying It All Together: Speed Dial Application 289 18_277959-ch12.qxp 5/5/08 11:28 AM Page 289 else { if ( $dial_friends[$f_index][‘thumbnail’] == 1 ) { echo “”; } else { echo “”; } echo “
”; echo “”; echo “”; } echo “”; } ?> Listing 12-4: db.php Chapter 12: Tying It All Together: Speed Dial Application 291 18_277959-ch12.qxp 5/5/08 11:28 AM Page 291 Listing 12-5: profile.php ”; $fbml .= “
”; if ( !$match ) { $fbml .= “”; $fbml .= “(None)
”; } else { if ( $dial_friends[$f_index][‘thumbnail’] == 1 ) { $fbml .= “”; $fbml .= “Poison Control
”; } else { 292 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 292 $fbml .= “”; $fbml .= “
”; } $fbml .= “”; $fbml .= “
”; } $fbml .= “”; return $fbml; } // Returns FBML content for the profile box function get_profile_fbml() { $fbml .= “”; $fbml .= “”; $fbml .= “”; $fbml .= “”; $fbml .= get_cell_contents(1); $fbml .= get_cell_contents(2); $fbml .= get_cell_contents(3); $fbml .= “”; $fbml .= “”; $fbml .= get_cell_contents(4); $fbml .= get_cell_contents(5); $fbml .= get_cell_contents(6); $fbml .= “”; $fbml .= “”; $fbml .= get_cell_contents(7); $fbml .= get_cell_contents(8); $fbml .= get_cell_contents(9); $fbml .= “”; $fbml .= “”; $fbml .= get_cell_contents(10); $fbml .= get_cell_contents(11); $fbml .= get_cell_contents(12); $fbml .= “”; $fbml .= “
” ; $fbml .= “

Random Speed Dial Quote:
”; (continued) Chapter 12: Tying It All Together: Speed Dial Application 293 18_277959-ch12.qxp 5/5/08 11:28 AM Page 293 $fbml .= “” . get_random_quote() . “
”; $fbml .= “”; $fbml .= “”; $fbml .= “
Random Speed Dial Quote:
”; $fbml .= “
” . get_random_quote() . “


”; $fbml .= “
Move profile box to narrow column to view speed dial.
”; $fbml .= “”; return $fbml; } // Renders the profile box function render_profile_box() { global $facebook; $fbml = get_profile_fbml(); $facebook->api_client->profile_setFBML(null, null, $fbml); } // Renders a profile action function render_profile_action() { global $facebook; $fbml =’Edit My Speed Dial’; $facebook->api_client->profile_setFBML(null,null,null,$fbml); }?> Listing 12-6: publish.php speed dial.’; } else if ($action == ‘pc’ ) { $fbml = ‘secretly placed you under the Poison Control label on speed dial.’; } try { $facebook->api_client->notifications_send($fid, $fbml); } catch (Exception $ex) { // maybe do something } } // Publish a news story related to a Speed Dial event function publish_news_story( $target_id, $action, $ranking) { global $facebook; global $APP_ROOT_URL; if ($action == ‘add’) { $title_template = ‘{actor} added {target} to spot {ranking} on speed dial.’; $title_data = json_encode(array( ‘ranking’ => $ranking, )); } else if ($action == ‘delete’ ) { $title_template = ‘{actor} removed {target} from speed dial.’; $title_data = null; } else if ($action == ‘pc’ ) { $title_template = ‘{actor} secretly placed {target} under the Poison Control label on speed dial.’; $title_data = null; } else if ($action == ‘clearall’ ) { $title_template = ‘{actor} cleared all friends from speed dial.’; $title_data = null; } else if ($action == ‘clearpc’ ) { (continued) Chapter 12: Tying It All Together: Speed Dial Application 295 18_277959-ch12.qxp 5/5/08 11:28 AM Page 295 $title_template = ‘{actor} unassigned the coveted Poison Control label from speed dial.’; $title_data = null; } // Wrap in an exception handler in case the max is reached try { $facebook->api_client->feed_publishTemplatizedAction($title_template, $title_data, $body_template, ‘’, ‘’, null, null, null, null, null, null, null, null, $target_id, null); } catch (Exception $ex) { // maybe do something } }?> Listing 12-7: invite.php api_client->fql_query(“SELECT uid FROM user WHERE has_added_app=1 and uid IN (SELECT uid2 FROM friend WHERE uid1 = $user)”); $exclude_list = “”; // Build a delimited list of users... if ($result_set) { for ( $i = 0; $i < count($result_set); $i++ ) { if ( $exclude_list != “” ) 296 Part III: Developing Facebook Applications 18_277959-ch12.qxp 5/5/08 11:28 AM Page 296 $exclude_list .= “,”; $exclude_list .= $result_set[$i][“uid”]; } } // Construct a next url for referrals $sNextUrl = urlencode(“&refuid=”.$user); // Build your invite text $invfbml = << invites you to add Speed Dial, the most Seinfeld-like app on Facebook. FBML; ?> ” invite=”true”> ”>
 

No comments