Archive

Archive for the ‘Facebook’ Category

Facebook: <Fb:page-admin-edit-header/> and canvas page POSTing

February 22nd, 2008

I came across this issue today where I was getting an error when trying to add the <Fb:page-admin-edit-header/> to a canvas page of a page-only app.
The app was installed on a Facebook Page of which I was an admin.

Basically I had the following situation:

[0] canvas page that had <Fb:page-admin-edit-header/> rendered correctly at top when the canvas page was first hit

[1] canvas page had a simple non-Ajax form that submitted back to itself via POST

[2] POST url just contained URL of page

When I submitted the form, I kept getting the following error:

---------------------
Errors while loading page from application
Runtime errors:

fb:page-admin-edit-header can only display on a fbpage application.
There are still a few kinks Facebook and the makers of Accountability Journal-cp are trying to iron out. We appreciate your patience as we try to fix these issues. Your problem has been logged - if it persists, please come back in a few days. Thanks!
---------------------

I verified that the app was indeed a 'page-only' app, and that I had admin rights to the Facebook Page on which it was installed.

It seemed to me that this app was working perfectly until today, though I may be wrong about that...

It turns out that you MUST add the 'fb_page_id=[pageID]' URL param to the POST URL. Even though all of the FB variables were present in the original POST (fb_sig_page_id, fb_sig_is_admin=1, etc.) it appears that you must still provide the URL param in order for the FBML processing to insert the <Fb:page-admin-edit-header/> correctly.

Facebook, Programming

Facebook API: Undocumented Cookie Management REST APIs

February 15th, 2008

The Facebook API does have a way to set and get cookies, but it is in beta so is not currently part of the FB PHP libraries. I found it out by searching through forums, by trial-and-error, and on the FB Beta docs page. There are 2 functions available:

 
  data.getCookies( user_id, cookie_name );
  data.setCookie( user_id, cookie_name, cookie_value, expires, path );
 

To use these functions from PHP I simply wrapped them in functions that call the RESTful APIs correctly:

 
// to get cookies for a given user (optionally by name):
function get_cookie($uid, $name=null) {
  global $facebook;
  return $facebook->api_client->call_method('data.getCookies', array('uid' => $uid, 'name' => $name) );
}
 
// to set a cookie for a given user:
function set_cookie($uid, $name, $value, $expires=null, $path=null){
  global $facebook;
  return $facebook->api_client->call_method('data.setCookie', array('uid' => $uid, 'name' => $name,
    'value' => $value, 'expires' => $expires, 'path' => $path) );
}
 

Bebo, Facebook, Programming

Facebook vs. MySpace/OpenSocial Development

February 8th, 2008

After having looked at MySpace's OpenSocial implementation I wanted to provide a comparison between it and the Facebook development platform. They are based upon completely different models and moving from one to the other is not as easy as one would hope.

