Gadget Developer's Guide (v0.8)

This guide will help you build OpenSocial applications. To get the most out of this guide, you should have some general familiarity with the Gadgets API and JavaScript. This guide also provides links to other documents that provide more information.

Writing a Social Application

A site that can host OpenSocial applications is called an OpenSocial container. A social application is simply an application running on an OpenSocial container.

To write an OpenSocial application, you need a working knowledge of JavaScript. Because OpenSocial applications are built as gadgets, you'll need to know how a gadget is structured, and to make your application social, you'll need to learn about the OpenSocial JavaScript API.

(If you prefer, you can write OpenSocial applications using the RESTful or RPC protocol.)

Many containers provide features beyond the OpenSocial specification that you might want to use in your gadgets. To learn more about these container-specific features, check out the /wiki/spaces/a/pages/526883 list. In addition, many containers provide test environments called sandboxes in which you can safely test your gadgets.

The OpenSocial API focuses on people. OpenSocial gadgets help users share their activities with each other and access information about their friends. There are three main feature areas in the OpenSocial API:

  • People and relationships. Members of social networks have friends. OpenSocial applications use the connections between people and their friends.
  • Persistence. OpenSocial applications can take advantage of persistence, the ability to store data that can be retrieved when the application runs again at a later time.
  • Activities. People use social applications to inform others about what they're doing: going to a movie, posting photos, and so on.

Many OpenSocial API calls are asynchronous: that is, the call returns immediately, but the action requested by the call does not take place right away. Instead, the call creates a server request to retrieve or update information. When you make an asynchronous call, you pass in a callback function. When the data is returned from the server, OpenSocial calls your callback function.

For more information about the concepts described in this section, see the following documents:

Importing the OpenSocial Library

Every gadget has the following structure:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
 <ModulePrefs title="Standard gadget structure">
 </ModulePrefs>
 <Content type="html">
 <![CDATA[
      Gadget content and features here
 ]]>
 </Content>
</Module>

When you create a gadget that uses OpenSocial, you must add the following line to the ModulePrefs section:

<Require feature="opensocial-0.8"/>

This makes the structure for an OpenSocial gadget as follows:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
 <ModulePrefs title="Standard gadget structure">
  <Require feature="opensocial-0.8"/>
 </ModulePrefs>
 <Content type="html">
 <![CDATA[
      Gadget content and features here
 ]]>
 </Content>
</Module>

Accessing People and Profiles

Let's get started with a simple example that lists the names of your friends. First, it's important to understand the roles defined by the OpenSocial API:

  • Viewer: the user logged into the browser. As a viewer you might be viewing your own page, or you might be viewing another user's profile.
  • Owner: the user who owns the profile or application in question.
  • Friends: users whom the owner or viewer has added as friends in the container.

The "List Friends" example discussed in this section fetches the viewer and the viewer's friends, and displays a list of the viewers friends. It illustrates how to fetch and operate on data in an OpenSocial application. The basic steps are as follows:

  1. Build the gadget specification.
  2. Create the application skeleton.
  3. Build the request.
  4. Build the response.

These steps are discussed in more detail below.

Build the gadget specification

We start creating the gadget by building the standard gadget specification:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
 <ModulePrefs title="List Friends Example">
   <Require feature="opensocial-0.8"/>
 </ModulePrefs>
 <Content type="html">
 <![CDATA[
   <script type="text/javascript">
   </script>
   <div id="message"> </div>
 ]]>
 </Content>
</Module>

Create the application skeleton

Next, we create the skeleton for the application. This consists of two JavaScript functions we'll use in the gadget:

  • A request function, which we'll use to ask for the owner and owner's friends.
  • A response callback function that OpenSocial calls with the names of the owner's friends.

We'll create stubs for these functions here, and we'll flesh them out in later sections. The final element of the application skeleton is a function call that ensures the request function is executed after the application loads.

Here is the application skeleton:

<script type="text/javascript">
  /**
   * Request the OWNER and OWNER's friends.
   */
  function request() {
  };

  /**
   * Parses the response and generates html to list the names of the owner and
   * his or her friends.
   *
   * @param \{Object\} dataResponse Friend information that was requested.
   */
  function response(dataResponse) {
  };
  // Execute the request function when the application is finished loading.
  gadgets.util.registerOnLoadHandler(request);
</script>

Build the request

Next, we'll flesh out the request function, which calls OpenSocial to get the owner and friends. This function does the following:

  • Create an idspec by calling newIdSpec().
  • Create a request (in the req variable) by calling opensocial.newDataRequest().
  • Call req.add() to add newFetchPersonRequest() and newFetchPeopleRequest() functions to the request.
  • Call req.send() to start the request, passing response as the callback function.

When you use the newFetchPeopleRequest function, you create an IdSpec object and pass it as the function argument. The IdSpec you use varies depending on which data you want to retrieve. Following is a table that shows how to create IdSpec objects for various requests:

To request the...

Use this code

viewer's friends

var idspec = opensocial.newIdSpec({ "userId" : "VIEWER", "groupId" : "FRIENDS" });

owner's friends

var idspec = opensocial.newIdSpec({ "userId" : "OWNER", "groupId" : "FRIENDS" });

Here is the code for the request() function:

/**
 * Request the OWNER and OWNER's friends.
 */
function request() {
  var idspec = opensocial.newIdSpec({ "userId" : "OWNER", "groupId" : "FRIENDS" });
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPersonRequest("OWNER"), "get_owner");
  req.add(req.newFetchPeopleRequest(idspec), "get_friends");
  req.send(response);
};

Note: You can use string literals as shorthand for JavaScript enums that represent OpenSocial fields and methods. For example, instead of writing

opensocial.IdSpec.PersonId.OWNER

you can simply write

"OWNER"

as in the above example. To find the string that corresponds with an enum, see the documentation for the enum in the /wiki/spaces/a/pages/526885.

For more information on newFetchPersonRequest and newFetchPeopleRequest, see the article /wiki/spaces/a/pages/527082.

Build the response

In this section, we'll build the response function. The server calls response() to handle the data – the owner and owner's friends, like this:

  • Create owner and friends variables, and call dataResponse.get() to initialize them with values returned by the server.
  • Start creating HTML that includes the name of the owner and friends.
  • Iterate over the collection of friends by using the each() function.
  • Call getDisplayName() on each friend to add the friend's name to the HTML output.
  • Call person.getDisplayName() to iterate over each friend and add that friend's name to the HTML.
  • Set the gadget's innerHTML property to the newly constructed HTML by calling document.getElementById('message').innerHTML = html .
/**
 * Parses the response and generates html to list the names of the owner and
 * his or her friends.
 *
 * @param {Object} dataResponse Friend information that was requested.
 */
function response(dataResponse) {
  var owner = dataResponse.get('get_owner').getData();
  var friends = dataResponse.get('get_friends').getData(); 
  var html = 'Friends of ' + owner.getDisplayName();
  html += ':<br><ul>';
  friends.each(function(person) {
      html += '<li>' + person.getDisplayName() + '</li>';
  });
  html += '</ul>';
  document.getElementById('message').innerHTML = html;
};

Complete Code for List Friends

This section combines the previous blocks of code into a complete listing for the List Friends application discussed in this section. The response() function gets the owner and the owner's friends, then inserts the owner's name into an HTML string for display. The function then iterates through the friends and adds each friend's name to the HTML. Finally, the HTML containing the names of the owner and owner's friends is inserted into the div tag for display.

