Tuesday, September 14, 2010

Messenger Connect – Getting Contacts Three Different Ways

Roadmap
This post is part of a series of related posts on how to use Windows Live Messenger Connect.

1. Using Messenger Connect REST Explorer for exploring Windows Live resources.
2. Using the Messenger Connect JavaScript API for listing your contacts.
3. (this post) Getting contacts in three different ways (API and REST).
4. Messenger Connect - Initializing Controls

Overview

Comparing Three Ways of Getting Contacts
A Page That Gets Contacts Three Different Ways

This post compares side-by-side three methods of getting a resource (in this case we stick with contacts):

1. Using the JavaScript API
- Pros: API does all the hard work for you; no cross browser issues.
- Cons: You have to load a library.

2. Using the REST endpoint directly and getting data with a jQuery.ajax call.
- Pros: Stays all in JavaScript; jQuery makes it easy.
- Cons: Cross-browser issues (to work in IE you must enable cross domain posts, other browsers?).

3. Using the REST endpoint directly and getting data with an HttpWebRequest in the page code-behind.
- Pros: Can use LINQ to process response; no cross-browser issues.
- Cons: You have to have a code-behind, a server-side page.*

* To be fair, approach 1 requires a code file that implements a session handler for unsecured (non HTTPS) scenarios. See the Messenger Connect JavaScript API post for details on that.

Method 1 is probably going to be what you'll use most of the time, but it is interesting to compare the other approaches. To that end, this post shows an ASPX and code-behind page that shows these three approaches for getting the first five contacts in your contact list. It is built on the Windows Live Application template which can be downloaded here and of course requires that you are registered as a Messenger Connect application. Again, all is explained here.

Update 2011-02-13: Here's a zip of the ASPX and code-behind page.

Without further ado, here's the code (a thorough explanation follows):

The Page Markup


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ContactsComparison.aspx.cs"
Inherits="WindowsLiveWebApplicationBeta.ContactsComparison" %>

<%@ Import Namespace="System.Web.Configuration" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wl="http://apis.live.net/js/2010">
<head id="Head1" runat="server">
<title>Get Contacts Comparison</title>
<script type="text/javascript" src="http://js.live.net/4.1/loader.js"></script>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" ></script>
<script type="text/javascript">
var dvContactList;
var dataContext;
var contactsView;
var totalNumContacts;
var displayNumContacts = 5;
var cid;
var accessToken;
var uriContactGet = "http://apis.live.net/V4.1/cid-{0}/Contacts/AllContacts";
jQuery.noConflict();
function appLoaded(applicationLoadCompletedEventArgs) {
}

function SignedOutCallback() {
}
function SignedInCallback(signInCompletedEventArgs) {
if (signInCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
log("Failed to sign in: " + signInCompletedEventArgs.get_resultCode());
return;
}
else {
auth = Microsoft.Live.App.get_auth();
accessToken = auth.get_accessToken();
cid = auth.get_cid();
}
}
// *** Using JavaScript API *** //
function getContactsUsingAPI() {
if (Microsoft.Live.App.get_auth().get_state() === Microsoft.Live.AuthState.authenticated) {
dataContext = Microsoft.Live.App.get_dataContext();
dataContext.loadAll(Microsoft.Live.DataType.contacts, contactsLoaded);
}
else {
log("Failed to get contacts using API. Are you authenticated?");
}
}
function contactsLoaded(dataLoadCompletedEventArgs) {
if (dataLoadCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
alert("Contacts failed to load...!");
return;
}

contactsCollection = dataLoadCompletedEventArgs.get_data();
totalNumContacts = contactsCollection.items.length;
for (var i = 0; i < Math.min(totalNumContacts, displayNumContacts); i++) {
var contact = contactsCollection.getItem(i);
addItemToList("ContactsAPI", contact.get_firstName() + " " + contact.get_lastName());
}

}
// *** Using REST with jQuery *** //
function getContactsUsingJQuery() {
if (Microsoft.Live.App.get_auth().get_state() === Microsoft.Live.AuthState.authenticated) {
try {
jQuery.ajax({
url: uriContactGet.replace('{0}', cid) + "?$top=" + displayNumContacts,
type: "GET",
async: true,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "WRAP access_token=" + accessToken);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Accept", "application/json");
},
success: function (data, status, xhr) {
var contactsCollection = data.Entries;
for (var i = 0; i < Math.min(contactsCollection.length, displayNumContacts); i++) {
if (contactsCollection[i] !== undefined) {
var contact = contactsCollection[i];
addItemToList("ContactsRESTJQuery", contact.FirstName + " " + contact.LastName);
}
}
},
complete: function (xhr, status) {
var response = status "no response text";
log("Ajax request completed with " + response + ".");
},
ajaxError: function (data) {
var response = data "no response text";
log("Failure: " + response);
},
error: function (xhr, status, err) { log('Exception: ' + status + ', status = ' + xhr.status + ', message = ' + xhr.statusText); }
});
}
catch (err) {
log("Could not complete a status request. Is cross domain access enabled? Message = " + err);
}
}
else {
log("Failed to get contacts using jQuery Ajax call. Are you authenticated?");
}
}
function addItemToList(parent, item) {
$get(parent).innerHTML += "<li>" + item + "</li>";
}
function log(msg) {
$get('Output').innerHTML += msg + ";";
}

