Messenger Connect Beta .Net Library

Important Note 8/30/2010

This is probably the shortest lived-blog post in history since I just received an email from Microsoft stating:

Based on feedback from our early adopters, we are discontinuing the strongly typed Messenger Connect .NET library in favor of providing developers with .NET samples which work directly with our RESTful, OData-compliant web services.

In other words, most of the content of this post is now toast. With the demise of the Live Framework API and the Messenger Connect API I’m starting to think Oscar Wilde was onto something with [t]o lose one parent, Mr Worthing, may be regarded as a misfortune; to lose both looks like carelessness. (Just kidding guys.)

Update 1/28/2011

Archis Gore has a post on the Windows Live for Developers blog showing how to Create Desktop Applications with Messenger Connect using the production version, Messenger Connect 4.1. A .Net API is used to generate an authorization token which is then used in the RESTful operations that replace the beta SDK described below.

Introduction

The early posts on this blog focused on the Live Framework API which at the time was part of Windows Azure. The Live Framework API provided a programming model for Live Mesh an attempt to integrate Live Services with synchronization. The Live Framework API was moved into the Live Services group and subsequently cancelled. The Live Mesh lives on although the website now advertises: Live Mesh will soon be replaced by the new Windows Live Sync. UPDATE 8/27/2010 – Microsoft has announced that Windows Live Sync is to be rebranded as Windows Live Mesh.

The Live Services group has now released the Windows Live Messenger Connect API Beta providing programmatic access to Live Services but without the synchronization that caused some confusion in the Live Framework API. This is a large offering encompassing ASP.Net sign in controls along with both JavaScript and .Net APIs. The Live Services team deserves credit for a solid offering – and this is particularly true of the documentation team which has provided unusually strong and extensive documentation for a product still in Beta.

The goal of this post is to provide a narrative guide through only the .Net portion of the Messenger Connect API.

OAuth WRAP

The Live Framework used Delegated Authentication to give user’s control over the way a website or application can access their Live Services information. Messenger Connect instead uses OAuth WRAP (Web Resource Authorization Protocol) an interim standard already been deprecated in favor of the not yet completed OAuth 2 standard. A number of articles – here and here – describe the politico-technical background of the move from OAuth 1 to OAuth2 via OAuth WRAP. From the perspective of the .Net API the practical reality is that this is an implementation detail burnt into the API so it is not necessary to know anything at all about the underlying standard being used.

Three core concepts from OAuth WRAP (and Delegated Authentication) are exposed in the Messenger Connect API:

  • scopes
  • access tokens
  • refresh tokens

Authorization to access parts of a user’s information is segmented into scopes. For example, a user may give permission for an application to have read access but not write access to the photos uploaded to Live Services. An application typically requests access only for those scopes it requires. The user can manage the consent provided to an application and revoke that consent at any time through the consent management website at http://consent.live.com.

The Messenger Connect API supports various scopes grouped into public and restricted scopes. Public scopes are usable by any application while restricted scopes – allowing access to sensitive information such as email addresses and phone numbers – are usable only by specific agreement with Microsoft.

The public scopes are:

  • WL_Activities.View
  • WL_Activities.Update
  • WL_Contacts.View
  • WL_Photos.View
  • WL_Profiles.UpdateStatus
  • WL_Profiles.View
  • Messenger.SignIn

The restricted scopes are:

  • WL_Calendar.Update
  • WL_Calendar.View
  • WL_Contacts.Update
  • WL_Contacts.ViewFull
  • WL_Photos.Update
  • WL_Profiles.ViewFull

The extent of these scopes is pretty apparent from their name with, for example, WL_Activities.Update being permission to add items to the activities stream of a user.

An application requests permission to some scopes and then initiates the sign in process which pops up a Windows Live sign-in page in which the user can view the scopes which have been requested. On successful sign-in Windows Live returns an access token and a refresh token. The access token is a short-lived token the application uses to access the user’s information consistent with the scopes authorized by the user. The refresh token is a long-lived token an application uses to reauthorize itself without involving the user and to generate a new access token – it essentially allows the application to continue accessing the user’s information without continually asking the user for permission. The access token is valid for the order of hours while the refresh token lasts substantially longer (very helpful, I know).

