Moving blog posts from MSN Spaces to Community Server 2.0

As I mentioned in my previous post I’m in the middle of moving a blog from MSN Spaces to my Community Server site. Of course there were some issues to resolve. You first need to turn on e-mail publishing for your MSN Spaces blog. You cannot use the MetaWeblog API without it. You can turn it on by following the steps documented in the Getting Started with the MetaWeblog API for MSN Spaces page. Note: you may have to use Firefox to do this. No lie. While using IE6 an error kept popping up on me. Weird.

Next you will have to download the XML-RPC dll. Unzip the file and create a reference to the XmlRpc.dll in your project.

I also figured out there is no way through the API to retrieve comments or trackbacks. Luckily for me there weren’t many comments or trackbacks so losing this data wasn’t a big deal. Also, MSN Spaces doesn’t let you retrieve any more than the most recent 20 posts via the API. This is a problem. The API does allow you to retrieve a single post item through the getPost() method. So I ran a test. I used the getRecentPosts() method to retrieve the postIDs of the most recent posts. I found out that the postIDs are composed of an alphanumeric key and a numeric id. In this case the key looked like D8F20BDF7ECC5C2B!106 where the last three digits would differ between each post. You can clearly see this postID in the permalink of any MSN Spaces post. There is my rosetta stone. If I was to loop through a simple counter and concatenate the counter value at the end of the D8F20BDF7ECC5C2B! (we’ll call this the MSNblogkey) I could ask the API for that post. If it threw an exception it didn’t find a post. If it did then we load the post into CS. After all of that I sucessfully loaded http://spaces.msn.com/flamingbluemonkeys/ to http://www.flamingbluemonkeys.com.  

