Orkut Application Platform

Lifecycle of an orkut Application

(OpenSocial API v0.8)

Vijaya Machavolu and Jason Cooper, orkut Team
December 2008

WARNING: This article has been deprecated. Although it is still available for reference purposes, but most of its content is outdated and no longer applicable.

There are several distinct stages of orkut application development, which are summarily explained in the sections below. If you have built applications on other platforms, these stages should look familiar:

This article discusses each of these stages: it covers the important design considerations, briefly describes the process of structuring and writing a small app, then delves into testing and how a good application should behave. Finally, it details how to submit a polished application to the orkut directory and how to update it once it's listed.

Designing the application

Prerequisites

Prerequisite Description
Background reading Before you begin actively designing your application, particularly if you're new to orkut development, you should browse through the wealth of additional content that has been assembled in order to get you off to a fast start:

  • Anatomy of an orkut application introduces the application directory as well as the application settings page, which users use to manage their installed apps. It also illustrates the two main application views.
  • The JavaScript Developer's Guide provides an overview of the application platform and indicates which profile fields are available through the API.
  • The orkut application guidelines provide clear-cut policies on acceptable and unacceptable application behaviors as well as violation terms.

Design considerations

If you browse through the orkut application directory, you'll notice that the top-ranked apps have varied themes and appeal. There is no magic bullet when it comes to designing applications that will captivate orkut's diverse user base, but there are ways to make your application stand out.

Since orkut is a social network, developers are encouraged to write applications that use the underlying API for fetching friend information and posting to orkut's built-in update stream. Taking advantage of these resources results in more personal applications—all apps should have one or more features that allow users to express themselves (e.g. letting users select which pictures to display on their profiles in a photo sharing app) or interact with friends (by displaying a list of friends' high scores in a game or allowing users to share "gifts" or other trinkets). Read these best practices for more on designing social applications.

During this planning stage, developers should also choose which notification channels to use and how to use them effectively in order to encourage promotion and adoption. For example, many apps include an "Invite" link which calls opensocial.requestSendMessage so users can notify their friends when they find a fun application. orkut's update stream is another great resource for promotion. Even though the developer guidelines restrict apps from posting updates when they're first installed, updates can still attract new users, especially if they reflect meaningful interactions with the application. For example, a gift-giving app should issue updates when users send a gift to a friend, and these updates can include tasteful graphics (such as an image of the gift) and the recipient's name and profile picture if available. Likewise, quiz apps can post an update when the owner completes a quiz comparing his score with the average score of friends who have completed the same quiz, and games may issue updates when the owner beats the high score set by a friend, etc.

Application structure

The orkut developer guidelines recommend designing your profile view to display the same content to all users, regardless of whether they have the application installed. This is typically done by making the profile view "owner-centric." For example, gaming apps may display the owner's high score and ranking in the profile. Likewise, social bookmarking utilities can display the most recent items favorited or commented on by the application owner. Think of the profile view as a channel for self-expression: users of your application want to show off their content to other orkut users who visit their profile, so you should design the profile view with this goal in mind. This, in turn, may lead to wider adoption of your application as the visitors follow suit and install your app to show off their content to their own friends.

The canvas view is larger and welcomes more user interaction, which presents problems for application developers trying to maintain separate interfaces for users with and without the app. You should not simply redirect users to the application install page if they don't have your app installed—automatic redirects are grounds for suspension from the directory. You are also encouraged to do more than show users a message asking them to install the app. While most applications require some information about the current user in order to be useful, there are several steps you can take to still provide a useful interface for those who haven't installed your application. In fact, the more useful and appealing this interface is to them, the more likely they are to bite the bullet and hit "add application," so you should strongly consider one of the options below:

  • Allow users to see the full user interface so they understand what they can do with the application and how it works, but disable the functions that are reserved for users with the application. For example, if your app allows users to browse, rate, and write comments for a selection of movies, you can allow guest viewers to browse the movies and see other users' ratings, but the rating function is disabled with a note asking them to add the application in order to rate or add comments. Alternatively, you may leave the reserved functions enabled but redirect the user to the application's install page if they attempt to execute one. This approach is not preferred to the disabled controls, which provides a better and less confusing user experience.
  • If nothing else, display the same view as that of the profile, which is viewer-agnostic. Since the idea is to entice potential users to add your application, you may add a note or splash page explaining what users can do once they add it since you're not showcasing the full user interface as in the above option.

Building the application

Prerequisites