The full listing:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="List Friends Example">
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
  <![CDATA[
    <script type="text/javascript">
      /**
       * Request the OWNER and OWNER's friends.
       */
      function request() {
        var idspec = opensocial.newIdSpec({ "userId" : "OWNER", "groupId" : "FRIENDS" });
        var req = opensocial.newDataRequest();
        req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), "get_owner");
        req.add(req.newFetchPeopleRequest(idspec), "get_friends");
        req.send(response);
      };
 
      /**
       * Parses the response and generates html to list the names of the owner and
       * his or her friends.
       *
       * @param {Object} dataResponse Friend information that was requested.
       */
      function response(dataResponse) {
        var owner = dataResponse.get('get_owner').getData();
        var friends = dataResponse.get('get_friends').getData(); 
        var html = 'Friends of ' + owner.getDisplayName();
        html += ':<br><ul>';
        friends.each(function(person) {
          html += '<li>' + person.getDisplayName() + '</li>';
        });
        html += '</ul>';
        document.getElementById('message').innerHTML = html;
      };
 
      // Execute the request function when the application is finished loading.
      gadgets.util.registerOnLoadHandler(request);
 
    </script>
    <div id="message"> </div>
  ]]>
  </Content>
</Module>

Working with Persistent Data

The OpenSocial API supports saving and retrieving per-user, per-application data. The sample code in this section does the following:

  • Generates data to save and stores it in three variables: data1, a random number from 0-5, data2, a random number from 0-100, and data3, the current timestamp.
  • Saves data to the viewer's profile, saving data1 under AppField1, data2 under AppField2, and data3 under AppField3.
  • Sends the request.

This code snippet shows how to save data to the shared persistent data:

/**
 * Generate 3 fields of random data and saves them to the persistent data store.
 *
 */
function populateMyAppData() {
  var req = opensocial.newDataRequest();
  var data1 = Math.random() * 5;
  var data2 = Math.random() * 100;
  var data3 = new Date().getTime();
 
  req.add(req.newUpdatePersonAppDataRequest("VIEWER", "AppField1", data1));
  req.add(req.newUpdatePersonAppDataRequest("VIEWER", "AppField2", data2));
  req.add(req.newUpdatePersonAppDataRequest("VIEWER", "AppField3", data3));
};

The next chunk of code retrieves the stored data by taking the following steps:

  • Specifies the names of the keys that were used to store the data.
  • Adds a newFetchPersonAppDataRequest call to the request, passing the key names as parameters.
  • Calls req.send to send the request to the server.
/**
 * Use newFetchPersonAppDataRequest() to read data from the store.
 *
 */
function requestMyData() { 
  var req = opensocial.newDataRequest(); 
  var fields = [ "AppField1", "AppField2", "AppField3" ]; 
  var p = {}; 
 
  p[opensocial.IdSpec.Field.USER_ID[]] = "VIEWER"; 
  var idSpec = opensocial.newIdSpec(p); 
  req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), "viewer"); 
  req.add(req.newFetchPersonAppDataRequest(idSpec, fields), "viewer_data"); 
  req.send(handleRequestMyData);
}

Here is the complete code for the application, which sets values for the three fields, stores them, and then retrieves them. This listing also adds functions to build output in the htmlout variable and then insert that HTML variable into a div for display.

