English | Site Directory

Orkut Developer Home

OpenSocial Tutorial for Orkut

Dan Holevoet, OpenSocial Team
April 2008

Overview

This tutorial will introduce you to gadgets and OpenSocial, and will walk you through the steps required to build a simple social gadget where you can give gifts to your friends. In addition, you will be introduced to some of the more advanced features of the OpenSocial API.

You can find the complete sample code in the opensocial-resources project on Google Code.

Gadget basics

At their core, social gadgets are XML files, sometimes known as gadget specifications. Here is a simple "Hello World" gadget (helloworld.xml), that illustrates the basic sections of a specification:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="Hello World!">
    <Require feature="opensocial-0.7" />
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      Hello, world!
    ]]>
  </Content>
</Module>

In the "Hello World" example, you can see several sections which control the features and design of the gadget.

  • <Module> indicates that this XML file contains a gadget.
  • <ModulePrefs> contains information about the gadget, and its author.
  • <Require feature="opensocial-0.7" /> denotes a required feature of the gadget — in this case, the OpenSocial API (v0.7).
  • <Content type="html"> indicates that the gadget's content type is HTML. This is the recommended content type for OpenSocial containers, but gadgets for other containers such as iGoogle support other content types.
  • <![ CDATA[…]]> contains the bulk of the gadget, including all of the HTML, CSS, and JavaScript (or references to such files). The content of this section should be treated like the content of the body tag on a generic HTML page.

Running your first gadget

Now that you know what a basic social gadget looks like, it's time to take it a step further and actually install it within an OpenSocial container. There are a few options on how to do this — this tutorial uses the Orkut sandbox. (The Getting Started Guide has information on using other OpenSocial containers.) Here are the things you need in order to get up and running. Don't worry, this tutorial will guide you through getting everything.

It's best to start with a simple gadget while walking through the steps to get up and running. Copy the helloworld.xml example above into a new plain text file on your computer, and save it as helloworld.xml.

To host the gadget externally, you will need a place to upload the file. Fortunately, there are many free places to upload gadget specifications, and Google provides two:

Using your own hosting is preferred — the flexibility it offers will be greater than free hosting. However, if you don't have your own hosting, and are willing to offer your gadget under an open source license, use Google Code: Project Hosting. Finally, if neither of those options is possible, use the GGE or another alternative. Below is a hosting-only version of the GGE that you can use to host OpenSocial applications.

Once you've chosen your hosting and have it configured, upload the helloworld.xml file to the host. Make sure the file is readable to the outside world by typing the URL in a browser window and confirming that the XML is shown to you.

Next, log into Orkut and create an account if you don't have one. When you have the account created, you'll need to request to be added to the whitelist for access to the Orkut sandbox. Once your account is approved, enter the sandbox. The navigation bar on the left side of the page has an "Apps" heading with an "edit" link directly beside it, which takes you to a page for adding and managing applications. After clicking this link, you should see a text input for adding an application by URL. Enter the publicly accessible URL that you entered in your browser before (the location of your gadget specification) and click "add application." This will prompt you to give the application authorization to your profile, friends, activity stream, and navigation bar. Grant it permission by clicking "add this app to my profile." This will return you to the previous page, where you can either click on the newly installed application under "My applications" or on the left side navigation menu. Click on your application in either location to navigate to the canvas page of the gadget. If you see "Hello, world!" then you've successfully installed your first gadget.

If something went wrong...

If you receive an error message when adding the gadget, Orkut is most likely having difficulty retrieving the file from your hosting service. Double check that the file is externally visible and that the XML is copied exactly as above. If you receive an error after installation, and you've already double checked the contents of the file, it is possible that the Orkut sandbox is undergoing maintenance — wait a few moments and try refreshing your browser window.

If you're making changes to your gadget spec but not seeing them reflected in the Orkut sandbox, this is because the Orkut sandbox currently caches your gadget spec to minimize the load on your server. Great for an application in production, but this can be a pain when you're actively developing your app. You can bypass the caching mechanism by adding "bpc=1" to the URL of the profile or canvas page that you're viewing. For an example, see the Orkut Developer's Guide.

Writing your first social application

Now it's time to bite into something a bit meatier, your first social application. This tutorial will help you write a simple application to give "gifts" to your friends. When the gadget is finished you will be able to:

  • Give simple gifts to your friends.
  • See the gifts you have given your friends.
  • See the gifts friends have given you.