Prerequisite Description
Sandbox access If you haven't already, you should sign up for orkut sandbox access. The sandbox is a testing environment for applications-in-progress. There are two flavors: the standard sandbox is more bleeding-edge and always has the newest API features before they're rolled out to production, but it can be more unstable as a result. The prod sandbox environment is configured identically to production orkut, the only difference being the ability to add applications that aren't listed in the directory. You should test your applications in both, but especially in prod sandbox since this is how your app will appear to all other orkut users.
Public host Applications must be hosted on a public domain, so make sure you have access to public hosting. If you don't run your own web server, gmodules.com and Google App Engine are popular alternatives.

An optional but recommended "prerequisite" to development is the OpenSocial Dev App (OSDA), an orkut application which enables you to execute API calls and other JavaScript snippets and preview the results. The app also has a data viewer to easily see what social data is available as well as pre-built samples demonstrating how to do various tasks with the API and even some orkut-specific functions which are described later.

Application source

For the purposes of this article, let's use a simple application which fetches and displays the name of the current viewer and the viewer's friends. Although the code looks innocent enough, it is untested and won't work properly in all cases, as we'll soon see. Here is the source (for now):

<Module>
  <ModulePrefs title="List Friends example">
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html" view="canvas">
    <![CDATA[
      <script type="text/javascript">
        function fetchFriends() {
          // Create a new data request
          var req = opensocial.newDataRequest();

          // Request the profile details of the current user
          var viewerIdSpec = opensocial.IdSpec.PersonId.VIEWER;
          req.add(req.newFetchPersonRequest(viewerIdSpec), 'viewer');

          // Request the profile details of the current user's friends
          var viewerFriendsIdSpec =
            opensocial.newIdSpec({'userId' : 'VIEWER', 'groupId' : 'FRIENDS'});
          req.add(req.newFetchPeopleRequest(viewerFriendsIdSpec), 'viewerFriends');

          // Send the data request and call the processFriends function when
          // data is available
          req.send(processFriends);
        }

        function processFriends(data) {
          var html = [];

          // Retrieve the requested data from the object passed in
          var viewerFriends = data.get('viewerFriends').getData();
          var viewer = data.get('viewer').getData();

          // Output the current user's name
          html.push('Friends of <b>');
          html.push(viewer.getDisplayName());
          html.push('</b>:');
          html.push('<br/>');

          // Output the names of the user's friends as a list
          html.push('<ul>');
          viewerFriends.each(
            function(friend) {
              html.push('<li>');
              html.push(friend.getDisplayName());
              html.push('</li>');
            }
          );
          html.push('</ul>');

          document.getElementById('content').innerHTML = html.join('');
        }

        // Execute the fetchFriends function when the app first loads 
        gadgets.util.registerOnLoadHandler(fetchFriends);
      </script>
      <div id="content"></div>
    ]]>
  </Content>
</Module>

Here is the application in action:

Testing the application

This section will take you through the process of testing the above application in several important scenarios and show you how to avoid some common errors.

Prerequisites

Prerequisite Description
JavaScript debugger A good script debugger is an essential part of any web developer's toolkit and comes in especially handy during orkut app development since the bulk of the business logic is likely to be implemented in JavaScript. Firebug is a highly regarded Firefox extension. It provides a JavaScript console as well as profiling tools so you can see which app resources take the longest to download. Internet Explorer has a built-in script debugger but other, more powerful utilities are available for purchase such as the Microsoft Script Editor which is included with Microsoft Office. This blog entry has more information.

Setting up for testing

The OpenSocial API, which the orkut platform is built on, defines two core roles: OWNER and VIEWER. The former refers to the user who installed the application on his profile while the latter represents the current user of the application who may or may not have the application installed himself. If you are unfamilar with these roles, please see the note on OpenSocial identifiers in the article on requesting data in OpenSocial.

From an application's perspective, there are two fundamental classes of users: those who have that application installed and those that don't. When a user installs an application, they give it permission to access their profile information, which is not the case for users who don't have the application installed, a class of users collectively referred to as "guest viewers." When an application attempts to fetch profile information for a guest viewer, orkut returns a null object, so it's imperative that you test your app with at least two users, one with the app and one without, in order to ensure it works correctly for all users.

For the purpose of this article, let's work with a sample app which outputs the friends of the current user as an HTML list. We'll also create two test users, both of which are whitelisted for sandbox access. (Remember that even if a user hasn't installed an unlisted application, he still needs to be in the sandbox to interact with it on another sandbox user's profile.)

Alice is friends with Digby and has the "List Friends example" application installed on her sandbox profile.
Digby is friends with Alice and is also signed into the orkut sandbox. However, he does not have "List Friends example" installed on his profile.