<?xml version="1.0" encoding="UTF-8" ?> 
<Module>
  <ModulePrefs title="Data Persistence Example" > 
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
  <![CDATA[
 
  <script type="text/javascript"> 
   gadgets.util.registerOnLoadHandler(populateMyAppData);
   var htmlout = "";
   var me = null;
 
   /**
    * Set user data
    */
   function populateMyAppData() {
     var req = opensocial.newDataRequest();
     var data1 = Math.random() * 5;
     var data2 = Math.random() * 100;
     var data3 = new Date().getTime();
     htmlout += "Setting AppField1 to " + data1 + "<br />";
     req.add(req.newUpdatePersonAppDataRequest("VIEWER", "AppField1", data1)) + "<br />";
     htmlout += "Setting AppField2 to " + data2 + "<br />";
     req.add(req.newUpdatePersonAppDataRequest("VIEWER", "AppField2", data2)) + "<br />";
     htmlout += "Setting AppField3 to " + data3 + "<br />";
     req.add(req.newUpdatePersonAppDataRequest("VIEWER", "AppField3", data3)) + "<br />";
     req.send(handlePopulateMyAppData);
   }
 
   /**
    * Handle responses from update person app data requests
    */
   function handlePopulateMyAppData(data) {
     if (data.hadError()) {
       htmlout += data.getErrorMessage();
     }
     requestMyData();
   }
 
   /**
    * Fetch app data
    */
   function requestMyData() { 
     var req = opensocial.newDataRequest(); 
     var fields = [ "AppField1", "AppField2", "AppField3" ]; 
     var p = {}; 
 
     p[opensocial.IdSpec.Field.USER_ID[]] = "VIEWER"; 
     var idSpec = opensocial.newIdSpec(p); 
     req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), "viewer"); 
     req.add(req.newFetchPersonAppDataRequest(idSpec, fields), "viewer_data"); 
     req.send(handleRequestMyData);
   }
 
   /**
    * Handle responses from app data requests
    */
   function handleRequestMyData(data) {
     var mydata = data.get("viewer_data");
     var viewer = data.get("viewer");
     me = viewer.getData();
 
     if (mydata.hadError()) {
       htmlout += data.getErrorMessage();
       return;
     }
     // Do something with the returned data - note the getData call
     doSomethingWithMyData(mydata.getData());
   }
 
   /**
   * Operate on user data
   */
   function doSomethingWithMyData(data) {
     //Data is indexed by user id, and represents an object where keys 
     //correspond with the app data fields.
     var mydata = data[me.getId()];
     var div = document.getElementById('content_div');
     htmlout += "My AppField1 data is: " + mydata["AppField1"] + "<br />";
     htmlout += "My AppField2 data is: " + mydata["AppField2"] + "<br />";
     htmlout += "My AppField3 data is: " + mydata["AppField3"] + "<br />";
     div.innerHTML = htmlout;
   }
 
 </script>
 <div id="content_div"></div>
  ]]> 
  </Content>
</Module>

Note: The data stored in an OpenSocial application is always a string. For most purposes, it is practical to make this a JSON-encoded string. Because OpenSocial performs automatic HTML escaping of all returned data including application data, you must unescape stringified JSON objects in persistent data before parsing them. For example:

var unescapedString = gadgets.util.unescapeString(jsondata); 
var obj = gadgets.json.parse(unescapedString);

Clearing persistent data

There are situations where your application may need to delete a key from persistent data. To erase a key and its value, use the newRemovePersonAppDataRequest method:

var req = opensocial.newDataRequest();
req.add(
    req.newRemovePersonAppDataRequest("VIEWER", "myKey"),
    "clear_data");
req.send(set_callback);

When this request is made, the key <em>myKey</em> and its stored value are both erased from persistent data.

You can clear multiple keys at once by passing an array of keys to this method:

var req = opensocial.newDataRequest();
req.add(
    req.newRemovePersonAppDataRequest("VIEWER", ["key1", "key2", "key3"]),
    "clear_data");
req.send(set_callback);

When this request is made, the keys <em>key1</em>, <em>key2</em>, <em>key3</em> and their stored values are erased from persistent data.

For more information on clearing persistent data, see this article on persistence.

Activities

The OpenSocial API lets you share activities with your friends through an activity stream. An activity stream is a feed in which each entry represents an action performed by the user. An activity could be anything from modifying an application's state to writing an online review for a movie.

If you request HIGH activity priority, the app will ask the user for permission to post activities on behalf of the user if it doesn't already have permission. If you request LOW priority, then the call will do nothing if the app doesn't have permission to post.

  • Call newActivity to create a new activity with the given text as its title.
  • Call requestCreateActivity to send the activity to the server, passing a callback function.
  • In the callback function, handle an error if the server returns one.

This simple example illustrates how to create an activity:

