Building social applications with OpenSocial 0.9 JavaScript APIs


The majority of OpenSocial networks implement (or support) version 0.8 or version 0.9 of the specification. While version 1.0 is the upcoming release, version 0.9 will be the main focus of this post as it is the current standard employed by the Yahoo! Application Platform. We will specifically explore the lightweight JavaScript APIs introduced in OpenSocial 0.9, as well as some of the helper methods available.


What Is OpenSocial

OpenSocial is a set of common APIs for working with data from various social networks. The concept behind OpenSocial is to develop once and distribute broadly, meaning that if the OpenSocial specification is used to build out an application, it can be easily and quickly ported to another platform. This concept is important, because when building a social application, you want to reach the largest audience you can. Portability, along with the vast amount of user data that can be pulled, makes OpenSocial an ideal tool for quick and scalable development of social applications.

 

Many networks and companies implement the OpenSocial specification, such as the Yahoo! Application Platform, MySpace, Orkut, and a large number of others. The OpenSocial site offers a full list of networks implementing the OpenSocial specification.


A Few Core Concepts

Before diving further into making OpenSocial JavaScript requests, there are a few core concepts of what we will cover in this spec as well as request methods that should be reviewed.

 

Lightweight JavaScript APIs

With the implementation of OpenSocial 0.9 comes a new set of lightweight JavaScript APIs to help developers by providing simple JSON input/output through simple methods when making data requests to the REST APIs. This new feature significantly reduces the amount of code required to make requests and should reduce ramp-up time for developers. These APIs use a new namespace (osapi) so that the requests will be nicely sandboxed from your older code base.

 

Batching Requests

One of the important aspects to be aware of when making OpenSocial JavaScript requests is request batching. Instead of having to make multiple requests to capture profile information, connection details or updates, a user can tie all of those data fetches into a single AJAX request instead of three. This brings quite a performance boost to your application, for obvious reasons.

 

Additionally, batching requests will ensure that all data is obtained from the request prior to the callback being executed. In Listing 1, we set up a request to capture profile details for the owner of the application (labeled as ownerData) and then another request for the friends of the owner (labeled as ownerFriends).

var batch = osapi.newBatch().
    add("ownerData", osapi.people.getOwner({fields: ['name']})).
    add('ownerFriends', osapi.people.get({userId: '@owner', groupId: '@friends'}));

batch.execute(function(result){
    var ownerName = result.ownerData.name.formatted;
});

Listing 1 – Batching Data Requests

 

We’ll explore these requests in more detail in the following sections.


Capture User Profile Data to Personalize Your Application

Due to the rise of many social networks over the years, one realization about users has become blatantly obvious: the vast majority of people want everyone to know as much about them as they can cram onto their profile. While this is a concern from a privacy and “good sense” perspective, this wealth of knowledge provides the application developer with an extremely large warehouse of data that can be used to personalize applications.

 

This can be anything, from demographically targeting ads, all the way to gender personalization. With all of this information readily available for use, the benefit here is quite clear.

 

Let’s look at this from another angle. One thing that many users have become intolerant of is having to build multiple profiles for every application or social network they use. If you’re using an application on a popular social network like Facebook or MySpace, why would you want to have to type out the exact same information in that application that you’ve already spent a painstaking amount of time entering into your profile? The simple fact is you don’t have to — and that’s why capturing and using the profile information of a user to pre-customize their experience is vital.

 

This task can be accomplished quickly and easily using the getViewer or getOwner methods within OpenSocial (Listing 2).

//opensocial person data request
osapi.people.getViewer({fields: ['name']}).execute(function(result){
    if (!result.error){
        var name = result.name.formatted;
    }
});

Listing 2 – Capturing User Profile Information

 

To create a request to get details about a person, you need to take a few steps (all chained nicely together though). Breaking down the request into its individual parts, we have three distinct sections.

  • osapi.people.getViewer(…) is the method which defines that we want to capture profile information from the viewer. The parameter that we pass into this method is a JSON structure, which defines the fields that we want to return from the profile. In this case, we want to capture the name of the viewer.
  • execute(…) will initialize the viewer data request.
  • the callback, listed as the parameter in the execute function, will be the callback that is hit when the viewer data request completes and will contain the person object returned (result parameter). From this result we can use standard dot notation to get at the data we requested.

 

Every network implements its own list of supported OpenSocial.Person fields, depending on what’s included in their profile system. The full list of OpenSocial 0.9 Person fields is described in the wiki.


Utilize User Connections to Build Your Social Graph

The connections a user creates generally builds out a social graph, which can be used to increase your user base and drive up activity. Connections are a two-way reciprocated link between two users, and when present, means that you are usually able to leverage off profile details of a wider range of people. These concepts work equally well in a follower model such as Twitter; you’re looking for a means of using a physical connection (like a friendship or work tie) to your advantage.

 