</script>
<style type="text/css">
div.left { float: left; width: 300px; border: 1px solid blue; min-height: 250px; padding: 5px;}
div.output {clear: both;}
ul { list-style-type: none;}
</style>
</head>
<body>
<wl:app
channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>"
callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?<%=SessionId%>"
client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>"
scope="WL_Contacts.View, WL_Profiles.View"
onload="appLoaded">
</wl:app>
<form id="form1" runat="server">
<div>
<h1>
Getting Contacts - Comparing Three Different Approaches</h1>
<wl:signin onsignin="SignedInCallback" onsignout="SignedOutCallback" signedouttext="Sign In To Authenticate and Begin">
</wl:signin>
<div class="left">
<h4>Using the JavaScript API.</h4> <input type="button" value="Fetch" onclick="getContactsUsingAPI()" />
<ul id="ContactsAPI">
</ul>
</div>
<div class="left">
<h4>Using REST with jQuery Ajax request.</h4> <input type="button" value="Fetch" onclick="getContactsUsingJQuery()" />
<ul id="ContactsRESTJQuery">
</ul>
</div>
<div class="left">
<h4>Using REST with C# HttpWebRequest.</h4> <asp:Button ID="GetContactsButton"
runat="server" Text="Fetch" onclick="GetContactsButton_Click" />
<ul id="ContactsRESTCodeBehind" runat="server">
</ul>
</div>
<div class="output"><span id="Output" runat="server" /></div>
</div>
</form>
</body>
</html>


The Page Code-Behind

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
// Additional using statements.
using System.Net;
using System.Web.Configuration;
using System.IO;
using Microsoft.Live;
using System.Xml.Linq;

namespace WindowsLiveWebApplicationBeta
{
public class AuthStoreProvider : Microsoft.Live.IAuthStoreProvider
{
public void StoreToken(HttpContext context, string userId, IDictionary<string, string> authInfo)
{
// Get and store the AccessToken.
AccessToken = authInfo["AccessToken"];
CID = authInfo["UID"];
}
public static string AccessToken { get; set; }
public static string CID { get; set; }
}
public partial class ContactsComparison : System.Web.UI.Page
{
public string SessionId
{
get
{
SessionIdProvider oauth = new SessionIdProvider();
return "wl_session_id=" + oauth.GetSessionId(HttpContext.Current);
}
}

protected void Page_Load(object sender, EventArgs e)
{

}

string baseApplicationsEndpoint = "http://apis.live.net/V4.0/cid-{0}/Contacts/AllContacts";
string ApplicationsEndpoint = "";
string MaxNumToDisplay = "5";
string br = "<>";

// *** Using REST with HttpWebRequest *** //
protected void GetContactsButton_Click(object sender, EventArgs e)
{
ApplicationsEndpoint = baseApplicationsEndpoint.Replace("{0}", AuthStoreProvider.CID) + "?$top=" + MaxNumToDisplay;
try
{
// Initialize a request and define its characteristics.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(ApplicationsEndpoint);

request.Method = "GET";
request.Accept = "application/atom+xml";
request.ContentType = "application/atom+xml";

// Get the AccessToken stored when the user signed in.
request.Headers.Add("Authorization", "WRAP access_token=" + AuthStoreProvider.AccessToken);

// Get the client Id from the web.config.
request.Headers.Add("X-HTTP-LiveFX-ApplicationId", WebConfigurationManager.AppSettings["wl_wrap_client_id"]);

// Send the request and get the response.
HttpWebResponse response = (HttpWebResponse)request.GetResponse();

Stream responseStream = response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);

string responseText = streamReader.ReadToEnd();

CreateContactList(responseText);

streamReader.Close();
responseStream.Close();
}
catch (WebException Ex) // Catch is reached when the request is not successful.
{
HttpWebResponse response = (HttpWebResponse)Ex.Response;

Stream responseStream = response.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream);

string errorText = streamReader.ReadToEnd();
Output.InnerHtml += errorText;
Output.InnerHtml += br;
Output.InnerHtml += "Access Token = " + AuthStoreProvider.AccessToken + br;
Output.InnerHtml += "CID = " + AuthStoreProvider.CID + br;

streamReader.Close();
response.Close();
}
}
protected void CreateContactList(string input)
{
XNamespace ns = "http://www.w3.org/2005/Atom";
XNamespace nsp3 = "http://schemas.microsoft.com/ado/2007/08/dataservices"; // p3
TextReader tr = new StringReader(input);
XDocument xdoc = XDocument.Load(tr);
var contentQuery = from entry in xdoc.Descendants(ns + "content")
select entry;

foreach (XElement contentElement in contentQuery)
{
string firstName = contentElement.Descendants(nsp3 + "FirstName").FirstOrDefault().Value;
string lastName = contentElement.Descendants(nsp3 + "LastName").FirstOrDefault().Value;
ContactsRESTCodeBehind.InnerHtml += "<li>" + firstName + " " + lastName + "</li>";
}
}
}
}


Web.config key

<add key="wl_wrap_store_provider_type"
value="WindowsLiveWebApplicationBeta.AuthStoreProvider,WindowsLiveWebApplicationBeta"/>


