Last week I ran into a minor issue related to my validating signed request code in the OpenSocial Dev App. A container which had been custom written against the 0.9 spec was sending OAuth messages which were not validating, even though I was certain that I had installed the correct key for the container. After some debugging, I realized that the container was sending a property named xoauth_public_key where I was expecting xoauth_signature_publickey. Consulting the spec, I was amazed to see that the parameter had changed names in this subtle way:

OpenSocial version Parameter name
Before 0.9 xoauth_signature_publickey
0.9 and later xoauth_public_key

Since there's no signal indicating which version of the spec a signed request follows, I realized that this change will likely trip up existing app developers once Shindig changes to the new system.

Finding the appropriate bug in Shindig's issue tracker, I was happy to see that both parameters will be sent by both Java and PHP Shindig for the time being (at least until we move on to whichever version comes after 0.9). This should give a reasonable window for most developers to change their code.

If you're an app developer, it's important to change your signing code as more containers begin to support OpenSocial 0.9. It's a simple change - just check for the xoauth_public_key parameter, and if it's not available, then look for xoauth_signature_publickey instead. Once 0.9 has been fully deployed, you should be able to delete the fallback (although keeping it around for a while shouldn't break anything).

Announcing the Developer Extension for OpenSocial

| | Comments (0) | TrackBacks (0)

I really enjoy using Firebug to debug my web applications. Nothing like being able to inspect items on the page, log debug information to the console, and execute arbitrary Javascript on the page to test out a quick fix.

When working with OpenSocial gadgets, I usually find myself using Firebug to inspect an IFrame to get the URL of the gadget spec that is being loaded. While Firebug gives me access to this data, it's a little hard to get to, and I need to decode the URL before I can paste it into a browser. So to help with this, I created a Firebug extension which scans the current page to see if any OpenSocial gadgets have been loaded. If the extension finds any gadgets, it attempts to discover the XML spec URLs for all the gadgets on the page and lists these as links which will go directly to the gadget XML spec. (Note that this feature works for Shindig based websites, but will not work on MySpace).

Additionally, I wanted to be able to execute JavaScript code inside of the gadget IFrame (and not the container page, which Firebug lets you do normally). This kind of functionality really help debug gadgets, because you can test out JavaScript without needing to save and upload an XML file again (or even reload the page!). So I added a box below each XML spec URL which will let you execute any JavaScript you want, in the context of the gadget's IFrame.

You can find the extension on addons.mozilla.org here. Please leave a review on that site if you wind up using it!

OAuth got you down? Maybe this can help!

| | Comments (0) | TrackBacks (0)

OpenSocial has been tied to the OAuth specification almost from the start, but it can be pretty confusing to try and figure out the differences between two-legged OAuth, three-legged OAuth, and OAuth request signing -- terms which are all thrown around pretty casually in the OpenSocial documentation.

To help make sense of it all, I've updated the OAuth Use Cases page on the OpenSocial wiki. You'll find additional diagrams and clearer request flows showing how data can be transferred between social networks, users, and third party application servers. Additionally, there are hypothetical examples showing where each type of OAuth comes in handy, as well as some links to container-specific resources for obtaining OAuth keys for your applications.

For those of you developing on a Google-run OpenSocial container (orkut, iGoogle, Google Friend Connect, or Gmail), please be aware that Google will be changing their User-Agent string for outgoing OpenSocial requests soon.

Previously, this string was:
Google OpenSocial agent (http://www.google.com/feedfetcher.html)

When the change goes live, the string will be:
Mozilla/5.0 (compatible) Feedfetcher-Google; (+http://www.google.com/feedfetcher.html)

This will be the agent used for all outgoing requests, including fetching gadget specs, osapi.http or gadgets.io.makeRequest calls, and proxied content fetches.

We're expecting few developers to be affected by this change, so if you're not checking the User-Agent for requests to your server, you shouldn't have to make any changes.

I'm happy to announce that we've just finished work on a preliminary "Getting Started" guide for the OpenSocial PHP client library.

This guide should help answer a lot of the questions people first have when starting to develop OpenSocial applications with PHP. We've covered such topics as getting and installing the library, how to batch requests and deal with errors, and how to make simple OpenSocial requests for People, Activities, and App Data. Also included is our current roadmap so you can see what we're working on.

While every effort has been made to create a comprehensive developer's guide, we certainly understand that there still may be areas of the client library which are not fully documented or where the examples are unclear. If you see any place where the documentation can be improved, please take a minute to file a feature request in our issue tracker, and we'll be able to make the documentation better for everyone.

Happy Coding!
~Arne (on behalf of the PHP client library team)

OpenSocial's first birthday party

| | Comments (0) | TrackBacks (0)
If anyone is heading to the OpenSocial Birthday party at MySpace next week, I'll be there to give a presentation on the OpenSocial Dev App and to talk to developers about getting their apps running on multiple containers.  See you there!

Presentation from DevFest Beijing

| | Comments (0) | TrackBacks (0)
In October, I was fortunate enough to be able to travel to Shanghai and Beijing for Google's DevFest developer events in those cities. 

Presenting OpenSocial to developers who hadn't had as much exposure to the technology was a refreshing challenge.  It made me look at our existing slide decks a bit more critically (especially since the slides were going to be translated) and I wound up creating a lot of content to explain the basic OpenSocial concepts. 

If you're a developer interested in OpenSocial but can't seem to find an overview of the entire technology stack (as of October 2008, at least), check out these slides - hopefully they'll give you some insight:

Open Social Tech Talk Beijing
View SlideShare presentation or Upload your own. (tags: sns 2008)


Here they are translated into Chinese.  My goal is to get some of the new images and diagrams into the official documentation soon.

Sending private messages on Friendster

| | Comments (0) | TrackBacks (0)
I had to write a quick sample today to send a private message to the OWNER on Friendster.  The following sends a message to your Friendster message inbox:

function sendNotification() {
  var params = {};
  params[opensocial.Message.Field.TITLE] =
      "Title of the notification goes here";
  params[opensocial.Message.Field.TYPE] =
      opensocial.Message.Type.PRIVATE_MESSAGE;
  var body="Text of the notification goes here";
  var message = opensocial.newMessage(body, params);
  var recipient = opensocial.DataRequest.PersonId.OWNER;
  opensocial.requestSendMessage(recipient, message,
      onSendNotification);
};

function onSendNotification(resp) {
  if (!resp.hadError() && resp.getData().status == "sent") {
    alert("The message was sent to the OWNER");
  } else {
    alert("There was a problem: " + resp.getErrorMessage());
  }
};

sendNotification();
Note the message type is set to PRIVATE_MESSAGE.  Friendster also supports type NOTIFICATION, but I haven't quite figured out where that shows up, and I get a "Insufficient permissions for action 'publicMessage'" error message when trying PUBLIC_MESSAGE.

Also note that this is for OpenSocial 0.7.  If you're migrating this code to another container on 0.8, you'll need to change the IdSpec stuff (as I've covered before).

Fetch by ID sample

| | Comments (0) | TrackBacks (0)
I needed to write the following sample of code to test fetching people by ID number.  This is an interesting sample because it demonstrates handling an error condition - namely, the "unauthorized" response that you should get when you try to access an account that you don't have permission to request.  On orkut, this means people who don't have your app installed, even if they're your friends.

The following runs in DAfOS:

function onPersonGot(result) {
  var response = result.get("person");
  if (response.hadError()) {
    output("There was a problem fetching this user: " + 
        response.getErrorCode());
  } else {
    output("Fetched " + 
        result.get("person").getData().getDisplayName());
  }
  gadgets.window.adjustHeight();
};

function closeGetPerson(id) {
  return function() {
    var params = {};
    var req = opensocial.newDataRequest();
    req.add(req.newFetchPersonRequest(id, params), "person");
    req.send(onPersonGot);
  };
};


function closePrintFriend(list) {
  return function(friend) {
    var item = document.createElement("li");
    var link = document.createElement("a");
    var text = document.createTextNode(friend.getDisplayName());
    list.appendChild(item);
    item.appendChild(link);
    item.appendChild(document.createTextNode(" [" + 
        friend.getId() + "]"));
    link.appendChild(text);
    link.onclick = closeGetPerson(friend.getId());
    link.href = "javascript:void(0);";
  };
};

function gotFriends(data) {
  var friends = data.get("of").getData();
  var list = document.createElement("ul");
  var main = document.getElementById("dom_handle")
  main.innerHTML = "";
  main.appendChild(list);
  friends.each(closePrintFriend(list));
  gadgets.window.adjustHeight();
};

function getFriends() {
  var params = {};
  params[opensocial.DataRequest.PeopleRequestFields.MAX] = 1000;
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPeopleRequest(
      opensocial.DataRequest.Group.OWNER_FRIENDS, params), "of");
  req.send(gotFriends);
};

getFriends();
Note that the example lists the friends of the owner as links.  Clicking on each name will attempt to retrieve that user by ID number. 

fetch_by_ids_2.png

On orkut, if a user doesn't have DAfOS installed, you'll get an "unauthorized" error:

fetch_by_ids_1.png

In your applications, make sure to handle cases like this, since you won't always be able to guarantee that all containers will have the same policies around which user accounts you may directly access.

Lately I've been updating some documentation to use version of 0.8 of the OpenSocial specification.  If you've developed on the 0.7 version of the API, you'll probably be happy to see that not a lot of breaking changes have been introduced in the new version, with one major exception: the IdSpec object.

In the past, when you wanted to specify a single person or a group of people, you would use one of the following:

Single person specifiers (OpenSocial 0.7):
  • opensocial.DataRequest.Person.OWNER
  • opensocial.DataRequest.Person.VIEWER
  • A string representing the OpenSocial ID of a person.
Multiple person specifiers (OpenSocial 0.7):
  • opensocial.DataRequest.Group.OWNER_FRIENDS
  • opensocial.DataRequest.Group.VIEWER_FRIENDS
  • An array of strings, each representing the OpenSocial ID of a person.
In 0.8, the idea of an IdSpec object was introduced.  IdSpec allows you to be much more expressive when specifying groups of people (including friends-of-friends, covered later) but introduces some additional complexity.  Single person specifiers still use the OWNER and VIEWER objects, but these have been moved to the opensocial.IdSpec.PersonId namespace.

To convert your app, update all functions that take a single person identifier to one of the following:
  • opensocial.IdSpec.PersonId.OWNER
  • opensocial.IdSpec.PersonId.VIEWER
  • A string representing the OpenSocial ID of a person.
Note that only the following take a single person modifier:
  • opensocial.DataRequest.newFetchPersonRequest
  • opensocial.DataRequest.newUpdatePersonAppDataRequest
  • opensocial.DataRequest.newRemovePersonAppDataRequest 
The last two only accept the value of opensocial.IdSpec.PersonId.VIEWER, so this is a pretty straightforward change.

Seleting a group of people requires a bit more of a code change, though.  Where you might write something like this in OpenSocial 0.7:

var params = {};
var req = opensocial.newDataRequest();
...
req.add(req.newFetchPeopleRequest(
    opensocial.DataRequest.Group.OWNER_FRIENDS, params);
req.send();
Now you need to create an IdSpec instead:

var params = {};
var idspec = opensocial.newIdSpec({ 
    "userId" : "OWNER", 
    "groupId" : "FRIENDS" 
});
...
req.add(req.newFetchPeopleRequest( idspec, params); req.send();
You can actually create IdSpec equivalents to all of the old person/people identifiers:

IdentifierIdSpec code
OWNER
var idspec = opensocial.newIdSpec({ 
    "userId" : "OWNER",  "groupId" : "SELF" 
});
VIEWER
var idspec = opensocial.newIdSpec({ 
    "userId" : "VIEWER",  "groupId" : "SELF" 
});
OWNER_FRIENDS
var idspec = opensocial.newIdSpec({ 
    "userId" : "OWNER",  "groupId" : "FRIENDS" 
});
VIEWER_FRIENDS
var idspec = opensocial.newIdSpec({ 
    "userId" : "VIEWER",  "groupId" : "FRIENDS" 
});

You can use these for the following functions:
  • opensocial.DataRequest.newFetchActivitiesRequest
  • opensocial.DataRequest.newFetchPeopleRequest
  • opensocial.DataRequest.newFetchPersonAppDataRequest

The IdSpec object exposes some additional functionality like NETWORK_DISTANCE that I'll cover later, but for the time being, this should be enough information to get you to start porting your existing code to 0.8.