Many of us who are present on any of the popular social networks have either participated in (or at least received) annoying status updates from one of the virally popular games such as Mafia Wars, Vampires, or Zombies. Peering into the innards of Mafia Wars, I can see that one of the reasons it has become so virally successful is because of the vast relevant social graph that they’ve built up in their game.

 

The important word here is relevant — something that many networks struggle with at times. Most users of a social platform add many more people other than friends and family — coworkers, the postman, the cousin of the neighbor you say hi to while getting the paper in the morning — and these connections aren’t really what we can call relevant to the user.

 

This means that, for the social container itself, the link between non-relevant users has far less value than one where the users know each other or interact with each other on a regular basis. This same concept is true for your application: When you build the links between the people who use your application, they need to have a way to interact with each other on a regular basis to be of any use to you.

 

Mafia Wars does this by adding your application connections into your “mob,” effectively making them interact with each other in some form whenever they play the game.

 

Much like our previous person request, we can make a simple get request to capture owner friends (Listing 3).

//get owner friends
osapi.people.get({userId: '@owner', groupId: '@friends', count: 10}).execute(function(result){
    if (!result.error){
        var friends = result.list;
        var html = '';
        for (var i = 0; i < friends.length; i++){
            html += friends[i].name.formatted + '';
        }
    }
});

Listing 3 – Capturing User Connections

 

Again, this request can be split up into three individual sections:

  • osapi.people.get(…) is a standard get method that can be used to capture any people data. In this case, we denote within the JSON passed into this function that we would like to capture information from the “owner” of the application where the groupId is “friends”. These are simple strings defining that we would like to capture the owner’s friends.
  • execute(…) will initialize the owner friend data request.
  • the callback, listed as the parameter in the execute function, will be the callback that is hit when the owner friend data request completes and contains all friend person objects for the owner (friends parameter). From this result, we can get the number of friends returned using .totalResults or loop through the list of person objects for each friend.


Promote and Customize Your Application Using Updates

A powerful tool for social application developers is the ability to send updates to the stream of a user. The update stream is the central news area for the user and their connections.

 

Getting Updates

Earlier I mentioned that users will go to great lengths to add in every detail about themselves and their lives into their profiles. What I didn’t mention is that those details might not always be fully accurate, or they may be out of date.

 

The defining truth in any social network is the update stream of the user. When the user sends messages, who they’re contacting, what they’re doing, and what applications they’re using will all become accessible through this stream. This type of request will follow the same syntax as our previous requests but will use the activities API rather than the person API (Listing 4).

//capture viewer activities
osapi.activities.get({userId: '@viewer', count: 20}).execute(function(result){
    if (!result.error){
        var activities = result.list;
        var html = '';

        for (var i = 0; i < activities.length; i++){
            html += 'Activity Title: ' + activities[i].title +
                    'Activity URL: ' + activities[i].url;
        }
    }
});

Listing 4 – Capturing User Activities

 

Within this request we are making a call to osapi.activities.get(...), denoting that we wish to return someone’s activity stream. The JSON object provided as the parameter to this request denotes that we want the activities for a userid that matches that of the current application viewer and we would also like to return only 20 activities.

 

Once this request completes, we can parse through each of the activities and use them however we’d like.

 

For a full list of activity fields defined within the spec, see Opensocial.Activity.

 

Setting Updates

One of the best methods that a developer can use to encourage user growth in an application is to promote the application through the user update stream. Sadly, networks are generally overly saturated with applications, and they sometimes place applications in undesirable locations.

 

The update stream is one of the few prime-time outlets to users that developers still have access to. When a developer taps into this by setting updates that draw in the users attention, they generally see a larger number of users coming to their application. When implementing this in your code, the syntax will look familiar to our previous GET request (Listing 5).

//insert new activity for the current viewer
osapi.activities.create({
    userId: '@viewer',
    activity: {
        title: 'This is my activity',
        url: 'http://www.yahoo.com/'
    }
}).execute();

Listing 5 – Setting User Updates Setup

 

To accomplish the update insert, we make a request to osapi.activities.create(...). As with our other requests, we will pass in a JSON payload defining what we’d like to insert. In the preceding example, we set the userId to viewer to denote that we would like to insert the update for the current application user. Within the activity definition, we create an object which contains the content of the activity. Here we define the title and URL that the title should link to.

 

There are numerous additional fields available for developers to use in order to further customize the update.


The Gadgets API Tool Set

