Windows Live Messenger Web Controls – Part 1

The Windows Live team have been busy over the past few months and recently launched the Windows Live Messenger APIs. This is basically split over three parts, the IM Control, the Windows Live Presence API and the Windows Live Messenger Library.

What all this allows you to do is to create a Windows Live Messenger plug-in for your own website. What this (and coming articles) will try to explain is how to use these API’s to actually create and integrate Messenger on your site as you can see below.

messenger%20signed%20in_thumb Developer

Getting Started with the Windows Live Sign-in Control

I’ve covered this topic in a previous article I’ve written but I’ll do a quick overview once again. In order to use Messenger you first need to sign-in to Windows Live (http://login.live.com). In order to sign-in you must host the sign-in control on your site and in order to host the sign-in control you need the following two files :-
Channel.htm and Privacy.htm
These files are required.  The Channel.htm file facilitates moving information back and forth from Windows Live to your site as the login control in reality uses a frame, so this allows for cross frame information sharing. Here is the code you will require for the Channel.htm file :-

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html >
   <head>
   <title>Channel</title>
   <meta name="ROBOTS" content="NONE"/>
   <script type="text/javascript">
   try
   {
    var hash = window.location.hash.substr(1);
    if (window.location.replace == null)
    window.location.replace = window.location.assign;
    window.location.replace("about:blank");
    var name = hash.split("/")[0];
    var win = null;
    if (name && (name != ".parent"))
    win = window.parent.frames[name];
    else
    win = window.parent.parent;
    if (win.Microsoft) {
    win.Microsoft.Live.Channels.Mux._recv_chunk(hash);
    }
   }
   catch (ex)
   {
    /* ignore */
   }
   </script>
   </head>
   <body></body>
</html>

The Privacy.htm file is just what it sounds like. It is the privacy policy for your actual website. In this page you are fairly much free to put whatever you wish, as an example :-

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html >
   <head>
   <title>Privacy Policy</title>
   <meta name="ROBOTS" content="NONE"/>
   </head>
   <body>
   <h1>No information is actually stored on this website.</h1>
   </body>
</html>

 

For more information on the privacy.htm file and guidance on what information to put it in, please see http://privacy.microsoft.com/en-us/default.aspx.

Next you need to register your website with Windows Live (this means that you must have a valid domain name/website). You do this by :-

Logging into https://msm.live.com/app/default.aspx

register1_thumb Developer  

Select “Register and application” from the menu on the side

register2_thumb Developer

and complete the details that it asks. Once you’ve completed all the information, it will give you an Application ID. You can also check all the Application IDs that you currently have from this site by clicking on the “Manage my applications” link on the side.

register3_thumb Developer

Keep that application ID and also Secret Key that you typed in on stage 2 as you will need these later on.
Once you have registered your application and setup your Privacy.htm and Channel.htm files, you are ready to start integrating Windows Live Messenger into your site.

Asp.Net Ajax

How you wish to implement Messenger into your site is really up to you. You can place it into a web control (.ascx file), code it into a  server control (.dll) or just put the code into one of your pages. For this article I’ll simply be using a blank page so as not to confuse you with any other code. At the end of the day we wish to end up with something like the following :-

messenger%20signed%20in_thumb_1 Developer

My site structure is extremely simple, I have the Privacy.htm and Channel.htm files at the top level of my site. The site only consists of a single page (default.aspx), a few graphics files, a theme and of course the web.config file as follows :-

sitestructure_thumb Developer

As all of this is client side, you don’t need to put any additional code in your code behind file, so all the work we will be doing will be in the default.aspx file itself. One thing you will need before going any further however is a good knowledge of not just Javascript but also of Microsoft’s Asp.Net Ajax as the messenger API relies heavily on this. In fact, if you did not know Asp.Net Ajax, the Messenger API’s would really be of no use to you. Why do I say this? Take the following as an example. Here is a page from the Messenger API :-

groupproperties_thumb Developer

This snippet (which you can find here) is the Properties associated with the Group class. As you can see Group has two properties, Contacts and Name. If you tried to use these as is on your web page then you would end up with a Javascript error. Why? Because these properties are actually Asp.Net Ajax properties, and in order to access Asp.Net Ajax properties you have to prefix them with “get_” and attach brackets to the end. For example, if you wanted to use the name property your code would actually have to be :-

var groupName = get_name();

Integration

Now that we have all this out the way, lets get started with our Default.aspx page. Before delving into the javascript, lets layout the actual page first.

<body onload="scriptMain()">
    <form id="form1" runat="server">
   <div id="msgr">
   <table>
   <tr><td>
   <div id="signinframe"></div>
   </td></tr>
   <tr><td>
   <div id="userInfo"></div>
   </td></tr>
   <tr><td>
   <div id="setUserStatus">
      <span><b>Change Your Status:</b></span>
      <select id="selectStatus" onchange="selectStatusChanged()">
        <option>Appear Offline</option>
        <option>Away</option>
        <option>Be Right Back</option>
        <option>Busy</option>
        <option>Idle</option>
        <option>In a Call</option>
        <option>Online</option>
        <option>Out to Lunch</option>
      </select>
   </div>
   </td></tr> 
   <tr><td>
   <div id="setPersonalMessage">
   <span><b>Personal Message: </b></span> <input id="personalMessage" type="text" />
   <input onclick="setPersonalMessage()" id="btnSetPersonalMessage" type="button" value="Set" />
   </div>
   </td></tr>
   <tr><td>
   <div id="sendMessage">
    <hr />
    <span><b>Send a Message:</b></span>
    <p id="contactLabel"></p>
    <p id="msgLastRecv"></p>
    <div id="txtConv"></div><br />
    <input id="txtMessage" type="text"/><br />
    <input onclick="sendMsg()" id="btnSend" type="button" value="Send Message" disabled="disabled" />
   </div>
   </td></tr>
   <tr><td>
   <div id="divConversations"></div>
   </td></tr>
   <tr><td>
        <div id="Contacts">
            <p><b>Contact List
                <a href="Javascript:ToggleContacts();">
                <img alt="ToggleContacts" src="minus_icon.gif" id="ContactsExpand" class="ContactImage" />
                </a>
            </b></p>
               <div id="divContacts"></div>
        </div>
   </td></tr> 
   
</table>
   </div>
    </form>
</body>

 

There is nothing really hard about this. We are defining various zones in which we will put various pieces of information. A zone for the sign-in frame which will actually house a iframe that links to Windows Live Identity. A zone where we will display various bits of information about the user that has signed in and allow them to change their messenger status just like you do in the Messenger client, zones for messages and for displaying your contacts. When the page loads it calls the scriptMain() Javascript function.

In order to use the Windows Live Messenger API you need to include a reference to it. It’s really just a big Asp.Net Ajax Javascript library. So at the top of your add the following reference :-

<script src="http://settings.messenger.live.com/api/1.0/messenger.js" type="text/javascript" language="javascript"></script>

Now let’s take a look at the scriptMain() function :-

function scriptMain() {
   var privacypage = ‘http://[weburl]/Privacy.htm’;
   var channelpage = ‘http://[weburl]/Channel.htm’;
   _signin = new Microsoft.Live.Messenger.UI.SignInControl(‘signinframe’, privacypage, channelpage, ‘en-US’);
   _signin.add_authenticationCompleted(Delegate.create(null, authenticationCompleted));
}  

Here we setup two variables that hold the URLs to our privacy page and our channel page as discussed earlier. You will of course need to replace “[weburl]” with your own URL. Next we create a new instance of the sign-in control. The sign-in control takes four parameters as defined here :-

public SignInControl( string controlId, string privacyStatementUrl, string channelUrl, string market )

The first parameter is the zone/control in which we wish to place and display the actual sign-in control. In our case this is defined as a <div> with the ID of signinframe. The next two parameters we pass in the variables we setup to point to our privacy and channel pages and the last parameter is for localization where you pass in language information.

The final statement simply adds a delegate (for those familiar with .Net programming) that calls a Javascript method authenticationCompleted when sign-in has completed. See http://msdn2.microsoft.com/en-us/library/microsoft.live.messenger.ui.signincontrol_events.aspx for further information.

The authenticationCompleted event handler is similarly easy. As it’s Asp.Net Ajax based events like this should look familiar to anyone who has experience coding Asp.Net web pages :-

function authenticationCompleted(sender, e) {
   _user = new Microsoft.Live.Messenger.User(e.get_identity());
   _user.add_signInCompleted(Delegate.create(null, signInCompleted));
   _user.signIn(null);
}

On completion of login, this event handler gets called and a user identity gets passed through as the parameter. So our first line of code in this event handler is to create an actual user object from the identity that gets passed in. Next we add another delegate that gets called once sign-in is completed for that actual user (there is a difference between the sign-in control being completed and an actual user finishing authorization hence the two different events). Finally we just call the signIn method of the user class and clear it out.  There are various events that you can hook into here. For a full list, please see http://msdn2.microsoft.com/en-us/library/microsoft.live.messenger.user_events.aspx. For example you should also include a delegate to hook up when the user signs back out so that you can clear cookies, clear up your UI etc.

function signInCompleted(sender, e) {
   if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success) {
    _user.get_presence().add_propertyChanged(Delegate.create(null, user_Presence_PropertyChanged));
    displayUserInfo();
    _addressList = new Array(_user.get_contacts().get_count());
    var enum1 = _user.get_contacts().getEnumerator();
    while (enum1.moveNext()) {
      var c = enum1.get_current();
      var address = c.get_currentAddress();
      address.get_presence().add_propertyChanged(Delegate.create(null, presence_PropertyChanged));
    }
    displayContacts();
    var selectStatus = document.getElementById('selectStatus');
    selectStatus.selectedIndex = 6;
    _contactCollection = _user.get_contacts(); 
    _user.get_conversations().add_propertyChanged(Delegate.create(null, conversation_collectionChanged));
   }
}

 