Here's what's different in MySpace's Open Social Development Platform from what I've seen using the Facebook/Bebo platform. Where the items are known issues I have indicated as such. Some of these items have been derived from research and some from the MySpace forums. I have given links where available.

  1. Your MySpace application code is hosted on MySpace servers, NOT YOURS. There is no callback URL that you can provide that MySpace can use to call back to your server.
  2. It appears that the only way to communicate with your own server from your MySpace app is to use the makeRequest() API call.
    • Currently this is ONLY supported for endpoints running on port 80! Out of luck if your development server runs on a different port.
    • Supposedly MySpace is going to open some ports in a higher range to alleviate this issue. This is really, really frustrating.
    • "MakeRequest gets around the XHR cross-domain restriction by utilizing a custom http proxy hosted by MySpace. This proxy relays incoming requests to other sites and pipes the results back to the browser. It also optionally signs the requests using the OAuth methodology…It is important to note that this is not a standard open proxy--it does its best to verify that the request came from a valid OpenSocial application hosted on MySpace." from MySpace Developer Docs
    • Maybe only able to send as GET params - unknown how to send as POST variables
    • Currently only working on some URLs (does not work for http://www.google.com for example – known issue)
    • Currently only returns responses in uncompressed format (known issue – they are working on this one)
    • MySpace will be providing an 'App Data' 'something' to store application data but this is currently unavailable.
  3. Currently the only way to actually get code into your MySpace app is to paste it in a ridiculously small textarea control on the app profile page. Seriously. And doing so results in:
  4. Currently every time you update the code in your app it puts a NEW copy of the app on the respective page. Known Bug.
  5. During the 'sandbox phase' (length unknown) of the MySpace Development Platform only friends can add apps
    • Only 3 installs of any given app allowed
    • If not a friend, cannot see app
    • There is no directory of apps yet
    • Have to log in as the app (because you have to use a unique email address for each) and friend people as that user in order for them to see and add it
    • More details here.
  6. There are no callbacks for any application events (post-add, pre-remove, etc.)
  7. You cannot encrypt or obfuscate your JavaScript code per the MySpace TOS…
  8. There is no datastore support
  9. There are 3 different places the app can be installed into a user's profile, height is modifiable (to some degree?), but NOT width
    • Home page
    • Profile page
    • NOTE: any portion of your MySpace Applications and MySpace Application Content that will appear on a MySpace Profile must not:
      • contain or create an <iframe>
      • cause their containing <iframe> to be navigated to new location, for example, by using JavaScript code, form posts, or containing links;
      • contain or create <script> tags pointing to external sources except those that are explicitly allowed; and
      • contain or create links to external CSS style sheets.
    • Canvas page
  10. Your app code always runs in an iframe (provided by their container I guess).
  11. There is no formatting language (like FBML) or controls available other than straight (D)HTML. Kind of strange, this one. Facebook uses FBML that it converts to HTML (on its side) which it then pumps to the user's browser. Profile data can be cached on their servers. Very speedy. Could be me but it looks like MySpace is going to require all of your JavaScript to be executed on every refresh of the page – add to that a few calls back and forth to the container/ your server… What is going to happen to MySpace when there are 20 apps installed on each person's profile? Ewwww.
  12. All your JavaScript runs under/is subject to manipulation by Google's Caja; this may limit what your JavaScript can do but makes it safer for MySpace to run 3rd-party scripts. This could slow your JavaScript down as Caja may modify or restrict it.
  13. No/very limited support for 3rd-party JavaScript libraries so far; unknown when more will be available. Appears that jQuery is available based on this post. But if you have existing Facebook code that heavily uses Prototype/Scriptaculous/mootools/EXT/YUI/et.al. you are going to have a lot of reimplementation to do!
  14. Perhaps I am confused but there currently appears to be no obvious/legal way to go from one page of your application to another (from profile->canvas for example). There is an API for this (requestNavigateTo()) but it's not implemented yet. TOS explicitly forbids redirection of the browser window.
  15. No popups are allowed – not sure if this would include Facebook-style AJAX dialogs and their ilk or if this just means browser popup windows. Facebook also disallows popups but does have the FBJS Dialog object available.
  16. oAuth is required to ensure that the user making the request from your MySpace application is valid/trusted. See this post. I am not sure if this API's behavior is fully baked yet, though.

Facebook, OpenSocial

Introduction to the Facebook Data Store API

February 8th, 2008

Facebook released the Data Store API Beta a few months ago, but I hadn’t had a chance to try it out until recently. There are many blog posts discussing the merits of its implementation, but I felt it would be useful to post a quick how-to guide to using it. The Data Store API uses an Object metaphor, which is much different than the table metaphor used by most other databases. However, the mapping between the two models is pretty simple.

Facebook has also made it much easier to work with the Data Store with the Data Store Admin tool added to the Developer page beside each application. This tool lets you create and edit the schema for the database and do FQL queries against it. You can do the same things programmatically, but sometimes it is nice to have a visual editor.

Note: All operations involving schema management can only be done by the developers of the application and will fail with a FacebookRestClientException of 200 – Permission error if done in the context of another user. So, it would be best to move this type of operation to an Admin-only page or do it via the Admin tool.

Another thing to note is that each application gets its own Data Store space, so multiple applications cannot share the same database. The Data Store API has Object Types, Objects and Associations. I will go over each one and show how it maps to the Table metaphor, then do a quick example of creating a table, adding a row and getting its values.

Object Types

Object Types represent Tables and their Properties represent Columns. You create an Object Type like this:

  $facebook-&gt;api_client-&gt;data_createObjectType("tablename");

You add properties or Columns like this:

  // 1 for integer,2 for string (max. 255 characters), 3 for text blob (max. 64kb)
  $facebook-&gt;api_client-&gt;data_defineObjectProperty("tablename", "columnname", 3);

Once you have created an Object Type, you can then create instances of it or Rows. You only need to create an Object Type once, although you can create many different Object Types if you need many different tables

Objects

Objects represent Rows in the Table. You set the values of the properties to set the value of a Column. You can create or update an existing Object, if you have its id. You can create a new Object and set its properties at the same time like this:

  $objectID = $facebook-&gt;api_client-&gt;data_createObject("tablename", array("columname" =&gt; "$value") );

or update an object like this:

  //pass true to overwrite the properties, or false to merge in
  $objectID = $facebook-&gt;api_client-&gt;data_updateObject($objectID, array("columname" =&gt; "$value"), true );

Once you have rows, you can fetch an array of the properties like this:

  $properties = $facebook-&gt;api_client-&gt;data_getObject($objectID);

Associations

Creating Objects is easy, but, as you can see above, you need the objectID to get an Object. Where do you get that from? Associations.

An Association is a link between two ids. You can use it to link the current user or page to an objectId, and then get the properties for that object.

After you create a new object, you need to create the association using the objectID you get back like this:

  $facebook-&gt;api_client-&gt;data_setAssociation( "user_to_row", $userID, $objectID );

Then you can retrieve the objectID any time by getting it out of the association for the current user:

  $objects = $facebook-&gt;api_client-&gt;data_getAssociatedObjects( "user_to_row", $userID, true );
 
  // There is only a single row for this objectID
  if( isset($objects) &amp;&amp; isset($objects[0]) ) {
    // id2 holds the object id
    $objectID = $objects[0]["id2"];
  }

Example

The example below defines a Table schema, adds a Row and then uses an Association to get the values.

  //Create the table
  $facebook-&gt;api_client-&gt;data_createObjectType("description");
  $facebook-&gt;api_client-&gt;data_defineObjectProperty("description", "text", 3);
  $facebook-&gt;api_client-&gt;data_defineAssociation( "user_to_row", 1, array("alias" =&gt; "user_id"), array("alias" =&gt; "row_id") );
 
  //Add a row
  $text = "All work and no play makes John unhappy";
  $rowID = $facebook-&gt;api_client-&gt;data_createObject("description", array("text" =&gt; "$text") );
  $facebook-&gt;api_client-&gt;data_setAssociation( "user_to_row", $userID, $rowID );
 
  //Fetch the row value
  $objects = $facebook-&gt;api_client-&gt;data_getAssociatedObjects( "user_to_row", $userID, true );
 
  // There is only a single row for this objectID
  if( isset($objects) &amp;&amp; isset($objects[0]) ) {
    // id2 holds the object id
    $objectID = $objects[0]["id2"];
  }
 
  $description = $facebook-&gt;api_client-&gt;data_getObjectProperty( $objectID, "text" );
  echo "The description for  is $description";

Error Handling

You will notice that there is none above. However, many of these calls are likely to throw one of the following:

  FacebookAPIErrorCodes::API_EC_DATA_OBJECT_NOT_FOUND
  FacebookAPIErrorCodes::API_EC_DATA_OBJECT_ALREADY_EXISTS
  FacebookAPIErrorCodes::API_EC_PERMISSION

so it makes sense to put each call into a try\catch block.

Summary

The Facebook Data Store API is still in Beta, so don’t expect it to work all of the time. However, it is a nice way to eliminate the need for any external database support for your application. You can find more information about the Data Store API here and a nice set of wrapper classes here.

(Updated based on alvin's comments)

Facebook

A quick Flash sound player to use on Facebook and Bebo

January 31st, 2008

For Doorbell on Bebo, I needed an easy way to play an arbitrary doorbell ring. Previously, I had been creating individual .swf files for each ring, and then using the <fb:swf> tag to put them on the page. I was using an AJAX callback to add them when the user clicked a doorbell. The swf was set to auto play one time after load. I had created a separate swf file for each ring using MP3 to SWF Converter. This worked fine, but I didn’t want to have to do this for each new ring. I also wanted the user to be able to click the bell to hear it, and these generated swf files only played once or in a loop, but not on a click. I searched and found FlashDevelop, an open source SWF editor. It is great because it doesn’t require purchasing the Adobe Flash IDE to work, and supports both Actionscript 2 and 3. It is a pretty good editor overall. I hadn’t spent any time with Actionscript before, but it ended up being a lot like Javascript, which I know pretty well. Most of the difficulties came with trying to understand the Flash model, rather than the language. Thankfully, there is Google. The result is a small class that can display an image as the background and can play an .mp3 over the Internet. Using passed in query params, I can control whether the image is shown, which sound to play, and whether it plays right away. Here is how it works: Query params passed in to Flash become global variables off the _root object. I prefaced mine with an “a_”, so I would know which were global variables. You can check them for null to see if they were passed in.

  // Globals passed in by query
  // a_play - if set, the mp3 will play immediately
  // a_ring - the mp3 to play
  // a_nopic - if should hide the background

First, I check to see if I should set the background image by checking for a query parm called “a_nopic”. If it is not set, I load a picture I have added to my swf library.

  if ( _root.a_nopic == null ) {
    _root.attachMovie("library.speaker.jpg", "flashBG", 0);
  }

This says that I should get that image, create a movie based on it, set it as a variable off _root called "flashBG", and put it at the bottom layer(0). Next, I create a new sound object off the _root.

  _root.firstSound = new Sound();

I load the passed in mp3, with the option to play it automatically when it is done by passing "true" as the second parameter. This is controlled by the query param "a_play". Note: the mp3's must have a sample rate of 11KHz, 22KHz, or 44.1KHz or they play too fast.

  _root.firstSound.loadSound(_root.a_ring, _root.a_play != null );

I want the user to be able to click and play the sound, so I set the onRelease function for the _root. This means that when the user releases the mouse button after clicking on the background, the sound will play. I also stop the sound before playing it, so that multiple clicks don't trigger multiple simultaneous sounds.

  _root.onRelease = function() {
    _root.firstSound.stop();
    _root.firstSound.start(0, 1);
  }

The code above is enough to do everything I needed, except for one thing. Unfortunately, the cursor doesn't change to the hand to show the user that the swf is clickable. HTML style tags have no effect, and Flash doesn't natively support changing the cursor easily. There are two ways I found to fix this:

  1. Create a custom cursor. You create a Movie based on a cursor, hide the existing system mouse, and call startDrag() to show your cursor.
      mouse.hide();
      startDrag(_root.yourCursor, true);
  2. Create a giant button that covers the whole swf. The cursor for a button is already the hand cursor.
      _root.rectangle = [Stage.width,Stage.height];
      _root.createEmptyMovieClip("button",300);
      _root.button.lineStyle(1,0x000000,0);
      _root.button.beginFill(0x00000,0);
      _root.button.lineTo(_root.rectangle[0],0);
      _root.button.lineTo(_root.rectangle[0],_root.rectangle[1]);
      _root.button.lineTo(0,_root.rectangle[1]);
      _root.button.lineTo(0,0);
      _root.button.endFill();
    
      // Handle left click
      _root.button.onRelease = function() {
        _root.firstSound.stop();
        _root.firstSound.start(0, 1);
      }

I went with the button option, since I didn't have to create my own cursor for the movie. Here is the whole script:

class Ringer
{
    public static function main()
    {
        // Globals passed in by query
        // a_play - if set, the will play immediately
        // a_ring - the sound to play
        // a_nopic - if should hide the background

        Stage.scaleMode = "noscale";

        // background image
        if ( _root.a_nopic == null ) {
            _root.attachMovie("library.speaker.jpg", "flashBG", 0);
        }

        // sound
        _root.firstSound = new Sound(); 

        // Handle left click
        _root.onRelease = function() {
            _root.firstSound.stop();
            _root.firstSound.start(0, 1);
        }	

        // Load the passed in sound (ex ?a_ring=test.mp3)
        // Sound must be mp3, and have sample rate of 11KHz, 22KHz, or 44.1KHz
        _root.firstSound.loadSound(_root.a_ring, _root.a_play != null );

        // Create a transparent button so that the mouse cursor changes to a hand
        // Make it the size of the whole stage
        _root.rectangle = [Stage.width,Stage.height];
        _root.createEmptyMovieClip("button",300);
        _root.button.lineStyle(1,0x000000,0);
        _root.button.beginFill(0x00000,0);
        _root.button.lineTo(_root.rectangle[0],0);
        _root.button.lineTo(_root.rectangle[0],_root.rectangle[1]);
        _root.button.lineTo(0,_root.rectangle[1]);
        _root.button.lineTo(0,0);
        _root.button.endFill();

        // Handle left click
        _root.button.onRelease = function() {
            _root.firstSound.stop();
            _root.firstSound.start(0, 1);
        }
    }
}

The FBML to call it would be:

  <fb:swf swfsrc='ringer.swf' flashvars='a_ring=your.mp3&a_play=1' imgsrc='yourbg.jpg' width='50' height='50' waitforclick='false' />

Bebo, Facebook

Facebook Applications can now function on any website! JavaScript Facebook API released

January 27th, 2008

Facebook took another step to becoming the defacto operating system of the web 2.0 world. Though currently in beta they released a JavaScript Client Library for the Facebook API. What does that mean? It means that the 13,000+ Facebook applications that are currently hostable only inside www.facebook.com can now have a presence on the rest of the world wide web.

How does it work? The key to a Facebook application is getting access to the underlying Facebook object and User ID for any given Facebook member. This usually is given to an application directly by Facebook when the user clicks on the application from within their Facebook profile page.
The JavaScript API attempts to login as the current browsing user. If the user already has a valid Facebook session available everything just works. If not the user is presented the following friendly dialog to log in to the application:

Facebook JavaScript API Login
The connection to Facebook is made from the user's browser. The hosting site does not get access to Facebook credentials at anytime.

There are a few limitations on the applications that wish to support this. They must be IFrame applications. They also have to deal with a different set of information than the traditional rich Facebook POST variables. I will have more information and an example up here shortly.

You can read the blog posting here: Facebook Blog- Facebook JavaScript API

Facebook

Facebook releases the Profile clean up tool

January 17th, 2008

When I got to my Profile box today, the message at the top was that I could clean up my profile by moving the applications that I had at the bottom of the page to an “extended section”.

My guess is that this will affect applications in a number of ways:

1. Any application in that section will be forgotten. That means that any exciting updates that might otherwise convince users to use it again won’t work, since they can’t see them. Apps need to get it right the first time, or they are done( at least for that user ). The first batch of applications moved there will have inflated numbers, since their actual users will be less than their installed users by even more.

2. Applications will either be uninstalled right away or won’t be uninstalled for a long time. Once they are moved to that extended section, why bother going through the effort of uninstalling them, since they “aren’t there any more”.

3. Applications will have to have strong profile presences to be kept.

We will see how this works out.

Facebook

Hidden Facebook API feature - Preload FQL

January 17th, 2008

I happened across this today as I was going through the Facebook developer forums.

Basically, using the new admin_setAppProperties and admin_getProperties functions, you can create pre-canned FQL queries that are run when you go to a particular page. It still has to run the queries, but it doesn’t have to do the back and forth calls betwen Facebook and your app and have the related delays. You get the results as fb_sig_<name> parameters.

It is explained really well on the Facebook Developer’s Wiki – here.

Facebook

Migrating a Facebook application to Bebo

January 17th, 2008

Recently, we created a Bebo version of our Doorbell application for Facebook.

Here is what I did:

  1. created a new app using the developer app. Used a new callback location.
  2. Updated my db to handle the larger sized userid and sessionkeys
  3. updated web server to handle the new callback location
  4. forked my fb source code to a new Bebo version
  5. included the new Bebo PHP library in my source files
  6. Change all $fb = new Facebook($apikey, $appsecret) to $bebo = new Bebo($apikey, $appsecret)
  7. Change all $fb->api_client calls to just $bebo-> calls, and manually tweaked the parameters to be associative arrays.
  8. Change all FBJS code to mock Ajax, where it can be, or remove otherwise
  9. go through each code path with tracing to figure out what bebo doesn't implement or implements differently.

What is nice, is that most of it just works. The tools are the same, the fb_* post variables are there. They just don't have complete parity with FB on the whole API yet, but they seem to be working on it.

The big gotchas are:

  1. The add method isn’t the same. On Bebo, it seems like a user must add your application to interact with it, and that add happens without any calls to your code. There is therefore less to handle in terms of user state than Facebook.
  2. The api calls are different. Facebook provides a nice wrapper for their REST implementation, and Bebo doesn’t have the same level of completeness in theirs. My next task is to write the same wrapper for Bebo.
  3. All callback parameters via Ajax are sent as POSTs rather than GETs regardless of how they are specified in the call.

Bebo, Facebook

Doorbell for Facebook is released

January 14th, 2008

After creating Inner Circle last year, we wanted to try to make something that was light and easy. Doorbell was born.

Doorbell adds a Doorbell to your Facebook Profile. You can choose between several styles of walls, bells and ring sounds. Your friends can ring it when they visit your profile and you can go to the Stats page to see who rang it and when. Doorbell also lets you compare how often your bell has been run versus your friends. See who’s profiles are the most popular. You can even browse your friends Stats pages to see who has been ringing their bell. Give it a try. Put a Doorbell on your Facebook Profile and see who rings.

Facebook