The gadgets API specification within OpenSocial contains a number of helpful utilities to make your development life easier. When building out applications in a container that has heavy restrictions on front-end code, these helper functions will be a lifesaver. Even if the restrictions aren’t there, not having to build out this functionality yourself makes development quicker — no need to reinvent the wheel.

 

AJAX Requests with gadgets.io

One of the helpful utilities under gadgets.io is the makeRequest function for making AJAX requests. When creating social applications, it’s important that the user experience should be as seamless as possible. In this regard, running data requests in the background is an important implementation tactic.

var params = {};
var url = 'http://www.mysite.com/myfile.php';
var callback = callbackFunc;
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.
io.ContentType.TEXT;
params[gadgets.io.RequestParameters.METHOD] = gadgets.
io.MethodType.GET;
gadgets.io.makeRequest(url, callback, params);
function callbackFunc(response){
   if (response.text){
      //use data returned from server
   }
}

Listing 6 – Making AJAX Requests

 

In the makeRequest method, we define our parameter list to be the type and method of data request that we’re making. We set the content type as “TEXT” and the method as “GET”. The makeRequest call takes in three parameters; the URL to be called, the callback function to call once completed, and the optional parameter list defining what the content / method types are. When the callback is instantiated, we can pull our response object from response.text.

 

The complete gadgets.io spec can be found on OpenSocial gadgets.io.

 

Localization Support with gadgets.Prefs

Another helpful utility within the gadgets spec provides the ability to pull out user localization information for your application using gadgets.Prefs. When building applications, chances are that your app will be seen throughout numerous countries, with numerous languages. Being aware of these details will allow you to better gear your application towards the needs of the viewer.

 

Using gadgets.Prefs, we can pull out the country and language of preference for the user. These two values will go a long way towards customizing your application for your user base (Listing 7).

var prefs = new gadgets.Prefs();
var country = prefs.getCountry();
var language = prefs.getLang();

Listing 7 – Capturing User Localization Information

 

The complete gadgets.Prefs spec can be found on OpenSocial gadgets.Prefs.


Summary

OpenSocial seeks to bridge a large gap, providing common methods for accessing social data on a wide range of networks, allowing developers to build once and distribute broadly. This concept allows a developer to construct an application and deploy it across many supporting networks, providing maximum impact and the most views for a minimal effort.

 

- Jonathan LeBlanc

  • Share/Bookmark


Pushing an Application Update with OpenSocial 0.8


Within the OpenSocial 0.8 specification is a definition available for pushing out a notification to the user stream automatically from an application.

 

What are updates and why should you care?

 

The update (or status) stream of a user is a listing of what they are currently doing or what they have posted for their friends and connections to see. What’s important to note here is that using this stream can drive quite a bit of traffic to your application if you use it. Personally, I never look through the application gallery of a platform like the Yahoo! Application Platform or MySpace. Most of the time I will only ever add applications when I see an app notification through one of the updates produced by one of my friends.

 

I mean – sometimes I just need to find out what Disney princess I am right?

 

In any event, using this update stream is going to push users to your application. This is one of the many tools that we can use to drive more traffic to your application.

 

How does this work?

 

Using standard JavaScript, let’s take a look at the steps that we will need to implement to insert an update. I would suggest that this JavaScript be inserted in a function that is driven by a user event, such as a click action.

var params = {}, activity;
params[opensocial.Activity.Field.TITLE] = "title";
params[opensocial.Activity.Field.URL] = "http://www.myserver.com/index.php";
params[opensocial.Activity.Field.BODY] = "body text";
activity = opensocial.newActivity(params);

First we need to set up the parameters that will define what will be pushed out in this update. In the above example, we assign a title string, the URL that the title string will be linked to and the body (content) of the update. There are a whole series of options available within an activity request that can be added to the above. These are listed on the OpenSocial documentation here: http://wiki.opensocial.org/index.php?title=Opensocial.Activity_%28v0.8%29#Fields. We then call the newActivity() method to define the activity that we want to build. The parameter that needs to be present in the method is the parameter list that we defined right before that request.

opensocial.requestCreateActivity(
	activity,
	opensocial.CreateActivityPriority.LOW,
	callback);

We then call requestCreateActivity to actually send the request and push the update out. There are three parameters present in this method:

  1. activity – The activity we defined in the first code sample
  2. priority – This is the priority that the update should use to send out. This can either be opensocial.CreateActivityPriority.HIGH (or the string ‘HIGH’) or opensocial.CreateActivityPriority.LOW (or the string ‘LOW’). The difference is in whether the user has granted your application permission to push updates out to their stream. If you define a HIGH priority and the user has not granted your application permission to push out updates, the application will attempt to load an authentication flow to allow the user to allow the update out. If you set a LOW priority, if the user has not granted your app permission, the update will be ignored and no authentication flow will be presented.
  3. callback – The function that will be executed when the update is inserted