Setting up the basics

If you're starting a new gadget, you should create a new XML file for it — call it gifts.xml. Begin with the usual XML boilerplate, and include the social API. Give the gadget a title as well, "Gifts," something reflective of the purpose of the application (the samples will amend the version number to help you keep track of the iterations in this lab). Here's what your shell of a gadget looks like:

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Gifts part 0 - Boilerplate">
    <Require feature="opensocial-0.7"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
    ]]>
  </Content>
</Module>

This gadget doesn't accomplish a lot, and in fact, accomplishes less than the "Hello World" gadget. However, it sets up the basis for the next, important steps.

Complete gadget specification for version 0

Check out the video for this section on YouTube.

Listing friends

If you're going to give gifts to your friends, you're first going to need to let the application discover who your friends are. First, create a function that is called once the gadget is loaded. For now, it, will only perform one job, but later will start multiple steps for initialization.

gadgets.util.registerOnLoadHandler(init);

function init() {
  loadFriends();
}

Now, of course, there needs to be a function to actually load the friend data. The following function creates a new data request object, then populates it with specific types of data that you'll need: the viewer and the viewer's friends. Then, it sends the request to the server, and gives it the name of a function to call when the data is returned.

function loadFriends() {
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPersonRequest('VIEWER'), 'viewer');
  req.add(req.newFetchPeopleRequest('VIEWER_FRIENDS'), 'viewerFriends');
  req.send(onLoadFriends);
}

The callback function, onLoadFriends, will take the data that the server has returned, and display it on the page.

function onLoadFriends(data) {
  var viewer = data.get('viewer').getData();
  var viewerFriends = data.get('viewerFriends').getData();

  html = new Array();
  html.push('<ul>');
  viewerFriends.each(function(person) {
    html.push('<li>' + person.getDisplayName() + "</li>");
  });
  html.push('</ul>');
  document.getElementById('friends').innerHTML = html.join('');
}

Several div elements have been inserted within the gadget specification as entry points for the new HTML.

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Gifts part 1 - Friends">
    <Require feature="opensocial-0.7"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script src="http://opensocial-resources.googlecode.com/svn/samples/tutorial/tags/api-0.7/gifts_1_friends.js"></script>
      <script>
        gadgets.util.registerOnLoadHandler(init);
      </script>
      <div id='main'>
        Your friends:
        <div id='friends'></div>
      </div>
    ]]>
  </Content>
</Module>

Complete gadget specification for version 1 and complete JavaScript code for version 1

Check out the video for this section on YouTube.

Giving Gifts

Now it's time to implement the raison d'être of your gadget, giving gifts. First, update the gadget specification to include a new JavaScript file (that has been provided for you), to convert basic JavaScript types into JSON strings for easy storage. Next, you'll need to modify the basic HTML in the gadget specification so that it can insert new information for gift giving into the layout. The resultant XML looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Gifts part 2 - Send Gifts">
    <Require feature="opensocial-0.7"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script src="http://opensocial-resources.googlecode.com/svn/samples/tutorial/tags/api-0.7/gifts_2_send_gifts.js"></script>
      <script>
        gadgets.util.registerOnLoadHandler(init);
      </script>
      <div id='main'>
        <div id='give'>
          <form id='gift_form'>
            Give <span id='gifts'></span> to <span id='friends'></span>. <a href="javascript:void(0);" onclick='giveGift();'>Give!</a>
          </form>
        </div>
      </div>
    ]]>
  </Content>
</Module>

Now that there are nice hooks into the HTML, modify the output of the friends list into a set of option tags for use within a select tag. This will allow you to select a friend to receive a gift.

function onLoadFriends(data) {
  var viewer = data.get('viewer').getData();
  var viewerFriends = data.get('viewerFriends').getData();

  html = new Array();
  html.push('<select id="person">');
  viewerFriends.each(function(person) {
    html.push('<option value="' + person.getId() + '">' + person.getDisplayName() + "</option>");
  });
  html.push('</select>');
  document.getElementById('friends').innerHTML = html.join('');
}

Next, you'll need to create another selection menu of gifts you can give. The sample uses a selection of different types of nuts, but you can feel free to use whatever you like. A small update to the initialization function calls this function when the page loads.