public static void PostMSNBlogData (){

  MsnSpacesMetaWeblog mw = new MsnSpacesMetaWeblog();

  string blogid = “MyBlog”; // doesn’t change will be MyBlog for everyone

  string username = “username”; // your msn blog name, found in the url

  string password = “password”; // your e-mail publishing password

  mw.Credentials = new NetworkCredential(username, password);

 

  net.jorriss.BlogService bservice = new net.jorriss.BlogService();

 

  // Loop from 100 to 234. 234 was the most recent post number.

  for(int i = 100; <= 234; i++) {

    try {

      // replace yourMSNblogkey with the

      MetaWeblogApi.Post msnPost = mw.getPost(“yourMSNblogkey” + i.ToString(), username, password);

 

      net.jorriss.BlogPost post = new net.jorriss.BlogPost();

      net.jorriss.ServiceCredentials bcred = new net.jorriss.ServiceCredentials();

 

      bcred.Username = “CSUsername”; // your CS username

      bcred.Password = “CSPassword”; // your CS password

      bcred.SectionName = “blogappkey”; // your CS BlogAppKey

 

      post.Body = msnPost.description;

      post.Date = msnPost.dateCreated;

      post.FormattedBody = msnPost.description;

      post.IsPublished = true;

      post.Title = msnPost.title;

      post.Categories = msnPost.categories;

      post.Syndicate = true;

      post.EnableTrackbacks = true;

      post.EnableComments = true;

      post.EnableAllOwnerNotification = true;

      post.EnableRatings = true;

      post.EnableCrossPosting = true;

      post.SydicateRoot = true;

      post.FeedbackNotificationType = net.jorriss.FeedbackNotificationType.AllFeedback;

 

      bservice.ServiceCredentialsValue = bcred;

      bservice.Create(post);

    }

    catch { // if it errors out no post found

    }

  }

 

MetaWeblogAPI object structures. Place this in a new .cs file. Taken from Microsoft’s MSN Spaces MetaWeblogAPI sample project.

 

using System;

using CookComputing.XmlRpc;

using System.Net;

 

namespace MetaWeblogApi {

 

  /// <summary>

  /// This struct represents information about a user’s blog.

  /// </summary>

 

  [XmlRpcMissingMapping(MappingAction.Ignore)]

 

  struct UserBlog{

    public string url;

    public string blogid;

    public string blogName;

  }

 

  /// <summary>

  /// This struct represents information about a user.

  /// </summary>

 

  [XmlRpcMissingMapping(MappingAction.Ignore)]

 

  struct UserInfo{

    public string url;

    public string blogid;

    public string blogName;

    public string firstname;

    public string lastname;

    public string email;

    public string nickname;

  }

 

  /// <summary>

  /// This struct represents the information about a category that could be returned by the

  /// getCategories() method.

  /// </summary>

 

  [XmlRpcMissingMapping(MappingAction.Ignore)]

 

  struct Category{

    public string description;         

    public string title;

  }

 

  /// <summary>

  /// This struct represents the information about a post that could be returned by the

  /// editPost(), getRecentPosts() and getPost() methods.

  /// </summary>

 

  [XmlRpcMissingMapping(MappingAction.Ignore)]

 

  struct Post {         

    public DateTime dateCreated;       

    public string description;         

    public string title;

    public string postid; 

    public string[] categories;                   

  }

 

  /// <summary>

  /// This class can be used to programmatically interact with a Weblog on

  /// MSN Spaces using the MetaWeblog API.

  /// </summary>

 

  [XmlRpcUrl(“https://storage.msn.com/storageservice/MetaWeblog.rpc”)]   

 

  class MsnSpacesMetaWeblog : XmlRpcClientProtocol {

 

    /// <summary>

    /// Returns the most recent draft and non-draft blog posts sorted in descending order by publish date.

    /// </summary>

    /// <param name=”blogid”> This should be the string MyBlog, which indicates that the post is being created in the user’s blog. </param>

    /// <param name=”username”> The name of the user’s space. </param>

    /// <param name=”password”> The user’s secret word. </param>

    /// <param name=”numberOfPosts”> The number of posts to return. The maximum value is 20. </param>

    /// <returns></returns>

 

    [XmlRpcMethod(“metaWeblog.getRecentPosts”)]

 

    public Post[] getRecentPosts(

      string blogid,

      string username,

      string password,

      int numberOfPosts){

 

      return (Post[]) this.Invoke(“getRecentPosts”, new object[]{ blogid, username, password, numberOfPosts});                                   

    }

 

    /// <summary>

    /// Posts a new entry to a blog.

    /// </summary>

    /// <param name=”blogid”> This should be the string MyBlog, which indicates that the post is being created in the user’s blog. </param>

    /// <param name=”username”> The name of the user’s space. </param>

    /// <param name=”password”> The user’s secret word. </param>

    /// <param name=”post”> A struct representing the content to update. </param>

    /// <param name=”publish”> If false, this is a draft post. </param>

    /// <returns> The postid of the newly-created post. </returns>

 

    [XmlRpcMethod(“metaWeblog.newPost”)]

 

    public string newPost(

      string blogid,

      string username,

      string password,

      Post content,

      bool publish){

 

      return (string) this.Invoke(“newPost”, new object[]{ blogid, username, password, content, publish});                                   

    }

 

    /// <summary>

    /// Edits an existing entry on a blog.

    /// </summary>

    /// <param name=”postid”> The ID of the post to update. </param>

    /// <param name=”username”> The name of the user’s space. </param>

    /// <param name=”password”> The user’s secret word. </param>

    /// <param name=”post”> A struct representing the content to update. </param>

    /// <param name=”publish”> If false, this is a draft post. </param>

    /// <returns> Always returns true. </returns>

 

    [XmlRpcMethod(“metaWeblog.editPost”)]

 

    public bool editPost(

      string postid,

      string username,

      string password,

      Post content,

      bool publish){

 

      return (bool) this.Invoke(“editPost”, new object[]{ postid, username, password, content, publish});                                   

    }

 

    /// <summary>

    /// Deletes a post from the blog.

    /// </summary>

    /// <param name=”appKey”> This value is ignored. </param>

    /// <param name=”postid”> The ID of the post to update. </param>

    /// <param name=”username”> The name of the user’s space. </param>

    /// <param name=”password”> The user’s secret word. </param>

    /// <param name=”post”> A struct representing the content to update. </param>

    /// <param name=”publish”> This value is ignored. </param>

    /// <returns> Always returns true. </returns>

 

    [XmlRpcMethod(“blogger.deletePost”)]

 

    public bool deletePost(

      string appKey,

      string postid,

      string username,

      string password,             

      bool publish){

 

      return (bool) this.Invoke(“deletePost”, new object[]{ appKey, postid, username, password,  publish});                                 

    }

 

    /// <summary>

    /// Returns information about the user’s space. An empty array is returned if the user does not have a space.

    /// </summary>

    /// <param name=”appKey”> This value is ignored. </param>

    /// <param name=”postid”> The ID of the post to update. </param>

    /// <param name=”username”> The name of the user’s space. </param> 

    /// <returns> An array of structs that represents each of the user’s blogs. The array will contain a maximum of one struct, since a user can only have a single space with a single blog. </returns>

 

    [XmlRpcMethod(“blogger.getUsersBlogs”)]

 

    public UserBlog[] getUsersBlogs(

      string appKey,

      string username,

      string password){

 

      return (UserBlog[]) this.Invoke(“getUsersBlogs”, new object[]{ appKey,  username, password});                                           

    }

 

    /// <summary>

    /// Returns basic user info (name, e-mail, userid, and so on).

    /// </summary>

    /// <param name=”appKey”> This value is ignored. </param>

    /// <param name=”postid”> The ID of the post to update. </param>

    /// <param name=”username”> The name of the user’s space. </param> 

    /// <returns> A struct containing profile information about the user.

    /// Each struct will contain the following fields: nickname, userid, url, e-mail,

    /// lastname, and firstname. </returns>

 

    [XmlRpcMethod(“blogger.getUserInfo”)]

 

    public UserInfo getUserInfo(

      string appKey,

      string username,

      string password){

 

      return (UserInfo) this.Invoke(“getUserInfo”, new object[]{ appKey,  username, password});                                           

    }

 

    /// <summary>

    /// Returns a specific entry from a blog.

    /// </summary>

    /// <param name=”postid”> The ID of the post to update. </param>

    /// <param name=”username”> The name of the user’s space. </param>

    /// <param name=”password”> The user’s secret word. </param>

    /// <returns> Always returns true. </returns>

 

    [XmlRpcMethod(“metaWeblog.getPost”)]

 

    public Post getPost(

      string postid,

      string username,

      string password){

 

      return (Post) this.Invoke(“getPost”, new object[]{ postid, username, password});                                   

    }

 

    /// <summary>

    /// Returns the list of categories that have been used in the blog.

    /// </summary>

    /// <param name=”blogid”> This should be the string MyBlog, which indicates that the post is being created in the user’s blog. </param>

    /// <param name=”username”> The name of the user’s space. </param>

    /// <param name=”password”> The user’s secret word. </param>

    /// <returns> An array of structs that contains one struct for each category. Each category struct will contain a description field that contains the name of the category. </returns>

 

    [XmlRpcMethod(“metaWeblog.getCategories”)]

 

    public Category[] getCategories(

      string blogid,

      string username,

      string password){

 

      return (Category[]) this.Invoke(“getCategories”, new object[]{ blogid, username, password});             

    }

  }   

}