Resource Model

The Messenger Connect Resource Model exposes four top-level resources to applications:

  • Activities
  • Contacts
  • Profiles
  • Photos

An application with permission to access the WL_Calendar.View restricted scope has access to an additional high-level resource:

  • Calendar

Activities refers to the activity stream of a user that reports actions such as uploading a photo which users perform in Windows Live and in third-party applications  The third-party application can add the notification of that upload as an AddPhotoActivity to the Live Services activity stream for the user. The Messenger Connect API provides native support for various types of activities as well as a generic CustomActivity.

Contacts provides access to the list of Live Services contacts a user has. Profiles provide access to the profile information a user has provided to Live Services. Photos provides access to any photos the user has uploaded to Live Services. Calendar provides access to the user’s calendars and the events in them.

LiveDataContext

The Messenger Connect Beta uses WCF Data Services to convert Messenger Connect API calls into the REST operations sent to the Live Services endpoint. The Messenger Connect API exposes the WCF Data Services runtime context, DataServiceContext, through the LiveDataContext class declared:

public class LiveDataContext : DataServiceContext {
    // Constructors
    public LiveDataContext(Uri serviceRoot);
    public LiveDataContext();

// Events
    public event SignInCompletedEventHandler SignInCompleted;

// Properties
    public DataServiceQuery AlbumsQuery { get; }
    public Uri BaseUri { get; }
    public DataServiceQuery CalendarEventsQuery { get; }
    public DataServiceQuery CalendarsQuery { get; }
    public IDictionary<String,Uri> CollectionBaseUris { get; set; }
    public DataServiceQuery ContactCategoriesQuery { get; }
    public DataServiceQuery ContactsActivitiesQuery { get; }
    public DataServiceQuery ContactsQuery { get; }
    public DataServiceQuery DocumentFoldersQuery { get; }
    public Boolean IsUserSignedIn { get; }
    public DataServiceQuery MyActivitiesQuery { get; }
    public DataServiceQuery ProfilesQuery { get; }
    public SignedInUser SignedInUser { get; }

// Methods
    public LiveDataServiceCollection<Album> GetAlbumsCollection();
    public LiveDataServiceCollection<CalendarEvent> GetCalendarEventsCollection();
    public LiveDataServiceCollection<Calendar> GetCalendarsCollection();
    public LiveDataServiceCollection<Activity> GetContactActivitiesCollection();
    public LiveDataServiceCollection<ContactCategory> GetContactCategoriesCollection();
    public LiveDataServiceCollection<Contact> GetContactsCollection();
    public LiveDataServiceCollection<DocumentFolder> GetDocumentFoldersCollection();
    public LiveDataServiceCollection<Activity> GetMyActivitiesCollection();
    public LiveDataServiceCollection<Profile> GetProfilesCollection();
    public void RefreshAccessToken(AppInformation applicationInfo);
    public void RefreshAccessTokenAsync(AppInformation applicationInfo);
    protected String ResolveNameFromType(Type clientType);
    protected Type ResolveTypeFromName(String typeName);
    public void SignIn(AppInformation applicationInfo);
    public void SignInAsync(AppInformation applicationInfo, Object state);
}

An application must be signed in to Live Services to access any resources and can do so synchronously or asynchronously using SignIn() or SignInAsync() respectively. SignIn() requires a parameter of type AppInformation. A method can be assigned to the SignInCompleted event to handle the completion of the sign-in when asynchronous sign-in is used. The IsUserSignedIn property is set to true on successful sign in.

The following is an example of a synchronous sign-in requesting access to all public scopes:

private LiveDataContext GetLiveDataContext()
{
    appInformation = new AppInformation(ClientId, ClientSecret);
    appInformation.RequestedOffers.Add(new Offer { Action = “View”, Name = “WL_Activities” });
    appInformation.RequestedOffers.Add(new Offer { Action = “Update”, Name = “WL_Activities” });
    appInformation.RequestedOffers.Add(new Offer { Action = “View”, Name = “WL_Contacts” });
    appInformation.RequestedOffers.Add(new Offer { Action = “View”, Name = “WL_Photos” });
    appInformation.RequestedOffers.Add(new Offer { Action = “UpdateStatus”, Name = “WL_Profiles” });
    appInformation.RequestedOffers.Add(new Offer { Action = “View”, Name = “WL_Profiles” });

LiveDataContext liveDataContext = new LiveDataContext();
    liveDataContext.SignIn(appInformation);

return liveDataContext;
}