function makeOptionsMenu() {
  var options = ['a cashew nut', 'a peanut', 'a hazelnut', 'a red pistachio nut'];

  var html = new Array();
  html.push('<select id="nut">');
  for (var i = 0; i < options.length; i++) {
    html.push('<option value="' + i + '">' + options[i] + '</option>');
  }
  html.push('</select>');
  document.getElementById('gifts').innerHTML = html.join('');
}

function init() {
  loadFriends();

  makeOptionsMenu();
}

To tie all of this together, implement giveGift, the function called when a user clicks the "Give!" button in the gadget. The function loads the gift to be given and the friend to give it to, from the form, updates a global object of gifts, and saves this to the persistent storage.

var givenGifts = {};

function giveGift() {
  var nut = document.getElementById('nut').value;
  var friend = document.getElementById('person').value;

  givenGifts[friend] = nut;
  var json = gadgets.json.stringify(givenGifts);

  var req = opensocial.newDataRequest();
  req.add(req.newUpdatePersonAppDataRequest(opensocial.DataRequest.PersonId.VIEWER, 'gifts', json));
  req.send();
}

Complete gadget specification for version 2 and complete JavaScript code for version 2

Check out the video for this section on YouTube.

Showing your generosity

Although your gift gadget can give gifts, once they're sent, they go into a vacuum and you're never really sure they've been sent. It would be helpful if it could list the gifts that have been given, so modify the gadget to load the list of gifts that you've sent to other people.

You could cheat a little bit here, and just use the global object givenGifts, but that would only display gifts that you've given in any one session, because right now it isn't linked to any persistent storage. Also, you wouldn't know whether your requests were actually successful, just that you'd sent them (the JavaScript object is currently updated regardless of success). A global object is a convenient way to store the gifts that you've sent, though, so if you keep it updated by linking to persistent storage, it will serve as a suitable place to load the data.

You'll need to update the global list of gifts in two instances. First, you'll need to update it when you load the gadget, to see all gives you've given previously. Second, you'll need to update it when you give a new gift, to both make sure that the gift was sent, and to keep your local object fresh.

First, add two requests onto your dataRequest object in giveGift to fetch the viewer's information, and the viewer's friends (for association purposes), when it makes the request. Then, take advantage of the opportunity to add a callback to your request to send a gift.

function giveGift() {
  var nut = document.getElementById('nut').value;
  var friend = document.getElementById('person').value;

  givenGifts[friend] = nut;
  var json = gadgets.json.stringify(givenGifts);

  var req = opensocial.newDataRequest();
  req.add(req.newUpdatePersonAppDataRequest(opensocial.DataRequest.PersonId.VIEWER, 'gifts', json));
  req.add(req.newFetchPersonRequest('VIEWER'), 'viewer');
  req.add(req.newFetchPeopleRequest('VIEWER_FRIENDS'), 'viewerFriends');
  req.add(req.newFetchPersonAppDataRequest('VIEWER', 'gifts'), 'data');
  req.send(onLoadFriends);
}

If you modify your initial request to load friend data, you can reuse the onLoadFriends function to handle both paths of execution.

function loadFriends() {
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPersonRequest('VIEWER'), 'viewer');
  req.add(req.newFetchPeopleRequest('VIEWER_FRIENDS'), 'viewerFriends');
  req.add(req.newFetchPersonAppDataRequest('VIEWER', 'gifts'), 'data');
  req.send(onLoadFriends);
}

Your onLoadFriends function now generates an easy-to-use array to associate friends with their names. This array will be passed on to the function that will handle the update of your list of given gifts.

function onLoadFriends(data) {
  var viewer = data.get('viewer').getData();
  var viewerFriends = data.get('viewerFriends').getData();
  var giftData = data.get('data').getData();
  var friends = new Array();

  html = new Array();
  html.push('<select id="person">');
  viewerFriends.each(function(person) {
    html.push('<option value="' + person.getId() + '">' + person.getDisplayName() + "</option>");

    friends[person.getId()] = person.getDisplayName();
  });
  html.push('</select>');
  document.getElementById('friends').innerHTML = html.join('');

  updateGiftList(viewer, giftData, friends);
}

Now, you'll need to write an updateGiftList function to update the global object and display the results, when the gadget gets back data. The sample function below is robust enough to not throw an exception when the list is blank, but bad data will cause the global list of gifts to be blank (and fail silently).