Explanation of the code:

  1. Approach 1 (Javascript API) is all based off the loader (http://js.live.net/4.1/loader.js) which figures out what is needed for your page and enables you to work with a data context, ask for resources through the data context, and then work with the returned resource collection. Behind the scenes it is using the REST endpoints similarly to what is done in Approach 2 and 3.
  2. Approach 2 (REST with jQuery) depends on an endpoint for the resource you are interested in (e.g. http://apis.live.net/V4.1/cid-{0}/Contacts/AllContacts). The {0} value is replaced with the unique ID of the authenticated user Windows Live User. How is this endpoint URI known? You can figure it out from using the Windows Live Rest Explorer or you can look in the documentation, for example, for the AllContacts collection documentation page.
  3. Approach 2 requires the access token to be sent as a header as part of the AJAX request. The access token can be obtained from the Auth class.
  4. Approach 3 (REST in code-behind) also depends on an endpont for the resource. In this example it's defined in the code-behind similarly to Approach 2.
  5. Approach 3 as well depends on putting the access token into the request header. It's a bit more complicated in this case because you need to define a class (AuthStoreProvider in this code exampled) to implement the IAuthStoreProvider interface and get access to the access token. Finally, you need to make sure you hook up your AuthStoreProvider with an app key in the web.config file shown above.

A screenshot of the resulting page is at the beginning of this post. No surprises, each approach returns the same data! For more information on using LINQ with the Atom feed see this post. Finally, if you are using this in a web farm scenario you might consider storing the access token and cid in a cookie instead of using a static as shown here.

Sunday, September 12, 2010

La Sirena

John William Waterhouse - A Mermaid (1901)

La sirena (‘The Siren") is story by Giuseppe Tomasi di Lampedusa (1896 – 1957) written in the late 1950s and first published in the early 1960s. Lampedusa is famous for Il gattopardo (‘The Leopard’) in which Burt Lancaster played the leading role in the movie adaptation. (I always thought he learned Italian for the role, but learned recently it was all voice over, doppiaggio as they say.) We are reading La sirena – in Italian, slowly over several weeks - as part of a conversation course at the Seattle Language Academy (SLA).

In the story, we meet a young Sicilian journalist, Paolo Corbèra, living in Turin just before the Second World War. At the beginning of the story Corbèra has a bit of woman trouble, loses two girlfriends in the same day, and exiles himself to a café that is like a purgatory for him. There he meets the eminent classical Greek scholar, Professor Rosario La Ciura. As their friendship develops we learn more about La Ciura and how the titular sirena figures into the story. Along the way, Corbèra takes a lot of good-natured abuse from the professor, but, nonetheless develops a deep affection and reverence for him.

While the sirena of the story translates better to what we call a mermaid (water-based) rather than siren (land-based), the terms have been confused in some European folklore over time and sirena in this case means a siren of Greek mythology that is a mermaid. In the story we learn that sirens have been seriously misunderstood and they don’t really lure sailors to their deaths.

Here’s a favorite part from the beginning of the story where Corbèra first notices La Ciura in the café and describes him:

Aveva bruttissime mani, nocchierute, rossastre con le unghie tagliate dritte e non sempre pulite, ma una volta che in una delle sue riviste s’imbatté nella fotografia d’una statua greca arcaica, di quelle con gli occhi lontani dal naso e col sorriso ambiguo, mi sorpresi vedendo che i suoi deformi polpastrelli accarezzavano l’immagine con una delicatezza addrittura regale.

Our approximate translation: He had ugly, knotty, reddish hands with fingernails cut straight and not always clean, but once, in one of his magazine he came upon a photo of an ancient greek statue, one of those with the eyes set far apart and with an ambiguous smile, and I was surprised to see his deformed fingertips carress the image in a manner that was quite delicate.

Bing translation (for comparison): “Had bruttissime hands, nocchierute, reddish with fingernails cut straight and not always clean, but once in one of his journals s ' came in the photograph of a statue Greek archaic, those with eyes away from nose and col ambiguous, I surprised smile seeing that his deformed pads they embraced the image with a delicate Regal addrittura”

Google translation (for comparison): “He had very bad hands, helmsman, with reddish nails cut straight and not always clean, but once he came in one of his magazines in the photograph of a statue of ancient Greek, those eyes away from his nose and his ambiguous smile I surprised seeing his deformed fingers caressed the image with a royal delicacy addrittura.”

Friday, September 10, 2010

Mount St. Helens Climb

Mount St Helens - About 1000 feet from the top

We’ll come clean right away. We climbed to the rim of Mount St. Helens and saw nothing. It was the weather: foggy with sleet and rain showers. But, we did make it to the top. It was a bit of a slog and our quadriceps were talking to us for days after. We climbed on September 7th, 2010.

The details:

  • Route: Summer route (aka Monitor Ridge) from Climbers’ Bivouac.
  • Length: 10.5 miles round trip.
  • Elevation Gain: 4,615 feet, starting at 3,750 feet and ending at 8,365 feet.
  • Suggested Season for the Hike: Late spring through fall.
  • Difficulty: Very Difficult.
  • Permit: NW Forest Pass Required for parking and a permit to go above 4,800 feet.
  • Our Duration: Started at 6:55am from Climbers’ Bivouac. Reached the summit at 10:15am. Back down and at our car at 1:15pm (includes a 20-30 minute stop for lunch).
The trail is pretty much like everyone describes. You start in forest and then suddenly you break out into this big boulder area (blocky lava flows) and it gets tough quickly and stays that way until you are about 1,500 feet from the top and then the boulders go away and you are in the sandy/pebbly (volcanic ash) terrain which goes straight up. At that point we were taking baby steps. Every step sunk several inches backwards and it felt like we were going nowhere. On the way down you, every steps goes several inches forward and you gain back what you lost as you semi-slide down this part. On our hike we saw snow (Dryer Glacier to one side and Swift Glacier to the other) but never had to walk on it.

The path is marked in the boulder field with large wooden posts. Use them as guideposts but don’t go directly from post to post – unless you want to. The post-to-post approach will wear you out. When you reach one post look to see where the next one is and also look for the footprints of where people have gone as a guide and you’ll see that there is usually a path to get you there. The wooden posts disappear at the last part of the hike and you are on your own as the trail seems faint. Hint: go up!

Planning:
  • The Mount St. Helens National Volcanic Monument site – it has all the information you need. Really read this page and weigh the risks and warnings.
  • You have to get a permit to climb above 4,800 feet on the volcano which can be picked up at the Lone Fir Resort, 16806 Lewis River Road, Cougar, WA. (The permit includes a pass to park at Climbers’ Bivouac.)
  • You should sign in before and after the hike at the Lone Fir Resort. You can sign in up to a day ahead of time – see the link above for details.
  • You should bring versatile gear and expect all kinds of weather. There is a 10 essentials list here.
  • What we brought: everything on the essentials list except a flashlight (we forgot), emergency shelter, and a hard hat. We were happy to have really good rain gear and rain paints. And to have brought warm gloves.
  • We brought a GPS that was handy for telling us elevation, because most of the time we couldn’t see that far ahead and didn’t know how much farther there was to go.
  • We also brought too much food thinking we could ‘picnic’ up at the top. That was clearly not the case for our hike. There really weren’t many ‘picnic’ spots. So perhaps just bring lots of snacks and save the food for the end. We ended up eating on the side of the trail on the way down at the edge of the forest.
  • Nearby lodging: The Lone Fir Resort. We stayed here.

Midway Up Mount St Helens Looking over Swift Glacier
Midway up and looking off over Swift Glacier

Mount St Helens Just Past the Boulders and into the Ash
Trudging up the volcanic ash

Mount St Helens - In the Boulders
Navigating the boulders

Mount St Helens - at the crater rim
At the crater rim, looking left into the crater

Mount St Helens - The Clouds Lift for a Second
The clouds lift for a bit

Thursday, September 9, 2010

Do Not Clean Fish or Other Game Animals in the Room

Fish and Fowl in The Room
On a recent hike to the crater rim of Mount St. Helens we stayed at the Lone Fir Resort in Cougar, WA. This little reminder on the nightstand “Please do not clean fish or other game animals in the room” released flights of fancy of what must have happened to warrant this reminder. Our room smelled fine.

The Lone Fir Resort is close to the starting point (Climbers’ Bivouac) of the summit hike and is where you pick up your permit to climb as well as to sign in and out for the hike. We are going to rate this lodging two bear claws out of four. They do have a good hamburger at the café.

Monday, August 16, 2010

Messenger Connect JavaScript API – Listing Contacts

Roadmap

This post is part of a series of related posts on how to use Windows Live Messenger Connect.

1. Using Messenger Connect REST Explorer for exploring Windows Live resources.
2. (this post) Using the Messenger Connect JavaScript API for listing your contacts.
3. Getting contacts in three different ways (API and REST).
4. Messenger Connect - Initializing Controls

Overview
In the last post Windows Live REST Explorer – Messenger Connect, we discussed a new offering from Microsoft Windows Live called Windows Live Essentials. A part of this offering includes APIs and tools that developers can use. Good developer overviews are here: Windows Live for Developers blog and your complete resource is dev.live.com.

In this post and the previous we are not interested in the new versions of Messenger or Photo Gallery, rather, we will focus on one of the APIs that allows us to talk to the Windows Live cloud. In the last post, we talked about an browser-based tool (Windows Live REST Explorer - REX) that allows you to see your Windows Live data (after signing in). In this post, we'll take the next step and work with the JavaScript API that abstracts much of repetitive working in dealing with constructing the correct requests to access resources and parsing the responses. Instead, the JavaScript API provides a familiar programmatic construct where we deal with collections, array, and types. To this end, we'll show some simple JavaScript code in a web page displaying your contacts.

The approach we'll take is to first start by downloading the SDK and using it with a web application project we create to get a successful (programmatic) sign-in to Windows Live. Then, we'll create a custom page that displays contacts. You will need to have a Windows Live ID account (___@live.com or ____@hotmail.com for example).

1. Registering An Application
2. Getting Your Computer Ready
3. Creating a New Web Application
4. Download the SDK and Add Files to the Web Application
5. Testing SignIn
6. Creating a Page That Displays Your Contacts

Registering An Application


1. To start go to dev.live.com and download the SDK and register for the Beta. If you can't get Beta access or don't want to, you can still follow along and get a sense of how the JavaScript API works. You can also work with the Interactive SDK without joining the Beta.

2. Much like Facebook Connect, there is a registration process for get a unique ID (called a client ID) that identifies your application and a key (called a secret key) that is used in token exchanges. You will get these when you register and are accepted for the Beta and then follow further instructions on the registration site: manage.dev.live.com. Shown below are two screenshots for the basic part of registering your web application (the type we are interested in here).


Registering a Web Application


Application Keys

You don't have to worry about going to live with your application. Just getting to the point where you application is registered and you can get your client ID and secret key means you are good to go.

Getting Your Computer Ready

1. If you have IIS running and it is using port 8080 you can either shut it off temporarily, OR change the port used in this tutorial to something else when following the instructions below.

2. Make sure your computer can serve up a page on the domain you registered with manage.dev.live.com. You can do this by editing your hosts file and adding an entry for the domain.

Creating a New Web Application

1. Create a new web application. For this tutorial we'll assume it is named WindowsLiveWebApplication.

2. Set the web properties of the web application as shown below, substituting your domain name where appropriate. The web properties tell Visual Studio how to act when serving up the pages on the site.

Windows Live Messenger Connect Web Application Properties
Web Application Web Properties

3. In the global.asax.cs code-behind add a session flag in the Session_Start handler as shown below.


void Session_Start(object sender, EventArgs e)
{
/* This is to ensure you have a unique sessionId for the request in the case where you keep information
* in the session state either in the implementation IAuthStoreProvider or ISessionIdProvider.
*/
Session.Add("wl_Session_started", true);
}


4. Modify the web.config file to have the following keys.


<appSettings>
<!-- Copy Client ID from http://manage.dev.live.com below for your application -->
<add key="wl_wrap_client_id" value="***PUT YOUR CLIENT ID HERE***"/>

<!-- Copy Secret Key from http://manage.dev.live.com below for your application -->
<add key="wl_wrap_client_secret" value="***PUT YOUR SECRET KEY HERE***"/>

<!-- Callback Url for your application, Register this for your app in http://manage.dev.live.com. -->
<add key="wl_wrap_client_callback" value="http://***PUT YOUR DOMAIN NAME HERE***:8080/OAuthWrapCallback.ashx"/>

<!-- Channel Url for your application.-->
<add key="wl_wrap_channel_url" value="http://***PUT YOUR DOMAIN NAME HERE***:8080/channel.htm"/>
</appSettings>


5. Add a callback handler.


<system.web>
<compilation debug="true" targetFramework="4.0" />

<httpHandlers>
<add verb="*" path="OAuthWrapCallback.ashx" type="Microsoft.Live.OAuthWrapHandler, Microsoft.Live.AuthHandler"/>
</httpHandlers>
</system.web>


The plumbing here is to identify the OAuthWrapCallback.ashx page as the handler of the consent process. The page (which you don’t create but is associated with the Microsoft.Live.OAuthWrapHandler.dll) processes tokens when your site talks to the Windows Live consent service. You can create a custom callback page as described here. In that case you don’t need to reference the Microsoft.Live.OAuthWrapHandler.dll which is what you would do for non-ASP.NET sites.

Note that if you were to use IIS 7 you would have to add the handler to the system.webServer, handlers section of the web.config. But in this tutorial we are using Visual Studio to serve the page.

Download the SDK and Add Files to the Web Application

1. Go and get the SDK download. Once you download the SDK you will see that there are two files in it: Windows Live SDK\v4.1\API Toolkits\Microsoft.Live.AuthHandler.dll and Windows Live SDK 4.1\Windows Live SDK\v4.1\Channel\channel.htm.

2. In the web application you created make a reference to the AuthHandler.dll. The reference to the DLL makes the communication between your web application and the Windows Live cloud possible through the callback page.

3. Put a copy of the channel.htm file in the root of the web application you created. The channel.htm file which is used for cross domain communication when Flash isn't installed or a HTML5's cross domain post message is not available - as in older browsers.

4. In the site master page code-behind Site.Master/.cs add a reference to the AuthHandler.dll with a “using Microsoft.Live;” statement and define a property called SessionId that will be use declarative markup. Your Site.Master.cs should look like this:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.Live;

namespace WindowsLiveWebApplication
{
public partial class SiteMaster : System.Web.UI.MasterPage
{
protected string SessionId
{
get
{
SessionIdProvider oauth = new SessionIdProvider();
return "wl_session_id=" + oauth.GetSessionId(HttpContext.Current);
}
}

protected void Page_Load(object sender, EventArgs e)
{

}
}
}


The SessionID property provides a unique identifier that can be used when the callback page is getting consent. Since we are not using HTTPS we need this extra step for security. The SessionID will be used to help formulate the application tag shown below.

5. In the Site.Master page you need to add several things to make the JavaScript API magic happen. Let's look at the declarative markup first. The whole page is be shown after showing the individual pieces.

First, add a loader statement which loads the JavaScript library.


<script type="text/javascript" src="http://js.live.net/4.1/loader.js"></script>


Add the namespace declaration referencing the Windows Live namespace.


<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:wl="http://apis.live.net/js/2010">


Add an application tag that initializes the application as a Messenger Connect-enabled application.


<wl:app
channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>
callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?<%=SessionId%>"
client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>"
scope="WL_Profiles.View, Messenger.SignIn, WL_Contacts.View"
onload="appLoaded">
</wl:app>


The first three attributes of the app tag read values out of the web.config file. The scopes attribute defines resource and access permission. The last attribute, onload, defines a handler to be executed once the application is loaded.

Add an import directive so the WebConfigurationManager class can be used above.


<%@ Import Namespace="System.Web.Configuration" %>


Add a script tag with the following JavaScript defining the appLoaded handler. In our example here it's just defines a shortcut for using the Microsoft.Live namespace and nothing else. But this is where you could put custom code that should run after the application is loaded.


<script type="text/javascript">
function appLoaded() {
Microsoft.Live.Core.Namespace.using("wl:Microsoft.Live");
}
</script>


Finally, add a wl:signin and a templated wl:userinfo control which together make signing in and showing a little information about who is signed in very easy. Also, add the wl:bar control in the footer div.

The Site.Master page so far should look like this:


<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="WindowsLiveWebApplication.SiteMaster" %>

<%@ Import Namespace="System.Web.Configuration" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:wl="http://apis.live.net/js/2010">
<head id="Head1" runat="server">
<script type="text/javascript" src="http://js.live.net/4.1/loader.js"></script>
<title></title>
<link href="~/Styles/Site.css" rel="stylesheet" type="text/css" />
<asp:ContentPlaceHolder ID="HeadContent" runat="server">
</asp:ContentPlaceHolder>
<script type="text/javascript">
function appLoaded() {
Microsoft.Live.Core.Namespace.using("wl:Microsoft.Live");
}
</script>
</head>
<body>
<wl:app
channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>"
callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?<%=SessionId%>"
client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>"
scope="WL_Profiles.View, Messenger.SignIn, WL_Contacts.View"
onload="appLoaded">
</wl:app>

<form id="Form1" runat="server">
<div class="page">
<div class="header">
<div class="title">
<h1>
My Windows Live Web Application
</h1>
</div>
<div class="loginDisplay">
<wl:signin id="mysignin" style='float:right'></wl:signin>
<!-- Display user first name after signed in -->
<wl:userinfo>
<div id="userInfoTemplate" class="sys-template">
<div id='profileElement' class='LiveConnectProfile'>
<div class='LiveConnectProfileTextFrame' style='float:right; margin-top:-2px'>
<span id='profileName' class='LiveConnectProfileName' style='position:relative; color:white' sys:innertext="{binding profile.firstName, convert={{new Function('a'\, 'return a?\'Hello \'+a:\'\'')}} }">
</span>
</div>
</div>
</div>
</wl:userinfo>
</div>
<div class="clear hideSkiplink">
<asp:Menu ID="NavigationMenu" runat="server" CssClass="menu" EnableViewState="false"
IncludeStyleBlock="false" Orientation="Horizontal">
<Items>
<asp:MenuItem NavigateUrl="~/Default.aspx" Text="Home" />
<asp:MenuItem NavigateUrl="~/About.aspx" Text="About" />
</Items>
</asp:Menu>
</div>
</div>
<div class="main">
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
<div class="clear">
</div>
</div>
<div class="footer">
<wl:bar></wl:bar>
</div>
</form>
</body>
</html>


Testing SignIn

To run the application press F5 or CTRL + F5 (no debug). The Visual Studio ASP.NET Development Server is used to serve up the page.

Windows Live Messenger Connect SignIn Page
SignIn Page Before Signing In

Notice the messenger bar widget at the bottom of the page. When you sign in it becomes active and provides useful Messenger features like chatting in the window. When you sign in the page should change UI to reflect your information.

Windows Live Messenger Connect - After Sign In
SignIn Page After Signing In

Creating a Page That Displays Your Contacts

In the previous sections, if all went well, you got a successful sign in to Windows Live. In this section, we show a simple approach for listing contacts of the user that signs in. This approach could be extended easily to include methods for importing and exporting contacts or a dashboard for managing contacts.

What we show here is similar to what is discussed in this video that shows working with contacts. The video walks through a sample project given at the http://code.msdn.microsoft.com/messengerconnect site.

1. Create a new web form (page) called contacts.aspx that derives from the Site master page. We are going to using the Microsoft Ajax preview library (to use the DataView control part of preview releases of Microsoft Ajax Library) and the jQuery library. Add the following to the header content div:


<script type="text/javascript" src="http://ajax.microsoft.com/ajax/beta/0911/start.debug.js"></script>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" ></script>


2. The next step is think about the programming pattern a bit. The Messenger Connect JavaScript API is designed so that you structure your data requests as asynchronous operations in a sort of cascade-fashion. For example, the wl:signin control we put on the page will call a SignedInCallback handler on sign-in which kicks off a cascade of subsequent calls (the full code sample below contains all the code). The cascade of function calls looks like this:


function SignedInCallback() { contactsLoaded();}
function contactsLoaded() { populateTable(); }
function populateTable() { contactProfile(); //populate the table with basic contact info }
function contactProfile() { //get specific info for each contact}


What's tricky about this is getting the right information to the UI at the right time. In the contactProfile function the particular piece of data returns and must be assigned to a named UI element after the table has already been rendered. (There is probably a better way of structuring this code, but on first pass this code seems to be what the API suggests we do.)

3. In this example, we decided to try and stick to using all client script instead of resorting a server-side control. We loaded the Microsoft Ajax library previously to use a DataView control to display data. The markup for the DataView looks like this:


<tbody id="contactList" class="sys-template">
<tr>
<td>{{firstName}} {{lastName}}</td>
<td>{{location}}</td>
<td><div sys:if="{{cid}}">
<img sys:if="{{thumbnailImageLink}}" width="40px" sys:src="{{thumbnailImageLink}}" />
<div sys:if="{{cid}}">says </div>
<span sys:id="{{cid}}" class="emp"></span>
</div>
</td>
</tr>
</tbody>


The items in the {{ }} will be determined in the populateTable function.

4. Contacts (collection) are loaded using a View which helps show only a specified number of items from a collection at any given time. You can then advance or go back through the collection and the JavaScript API will handle to queries to Windows Live to get the data. It's an easy way to move through data.


function contactsLoaded(dataLoadCompletedEventArgs) {
contactsCollection = dataLoadCompletedEventArgs.get_data();
contactsView = contactsCollection.get_view(); // get the view
contactsView.set_pageSize(pageSize); // set the page size
populateTable(contactsView); // pass the view to the function that iterates through and displays contacts
}


5. Finally, when you put it all together and sign in you get something like the screen shot shown below.

Windows Live Messenger Connect Contacts

The full code sample is shown below.


<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Contacts.aspx.cs" Inherits="WindowsLiveWebApplication.Contacts" %>

<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/beta/0911/start.debug.js"></script>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" ></script>
<script type="text/javascript">
var dvContactList;
var dataContext;
var contactsView;
var totalNumContacts;
var pageSize = 5;
jQuery.noConflict();

Microsoft.Live.Core.Loader.onReady(function () {
Sys.require([Sys.components.dataView], function () {
dvContactList = $create(Sys.UI.DataView, {}, {}, {}, $get('contactList'));
});
});

function SignedOutCallback() {
// Clear the DataView
dvContactList.set_data([]);
$get('contactTable').className = 'hidden';
$get('prevButton').className = 'hidden';
$get('nextButton').className = 'hidden';

}
function SignedInCallback(signInCompletedEventArgs) {
if (signInCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
alert("Failed to sign in: " + signInCompletedEventArgs.get_resultCode());
return;
}
dataContext = Microsoft.Live.App.get_dataContext();
dataContext.loadAll(Microsoft.Live.DataType.contacts, contactsLoaded);
}

function contactsLoaded(dataLoadCompletedEventArgs) {
if (dataLoadCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
alert("Contacts failed to load...!");
return;
}

contactsCollection = dataLoadCompletedEventArgs.get_data();
totalNumContacts = contactsCollection.items.length; // not used, but could be
contactsView = contactsCollection.get_view();
contactsView.set_pageSize(pageSize);

setButtons(contactsView);
$get('contactTable').className = 'visible';
$get('prevButton').className = 'visible';
$get('nextButton').className = 'visible';
populateTable(contactsView);
}

function setButtons(contactsView) {
contactsView.hasNextPage(function (hasNext) {
if (!hasNext) {
$get('nextButton').disabled = true;
}
else {
$get('nextButton').disabled = false;
}
});
if (!contactsView.hasPreviousPage()) {
$get('prevButton').disabled = true;
}
else {
$get('prevButton').disabled = false;
}
}

function populateTable(contactsView) {
var contactsJSON = [];
for (var i = 0; i < contactsView.get_data().length; i++) {
var contact = contactsView.get_data()[i];
var windowsLiveUser = (contact.get_isFriend() === undefined) ? false : true;
var cid = (contact.get_cid() === undefined) ? null : contact.get_cid();
var thumbnailImageLink = (contact.get_thumbnailImageLink() === undefined) ? null : contact.get_thumbnailImageLink();
contactsJSON.push({ firstName: contact.get_firstName(),
lastName: contact.get_lastName(),
location: (((contact.get_locations()[0]) === undefined) ? "" : (contact.get_locations()[0]).get_city()),
windowsLiveUser: windowsLiveUser,
cid: cid,
thumbnailImageLink: thumbnailImageLink
});
if (windowsLiveUser) {
contact.loadProfiles(contactProfile);
}

}
dvContactList.set_data(contactsJSON);
}

function contactProfile(dataLoadCompletedEventArgs) {
if (dataLoadCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
alert("Profile for contact failed to load...!");
return;
}
var profileCollection = dataLoadCompletedEventArgs.get_data();
var cid = profileCollection.get_profile().get_cid()
var statusMessageLink = profileCollection.items[0].get_statusMessageLink();
var statusText = "";
if (statusMessageLink !== undefined) {
var statusCollection = new Microsoft.Live.Services.StatusCollection(statusMessageLink);
statusCollection.load(function (dataLoadCompletedEventArgs) {
if (dataLoadCompletedEventArgs.get_resultCode() !== Microsoft.Live.AsyncResultCode.success) {
log("Couldn't load status.");
return;
}
else {
var statusCollection = dataLoadCompletedEventArgs.get_data();
var statusMessage = statusCollection.get_statusMessage();
if (statusMessage) {
statusText = statusMessage.get_statusText();
jQuery('#' + cid.toLowerCase()).text(statusText);
}
}
});
}
}

function getPage(direction) {
switch (direction) {
case 'next':
contactsView.nextPage(function (evtArgs) {
if (evtArgs.get_resultCode() === Microsoft.Live.AsyncResultCode.success) {
populateTable(contactsView);
}
});
break;
case 'prev':
if (contactsView.hasPreviousPage()) {
contactsView.previousPage();
populateTable(contactsView);
}
break;
}
setButtons(contactsView);
}

</script>
<style type="text/css">
table thead tr td
{
font-weight: bold;
}
table tr td { text-align: center; width: 250px; height: 40px;}
table.hidden, input.hidden
{
display: none;
}
table.visible, input.visible
{
display:inline;
}
span.emp { font-style: italic}
</style>

</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h1>
Contacts DataView</h1>
<wl:signin onsignin="SignedInCallback" onsignout="SignedOutCallback" signedouttext="Sign In To View Contacts">
</wl:signin>
<input id="prevButton" type="button" onclick="getPage('prev')" value="Prev Page" class="hidden" />
<input id="nextButton" type="button" onclick="getPage('next')" value="Next Page" class="hidden"/>


<table id="contactTable" class="hidden">
<thead>
<tr>
<td>First/Last Name</td>
<td>City</td>
<td>Windows Live User?</td>
</tr>
</thead>
<tbody id="contactList" class="sys-template">
<tr>
<td>{{firstName}} {{lastName}}</td>
<td>{{location}}</td>
<td><div sys:if="{{cid}}">
<img sys:if="{{thumbnailImageLink}}" width="40px" sys:src="{{thumbnailImageLink}}" />
<div sys:if="{{cid}}">says </div>
<span sys:id="{{cid}}" class="emp"></span>
</div>
</td>
</tr>
</tbody>
</table>

</asp:Content>

Sunday, August 15, 2010

The Shape of Water (La forma dell’Aqua)

The Shape of Water - Andrea Camilleri
The Shape of Water (2002) is a detective story (il giallo, romanzi polizieschi) in the Inspector Montalbano series by Andrea Camilleri (1925 – ). La forma dell’acqua (1994) is the Italian title. The story is set in the fictional town of Vigàta in Sicily in the fictional province of Montelusa. Salvo Montalbano, a police inspector (commissario di polizia), investigates a suspicious but apparently natural death. Montalbano does get to the bottom of it and along the way we are treated to good food, witty dialogue, a cast of characters, and a sampling of the grit, grime and beauty of Sicilian life (or at least a version in Camilleri’s mind).

In Vigàta things seemingly never get done and are so controlled by corrupt officials that people accept this as the state of affairs, occasionally making a deal or play to better their own situation. In the middle of the book, a leftist journalist explains how “in Sicily, and in the province of Montelusa in particular, mutatis mutandis – or zara zabara – to say it in Sicilian – things never budged, even when there was a storm on a horizon. He quoted, with obvious facility, the prince of Salina’s famous statement about changing everything in order to change nothing…” The prince of Salina being the main character in Il Gattopardo.

The Italian phrasing of the statement is this “zara zabara per dirla in dialetto o mutatis mutandis per dirla in latino, le cose nell’isola, e nella provincia di Montelusa in particolare, non si cataminavano mai, magari se il barometro segnava tempesta. Citò, ed ebbe gioco facile, la frase saliniana del cangiar tutto per non cangiare niente...”

The story’s title is revealed when Signora Luparello is speaking to Montalbano and advising him on the next steps in the investigation: “That is up to you to discover, if you so desire. Or else you can stop at the shape they’ve given the water.”

The image shown below is the Italian version front cover. In copertina: Gonna a pieghe di Allen Jones. Collezione privata, Brescia.
La forma dell'acqua - Andrea Camilleri

Coming Soon… in Fremont

Coming Soon - Fremont
We walk by Coming Soon [a retail pop-up installation space] a couple of times a week as we roll down the hill into the center of the universe, aka Fremont. From what we’ve read the space is inspired by pop-up art stores in New York City. We don’t know what pop-up art store means exactly - I still call contemporary art, modern art and receive stern looks from those in the [k]now - but we like to stop and gaze at the installations. They bring a smile to nostre faccie. The current show (and pictured above) is called Of Paper and Light by artist Jesse Brown which features cut-out paper sculptures.

Sunday, August 8, 2010

The Periodic Table – Primo Levi

The Periodic Table Front Cover
The Periodic Table (1975) is a collection of fascinating stories by the Italian chemist and novelist Primo Levi (1919 – 1987). The collection, titled in Italian as Il sistema periodico, was translated into English in 1985. Each of the 21 chapters is based on an element that sometimes playing a figurative role in the story, but most often, playing a literal role. The majority of the stories are based on events in Levi’s life. In chapter 10, Gold, Levi is captured by the Fascists. In chapter 11, Cerium, Levi is in Auschwitz. How the elements gold and cerium figure into the stories is Levi’s narrative magic. Another important work by Levi is Se questo è un uomo (If This is a Man) which describes his 11 months in the Auschwitz and more generally the human condition in all its extremes.

The periodic table is the organizional scheme of how we understand elements and their relationships. It’s clever and in hindsight it makes sense, that Levi, a chemist, chose this title of the collection as such and structured each story around an element. Throughout the book it’s clear that Levi is a probing mind, a detective in matters of life and science, and in a battle with nature to unlock its secrets. To Levi, “incomprehensible matter” must be examined, writing that “[w]e must never feel disarmed: nature is immense and complex, but it is not impermeable to the intelligence; we must circle around it, pierce and probe it, look for the opening or make it.” There are many passages in the book where battle metaphors are used.

In the stories, there are many instances when Levi starts with an observation or fact about life or chemistry and generalizes it in a matter-of-fact way to a quasi-mediation on life. A good example is from chapter 3, Zinc, where Levi goes from a specific chemical behavior of zinc to wondering about the Fascist mentality:

The course notes contained a detail which at first reading had escaped me, namely, that the so tender and delicate zinc, so yielding to acid which gulps it down in a single mouthful, behaves, however, in a very different fashion when it is very pure: then it obstinately resists the attack. One could draw from this two conflicting philosophical conclusions: the praise of purity, which protects from evil like a coat of mail; the praise of impurity, which gives rise to changes, in other words, life. I discarded the first, disgustingly moralistic, and I lingered to consider the second, which I found more congenial. In order for the wheel to turn, for life to be lived, impurities are needed, and the impurities of impurities in the soil, too, as is known, if it is to be fertile. Dissension, diversity, the grain of salt and mustard are needed: Fascism does
not want them, forbids them, and that’s why you’re not a Fascist; it wants everybody to be the same, and you are not.

One aspect that you get a sense of reading these stories is Levi’s love of the origin of words and their meaning. The collection starts out in Chapter 1, Argon, with Levi relating what he knows of his ancestors, part of the larger Jewish community in Piedmont first arriving there in 1500. In the backstory, Levi introduces many Jewish-Piedmontese terms from his childhood. His ancestors to him were like argon, a noble gas, inert in that they never really mixed with other elements/society – or at least seemed relegated to the margins of society. Another example of his love of words is from chapter 6, Nickel, where Levi writes:

The entrails of the earth swarm with gnomes, kobolds (cobalt!), nickel, German “little demon” or “sprite,” and from which we derive the word nickel, creatures who can be generous and let you find a treasure beneath the tip of your pickax, or deceive you and bedazzle you, making modest pyrites glitter like gold, or disgusting zinc in the garb of tin: and in fact, many are the minerals who names have roots that signify ‘deception, fraud, bedazzlement.’
The collection ends with the story that seems to have been the nucleus for Levi’s writing career. He alludes that that story was “dreamed in an hour and place when my life was not worth much” – probably while imprisoned. It is the story of a carbon atom and this story is perhaps the most beloved of the 21 stories. This story appeared in The Oxford Book of Modern Science Writing and is what led me originally to The Periodic Table.
A Visual Representation of the The Periodic Table - Il sistema periodico