On successful sign-in, an access token and a refresh token are assigned to the AuthInfo.AccessToken and  AuthInfo.RefreshToken properties of the AppInformation object passed into the call to SignIn() (or via SignInCompletedEventArgs in the asynchronous case). The AccessToken has a short expiration period of the order of a few hours while the RefreshToken is valid for the period for which the user has given consent. The RefreshAccessToken() and RefreshAccessTokenAsync() methods can be used to request a new AccessToken provided user consent has not expired or been revoked. (Note that the user can revoke consent on the Consent Management website.) An application must still sign in with a refreshed AccessToken before any Live Services resources can be accessed.

The following example requests a refreshed AccessToken and uses it to sign in:

private LiveDataContext GetLiveDataContextFromRefreshToken( String refreshToken)
{
    Dictionary<string, string> dictionary = new Dictionary<String, String>();
    dictionary.Add(“RefreshToken”, refreshToken);

AppAuthentication appAuthentication = new AppAuthentication(dictionary);

appInformation = new AppInformation(ClientId, ClientSecret, appAuthentication);

LiveDataContext liveDataContext = new LiveDataContext();
    liveDataContext.RefreshAccessToken(appInformation);
    liveDataContext.SignIn(appInformation);

return liveDataContext;
}

The remaining LiveDataContext methods are of the form GetTCollection() returning an initialized object of type LiveDataServiceCollection<T> where T is the type of a Live Services resource. This initialized object is hydrated from the Live Service resource by invoking Load() or (asynchronously) LoadAsync(). There are two overloaded Load() methods: one with no parameter retrieving all the resources of the specified type; the other using an IEnumerable<T> to specify a filter query sent to the Live Service with the request. Each GetTCollection() method has a matching property, XQuery, of type DataServiceQuery which simplifies the construction of these LINQ queries. The following example demonstrates the use of ProfilesQuery to simplify the LINQ query used to load the profiles collection and restricts it to retrieve only the StatusProfile:

private void GetProfiles(LiveDataContext liveDataContext)
{
    LiveDataServiceCollection<Profile> profiles = liveDataContext.GetProfilesCollection();

profiles.Load(from p in liveDataContext.ProfilesQuery where p.Title == “StatusProfile” select p);
    StatusProfile statusProfile = profiles.First() as StatusProfile;

String status = String.Format(“Status: {0}”, statusProfile.StatusText);
}

AppInformation

The AppInformation class wraps information used by LiveDataContext.SignIn(), LiveDataContext.RefreshAccessToken() and their asynchronous equivalents. AppInformation is declared:

public class AppInformation {
    // Constructors
    public AppInformation(String clientId, String clientSecret, AppAuthentication authInfo, Boolean disableUXSignIn);
    public AppInformation(IDictionary<String,String> initParams);
    public AppInformation(String clientId, String clientSecret, AppAuthentication authInfo);
    public AppInformation(String clientId, String clientSecret);
    public AppInformation(String clientId, String clientSecret, Collection<Offer> requestedOffers);

// Properties
    public AppAuthentication AuthInfo { get; }
    public String CallbackUrl { get; set; }
    public String ClientId { get; }
    public String ClientSecret { get; }
    public String ClientState { get; set; }
    public Uri ConsentRoot { get; set; }
    public String PolicyUrl { get; set; }
    public Collection<Offer> RequestedOffers { get; set; }
    public Uri ServiceRoot { get; set; }
    public String TouUrl { get; set; }
}

The AppInformation constructors take variously a client Id and client secret identifying the calling application with optionally an AppAuthentication object and a list of requested offers (i.e. scopes). There is also a constructor taking an IDictionary<String,String> of initial parameters that can be used to construct an AppInformation object from an existing access token (key: AccessToken). For example:

private LiveDataContext GetLiveDataContextFromDictionary( String accessToken)
{
    Dictionary<string, string> dictionary = new Dictionary<String, String>();
    dictionary.Add(“AccessToken”, accessToken);

appInformation = new AppInformation(dictionary);

LiveDataContext liveDataContext = new LiveDataContext();
    liveDataContext.SignIn(appInformation);

return liveDataContext;
}

AppAuthentication

The AppAuthentication class exposes the access and refresh tokens and the offers acquired during sign in or when tokens are refreshed. AppAuthentication is declared:

public class AppAuthentication {
    // Constructors
    public AppAuthentication(IDictionary<String,String> authInfo);
    public AppAuthentication(String accessToken, String refreshToken);
    public AppAuthentication();

// Properties
    public String AccessToken { get; }
    public Exception Error { get; }
    public DateTime ExpirationDate { get; }
    public Collection<Offer> GrantedOffers { get; }
    public String RefreshToken { get; }
    public String UserId { get; }

// Methods
    public IDictionary<String,String> ToDictionary();
}

Following successful sign-in the AuthInfo property of the AppInformation object passed-in to SignIn() containsvalues returned by Live Services – specifically the AccessToken and the RefreshToken.

LiveResource

The base class for all Messenger Connect resources is LiveResource which is declared:

public class LiveResource : INotifyPropertyChanged {
    // Constructors
    public LiveResource();

// Events
    public event PropertyChangedEventHandler PropertyChanged;

// Properties
    public Person Author { get; }
    public DataServiceCollection AuthorProfiles { get; }
    public Person Contributor { get; set; }
    public DataServiceCollection ContributorProfiles { get; }
    public String Id { get; }
    public Boolean IsDeleted { get; }
    public DateTime Published { get; }
    public String Title { get; set; }
    public DateTime Updated { get; }

// Methods
    public LiveDataContext GetDataContext();
    protected virtual void OnPropertyChanged(String property);

// Implemented Interfaces and Overridden Members
    public override String ToString();
}

The LiveDataContext used to connect to Live Services uses the INotifyPropertyChanged interface implemented by LiveResource to track changes to resources and then persist them to Live Services using SaveChanges() from its base class DataServiceContext.

The general pattern for dealing with resources:

  • Initialize the resource from a LiveDataContext.
  • Load the resource from Live Services
  • Modify the properties of the resource
  • Save the resource to Live Services using the LiveDataContext.

LiveResource is the base class for the following resource classes:

Activity is the base class for a large number of activities which are described in more detail in the Activities section below. Activities are one of the top-level resources of the Messenger Connect Resource Model.

The Contact class exposes various properties, such as names and birthday, of a contact for a Windows Live user. Contacts are one of the top-level resources of the Messenger Connect Resource Model and are described in the Contacts section below. The ContactCategory class represents a user-specified grouping of contacts such as Neighbors.

The Profile class exposes the various profiles of a user. Profiles are another top-level resource for the Messenger Connect API and are further described in the Profiles section below.

The Calendar resource provides access to the user’s calendars and the events, represented by CalendarEvent, in them. Calendars are a top-level resource of the Messenger Connect API and are further described in the Calendars section below.

The FileEntry class serves as the base class for a set of classes describing individual files uploaded to the user’s Live Services account. The following classes are derived from FileEntry:

These classes represent respectively documents, folders, photos and videos uploaded to the user’s Live Services account. The resources represented by FileEntry objects are contained inside TopLevelFolder resources. There are two types of TopLevelFolderAlbum and DocumentFolder – which contain photos and documents respectively. A FolderEntry represents a lower-level folder inside a TopLevelFolder containing FileEntry or FolderEntry resources. Note that Photos are also top-level resources in the Messenger Connect Resource Model.

The Comment class represents a comment associated with a FileEntry (Photo, Video, etc.) which has been shared with other users. Note that it is not possible to add a comment, even on the Live Services portal, for a FileEntry that has not been shared.

The following example shows a TopLevelFolder (Album) and its contained FileEntry (Photo) resources being loaded and then all the comments associated with a Photo being retrieved:

private void GetCommentsOnPhoto(LiveDataContext liveDataContext)
{
    LiveDataServiceCollection<Album> albums = liveDataContext.GetAlbumsCollection();

albums.Load(
          from a in liveDataContext.AlbumsQuery
          where a.Title == “Portraits”
          select a);

Album portraitsAlbum = albums.FirstOrDefault()’;
    portraitsAlbum.Files.Load(
          from p in portraitsAlbum.Files.GetQuery()
          where p.Id == “urn:uuid:8AD8646BDC1243B4!130”
          select p);

Photo thePhoto = portraitsAlbum.Files.FirstOrDefault() as Photo;

LiveDataServiceCollection<Comment> theComments = thePhoto.Comments;
    theComments.Load();
}

Note that the sub-collections of the Album (Files and then Comments) have to be loaded separately after the Album is loaded – they are not loaded automatically . This is a general feature of the Messenger Connect API.

The Tag class represents a tag linking a person to a Photo or Video.

The simplest way to view the resources exposed through the Messenger Connect API is to use the Windows Live REST Explorer developed as part of the Messenger Connect Beta.

LiveDataServiceCollection<T>

The LiveDataServiceCollection<T> class extends the WCF Data Services DataServiceCollection<T> class with functionality specific to Live Service resources. These classes are derived from ObservableCollection<T> a collection class which notifies observers of changes. A LiveDataContext object hosting a LiveDataServiceCollection<T> tracks changes to it and persists them when SaveChanges() is invoked.

LiveDataServiceCollection<T> is declared:

public class LiveDataServiceCollection<T> : DataServiceCollection {
    // Constructors
    public LiveDataServiceCollection<T>(LiveDataContext context, IEnumerable<T> items);

// Events
    public event EventHandler<AsyncCompletedEventArgs> LoadCompleted;

// Properties
    public LiveResource Owner { get; }

// Methods
    public DataServiceQuery GetQuery();
    public void Load();
    public void Load(IEnumerable<T> items);
    public void LoadAsync(IEnumerable<T> items, Object state);
    public void LoadAsync(Object state);
    public void LoadNextPartialSet();
    public void LoadNextPartialSetAsync(Object state);
}

A LiveDataServiceCollection<T> is initialized by one of LiveDataContext GetX() factory methods. The resource information from Live Services is loaded into the LiveDataServiceCollection<T> using one of the Load() methods or their asynchronous equivalents LoadAsync(). The IEnumerable<T> parameter allows a LINQ query to filter the information returned from Live Services. GetQuery() is a helper method providing a query that can be used for those collections for which LiveDataContext does not provide an XQuery() factory method. The GetCommentsInPhoto() example shows these methods being used.

LoadNextPartialSet() and LoadNextPartialSetAsync() support server-side paging when only a subset of the requested resources are returned from Live Services. DataServiceCollection.Continuation is set to a non-null value when there is still information to be retrieved from Live Services following a Load().  LoadNextPartialSet() should be invoked as often as necessary until there is no Live Services information left to be loaded.

Activities

WARNING: The following appears in the Messenger Connect documentation:

For activities to work, your application must be in Active status

Knowing this sooner would have saved me a fair amount of time. One workaround if you want to investigate Activities is to sign-in using the Windows Live Rest Explorer and “borrow” an access token.

Activities are simple reports of things a user does – such as friending another user. An application can use the Messenger Connect API to add activities to the Live Services activities stream of a user. For example, a user may have added a photo to a website which can then record this as an AddPhotoActivity in the user’s Live Services activities stream. Windows Live displays these activities, in a form specific to the activity, in the Messenger Social section of the Profile page and provides direct links where appropriate – such as to an uploaded photo.

Note that an application can access only those activities it adds to Live Services and those added by Live Services (such as friending activities). An application can not access activities added by another application.

Rob Dolin of the Windows Live team has a very detailed post describing activity streams. You should probably read it before reading anything in this section because I cover only the part of Activities pertaining to the Messenger Connect .Net API.