With a few lines of code, you now have an application that can draw in more users using the update stream.

 

- Jonathan LeBlanc

  • Share/Bookmark


Fetching Viewer Social Data with OpenSocial 0.8


Profile information within a social network (e.g. MySpace, Orkut, YAP, Hi5) is something that users take painstaking amounts of time to input so that they can tell their friends and the world who they are. If a developer is building an application on one of these same platforms, that same profile detail is a gold mine of information that can be obtained to customize and personalize an application. Why would you ever ask a user to input their name, interests or gender if they already have this information freely available on their profile? Having the user re-enter this information is just asking for the user to drop off of your application. Using the knowledge wealth will both decrease user drop rates and make your applicatio more appealing to the masses.

 

Within a container that supports the OpenSocial 0.8 JavaScript specification, fetching the profile information of a person can be accomplished and customized with several steps.


The first thing we do is call newDataRequest to set up a new information request object within OpenSocial. Once this has been done we create our parameter list which will define what type of information we want to access within the information request. In our case here, we specify that we would like to capture “PeopleRequestFields”, namely a user’s “PROFILE_DETAILS”, and of those profile details we want to return the name and the thumbnail_url to their profile image. We can add in a whole series of profile information here, depending on what the container supports within the profile information specification. We then add to our request a newFetchPersonRequest and specify that we want the “VIEWER” profile (this could also be OWNER) and then input the params we set up to define what data we want from the viewer. Then we just name that request as “viewer_profile” so that we can access it later and send the request. The request function takes the name of the callback function as a parameter.


When the request completes and gets to our callback function, our data is returned to us with the profile information of the viewer presuming that everything completed correctly. In the callback we can call get on the returned data, inputting the name of the request, and then call getData on that. If we want a finer granularity of control over the data that is returned, we can then call the getField function with the information that we want (in this case the viewer name).

 

That’s all there is to it – with a few calls you now have all the information you need to personalize your application to every users that uses it.

 

Jonathan LeBlanc

  • Share/Bookmark


Using yml:include to create an automatically updating YAP small view