The first thing that we do in our signInCompleted event handler is to check that the sign-in process was actually successful (the user may have typed an incorrect password for example so you do want to check for this as this event handler will get called regardless of whether login was successful or not). If the user logged in to Windows Live Identity correctly then we add another delegate so that we can change the users presence or status (i.e. now show that the user is online).  We call a method to display various information about the user then we cycle through the users contact list and get the presence or status information of everyone in their contact list (so that the user knows online, who’s away etc) and display that information in using the displayContacts() function. The next couple of lines we simply use the dropdown list that we defined in our html and set the it to “online” and finally we setup yet another delegate, this time for actual conversations that will take place.
The three delegates defined above will work at any time, therefore if the user changes their status to “appear offline” then the first delegate we defined will again be invoked to propagate that status to the users contacts. Similarly if one of the users contacts changes their status then the second delegate will automatically get invoked. The last delegate is a bit more tricky to visualize.
In the normal client based Windows Live Messenger, each conversation you have spawns a new window. So you can have multiple conversations taking place at the same time and multiple windows open. You just click on the conversation window you wish to reply to and type your reply. Now think about how that could be accomplished using the web. You can’t have multiple windows open as how are they going to communicate back and forth with the actual base messenger object, handle events etc? So everything needs to take place on the same page. So if everything is taking place on the same page how are you going to handle multiple conversations? The Windows Live Messenger API does this by creating a collection of conversation. You then pick which collection you wish to be responding to and type your response. That specific conversation collection will then be updated and your message posted.
But we’re getting a little ahead of ourselves here. So back to the code. The first event handler we need to define is the user property changed handler :-