<?xml version="1.0" encoding="UTF-8" ?> 
<Module>
  <ModulePrefs title="Activities - v0.8" >
    <Require feature="opensocial-0.8"/>
  </ModulePrefs> 
  <Content type="html">
  <![CDATA[ 
  <div id="content_div"></div>
  <script type="text/javascript">
 
  var div = document.getElementById('content_div');
 
  /**
   * Create the new activity and send it to the server.
   */
  function postActivity(text) {  
    var params = {};  
    params[opensocial.Activity.Field.TITLE] = text;
    var activity = opensocial.newActivity(params); 
    opensocial.requestCreateActivity(activity, opensocial.CreateActivityPriority.HIGH, callback);
    div.innerHTML = "Activity title is: " + activity.getField(opensocial.Activity.Field.TITLE);
  };        
 
  /**
  * Server calls this function to indicate whether the activity post succeeded or failed.
  */
  function callback(status) {
    if (status.hadError())
    {
      alert("Error creating activity.");
    }
    else 
    {
      alert("Activity successfully created.");
    }
  };
 
  /**
   * Start the process of posting an activity.
   */
  function startActivity() {
    postActivity("This is a sample activity, created at " + new Date().toString());
  };
 
  //Call startActivity as soon as the gadget finishes loading.
    gadgets.util.registerOnLoadHandler(startActivity);
  </script>
  ]]> 
  </Content>
</Module>

Permission Control

The OpenSocial API supports permission control in applications. When an application uses a data request to fetch a viewer from the server, it is only returned if that application has access. If the application does not have access, one of the standard error codes, opensocial.ResponseItem.Error.UNAUTHORIZED, is returned instead. An application can also check ahead of time for its access by using the new opensocial.hasPermission call. If access is denied, you can use opensocial.requestPermission to ask the viewer for the specified permission. Of course, some containers may always grant viewer access, and some may always deny, but this decision is up to the container.

This excerpt illustrates how to use the permission control API:

<?xml version="1.0" encoding="UTF-8" ?>
 
<Module>
  <ModulePrefs title="Checking for Permission" >
 
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
  <![CDATA[
    <h2>Owner:</h2>
    <div id="owner-output">No data.</div>
 
    <h2>Viewer:</h2>
    <div id="viewer-output">No data.</div>
    <script type="text/javascript">
      /**
       * Request permission for the VIEWER if the app does not have it.
       */
      function requestPermission() {
        if (opensocial.hasPermission(opensocial.Permission.VIEWER)) {
          requestData();
        } else {
          var reason = "To demonstrate permission capabilities";
 
          opensocial.requestPermission(opensocial.Permission.VIEWER, reason, onPermissionRequested);
        }
      };
 
      /**
       * Handle the response for the request for VIEWER information.
       */
      function onPermissionRequested(response) {
        if (response.hadError()) {
          switch(response.getErrorCode()) {
            case opensocial.ResponseItem.Error.NOT_IMPLEMENTED :
              alert("Requesting permission is not implemented on this container.");
              break;
 
            default:
              alert("There was a problem requesting permission: " + response.getErrorMessage());
              break;
          };
        }
        requestData();
 
      };
 
      /**
       * Request the OWNER (and VIEWER if we have permission).
       */
      function requestData() {
        var req = opensocial.newDataRequest();
        req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), "owner");
 
        // If we have permission to see the viewer's info, then add it to the request.
        if (opensocial.hasPermission(opensocial.Permission.VIEWER)) {
          req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), "viewer");
 
        }
        req.send(showData);
      };
 
      /**
       * Shows the response from the request for Person data.
       */
      function showData(data) {
        var owner = data.get("owner").getData();
        var ownerOutput = document.getElementById("owner-output");
 
        showPerson(owner, ownerOutput);
        if (opensocial.hasPermission(opensocial.Permission.VIEWER)) {
          var viewer = data.get("viewer").getData();
          var viewerOutput = document.getElementById("viewer-output");
          showPerson(viewer, viewerOutput);
        }
      }
 
      /**
       * Prints information about a person.
       */
      function showPerson(person, div) {
        var name = person.getDisplayName();
        var thumb = person.getField(opensocial.Person.Field.THUMBNAIL_URL);
        var html = '<img src="' + thumb + '"/>' + name;
        div.innerHTML = html;
      }
 
      //Execute requestData when the page is done loading
      gadgets.util.registerOnLoadHandler(requestPermission);
    </script>
  ]]>
  </Content>
</Module>

To Learn More

To learn more about the OpenSocial API, see these additional resources: