Adding Windows Live Writer support to your Blog engine

comments

If there is one blogging related tool that I've come across lately that i think has been a game changer it would have to be Windows Live Writer. However some people are using either a custom written blog engine (like me) or are using a blog engine that doesn’t support Live Writer. If that is the case i will show you a simple way to integrate Windows Live Writer support into you blog with WebServices and the MetaWebLog API.

So there i was coding away, minding my own business trying to get this blog off the ground and my mind wandered and one of my adventures into the forever continuing void that is “surfing the net” happened.

As most people will relate to, working on the internet can be a dangerous thing when it come to distraction, and I'm no patron saint. Lucky for me i have a fetish for .Net blog articles and therefore can sometimes (well i like to think so) justify my distractions. It was then that i came across Scott Hanselman’s new Ultimate tools list, and he mentioned Windows Live Writer.

I thought:

Well, i am new to the web 2.0 blogging thing, and usually i think things Microsoft create to try and value add can just be because they like to have every finger in every pie, and in reality are just pretty crap...

I was wrong…

I downloaded and installed it and hit a brick wall. My new oober cool blog (this one) was not on the list of supported blog types (how rude). But i did see one cool thing on the list:

 

LiveWriter01_thumb3

 

MetaWebLog API

That’s right kids. Its called Metaweblog API. And iisssssss daaaa besttttt.

Basically it allows you to write your weblog to support Windows Live Writer (and other tools) for managing, editing and publishing your blog posts.

This is pretty epic.

Although i must add at this point that i hadn’t had the glory of actually using it (Windows Live Writer) yet so i was just going off what other people had said and i didn’t really know how much this was a bonus. So I was bored and home one night and decided to jump right in and simply make it happen so that i could check out Windows Live Writer – i know this sounds silly but i love trying new things for fun.

“On Ya Bike Son”

So off i went. Looking into some info here, here and a few more readings on “da intarnetz” i was on my way to adding MetaWebLog API support to my blog. It is remarkably simple.

Steps

1. Create a new Generic Handler in your project
2. Create new methods to support the API
3. Wire-up these methods with your data layer
4. Connect to your new endpoint using Windows Live Writer
5. Enjoy your new blogging abilities

All you’ll need is a copy of the Cook Computer XML RPC Library from here and your off.

ShowMeTheMoney_thumb

So if you like me, you want example code, and you want it now. You’ve listened to me blab and want to get onto it yourself. So here is my minimised version of my handler – I've defined all the important RPC parts so you can reuse them easily.

If something needs clarification, post a comment and I'll add to my code:

using System;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Compilation;
using System.Web.UI;
using CookComputing.XmlRpc;
using System.IO;
namespace Ninja.WebServices
{
[XmlRpcService(
    Name = "Your Blog Name :-)",
    Description = "This is my XML RPC implementation of MetaWebLog API",
    AutoDocumentation = true)]
//Change this to your endpoint URL
[XmlRpcUrl("http://www.blog.com/LiveWriter")] 
public class MetaBlogApi : XmlRpcService
{
    private static void CheckLoggedIn(string username, string password)
    {
        bool loggedin = true;
        if (!loggedin)
            throw new System.Security.Authentication
                .InvalidCredentialException("Access denied");
    }
[XmlRpcMethod("blogger.getUsersBlogs")]
public XmlRpcStruct[] getUsersBlogs(string appKey, string username, 
    string password)
{
    CheckLoggedIn(username,password);
    //Uses xslt transformation to retrive recent posts
    XmlRpcStruct[] posts = new XmlRpcStruct[1];
    XmlRpcStruct rpcstruct = new XmlRpcStruct();

    //if you have more than one blog then loop them here
    rpcstruct.Add("blogid", "1");
    rpcstruct.Add("blogName", "My blog name");
    rpcstruct.Add("url", "http://www.myblog.com"); // Blog URL
    posts[0] = rpcstruct;
    return posts;
}
[XmlRpcMethod("metaWeblog.setTemplate")]
public bool setTemplate(string appKey, string blogid, string username, 
    string password, string template, string templateType)
{
    //WINDOWS LIVE WRITER DOESN"T USE THIS YET
    return true;
}
[XmlRpcMethod("metaWeblog.getCategories")]
public XmlRpcStruct[] getCategories(string blogid, string username, 
    string password)
{
    CheckLoggedIn(username,password);

    int intCatCount = 1;
    XmlRpcStruct[] posts = new XmlRpcStruct[intCatCount];

//START LOOP
    XmlRpcStruct rpcstruct = new XmlRpcStruct();
    rpcstruct.Add("categoryid","1"); 
    rpcstruct.Add("title", "Blog category");
    rpcstruct.Add("description", "My blog category");
    posts[0] = rpcstruct;
//END LOOP

    return posts;
}
[XmlRpcMethod("metaWeblog.getRecentPosts")]
public XmlRpcStruct[] getRecentPosts(string blogid, string username, 
    string password, int numberOfPosts)
{
    CheckLoggedIn(username, password);
//RETRIEVE YOUR BLOG POSTS AND LOOP THROUGH THE RETURN BELOW
    int intBlogCount = 1;
    XmlRpcStruct[] posts = new XmlRpcStruct[intBlogCount];
    int i = 0;

//Populate structure with posts
//START LOOP

    XmlRpcStruct rpcstruct = new XmlRpcStruct();
    rpcstruct.Add("title", "My Blog Post");
    rpcstruct.Add("link", "http://myblog/blog01");
    rpcstruct.Add("description", "My blog content");
    rpcstruct.Add("pubDate", "01/01/2009");
    rpcstruct.Add("guid", "0001");
    rpcstruct.Add("postid", "1");
    rpcstruct.Add("keywords", "123 123");
    rpcstruct.Add("author", "Doug");
    posts[i] = rpcstruct;
//END LOOP
return posts;
}
[XmlRpcMethod("metaWeblog.getTemplate")]
public string getTemplate(string appKey, string blogid, 
    string username, string password, string templateType)
{
    CheckLoggedIn(username, password);
    // LIVE WRITER DOESNT USE THIS YET
    string strTemplate = string.Empty;
    return strTemplate;
}

[XmlRpcMethod("metaWeblog.newPost")]
public string newPost(string blogid, string username, 
    string password, XmlRpcStruct rpcstruct, bool publish)
{
    CheckLoggedIn(username, password);
    string newBlogID = "1";
    string blogTitle = rpcstruct["title"].ToString();
    string blogLink = rpcstruct["link"].ToString();
    string blogContent = rpcstruct["description"].ToString();
    string blogAuthor = rpcstruct["author"].ToString();
    string[] blogCategories = (string[])rpcstruct["categories"];

    return newBlogID;
}

[XmlRpcMethod("metaWeblog.editPost")]
public bool editPost(string postid, string username, string password, 
    XmlRpcStruct rpcstruct, bool publish)
{
    CheckLoggedIn(username, password);
    string blogTitle = rpcstruct["title"].ToString();
    string blogLink = rpcstruct["link"].ToString();
    string blogContent = rpcstruct["description"].ToString();
    string blogAuthor = rpcstruct["author"].ToString();
    string[] blogCategories = (string[])rpcstruct["categories"];

    return true;
}

[XmlRpcMethod("metaWeblog.getPost")]
public XmlRpcStruct getPost(string postid, string username, 
    string password)
{
    CheckLoggedIn(username, password);
    XmlRpcStruct rpcstruct = new XmlRpcStruct();
    rpcstruct.Add("title", "My blog title");
    rpcstruct.Add("link", "http://myblogpostURL.com");
    rpcstruct.Add("description", "My blog post content");
    rpcstruct.Add("pubDate", "01/01/2009");
    rpcstruct.Add("guid", "00001");
    rpcstruct.Add("postid", "1");
    rpcstruct.Add("author", "Doug");
    rpcstruct.Add("publish", "1");
    return rpcstruct;
}

[XmlRpcMethod("blogger.deletePost")]
public bool deletePost(string appKey, string postid, string username, 
    string password, bool publish)
{
    CheckLoggedIn(username, password);
    ///DELETE THE POST WITH THE ID 'postid'
    return false;
}

[XmlRpcMethod("metaWeblog.newMediaObject")]
public XmlRpcStruct newMediaObject(string blogid, string username, 
    string password, XmlRpcStruct rpcstruct)
{
    CheckLoggedIn(username, password);

    string name = rpcstruct["name"].ToString(); //file name
    string type = rpcstruct["type"].ToString(); //file type
    byte[] media = (byte[])rpcstruct["bits"];   //file contents
    FileStream stream = File.Create(name);
    stream.Write(media, 0, media.Length);
    stream.Flush();
    stream.Close();
    stream.Dispose();
    XmlRpcStruct rstruct = new XmlRpcStruct();
    rstruct.Add("url","/new filename path/");
    return rstruct;
}
}
}

Additional thoughts

To setup your live writer simply point its MetaWebLog API handler URL you have setup above when setting up your blog account.

You should probably use an SSL website for your web service endpoint to make sure your open text password doesn’t get snooped by l33t script kiddies (you’ve been warned).

You may want to use soft deletes on your blog simply in case someone gets a hold of you live writer install and gets over excited – the thought that someone can remotely delete your posts can be a dangerous thought in the wrong hands.