function updateGiftList(viewer, data, friends) {
  var json = data[viewer.getId()]['gifts'];

  if (!json) {
    givenGifts = {};
  }
  try {
    givenGifts = gadgets.json.parse(gadgets.util.unescapeString(json));
  } catch (e) {
    givenGifts = {};
  }

  var options = ['a cashew nut', 'a peanut', 'a hazelnut', 'a red pistachio nut'];

  var html = new Array();
  html.push('You have given:');
  html.push('<ul>');
  for (i in givenGifts) {
    if (+(i) > 0) {
      html.push('<li>' + friends[i] + ' received ' + options[givenGifts[i]] + '</li>');
    }
  }
  html.push('</ul>');
  document.getElementById('given').innerHTML = html.join('');
}

The last thing you'll need is a hook in the HTML where you can insert the list of given gifts:

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Gifts part 3 - Showing Gifts">
    <Require feature="opensocial-0.7"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script src="http://opensocial-resources.googlecode.com/svn/samples/tutorial/tags/api-0.7/gifts_3_showing_gifts.js"></script>
      <script>
        gadgets.util.registerOnLoadHandler(init);
      </script>
      <div id='main'>
        <div id='give'>
          <form id='gift_form'>
            Give <span id='gifts'></span> to <span id='friends'></span>. <a href="javascript:void(0);" onclick='giveGift();'>Give!</a>
          </form>
        </div>
        <div id='given'></div>
      </div>
    ]]>
  </Content>
</Module>

Complete gadget specification for version 3 and complete JavaScript code for version 3

Check out the video for this section on YouTube.

Showing gifts you've received

So far, the gift gadget sends gifts to your friends, but your friends have no way of knowing they've received them. When they see their gift application, it doesn't tell them what other people have sent them, just what they, themselves have sent.

Adding the ability to see what others have sent you isn't too difficult, but it does involve a bit of a trick. Currently an application can only write to the persistent storage of the viewer of the gadget, so this gadget stores all the gifts you've given in the 'gifts' field of the viewer's application data. When one of your friends views her instance of the gadget, the gadget will have to seek out the gifts that she has received by checking the 'gifts' field of each of her friends. In short, you can only write to your own storage, but you can read the application data of your friends (as long as they have the app installed, too).

To start, add another hook into the HTML as a placeholder for the list of received gifts.

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Gifts part 4 - Showing What You Got">
    <Require feature="opensocial-0.7"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script src="http://opensocial-resources.googlecode.com/svn/samples/tutorial/tags/api-0.7/gifts_4_showing_received.js"></script>
      <script>
        gadgets.util.registerOnLoadHandler(init);
      </script>
      <div id='main'>
        <div id='give'>
          <form id='gift_form'>
            Give <span id='gifts'></span> to <span id='friends'></span>. <a href="javascript:void(0);" onclick='giveGift();'>Give!</a>
          </form>
        </div>
        <div id='given'></div>
        <div id='received'></div>
      </div>
    ]]>
  </Content>
</Module>

Next, you'll need to make a number of small changes to the functions that load persistent data. First, update loadFriends to request the application data for the viewer's friends.

function loadFriends() {
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPersonRequest('VIEWER'), 'viewer');
  req.add(req.newFetchPeopleRequest('VIEWER_FRIENDS'), 'viewerFriends');
  req.add(req.newFetchPersonAppDataRequest('VIEWER', 'gifts'), 'data');
  req.add(req.newFetchPersonAppDataRequest('VIEWER_FRIENDS', 'gifts'), 'viewerFriendData');
  req.send(onLoadFriends);
}

Then, update giveGift to do the same. (Remember that these two entry points to updating data rely on one callback function, so the data needs to be consistently fetched.)

function giveGift() {
  var nut = document.getElementById('nut').value;
  var friend = document.getElementById('person').value;

  givenGifts[friend] = nut;
  var json = gadgets.json.stringify(givenGifts);

  var req = opensocial.newDataRequest();
  req.add(req.newUpdatePersonAppDataRequest(opensocial.DataRequest.PersonId.VIEWER, 'gifts', json));
  req.add(req.newFetchPersonRequest('VIEWER'), 'viewer');
  req.add(req.newFetchPeopleRequest('VIEWER_FRIENDS'), 'viewerFriends');
  req.add(req.newFetchPersonAppDataRequest('VIEWER', 'gifts'), 'data');
  req.add(req.newFetchPersonAppDataRequest('VIEWER_FRIENDS', 'gifts'), 'viewerFriendData');
  req.send(onLoadFriends);
}