Now that we have two test accounts set up, it's time to begin the testing.

Testing OWNER role

To test your application as the OWNER, you must be signed into orkut as a user who has the application installed—Alice in this case. As Alice, log into orkut and navigate to the profile. Expand the application if necessary by clicking on the black disclosure triangle in the upper-left corner of the application frame. This will display the profile view.

If the application does not have a profile view, as in the above sample app, an error will be displayed:

The developer guidelines mandate that all applications must have a profile view, preferably one that allows for interaction by all users regardless of whether they have the application installed. To add a profile view, add a new Content element with a profile as its view attribute value:

<Content type="html" view="profile">

Alternatively, you can use a single Content element for both profile and canvas views by omitting the view attribute entirely and use the API to determine the view at runtime:

if (gadgets.views.getCurrentView().getName() == "canvas") {
  // Canvas view-specific script
} else if (gadgets.views.getCurrentView().getName() == "profile") {
  // Profile view-specific script
}

For this simple application, let's just remove the view attribute entirely and let the profile and canvas share the same view. For more advanced applications that display more than a simple friend list, it's usually preferable to define a custom view for the profile which fits within the smaller space provided.

<Content type="html">
  <![CDATA[
    <!-- Functions to list friends -->
  ]]>
</Content>

Now the application loads as expected in the profile view:

Testing "guest" VIEWER role

To test your application as a guest viewer, sign out of orkut and sign in with a second test account that doesn't have the application installed—Digby here. Since Digby and Alice are friends, it's relatively simple to locate Alice's profile by browsing his orkut friends.

After opening Alice's profile, it's natural to expect the "List Friends example" application to display Digby's friends since Digby is the current user (i.e. VIEWER) and the sample application requests the viewer's information and friend list when it loads. Unfortunately, Firebug shows a JavaScript error instead (see, debugging tools really are handy!):

Remember that viewer information is only available if the current user has the application installed. Our mistake here was assuming that the viewer object is always available, which is easy to do if you only test your application on profiles that have it installed. It's important to remember that, once your application is published, the vast majority of orkut users will see and experience it for the first time on a friend's profile. If you want them to install it themselves, you have to provide the best experience possible, and making sure your application actually works in the first place goes a long way towards this goal.

Fixing this bug is relatively straightforward. We can request the owner and owner friends along with the viewer and viewer friends, and in the case when the viewer is null, simply output the owner's friends instead. The owner object should never be null because the OWNER represents the user who installed the application in the first place. Thus, our final application looks like this (changes in bold):

<Module>
  <ModulePrefs title="List Friends example">
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/javascript">
        function fetchFriends() {
          // Create a new data request
          var req = opensocial.newDataRequest();
          
          // Request the profile details of the app's owner
          var ownerIdSpec = opensocial.IdSpec.PersonId.OWNER;
          req.add(req.newFetchPersonRequest(ownerIdSpec), 'owner');

          // Request the profile details of the owner's friends
          var ownerFriendsIdSpec =
            opensocial.newIdSpec({'userId' : 'OWNER', 'groupId' : 'FRIENDS'});
          req.add(req.newFetchPeopleRequest(ownerFriendsIdSpec), 'ownerFriends');

          // Request the profile details of the current user
          var viewerIdSpec = opensocial.IdSpec.PersonId.VIEWER;
          req.add(req.newFetchPersonRequest(viewerIdSpec), 'viewer');

          // Request the profile details of the current user's friends
          var viewerFriendsIdSpec =
            opensocial.newIdSpec({'userId' : 'VIEWER', 'groupId' : 'FRIENDS'});
          req.add(req.newFetchPeopleRequest(viewerFriendsIdSpec), 'viewerFriends');
          
          // Send the data request and call the processFriends function when
          // data is available
          req.send(processFriends);
        }

        function processFriends(data) {
          var html = [];

          // Retrieve the requested data from the object passed in
          var viewerFriends = data.get('viewerFriends').getData();
          var ownerFriends = data.get('ownerFriends').getData();
          var viewer = data.get('viewer').getData();
          var owner = data.get('owner').getData();

          var friendCollection = null;

          // Determine which user's information to use -- if the current user's
          // profile details aren't available, use the application owner's info.
          // instead, which is always available; output that user's name
          if (viewer != null) {
            html.push('Friends of viewer <b>');
            html.push(viewer.getDisplayName());
            friendCollection = viewerFriends;
          } else {
            html.push('Friends of owner <b>');
            html.push(owner.getDisplayName());
            friendCollection = ownerFriends;
          }

          html.push('</b>:');
          html.push('<br/>');

          // Output the names of the user's friends as a list
          html.push('<ul>');
          friendCollection.each(
            function(friend) {
              html.push('<li>');
              html.push(friend.getDisplayName());
              html.push('</li>');
            }
          );
          html.push('</ul>');

          document.getElementById('content').innerHTML = html.join('');
        }

        // Execute the fetchFriends function when the app first loads 
        gadgets.util.registerOnLoadHandler(fetchFriends);
      </script>
      <div id="content"></div>
    ]]>
  </Content>
