April 2006 - Posts

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});             

    }

  }   

}

I will be needing to migrate a MSN Spaces blog over to my Community Server site. I can't tell you what blog yet but it involves primates, colors and flames. Fortunately, Microsoft has made available the MetaWeblog API on MSN Spaces. They also have a decent getting started with MetaWeblog API for MSN Spaces page. Unfortunately, they limit the metaWeblog.getRecentPosts method to only 20 posts. Ughhh. I can't fault MS too much their documentation is not too bad. I've got an idea on how I'm going to port the data. I'll post the code I've completed the port.

Posted by Jorriss | with no comments

I see that you've found my new technical blog. Welcome. Just so you know I'm using Community Server 2.0 as the software for this blog. I have loaded all of the posts in the Tech category on my personal blog at www.jorriss.com on this site. To do this I was able hook into the Community Server web service, loop through my technical blog posts (stored in .Text) and load each post. I wasn't worried about the comments or referrals since I wasn't going to remove the original posts from jorriss.com. This way, I get a fresh blog with all of my original tech posts. And yes, a new design is coming. Here is a code listing of how I ported the data. It's simple, It's dirty, It works.

public static void PostBlogDataWS() {

  // Get Old Blog Data
  SqlConnection connOldBlog = new
SqlConnection(); 
  // Replace servername, database, userid, password with the .Text DB values
  connOldBlog.ConnectionString="Data Source=servername;initial catalog=databasename;user id=userid;password=password;";
  connOldBlog.Open();
  SqlDataReader blogData;
  SqlCommand cmd =
new
SqlCommand();
  cmd.Connection = connOldBlog;
  cmd.CommandText = "select b.title, dateadded, text from blog_content b join blog_links l on b.id =  l.postid and categoryid = 2";
  blogData = cmd.ExecuteReader();

  // Loop through blog posts and load them into CS via the WS
  while
(blogData.Read()) {
    net.ws.BlogPost post =
new
net.jorriss.BlogPost();
    net.ws.BlogService bservice =
new
net.jorriss.BlogService();
    net.ws.ServiceCredentials bcred =
new
net.ws.ServiceCredentials();

    bcred.Username = "username"; // use your CS username
    bcred.Password = "password"; // use your CS password
    bcred.SectionName = "blogappkey"; // use your CS blog appkey

    post.Body = blogData[2].ToString();
    post.Date = Convert.ToDateTime(blogData[1].ToString());
    post.FormattedBody = blogData[2].ToString();
    post.IsPublished =
true

    post.Title = blogData[0].ToString();
    post.Syndicate =
true
;
    post.EnableTrackbacks =
true
;
    post.EnableComments =
true
;
    post.EnableAllOwnerNotification =
true
;
    post.EnableRatings =
true
;
    post.EnableCrossPosting =
true
;
    post.SydicateRoot =
true
;
    post.FeedbackNotificationType = net.ws.FeedbackNotificationType.AllFeedback;
    bservice.ServiceCredentialsValue =bcred;
    bservice.Create(post);
  }
  connOldBlog.Close();
}

Posted by Jorriss | with no comments
Filed under: , ,

Now Playing: MercyMe - Here Am I

Posted by Jorriss | with no comments
Filed under:

For almost four years I’ve been using Rockford Lhotka’s CSLA. CSLA or the Component-based Scalable Logical Architecture is a framework to build business objects. It’s a powerful architecture that provides many different features for middle-ware objects (automating validation logic, n-level undo, and databinding support just to name a few). Personally, I wouldn’t start an application without it. Rocky has updated the framework once again this time with version 2.0. It includes better validation (it also breaks some of my code, thanks Rocky), authorization support and updates to the data portal. As usual Rocky has updated his books Expert VB 2005 Business Objects and Expert C# 2005 Business Objects to reflect these new changes. DotNetRocks has been busy showing the world CSLA.Net 2.0. They have featured Rocky and the new changes to CSLA in their weekly podcast. They have also created a video on how CSLA works on their DNR TV show. For more information on CSLA you can try the CSLA message board, the CSLA Desktop Reference, or the CSLA wiki.

Posted by Jorriss | with no comments
Filed under: , , ,