Activity is the abstract base class for Activities. Individual types of activity are represented by individual activity-specific classes derived from Activity, such as AddPhotoActivity, PlayVideoActivity, etc. Additionally, there is a general purpose CustomActivity class.

The Activity class is declared:

public abstract class Activity : LiveResource, INotifyPropertyChanged {
    // Constructors
    protected Activity();

// Properties
    public ActivityUser ActivityActor { get; }
    public String ApplicationId { get; }
    public String ApplicationLink { get; set; }
    public String ApplicationName { get; }
    public String HtmlContent { get; }
    public String HtmlSummary { get; }
    public DateTime LastReplyTime { get; }
    public LiveDataServiceCollection<AddCommentActivity> replies { get; }
    public Int32 RepliesCount { get; }

// Methods
    public void AddReply(String reply);
}

Note that an exception is raised when an attempt is made to add an activity without setting ApplicationLink (to some URL).

The actual work of adding an activity is performed using one of the activity-specific classes:

The purpose of these activities is pretty much self evident. DefeatUserActivity, for example, is documented as Represents an activity to defeat a user in a game.

The AddPhotoActivity class is declared:

public class AddPhotoActivity : Activity {
    // Constructors
    public AddPhotoActivity(Uri photoPreviewLink, Uri alternateLink, String photoTitle, String photoDescription);
    public AddPhotoActivity(Uri albumLink, String albumTitle);
    public AddPhotoActivity(Uri photoPreviewLink, Uri alternateLink, String photoTitle, String photoDescription,
          Uri albumLink, String albumTitle);
    public AddPhotoActivity();
    public AddPhotoActivity(Uri photoPreviewLink);
    public AddPhotoActivity(Uri photoPreviewLink, Uri alternateLink);

// Properties
    public ActivityPhotoAlbum ActivityTarget { get; set; }
    public ActivityPhoto Photo { get; set; }
}

The following example shows an AddPhotoActivity being added using the highlighted constructor.:

private void AddPhotoActivity(LiveDataContext liveDataContext)
{
    LiveDataServiceCollection<Activity> activitiesCollection = liveDataContext.GetMyActivitiesCollection();

AddPhotoActivity addPhotoActivity = new AddPhotoActivity(
        new Uri(“http://www.mywebsite.com/Images/MountEverest_Thumbnail.jpg”),
        new Uri(“http://www.mywebsite.com/Mountains/MountEverest.aspx”),
        “Mount Everest”,
        “South West Face of Mount Everest from Camp 2”);

addPhotoActivity.ApplicationLink = “http://www.mywebsite.com/profile/nmackenzie”;
    activitiesCollection.Add(addPhotoActivity);

liveDataContext.SaveChanges();
}

Windows Live displays this activity headlined by the text:

Neil Mackenzie shared a photo – A moment ago – via My Application – Comment

and displays the thumbnail image (MountEverest_Thumbnail.jpg) with an active link to the hosting page (MountEverest.aspx). It also displays the descriptive text South West Face of Mount Everest from Camp 2.

Other activities are displayed with relevant texts. Note that the template for the display of activities is documented on MSDN.

A CustomActivity can be used to add arbitrary activities to the user’s Live Services profile. For example:

private void CustomActivity(LiveDataContext liveDataContext)
{
    LiveDataServiceCollection<Activity> activitiesCollection = liveDataContext.GetMyActivitiesCollection();

CustomActivity customActivity = new CustomActivity(“Doing nothing: “, new Uri(“http://www.bing.com”),
          “Checking out today’s Bing image”);

customActivity.ApplicationLink = “http://www.mywebsite.com/profile/nmackenzie”;
    activitiesCollection.Add(customActivity);

liveDataContext.SaveChanges();
}

This adds an activity headlined by the text Neil Mackenzie Doing nothing: Checking out today’s Bing image.

The first parameter to the CustomActivity constructor is documented as verb and described as The type or verb of the activity. In this example I used Doing nothing. However, the Live Services friending activity uses the following verb:

http://activitystrea.ms/schema/1.0/make-friend

which somehow generates the headline text Neil Mackenzie and John Doe are now friends.

Contacts

The contacts resource represents the user’s contact list on Windows Live as a sequence of Contact objects. Contact is declared:

public class Contact : LiveResource, INotifyPropertyChanged {
    // Constructors
    public Contact();

// Properties
    public DateTime Birthday { get; set; }
    public ObservableCollection<CategoryInfo> Categories { get; set; }
    public String Cid { get; }
    public ObservableCollection<ContactEmail> Emails { get; set; }
    public String FirstName { get; set; }
    public String FormattedName { get; }
    public String HonorificPrefix { get; set; }
    public String HonorificSuffix { get; set; }
    public Boolean IsFriend { get; }
    public Boolean IsIMEnabled { get; set; }
    public String JobTitle { get; set; }
    public String LastName { get; set; }
    public ObservableCollection<Location> Locations { get; set; }
    public String MiddleName { get; set; }
    public ObservableCollection<ContactPhone> PhoneNumbers { get; set; }
    public MessengerStatus Presence { get; }
    public LiveDataServiceCollection<Profile> Profiles { get; }
    public Uri ThumbnailImage { get; }
    public ObservableCollection<ContactUrl> Urls { get; set; }
    public String WindowsLiveID { get; set; }

// Methods
    public void LaunchMessengerConversation();
}

These properties map into the obvious properties for a contact. LaunchMessengerConversation() initiates a conversation with another user provided Windows Live Messenger is installed.

Note that WL_Contacts.ViewFull scope is required to access the phone numbers and email addresses of a contact. This is a restricted scope requiring specific agreement with Microsoft.

Profiles

The Profiles resource represent the various parts of a user’s profile on Live Services.

The base Profile class is declared simply:

public class Profile : LiveResource, INotifyPropertyChanged {
    // Properties
    public String ProfileType { get; }
}

ProfileType, defined by Strings in the ProfileType class, identifies the Profile as one of:

The SocialProfile, for example, is declared:

public class SocialProfile : Profile {
    // Constructors
    public SocialProfile();

// Properties
    public String Fashion { get; }
    public String FavoriteQuote { get; }
    public String Hometown { get; }
    public String Humor { get; }
    public String InterestedIn { get; }
    public String PlacesLived { get; }
    public String RelationshipStatus { get; }
}

The other classes in the Profile hierarchy similarly expose a set of properties pertinent to the specific Profile.

The StatusProfile is used to set the user’s status which appears immediately under the user’s name at the top of the Windows Live Profile page. The following example shows the user status being set to the value of statusText:

private void SetStatus(LiveDataContext liveDataContext, String statusText)
{
    LiveDataServiceCollection<Profile> profiles = liveDataContext.GetProfilesCollection();
    profiles.Load(from p in liveDataContext.ProfilesQuery where p.Title == “StatusProfile” select p);

StatusProfile statusProfile = profiles.FirstOrDefault() as StatusProfile;
    if (statusProfile != null)
    {
        statusProfile.StatusText = statusText;
        liveDataContext.SaveChanges();
    }
}

Note how the example demonstrates the general pattern for dealing with resources: initialize the resource; load the resource; update the resource; save changes.

Photos

In Live Services, a Photo is stored in an Album. A Photo is a FileEntry allowing the Upload and Download functionality of a FileEntry to be used. An Album is a TopLevelFolder and the sequence of Photo objects are exposed through the Files collection of the TopLevelFolder. Note that uploading of a Photo requires the WL_Photos.Update restricted scope.

TopLevelFolder is declared:

public abstract class TopLevelFolder : LiveResource, INotifyPropertyChanged {
    // Constructors
    protected TopLevelFolder();

// Properties
    public Uri Browse { get; }
    public LiveDataServiceCollection<FileEntry> Files { get; }
    public String SharingLevel { get; set; }
    public Int32 Size { get; }
    public String Summary { get; set; }
    public Uri Ux { get; }

// Methods
    public void AddFile(Stream fileStream, FileEntry file, Boolean closeStream, String httpContentType);
    public void AddFile(String filePath, FileEntry file);
}

Album is declared:

public class Album : TopLevelFolder {
    // Constructors
    public Album();

// Properties
    public Uri AlbumCover { get; }
    public Uri SlideShow { get; }
}

The following example downloads a Photo, titled photoTitle, from an Album titled albumTitle to a file whose full path including filename is filePath.

private void GetPhoto(LiveDataContext liveDataContext, String albumTitle, String photoTitle, String filePath)
{
    LiveDataServiceCollection<Album> albums = liveDataContext.GetAlbumsCollection();
    albums.Load(from a in albums.GetQuery() where a.Title == albumTitle select a);

Album album = albums.FirstOrDefault();
    album.Files.Load(from p in album.Files.GetQuery() where p.Title == photoTitle select p);

Photo photo = album.Files.FirstOrDefault() as Photo;
    photo.DownloadFile(filePath);
}

Calendars

A Live Services user can have multiple calendars identified by name – Neil’s Calendar, Birthday Calendar, etc. Each calendar comprises a series of events and that is how they are represented in the Messenger Connect API – as Calendar and CalendarEvent objects.

Calendar is declared:

public class Calendar : LiveResource, INotifyPropertyChanged {
    // Constructors
    public Calendar();

// Properties
    public LiveDataServiceCollection<CalendarEvent> Events { get; }
    public Boolean IsDefaultCalendar { get; }
    public Boolean IsShared { get; }
    public String SharingPermission { get; }
    public Uri SubscribableUrl { get; }
    public CalendarTimeZone TimeZone { get; }
    public String Type { get; }
}

CalendarEvent is declared:

public class CalendarEvent : LiveResource, INotifyPropertyChanged {
    // Constructors
    public CalendarEvent();

// Properties
    public String Availability { get; set; }
    public Double EndTimeZoneOffset { get; }
    public Boolean IsAllDayEvent { get; set; }
    public Boolean IsMeetingRequest { get; }
    public String Location { get; set; }
    public ObservableCollection<CalendarEventOccurrence> Occurrences { get; }
    public ObservableCollection<EventParticipant> Participants { get; set; }
    public String RecurrenceDescription { get; }
    public Int32 ReminderTimeInMin { get; }
    public Double StartTimeZoneOffset { get; }
    public DateTime UtcEndTime { get; set; }
    public DateTime UtcStartTime { get; set; }
    public String Visibility { get; set; }
}

Participants is a list of EventParticipant objects describing each participant in the event. Occurrences comprises a CalendarEventOccurrence collection of different occurrences of a recurring CalendarEvent.

The following example shows how to add an event to a calendar:

private void AddConcertToCalendar(LiveDataContext liveDataContext)
{
    LiveDataServiceCollection<Microsoft.Live.Calendar> calendars = liveDataContext.GetCalendarsCollection();

calendars.Load(from c in liveDataContext.CalendarsQuery where c.Title == “Concert Calendar” select c);
    Microsoft.Live.Calendar concertCalendar = calendars.FirstOrDefault();

CalendarEvent aConcert = new CalendarEvent();
    aConcert.Title = “Beak>”;
    aConcert.Availability = “Busy”;
    aConcert.Visibility = “Public”;
    aConcert.UtcStartTime = new DateTime(2010, 9, 7, 20, 0, 0);
    aConcert.UtcEndTime = aConcert.UtcStartTime.AddHours(3);
    aConcert.Location = “The Independent, San Francisco”;

EventParticipant aParticipant = new EventParticipant();
    aParticipant.Email = “johndoe@live.com”;
    aParticipant.Name = “John Doe”;
    aConcert.Participants.Add(aParticipant);

EventParticipant anotherParticipant = new EventParticipant();
    anotherParticipant.Email = “janedoe@live.com”;
    anotherParticipant.Name = “Jane Doe”;
    aConcert.Participants.Add(anotherParticipant);

concertCalendar.Events.Add(aConcert);

liveDataContext.SaveChanges();
}

The first step is to use a LiveDataContext to initialize the Calendar collection which is then populated with a single calendar named Concert Calendar. A new CalendarEvent is created and initialized appropriately and two participants, (specified in EventParticipant) added to the event. The LiveDataContext is then used to save the changes to the Calendar back to Live Services which will send invitation emails to the participants.

About Neil Mackenzie

Cloud Solutions Architect. Microsoft
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s