2009
08.06

This is part 3 of my Google Wave developer sandbox review. Part 1 and part 2 were both an introduction to some wave concepts. In this part we’ll be taking a look at the Google Wave Gadget API by writing a simple rating gadget. If you don’t know what gadgets are or what they do, please read my previous post here.

The gadget

The gadget I have written is supposed to be used when you want wave participants to rate something inside your wave (or the wave itself). You simply add the gadget to your wave, set the title and you’re good to go. I also wanted the gadget to have a settings menu that was only visible by the person who added the gadget to the wave.

Rate me gadget

The full source of this gadget is available at http://vbsteven.be/wave/rateme.xml . You can add the gadget to one of your own waves by choosing extensions/add gadget in the debug menu and then pasting the url into the textbox at the bottom of the dialog.

Basic code layout

Google Wave Gadgets are simple xml files with html and javascript code embedded in them.

As you can see here, this is just an xml file with some wave-specific settings at the top like the title and the height of the gadget. Then there is a CDATA block which contains all our code. At the top of this block you’ll find the javascript includes (for JSON) and some javascript functions which define the behaviour of the gadget. At the bottom of the CDATA block you’ll find the HTML code which define the UI elements. When you want to use other external files (CSS for example), I suggest you link to those files at the top of the CDATA block.

Initializing

To initialize our gadget we need to define and register an init() method. This method will be called every time the gadget is loaded (for example when someone opens the wave). This method does not need to be long and is basically just to register the necessary callbackfunctions. In this case just the callback function that will notify our gadget when the shared state has changed is registered. As the shared state may not be fully initialized it is important that you don’t access the state object in this function.

function init() {
	if (wave) {
		wave.setStateCallback(stateUpdated);
	}
}
gadgets.util.registerOnLoadHandler(init);

Manipulating state

It is very important to notice that the state of a gadget is shared among all participants of the wave. When one user performs an action that changes the state, this change will be reflected on every instance of that gadget that is currently open. The state of a gadget is represented by a state object that contains key-value pairs. Values can be retrieved from the state with the get(’key’) function and stored in the state with the submitDelta({’key’ : value, ‘key’ : value}) function.

Currently the state object only works with strings. So it is not possible to store complex objects or arrays into the state. When you do need to store a complex object or an array, you can convert the object to a JSON string for storage and parse that string back to an object after you get it back from the state. The documentation on the get and submitDelta functions wasn’t very clear to me at first, which led to me losing some time trying to figure out why my array of voters wouldn’t show up into the state object (thank you firebug).

You can find some examples of how to manipulate the shared state in my vote() and saveSettings() functions.

		function vote() {
			var total = parseInt(wave.getState().get('total', '0'));
			var count = parseInt(wave.getState().get('count', '0'));
			var voters = JSON.parse(wave.getState().get('voters', '[]'));
 
			total += parseInt(document.getElementById("rating").value)
			count++;
			var rating = total/count;
			rating = rating.toFixed(1);
			voters.push(wave.getViewer().getId());	
 
			var voters_json = JSON.stringify(voters);	
 
			wave.getState().submitDelta({'count': count, 'total': total, 'rating': rating, 'voters': voters_json });
		}

Reacting to a state-change

If you registered your callbacks well, your stateUpdated() function should be called whenever a value changed in the shared state. This is the ideal time to get the updated state and update the UI. Most of the UI-related code should be in this function. Every time this function is called in my rateme gadget, I fetch the latest values from the state and update every UI component.

		function stateUpdated() {
			var p_rating = document.getElementById("p_rating");
 
			if (!wave.getState().get('count')) {
				p_rating.innerHTML = "rating : n/a";
				p_rating.innerHTML += "
votes : 0";
			}
			else {
				p_rating.innerHTML = "rating : " + wave.getState().get('rating');
				p_rating.innerHTML += "
votes : " + wave.getState().get('count');
			}
 
			var title = document.getElementById("h2_title");
			title.innerHTML = wave.getState().get('title', 'Rate me');
 
			if (wave.getViewer() == wave.getHost()) {
				// current user is the user that added the gadget to this wave
				var p = document.getElementById("p_settings");
				p.innerHTML = '<a onclick="showSettings()" href="#">show settings</a>';
			}
		}

Getting user info

Because I wanted to make a difference between the original user who added the gadget to the wave and the other users I had to have access to some user info. Luckily the wave object supplied by the API provides us with the necessary data. The wave.getParticipants() function returns an array of all users in the wave, getViewer returns the current viewer and getHost() returns the user that added the gadget to the wave. As you can see in the above code snippet, a simple equals check is enough to authenticate the user as the host and display the link tag.

Security

Ofcourse the authentication above looks easy. and it is,  but it also is a security issue. When you ask  any security specialist about AJAX apps, there is a very big chance he will say “Never trust the client”. And he’s right, as all javascript code is run inside the browser, any kid with firebug installed can alter that code. If you take a look at the above code sample you’ll see that a simple if (true) is enough to gain access to the settings panel.

In a normal webapp this wouldn’t be such a big deal. Even when the malicious user gained access to the settings by messing with the clientcode, the server would still verify every action and will prevent unauthorised actions. Because our gadget runs inside of google wave and we have no control over the code running on google servers, there is no way to validate the user input on the server and therefore the server will accept this user input.

Because of this issue there is no real safe way to handle authentication so every user inside the wave is able to alter the shared state, making gadgets unsafe for sensitive data.

My opinion

In my opinion the Google Wave gadget api is very easy to work with. As gadgets are written in plain html/javascript, webdevelopers with some experience in js won’t have a problem writing apps for the Google Wave platform. Although I’m still rather new to javascript (only did some basic ajax programming in school), I didn’t have any major problems while writing this little gadget. The api itself is very simple, straightforward and well-documented with very good code-samples.

On the downside, because of the security issue mentioned above I’m afraid gadgets will be limited to simple apps handling unimportant data (games, polls, etc).

Share this article
  • Digg
  • Facebook
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Twitter

3 comments so far

Add Your Comment
  1. It’s true that the state object in wave can’t be trusted, and shouldn’t store sensitive data. However, there’s nothing to say that you can’t drive your gadget from a database on your secure servers and check any state data when stateUpdate is called to make sure everything if fine. In short, make your own DB the trusted source, and treat Wave and its state data as the client that can’t be trusted.

  2. If you are interested in Google Wave Gadgets developed in Java (with some JSNI code), a small tutorial is available at my blog:
    http://dendrytsoft.blogspot.com/2009/10/building-google-wave-gadget-with-gwt.html
    I am waiting for feedback:)

  3. Does anyone know how the chess gadget was built?