</Module>

Now the application displays Alice's friends. But if Digby were to add the application later, he would see his own friends listed since the viewer object would no longer be null.

Identifying and plugging security vulnerabilities

All application developers must verify that their applications are safeguarded against cross-site scripting (XSS) attacks. These attacks are made possible when developers don't explicitly escape user input before displaying it on the screen. This enables developers to insert malicious JavaScript and/or HTML which can do any number of dangerous actions such as launching denial-of-service attacks or redirecting users to phishing sites which are often deceptively styled in an attempt to trick users into entering their username and password. Unfortunately, these vulnerabilities are all too easy to introduce. Take the following simple, seemingly harmless application:

<?xml version="1.0" encoding="UTF-8" ?>
<Module>
  <ModulePrefs title="XSS Vulnerability example">
    <Require feature="opensocial-0.8"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/javascript">
       function displayPreview(form) {
         var text = form.myText.value;
         document.getElementById('preview').innerHTML = text;
       }
      </script>

      <form id="myForm">
        <input type="text" name="myText" size="45"/>
        <input type="button" onclick="displayPreview(this.form)" value="Display"/>
      </form>

      <div id="preview"></div>
    ]]>
  </Content>
</Module>

As demonstrated in the screenshot above, all this application does is echo the text string entered in the provided input into a div element. Because, the text is never filtered or escaped, the door is wide open for attackers. To see this, try entering raw HTML in the field:

Just as we feared! Instead of displaying the literal text, the input is parsed as HTML and displayed in the div element. In this particular case, the end result was harmless, but this potential exploit provides attackers with the means to do far more destructive inserts such as inserting an IFrame which, when loaded, redirects the user to phishing site or worse.

We can avoid this problem altogether by escaping the text before displaying it. This involves substituting entity references for certain sensitive characters (e.g. < and >). Fortunately, the gadgets.* library provides a function that does all the hard work for you. Let's modify the displayPreview function above slightly to escape the text before displaying it:

function displayPreview(form) {
  var text = form.myText.value;
  var escapedText = gadgets.util.escapeString(text);
  document.getElementById('preview').innerHTML = escapedText;
}

Now that the text is passed through gadgets.util.escapeString function, the literal text is displayed as expected. You must pass all user-entered text through this function to prevent exploits, as recommended in the orkut developer guidelines. Security vulnerabilities are subject to immediate takedown from the directory, so make sure to be extra vigilant in your escaping of user input.

The above tip applies to data returned by the API as well, since names and other fields can and often do include special characters such as < and >.

To make matters more grim, cross-site scripting is just one type of attack. If you are passing text to your server-side back-end for insertion in a database, you must escape the input to avoid script injection attacks, which, in some circumstances, can let attackers execute arbitrary queries or commands. A complete discussion of securing gadgets is outside the scope of this article, but there are many web resources available which cover these topics in-depth, and they should be considered a required read before deploying your application on orkut.

Publishing the application

When your application is fully tested and ready to be published, use the application submission form to submit the URL of the application specification (gadget XML). Your application must be reviewed before it can be listed in orkut's directory, and you should receive an email within several days with your application's status. If your app is approved, it will be added to the directory and listed shortly thereafter. Otherwise, you'll receive feedback on why it was rejected and suggested changes to make.

Modifying a published application

After your app has been published to the directory, you can make updates by editing the application specification and saving it in place (with the same name and at the same location). You do not need to resubmit your application. You may not see your updates immediately because orkut caches your gadget XML. Once this cache expires (based on the Cache-Control and Expires response headers returned by your host), your changes will be visible to all users who have added the app.

It is possible to manually flush the cache using the OpenSocial Dev App covered above. Just open OSDA's canvas view, click on the "Tools" tab, enter the URL of your specification, and click "Refresh cache" as shown below:

More resources

Now that you're familiar with the basic orkut application lifecycle, check out the following articles and guides which walk you through the process of building a real-world application and discuss optimization strategies that you can utilize in order to make your own applications run as efficiently as possible in orkut. HAPPY APPING!

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.