function user_Presence_PropertyChanged(sender, e) {
   displayUserInfo();
   if (_user.get_presence().get_status() === Microsoft.Live.Messenger.PresenceStatus.offline) {
            document.getElementById("divContacts").innerHTML = "";
            document.getElementById("txtConv").innerHTML = "";
      document.getElementById('btnSend').disabled = true;
   }
}

Here we update the users status with the call to displayUserInfo. Then we check to see if the user has actually signed out. If they have then their status will be set to offline. If they have indeed signed out then we clear out various zones on our web page like the users list of contacts.  The presence status can be one of the following enumerations :-

presencestatus_thumb Developer

If you’ve followed the above code so far, you’ll see that we’ve called the displayUserInfo function a couple of times. Lets take a look at that function :-

function displayUserInfo() {
   var userInfo = document.getElementById('userInfo');
   var userAddress = _user.get_address().get_address();
   var userDispName = _user.get_presence().get_displayName();
   var userPersonalMessage = _user.get_presence().get_personalMessage();
   var userStatus = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, _user.get_presence().get_status());
   var statusLine = document.createElement('p');
   removeChildrenFromNode('userInfo');
   if (userDispName !== '') {
      statusLine.appendChild(document.createTextNode(userDispName + ' (' + userAddress + '): ' + userStatus));
   }
   else {
      statusLine.appendChild(document.createTextNode(userAddress + ': ' + userStatus));
   }
   userInfo.appendChild(statusLine);
   document.getElementById('personalMessage').value = userPersonalMessage;
}

 