Third, update the callback function onLoadFriends to pull the data of the owner's friends out of the returned data, and pass it along to the function that will do the real work, updateReceivedList.

function onLoadFriends(data) {
  var viewer = data.get('viewer').getData();
  var viewerFriends = data.get('viewerFriends').getData();
  var giftData = data.get('data').getData();
  var viewerFriendData = data.get('viewerFriendData').getData();
  var friends = new Array();

  html = new Array();
  html.push('<select id="person">');
  viewerFriends.each(function(person) {
    html.push('<option value="' + person.getId() + '">' + person.getDisplayName() + "</option>");

    friends[person.getId()] = person.getDisplayName();
  });
  html.push('</select>');
  document.getElementById('friends').innerHTML = html.join('');

  updateGiftList(viewer, giftData, friends);
  updateReceivedList(viewer, viewerFriendData, viewerFriends);
}

The final change, implementing updateReceivedList closely parallels updateGiftList, but rather than iterating once through the list of gifts you've sent, iterates once through the gifts each of your friends have sent, and pulls out just the ones for you. These are collected nicely, and displayed.

function updateReceivedList(viewer, data, friends) {
  var viewerId = viewer.getId();
  var options = ['a cashew nut', 'a peanut', 'a hazelnut', 'a red pistachio nut'];

  var html = new Array();
  html.push('You have received:<ul>');
  friends.each(function(person) {
    var personData = data[person.getId()];
    if (personData) {
      var json = data[person.getId()]['gifts'];

      var gifts = {}
      if (!json) {
        gifts = {};
      }
      try {
        gifts = gadgets.json.parse(gadgets.util.unescapeString(json));
      } catch (e) {
        gifts = {};
      }

      for (i in gifts) {
        if (+(i) > 0 && i == viewerId) {
          html.push('<li>' + options[gifts[i]] + ' from ' + person.getDisplayName() + '</li>');
        }
      }
    }
  });
  html.push('</ul>');
  document.getElementById('received').innerHTML = html.join('');
}

Complete gadget specification for version 4 and complete JavaScript code for version 4

Check out the video for this section on YouTube.

Bragging about it

So, now your friends know how generous you are because they can see the peanuts you gave them, but the real coup de grâce would be if you could show this off to everyone. The good news is that you can, if you post your giving in the activity stream. These activities will be displayed in different ways depending on the container. For example, the container may show activities on an "updates" page or in a gadget.

The first step towards posting an activity is simple, add a call to a new function at the end of giveGift, passing in both the gift and the friend who'll receive it.

function giveGift() {
  var nut = document.getElementById('nut').value;
  var friend = document.getElementById('person').value;

  globalGivenGifts[friend] = nut;
  var json = gadgets.json.stringify(globalGivenGifts);

  var req = opensocial.newDataRequest();
  req.add(req.newUpdatePersonAppDataRequest(opensocial.DataRequest.PersonId.VIEWER, 'gifts', json));
  req.add(req.newFetchPersonRequest('VIEWER'), 'viewer');
  req.add(req.newFetchPeopleRequest('VIEWER_FRIENDS'), 'viewerFriends');
  req.add(req.newFetchPersonAppDataRequest('VIEWER', 'gifts'), 'data');
  req.add(req.newFetchPersonAppDataRequest('VIEWER_FRIENDS', 'gifts'), 'viewerFriendData');
  req.send(onLoadFriends);

  postActivity(nut, friend);
}

Now, you might notice that the new function postActivity isn't being given the friend's name, or the name of the viewer, which it might conceivably need. Unfortunately, both of these bits of information are outside of the scope of the function call, because giveGift is called when the user clicks a button, not when the API is being used.

There are two ways to go about solving this issue. The first solution is to pass more information into the form, including the real names of all the friends, and the id of the viewer. The second, and probably more practical solution, is to put some of the application data into the global scope, so that postActivity can access it anytime it wants. It's likely that other parts of your application will want some bits from persistent storage (or the viewer's id) at some point where that data isn't passed into the current function, so having some data stored at the global level of the gadget is often a good, and convenient, idea.

For the purposes of sending messages to the activity stream, making the viewer and the small table of friend names and ids global is sufficient.

var globalViewer = {};
var globalFriends = {};