One of the biggest issues that many people have with the small view of a YAP open application is that the view is cached on Yahoo! servers and can only be updated with manual intervention from either the application owner or the application user. In the past, application owners had a few options for updating this small view. The most common were:

  • When the user visited the canvas view of the application the owner would have a setSmallView call set up to refresh the small view of the user (process described here: http://www.nakedtechnologist.com/?p=181. The problem with this is that it requires the user to go to the canvas view. Many users would just view their small view on My Yahoo! so the content of the app was either generic or stale.
  • The owner would set up a cron job to update the small view of their app users on regular intervals (process described here: http://www.nakedtechnologist.com/?p=220). The issue with this one is that the content will only be updated at regular times and will be stale for a user in between those times.
  • The owner would implement a yml:a tag to call a server-side script to update the small view of the user when the user clicked on the link (process described here: http://www.nakedtechnologist.com/?p=204). This relied on user interaction to function and in many cases links on the small view would need to be added just for this requirement.

Many developers, myself included, opted for a mix of all of the options above. This worked fairly well to mimic a dynamic small view, but having three solutions for a single problem was unwieldy.

 

Enter the yml:include tag
The purpose of this tag is to dynamically load content onto a page. This tag can be used in both the canvas and small views, but the real benefit to its use is the small view.

I would recommend looking over the documentation for yml:include as there is quite a lot of detail that a developer would find useful: http://developer.yahoo.com/yap/guide/include.html

The features that are most important to note here are:

  • insert – if a node id is specified in the insert parameter, the content of the include request will load into that node.
  • delay – the amount of time to delay the loading of the content (in milliseconds)

These parameters will help in setting up a dynamic small view for the user. Using these, I’ll go through two examples on how to use yml:include. The first will cover a simple single load case and the second will display how to build a continually updating small view set to update on a specific delay.

 

Simple Use Case – Single Dynamic Load
This first example will display a simple use case for yml:include. We will use the setSmallView call to insert an include tag into the small view of the current user.

 

The PHP layer to this is just to insert that yml:include tag the first time, and is only needed once. This will replace the cached small view of the user with the include tag. For my applications, I ran this in a cron job to update the small view for all users at once. Doing so replaced my need to constantly update the small view of my users on a daily basis.

 

The full details for setting up this example can be found here: http://www.nakedtechnologist.com/?p=181. This piece is the PHP layer that just sets the small view of the user with a yml:include tag and the div node that the content will be inserted into.

$html = '<yml:include params="training_sdk.php" insert="loadHere"></yml:include><div id="loadHere">REPLACE</div>';
if (!$yahoo_user->setSmallView($html)){
	echo "NO SMALL VIEW";
}

When the dropzone (my.yahoo) and application load, an AJAX GET request will be made to training_sdk.php. This file will return the content of the small view and then be inserted into the div with the id of “loadHere” (as denoted by the insert parameter). This allows us to generate whatever content we want within training_sdk.php and the small view will reflect that change as soon as the user loads the application.

 

Extended Use Case – Updating the Small View Every x seconds on a delay
Now we’re going to look into an extended use case for yml:include. This version will extend upon the basic example to refresh the small view automatically every 5000 milliseconds (every 5 seconds). We will start off with the same code that we insert into the small view to include the yml:include tag. From that point we take a look at the file that we capture the content of the small view from, training_sdk.php.

<yml:include params="training_sdk.php" insert="loadHere" delay="5000"></yml:include>
<?= time(); ?>

What we’re doing here is inserting another yml:include tag onto the page. Just like the first tag we are calling the same file, training_sdk.php, and inserting it into the same DOM node. The difference here is that we are delaying the load of this request by 5 seconds with the delay parameter. Following that all we do is display the current UNIX timestamp. What’s going to happen when this content is inserted into the small view is that the new yml:include will count down 5 seconds and then make an AJAX call to the same file and insert another UNIX timestamp and yml:include tag (which is delayed 5 seconds). So there we have it, we now have a small view that will update itself automatically every 5 seconds.

 

There’s only one catch here. There is a limit on the number of times you can call yml:include within a span of time. If too many consecutive yml:include calls are made, you will see the following error message:

 

error-243: maxIncludes limit hit for appid XXX

 

If you see this message, you have made too many yml:include calls within the particular dropzone. Currently the front page will allow unlimited yml:include calls and http://my.yahoo.com will allow 10 calls per session. My Yahoo! is looking into this at the moment so keep checking back to see if there have been changes with this tag. If you see this error, increase the delay on your yml:includes and that should solve the issue.

 

- Jonathan LeBlanc

  • Share/Bookmark


YAP: Capturing Node X/Y Coordinates and Adding Mouse Events Using Caja Safe JavaScript


One of the requests that has come up while working with JavaScript on the Yahoo! Application Platform is: How do you assign an event to a DOM node which then captures the X/Y coordinates of that node under caja?

 

I put together a sample that will display how to assign a mouseover and mouseout event to a DOM node which will call functions to display a tool tip to the immediate right of the node. To do this we need to capture the x/y coordinates and width of that node to calculate the positioning offset. First we create the CSS and HTML for the nodes that will act as our event and tool tip containers.

 

<style type="text/css">
div#tooltip{ position:absolute; top:0; left:0; width:100px;
    height:40px; display:none; }
div#padNode{ position:relative; margin:50px; }
div#myContainer{ border: 1px solid #000000; padding: 10px;
    position: absolute; top: 50px; left: 50px;
    width: 200px; }
</style>

<div id="tooltip"></div>
<div id="padNode">
    <div id="myContainer">Container</div>
</div>

There are a few items to note about the above HTML:

  1. The tooltip container is an absolute positioned node that starts out hidden. The tooltip is placed at a root location so that the offset can be properly applied. We don’t want to embed this node within a positioned / floated container because that will affect our positioning offset location.
  2. The “myContainer” div is placed within a relative container with a margin – this is done to simulate embedded positioned nodes on a page.

Now, let’s look at the JavaScript:

<script type="text/javascript">
//assign mouse over handler to container
var myDiv = document.getElementById('myContainer');
if (myDiv.addEventListener) {
    myDiv.addEventListener('mouseover', overCallback, false);
    myDiv.addEventListener('mouseout', outCallback, false);
} else if (myDiv.attachEvent) {
    myDiv.attachEvent('mouseover', overCallback);
    myDiv.attachEvent('mouseout', outCallback);
}

//callback for mouseover - sender is the triggering event
function overCallback(sender){
    var target;

    //capture the object that the mouse over event was from
    if (window.event) {
        target = window.event.srcElement;
    } else if (sender) {
        target = sender.target;
    } else { return; }

    //get the top and left x / y coords of the target
    var nodeTop = target.offsetTop;
    var nodeLeft = target.offsetLeft;

    //if the node is within a parent container, loop through each parent
    //and add their height / width offsets to the top / left
    var nodeParent = target.offsetParent;
    if (nodeParent != target) {
        while (nodeParent) {
            nodeLeft += nodeParent.offsetLeft;
            nodeTop += nodeParent.offsetTop;
            nodeParent = nodeParent.offsetParent;
        }
    }

    //set tooltip data and display
    var tooltip = document.getElementById('tooltip');
    tooltip.innerHTML = "This is a tooltip";
    tooltip.style.display = "block";

    //set the tooltip top positioning to the same as the target node
    tooltip.style.top = nodeTop + "px";

    //set the tooltip left positioning to the end of the target node
    //(left location + width of the container)
    tooltip.style.left = (nodeLeft + target.offsetWidth) + "px";
}

//callback for mouseout
function outCallback(sender){
    //hide the tooltip
    document.getElementById('tooltip').style.display = "none";
}
</script>

Going from top to bottom, here’s what everything does:

 

Attaching Events
The first bit of code will check if addEventListener or attachEvent are available methods (basically checking if you are using IE or every other browser known to man). This block will then set up two events: The mouseover event on the “myContainer” node will call the function “overCallback” and the mouseout event on the node will call the function “outCallback”.

 

The MouseOver callback
In the function “overCallback” we have a few things going on. The function takes in one parameter, the event that was enacted on the node (so our mouseover event). Using this event we determine the node that the event was performed on. This will be valuable if you have multiple nodes all calling the same mouseover function. Next we capture the x/y (or top/left) offsets of the node from its current container (so the number of pixels from the 0,0 location of the “padNode” div.

 

But wait…the “padNode” div has a 50px margin from the 0,0 location of the overall container…so won’t the offsets be off by 50px? Yes, yes they will.

 

This leads us to our next block of code. While there is still a parent node to calculate we add the parent to child offset to our overall offset values. Basically what we’re doing here is looping through every container that the “myContainer” div is located within to get the “true offset” from the 0,0 position of the container itself.

 

Finally, we capture the tooltip node, set its content and display it. We then set the “top” pixel CSS offset of the tooltip to our calculated top offset. After that we set the “left” pixel CSS offset of the tooltip to our calculated left offset plus the width of the “myContainer” node so that it will display to the immediate right.

 

The MouseOut callback
There isn’t much to the mouseout callback…all we have to do is hide the tooltip container.

 

There you have it…we attach events to a node which then capture the true x/y position of that node to display a tool tip.

 

– Jonathan LeBlanc

  • Share/Bookmark


Automatically Updating the Small View for All Users in a YAP Application


In my last post I provided code to update the small view for an application on the Yahoo! Application Platform. The only current dropzone for these applications is on http://my.yahoo.com. In this tutorial I want to provide a method for developers to automatically update the small view for every user of their application without their interaction. With this script in place, you can run it within a cron job nightly so you can let technology do all the work for you.

 

Here’s a little overview of the application that I tied this functionality to (to see the application in action go to: http://apps.yahoo.com/-Oob7ZC78). I built out a weather application that allows users to set their city and get a simple weather forecast. On the small view I display a 4 day forecast and the current weather for the day. Since most users will only set their city on an infrequent basis and only interact with the small view regularly, I needed a way to update that cached small view on a daily basis. This would normally be done when a user goes to the large view of the application, but that was not acceptable in this case.

 

I have a basic mysql table that contains the GUID of the user to identify them, as well as their city search criteria to rebuild the user views with their selected city criteria. The basic table looks like this:

Table Structure

 

Now let’s take a look over the code that updates the small views based on the table data above. Here are the steps I’m taking:

  • Include the PHP SDK (Yahoo.inc), define your keys and application id, then instantiate a 2-legged OAuth session using new YahooApplication(…)
  • Set your database information, initialize the mysql database connection, set the SQL query and make the query using the mysql_query method
  • Set up the while loop to keep capturing the specific row as long as there are still hows to capture
  • Create your small view HTML using the data from the database – store it to a variable ($html)
  • Set the small view for the user GUID using the created small view HTML. Echo out a status message for some detail.

Let’s take a look at the code for this:

 

<?php
require_once('apis/Yahoo.inc');

//session information
define('API_KEY', 'KEY HERE');
define('SHARED_SECRET', 'KEY HERE');
define('APP_ID', 'APP ID HERE');

//instantiate session
$session = new YahooApplication(API_KEY, SHARED_SECRET, APP_ID);

//db information
$dbhostname = 'YOUR INFO';
$dbusername = 'YOUR INFO';
$dbpassword = 'YOUR INFO';
$dbname = 'YOUR INFO';

//for each user in the database, update the small view
mysql_connect($dbhostname, $dbusername, $dbpassword) OR DIE ('Unable to connect to database. Please try again later.');
mysql_select_db($dbname);
$query = "SELECT * FROM user_store";
$result = mysql_query($query);
while (($row = mysql_fetch_assoc($result)) !== false) {
    if ($row['uid'] && $row['search_type'] && $row['search_value']){
        //***********************************
        //Create HTML for the small view here
        //***********************************

        //set the small view of the current user $row['uid']
        //with the small view HTML stored in $html
        if (!$session->setSmallView($row['uid'], $html)){
            echo "NO SESSION SET FOR {$row['uid']}\n";
        } else {
            echo "SESSION SET FOR {$row['uid']}\n <div style='width:300px'>$html</div>";
        }
    }
}
?>

 

Now that we have the script ready, just set up a cron job to run through this script whenever you want to update the user content and that’s it…you now have an automated script that will keep the small view for all of your users up to date.

 

– Jonathan LeBlanc

  • Share/Bookmark


Updating your YAP small view using yml:a


For the second installment in the dynamic small view using yml:a talks, I wanted to take you through another implementation of the yml:a tag created during some head pounding platform work.

 

The Problem
The My Yahoo! small view of an application can’t automatically refresh itself with fresh content. If a user never goes to the large view of your application, they will always see the same stale content in the small view on their My Yahoo! page. If your application relies on fresh content being presented to the user, you need a way of updating the small view content from the small view.

 

The Solution
Basically, what you are trying to do to make this happen is to find a way to make a request to a server side script which will, in turn, instantiate a new Yahoo! session and set the small view content. Again, because of the AJAXian behavior that can be accomplished using the yml:a tag, it’s the perfect candidate for our needs.

 

There are two methods that we’ve used for this task. The first one uses the tab example I spoke about in my last post. Each time a tab (which is a stylized yml:a tag) is clicked, yml:a will call a server side script to rebuild the tab group. While this is occurring, new content can be cached for the user’s small view.

 

The second method, and the one that I’ll go over in depth, is to includ a yml:a link on your small view to “Update Small View”. I like the idea of updating the small view automatically using the tab method, but I also like having this option available to users so that if they see something that is out of the ordinary they can refresh to update their view at will. Let’s look at a code implementation behind this example.

 

First things first, when the large view of your application loads, you’ll need to set the small view. For our needs, all we’re going to insert in the small view (from PHP) is the yml:a link to update:


<?php
require_once('Yahoo.inc');

 

//define constants to store your API Key (Consumer Key) and Shared Secret (Consumer Secret)
define(“API_KEY”,“YOUR KEY”);
define(“SHARED_SECRET”,“YOUR KEY”);

 

//initializes session and current user

 

$yahoo_session = YahooSession::requireSession(API_KEY, SHARED_SECRET);
$yahoo_user = $yahoo_session->getSessionedUser();

 

//set the small view
$uid = $yahoo_user->guid;
$yml = “<div id=’containerHolder’></div><yml:a params=\”updateSmall.php?uid=”.$uid.“\” replace=\”containerHolder\”><span style=\”color:#fff;\”>Click Here to Refresh Your View</span></yml:a>”;

 

$yahoo_user->setSmallView($yml);
?>

 

As we can see, the small view will now have a refresh link on it which in turn calls updateSmall.php through an AJAX request with the user id of the person that clicked on the link, then replaces the DOM element with the id of containerHolder. In updateSmall.php, we’ll capture the user id passed in to us, instantiate a 2-legged OAuth session, then use that id to update the small view cache of the user and finally dump out the new small view content that will be replaced. This would look something like:


<?php
require_once('Yahoo.inc');
define("API_KEY","YOUR KEY");
define("SHARED_SECRET","YOUR KEY");


$uid = $_REQUEST['uid'];


$yahoo_session = new YahooApplication(API_KEY, SHARED_SECRET);
$small_view = ‘Hey, this is my new small view!’;


if (!$yahoo_session->setSmallView($uid, $small_view)){
echo
“ERR: $uid <br />”;
}
echo
$small_view;
?>

 

That’s it, you should now have an easy method for updating the small view for the user using yml:a, as well as updating their small view cache so that they will see the new small view if they refresh the page.

 

– Jonathan LeBlanc

  • Share/Bookmark


Building a dynamic small view on YAP using yml:a


My Yahoo!’s implementation of the Yahoo! Application Platform (YAP) is usually perceived as a mixed bag with the developers and partners we’ve been working with.  On the one hand this is the implementation of the platform on Yahoo! and on the other developers feel restricted because the small view that displays on My Yahoo! only allows HTML, CSS and YML.  Some developers have gone the route of just placing glorified ads or static content on their small views.  Enter the yml:a tag…one of the most powerful tools at a developer’s disposal on the platform.

 

The yml:a tag can be seen as a replacement to the normal HTML anchor tag, but it is much more than that.  Including AJAX functionality with DOM insert / replace techniques, this tag can mimic a dynamic environment to allow developers a greater degree of flexibility.

 

So, I can spout off all of the promotional material I want, but what use is it without examples to back it up.  This code sample will provide all of the functionality of a dynamic tab set within the small view, where the tabs are defined as yml:a tags:


<?php
require_once('Yahoo.inc');

if ($_GET['tabNum']){
    echo 
build_tabs($_GET['tabNum']);

} else {
    
//define application key constants
    
define("API_KEY","YOUR KEY HERE");
    
define("SHARED_SECRET","YOUR KEY HERE");    

    //initialize 2-legged OAuth session
    
$yahooSession = new YahooApplication(API_KEYSHARED_SECRET);
    
    
//set up static html / style block

    $tabNum = ($_GET['tabNum']) ? $_GET['tabNum'] : 1;
    
$tabCode "<style>

                div.pad5{ padding:5px }
                div.tabGroup{ position:relative; border-bottom:0; }
                div.tabGroup div{ background-color:#fff; float:left; width:50px; border:1px solid #795089; 
                                  border-bottom:0; padding:5px 10px; text-align:center; 

                                  font:bold 12px arial,helvetica,sans-serif; cursor:pointer; }
                div.tabGroup div a{ color:#795089; text-decoration:none; }
                div.tabGroup div.selected{ background-color:#795089; }
                div.tabGroup div.selected a{ color:#fff; }

                div.tabGroup div:hover{ background-color:#e2ceea; }
                div.tabGroup div.selected:hover{ background-color:#795089; }
                div.tabContent{ border:1px solid #795089; border-top:10px solid #795089; padding:5px 10px; }

                div.myBox{ width:232px; height:44px; background-image:url(http://l.yimg.com/a/i/ww/beta/y3.gif); }
                </style>"
;
    
$tabCode .= "<div class='pad5'>" build_tabs($tabNum) . '</div>';

    
    
$guid "YOUR GUID";
    if (!
$yahooSession->setSmallView($guid$tabCode)){
        echo 
"UNABLE TO SET SMALL VIEW";

    }
    
    echo 
"LARGE VIEW CONTENT";
}
    
function 
build_tabs($tabNum){
    
$tabCode "<div id='tabContainer'><div class='tabGroup'>";

    
    
//create tab set using the tabNum as the selected tab
    
for ($i 1$i 4$i++){

        $isSelected = ($i === intval($tabNum)) ? 'selected' '';
        
$tabCode .= "<div class=\"$isSelected\"><yml:a params=\"?tabNum=$i\" 

                     replace=\"tabContainer\">Tab $i</yml:a></div>";
    }
    
    
//create tab content
    
$tabContent '';
    switch(
$tabNum){

        case 1$tabContent 'Hi <yml:name uid="viewer" />! This is tab content 1!'; break;

        case 2$tabContent 'Here is my profile badge:<br /><yml:user-badge />'; break;

        case 3$tabContent 'My Pics:<br /><yml:image 
                               src="http://farm1.static.flickr.com/195/449142212_c2e72f83d7.jpg"
                               height="166" width="250" alt="Faceball" />'
; break;

        default: break;
    }
    
    
//build tab content with defined string
    
$tabCode .= "<br style='clear:both'></div><div class='tabContent'>$tabContent</div></div>";

    return $tabCode;
}
?>

Let’s break the code down into the pertinent parts and explain what the heck is going on. This code can be placed as the landing page for your application full view. What will happen is that it will generate a new small view which will display the tabs as yml:a tags. When a tab is clicked, yml:a will send an AJAX request to this same file, but will pass in a numeric query parameter (defined as ‘tabNum’) which will signal which tab has been clicked. The if / else statement at the top will control that flow. If tabNum is not defined, the core container and CSS will be created and the small view will be set using setSmallView. If tabNum is set, just the build_tabs function will be called to recreate the tab container and return it. Essentially, the yml:a tag is just replacing its own container with a new tab view in order to make it look like a new tab has been clicked, without refreshing the page, resetting the css or re-encoding the small view.

 

Let’s look specifically at the yml:a tag within the build_tabs function, where each tab is defined as:
<yml:a params=”?tabNum=$i” replace=”tabContainer”>Tab $i</yml:a>

 

The params attribute specifies that the same file will be called (since no file is defined) and will pass one query parameter, the tab number. The replace attribute specifies the id of the DOM element that will be replaced in the small view once the AJAX request successfully returns. In this case we are replacing the entire tab container, the div with the id of “tabContainer”.

 

That’s all there is to it. That’s one of the many dynamic things you can do with the yml:a tag.

 

Links:
- Tab tutorial on YDN
- yml:a tag description and samples

 

– Jonathan LeBlanc

  • Share/Bookmark