First we get a reference to the zone we defined on the page to display information about the user who is sign-in. In our page this is simply a <div> block. Next we collect various bits of information from the user object like the users email address, their status (online, offline, away etc.), their personal message, display name etc. We create a new paragraph element to house all this information and we clear out any information that may already be contained in our user information zone. Depending on whether the user has a display name defined or not we create an display message that will either consist of their display name, email address and status or just their email address and status. We add this information to the paragraph tag we created earlier then we add all this into our user information zone. Finally we update the users personal message (if they have one defined) separately. The reason we do this separately is that the users personal message is contained in a textbox allowing the user to update their personal message if they so desire. This function calls another function removeChildrenFromNode :-

function removeChildrenFromNode(id){
   var node = document.getElementById(id);
   if(node == undefined || node == null)
    {
       return;
    }
   var len = node.childNodes.length;
   while (node.hasChildNodes())
   {
    node.removeChild(node.firstChild);
   }
}

 

All that this function does is to remove all the content (sub elements) that may be contained within the element that is passed in as the parameter to the function call.

Going back to our signInCompleted event handler. We have now covered the first of the event handlers. The next event handler gets called whenever one of the users contacts changes their status :-

function presence_PropertyChanged(sender, e) {
   displayContacts();
}

 

Here we simply call another method to update the users contacts list. We use another method rather than placing all the code in this event handler as that other method will be called from various places whereas we cannot call this event handler from our code normally (this is not strictly true but this way is a lot easier).