function onLoadFriends(data) {
  var viewer = globalViewer = data.get('viewer').getData();
  var viewerFriends = data.get('viewerFriends').getData();
  var giftData = data.get('data').getData();
  var viewerFriendData = data.get('viewerFriendData').getData();
  var friends = new Array();
  
  html = new Array();
  html.push('<select id="person">');
  viewerFriends.each(function(person) {
    html.push('<option value="' + person.getId() + '">' + person.getDisplayName() + "</option>");
    
    friends[person.getId()] = person.getDisplayName();
  });
  html.push('</select>');
  document.getElementById('friends').innerHTML = html.join('');
  
  globalFriends = friends;
  updateGiftList(viewer, giftData, friends);
  updateReceivedList(viewer, viewerFriendData, viewerFriends);
}

Now that all the helpful information is in the global scope, postActivity can be fully functional. Posting to the activity stream is quite simple — the creation request consists of an activity, a priority, and a callback function. Create an activity with a title along the lines of "You gave your friend a red pistachio." The priority tells the container to post the activity if the user has given it permission to do so, or ask if for permission. Finally, the callback function is an optional parameter, but if it's omitted the createActivity function will trigger a page refresh. Since a refresh is not necessary here, the postActivity method supplies an empty function to execute instead.

function postActivity(nut, friend) {
  var options = ['a cashew nut', 'a peanut', 'a hazelnut', 'a red pistachio nut'];

  var title = globalViewer.getDisplayName() + ' gave ' + globalFriends[friend] + ' ' + options[nut];
  var params = {};
  params[opensocial.Activity.Field.TITLE] = title;
  var activity = opensocial.newActivity(params);
  opensocial.requestCreateActivity(activity, opensocial.CreateActivityPriority.HIGH, function() {});
}

Complete gadget specification for version 5 and complete JavaScript code for version 5

But wait there's more

Congratulations, you've officially written an OpenSocial gadget, one that takes advantage of a number of API features including profile information and persistent storage. But, don't cut the party short—there's more work to be done. When you feel up to it, investigate the documentation and see if you can tackle some of the following issues on your own.

Error checking

Tutorials are often light on error checking, and will assume that everything works as intended. Unfortunately for developers, things don't always work. You'll want to make sure your application is handling edge cases that might come up. For example, if your gadget doesn't get back a list of given gifts for the viewer, even thought they have given gifts, the tutorial code will gladly overwrite the data in the application storage on the next update, deleting all the gifts the viewer has given.

Viewer or owner, that is the question

The gifts gadget makes light of the difference between the viewer of the gadget and the owner (that is the person who chose to put it on their profile page). It will happily assume that these two personalities are, in fact, always the same person.

While this makes it easier for demonstration purposes, in real applications, this is an important distinction to manage. Take for instance a scenario where you're viewing the gifts gadget on someone else's profile. It might be less prudent to be able to send gifts to all of your friends, perhaps you should only be able to send them to that one person. Or, perhaps instead of seeing which gifts you've sent, maybe you'd want to see the gifts that the owner had sent to, and received from, his friends.

The distinction is also important because permissions on data storage will vary depending on who the viewer and owner are. It's only possible for a gadget to fetch data for the current viewer, owner, and friends of the owner of a gadget, so if you expect your application to be useful to those looking at others' profiles, remember that they might not be able to see data from their own friends.

Lastly, remember that it's only possible to update application data for the viewer. Data sharing between friends will have to occur by the store-and-pull method that is used in the gifts application.

Profile vs. canvas

Every gadget has two views, the profile and the canvas. The gifts gadget isn't particularly tailored for either. It doesn't need a large amount of space, so it fits adequately within the profile view, but it won't take specific advantage of lots of room if it's given it.

To design a robust application, it's important to keep the dichotomy of views in mind, and to design the functionality of your application around the particular outlets in which it might be viewed. This can often go hand in hand with the distinction of viewer and owner — a gadget in the profile might want to just display a digest of given and received gifts (with maybe an option to give just a gift to the gadget's owner), saving the full-blown giving interface for the canvas view.

Handling views is straightfoward, the gadgets.views.getCurrentView() call will return the current view, and gadgets.views.requestNavigateTo(view) call will attempt to navigate the gadget to the given view (if the container allows it). A combination of these calls will allow you to render the appropriate content for the current view, and direct the gadget to a different view when required.

And the rest…

Designing great social applications can be difficult, but the next steps are up to you. There's already a lot of documentation available, a helpful group for asking and answering questions, and a blog to keep you up to date. Here's to your application!