function displayContacts() {
   var sb = new StringBuilder();
   _addressList = new Array(_user.get_contacts().get_count());
    var groupList = new Array(_user.get_groups().get_count());
   var enum2 = _user.get_groups().getEnumerator();
   var enum1 = _user.get_contacts().getEnumerator();
   var groupindex = 0;
   while (enum2.moveNext())
   {
        var c = enum2.get_current();
        var name = c.get_name();
        sb.append("<div>");
        sb.append("<a href="javascript:ToggleContactGroup('");
        sb.append(name);
        sb.append("');">");
        sb.append("<img class="ContactImage" alt="ToggleContacts" src="minus_icon.gif" id="ContactsExpand");
        sb.append(name);
        sb.append(""/>");
        sb.append("</a>");
        sb.append(name);
        sb.append('<hr />');
        sb.append("<div id='");
        sb.append(name); 
        sb.append("'>");
        var _contacts = new Array(c.get_contacts().get_count());
        var enum3 = c.get_contacts().getEnumerator();
        index = 0;
   while (enum3.moveNext()) {
    var contact = enum3.get_current();
    var address = contact.get_currentAddress();
    var dispName = address.get_presence().get_displayName();
    var currAddress = address.get_address();
    var status = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, address.get_presence().get_status());
        //Change status to an image
        var statusimg = null;
        switch(status)
        {
            case "online":
                statusimg = "<img src='online.gif' class='ContactImage' alt='online' />";
                break;
            case "offline":
                statusimg = "<img src='offline.gif' class='ContactImage' alt='offline' />";
                break;
            case "appearOffline":
                statusimg = "<img src='offline.gif' class='ContactImage' alt='offline' />";
                break;
            case "away":
                statusimg = "<img src='away.gif' class='ContactImage' alt='away' />";
                break;
            case "beRightBack":
                statusimg = "<img src='away.gif' class='ContactImage' alt='beRightBack' />";
                break;
            case "busy":
                statusimg = "<img src='busy.gif' class='ContactImage' alt='busy' />";
                break;
            case "idle":
                statusimg = "<img src='away.gif' class='ContactImage' alt='idle' />";
                break;
            case "inACall":
                statusimg = "<img src='busy.gif' class='ContactImage' alt='InACall' />";
                break;
            case "outToLunch":
                statusimg = "<img src='away.gif' class='ContactImage' alt='OutToLunch' />";
                break;
            default:
                statusimg = "<img src='offline.gif' class='ContactImage' alt='offline' />";
                break;
            }
    var statusLine = '';
    var strDelete = '';
    _addressList[index] = address;
    if (dispName !== '') {
   statusLine = '<a href='javascript:createConv(' + index + ')'>' + statusimg + dispName + '</a>';
}
else {
   statusLine
= '<a href='javascript:createConv(' + index + ')'>' + statusimg + currAddress + '</a>';
}
    sb.append(statusLine);
    sb.append('<hr />');
 
    index++;
   }
        sb.append("</div></div>");
        groupindex++;
   }
   document.getElementById('divContacts').innerHTML = sb.toString();
}

 

Now this method may look fairly daunting but really it’s not. First we get create a new StringBuilder object (part of the Asp.Net Ajax framework). Then we get a list of all the users contacts. Next we also get a list of all the users groups. In the Windows Live Messenger client you can place your contacts in various groups (and even create your own groups) for example co-workers, friends, family etc. Both of these lists are collections. What we want to do is to display each contact in the correct group. So first we create a loop to loop through all the groups.
This first couple of lines within our group loop gets the current group collection that we are looping through and extracts the name for that group :-

var c = enum2.get_current();
var name = c.get_name();

Within the first loop (group loop) we’ve hard-coded some html that will allow a user to close and expand a group (otherwise the page may get very long if the user has a lot of contacts). Here we’re just defining a couple of zones (<div> elements) to put the information in. The outer zone will hold the group name and an icon to display whether the zone is collapsed or expanded. The inner zone will hold all the contacts for that group and will be the zone that actually gets collapsed or expanded. Next we get a list of all the contacts that belong to that particular group :-

var _contacts = new Array(c.get_contacts().get_count());

 

And we define a second inner loop (contact loop). In the contact loop we get each contact, extract various pieces of information from it like their email address, current status, display name etc. ready for display.

var contact = enum3.get_current();
var address = contact.get_currentAddress();
var dispName = address.get_presence().get_displayName();
var currAddress = address.get_address();
var status = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, address.get_presence().get_status());

Next, based on the contacts status, we insert the appropriate messenger presence icon. We do this by simply checking their status against the values of the presence enumeration (see above) and hard coding the appropriate image element against the status. Finally we construct what it is that we will actually be displaying to the user. If the contact has a display name then we display that along with the appropriate status icon, if not then we display the contacts email address along with the appropriate status icon. These we make into links that call the function createConv() so that we can start a conversation with that contact simply by clicking on their information, much the same as the Windows Live Messenger client does.
We add a horizontal rule to separate each of the contacts, increment our counter and close the contacts loop.
Before closing the group loop we close off the two zones we defined and again increment our counter.
Finally once we’ve looped through everything we insert our generated markup into the contact zone that we defined on our html page.

At this point we are about half way through the Javascript code necessary to create Windows Live Messenger within your own web pages.  In part two of this article we will finish off our discussion with the remaining Javascript which covers conversations.