[ASP] Rendre un site exploitable via Live Writer

Publié le 03 décembre 2008 par Jeremy.jeanson

Pour les amoureux des blogs... ou plutôt ceux qui ne savent plus comment vivre avec, il existe un outil magnifique "Windows Live Writer". Je ne vais pas m' attarder sur les bons ou mauvais points du logiciel, mais sur ce qu'il faut faire pour l' exploiter avec un blog ASP existant.

Ce qu'il faut comprendre avant de commancer : Live Writer permet d'utiliser un grand nombre d'API pour le control de votre blog. Dans le code présenté ici je me suis basé sur l'API MetaBlog (à peu de choses près le même que celui qui est utilisé par blogger et community). Cet API a besoin d'un handler (.ashx) et du protocole XMLRPC.

Afin de ne pas avoir à trop entrer dans XMLRPC j'ai utilisé l'assembly CookComputing.XmlRpcV2.dll qui est disponible sur le site http://www.xml-rpc.net/. Celui-si se charge de retourner les réponses à Live Writer dans ce fameux format XML.

Maintenant attaquons nous à la mise en place!

Pour réaliser notre potion magique, nous avons besoin d'un handler ashx et d'une classe cs. Retenez l'url du fichier ashx, car ce serra celle qu’il faudra entrer dans Live Writer pour piloter votre blog.

Fichier metabloweblog.ashx :

<%@ WebHandler Language="C#" Class="MonNameSpace.MetaWeblog, MonAssembly" %>

Le canevas de la classe MetaWeblog.cs :

namespace MonNameSpace
{
    using System;
    using System.Data;
    using System.Configuration;
    using System.Linq; // dans mon cas je manipule la couche de données avec Linq :)
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Linq;
    using CookComputing.XmlRpc;
    using System.Collections.Generic;

    /// <summary>
    /// Description résumée de MetaWeblog
    /// </summary>
    [XmlRpcService(
        Name = "blogger",
        AutoDocumentation = true)]
    public class MetaWeblog : XmlRpcService
    {
        /// <summary>
        /// Valider l'utilisateur
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <returns></returns>
        private Boolean ValideUser(String userName, String passWord)
        {
			// Mon blog utilisant le provider ASP voici ma méthode d'authentification
            if (Membership.ValidateUser(userName, passWord.Substring(usage.Length) ))
            {
                return Roles.IsUserInRole(userName, "Administrateurs");
            }
            else
            {
                // Retourner le code erreur
                HttpContext.Current.Response.Clear();
                HttpContext.Current.Response.Write("403");
                HttpContext.Current.Response.Flush();
                return false;
            }
        }

        /// <summary>
        /// Ecrire un post au format xmlRpc
        /// </summary>
        /// <param name="post"></param>
        /// <returns></returns>
        private XmlRpcStruct Post2XmlRpc(String postId)
        {
			// TODO : implémenter le code accédant à votre post
            XmlRpcStruct retour = new XmlRpcStruct();
            retour.Add("postid", postId);
            retour.Add("dateCreated", ...date de création du post...);
            retour.Add("title", ...le titre du post...);
            retour.Add("link", ...le lien vers la page de ce post...);
            retour.Add("description", ...le corps de votre post...);
            retour.Add("categories", new String[] { ...votre liste de categories pour ce post... });
            retour.Add("publish", true);

            return retour;
        }

        /// <summary>
        /// Retourne la liste des blogs de l'utilisateur
        /// </summary>
        /// <param name="appKey"></param>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <returns></returns>
        [XmlRpcMethod("blogger.getUsersBlogs")]
        public XmlRpcStruct[] getUsersBlogs(String appKey, String userName, String passWord)
        {
            if (!this.ValideUser(userName, passWord)) return null;
			
			// Méthode très importante car elle sert à vérifier que votre utilisateur a bien le droit d'administrer votre blog
			// Exemple de retour dans le cas d'un seul blog
            XmlRpcStruct retour = new XmlRpcStruct();
            retour.Add("blogid", ... id de votre blog ...);
            retour.Add("url", ... url du blog de l'uilisateur ...);
            retour.Add("blogName", ... nom du blog ...);

            return new XmlRpcStruct[] { retour };
        }

        /// <summary>
        /// Retourner la liste des catégories
        /// </summary>
        /// <param name="blogId"></param>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.getCategories")]
        public XmlRpcStruct[] getCategories(String blogId, String userName, String passWord)
        {
            if (!this.ValideUser(userName, passWord)) return null;

            // TODO : implémenter ici une méthode qui liste les catégories de votre blog
            // chaque rpcstruct doit contenir les champs suivants :
            // "categoryid"
            // "title"

			// Par exemple
            XmlRpcStruct retour = new XmlRpcStruct();
            retour.Add("categoryid", ...);
            retour.Add("title", ...;
			
			return new XmlRpcStruct[] { retour };
        }

        /// <summary>
        /// Retouner la liste des posts récents
        /// </summary>
        /// <param name="blogId"></param>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <param name="numberOfPosts"></param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.getRecentPosts")]
        public XmlRpcStruct[] getRecentPosts(String blogId, String userName, String passWord, Int32 numberOfPosts)
        {
            // Valider l'utilsiateur
            if (!this.ValideUser(userName, passWord)) return null;

            // Todo : Retourner la liste des post ("numberOfPosts" indique le nombre de post demandés)
			// une petite boucle for avec la méthode Post2XmlRpc(id) ci-dessu et le tour est joué
        }

        /// <summary>
        /// Retouner le post demandé
        /// </summary>
        /// <param name="postId"></param>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.getPost")]
        public XmlRpcStruct getPost(String postId, String userName, String passWord)
        {
            if (!this.ValideUser(userName, passWord)) return null;

            // TODO : implémenter ici le code permettant de retourner l'un post
			this.Post2XmlRpc(postId);
        }

        /// <summary>
        /// Supprimer un post
        /// </summary>
        /// <param name="appKey"></param>
        /// <param name="postid"></param>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <param name="publish"></param>
        /// <returns></returns>
        [XmlRpcMethod("blogger.deletePost")]
        public Boolean deletePost(String appKey, String postId, String username, String password, Boolean publish)
        {
            if (!this.ValideUser(username, password)) return false;

            // TODO : implémenter ici le code permettant la suppresion d'un post
        }

        [XmlRpcMethod("metaWeblog.newMediaObject")]
        public XmlRpcStruct newMediaObject(String blogid, String userName, String passWord, XmlRpcStruct rpcStruct)
        {
            if (!this.ValideUser(userName, passWord)) return null;

			// la strucure XMLRP comprend entre autre :
			// rpcStruct["name"]
			// rpcStruct["type"]
			// rpcStruct["bits"]

			// TODO : implémenter ici le code permettant la création d'un nouveau fichier sur votre serveur

            // par exmple :

            String name = rpcStruct["name"].ToString(); //object name
            String type = rpcStruct["type"].ToString(); //object type
            Byte[] media = (Byte[])rpcStruct["bits"];   //object body
            String path = HttpContext.Current.Server.MapPath("..."); // path vers votre espace de stockage
            if (!System.IO.Directory.Exists(path)) System.IO.Directory.CreateDirectory(path);

            Int32 index = name.LastIndexOf('/');
            if (index != -1)
            {
                name = name.Substring(index + 1);
            }
            System.IO.FileStream stream = System.IO.File.Create(
                String.Concat(
                    path,
                    name));

            stream.Write(media, 0, media.Length);
            stream.Flush();
            stream.Close();
            stream.Dispose();

            XmlRpcStruct retour = new XmlRpcStruct();
            retour.Add("url",
                String.Format(
                    "path relatif vers votre epace de stockage {0}",
                    name));

            return retour;
        }

        /// <summary>
        /// Créer un nouveau post
        /// </summary>
        /// <param name="blogId"></param>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <param name="rpcStruct"></param>
        /// <param name="publish"></param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.newPost")]
        public String newPost(String blogId, String userName, String passWord, XmlRpcStruct rpcStruct, Boolean publish)
        {
            if (!this.ValideUser(userName, passWord)) return false;

			// la strucure XMLRP comprend entre autre :
			// rpcStruct["title"]
			// rpcStruct["description"]
			// rpcStruct["categories"]

			// TODO : implémenter ici le code permettant la création d'un nouveau post
            return true;
        }

        /// <summary>
        /// Editer un post
        /// </summary>
        /// <param name="postId"></param>
        /// <param name="userName"></param>
        /// <param name="passWord"></param>
        /// <param name="rpcStruct"></param>
        /// <param name="publish"></param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.editPost")]
        public Boolean editPost(String postId, String userName, String passWord, XmlRpcStruct rpcStruct, Boolean publish)
        {
            if (!this.ValideUser(userName, passWord)) return false;

			// la strucure XMLRP comprend entre autre :
			// rpcStruct["title"]
			// rpcStruct["description"]
			// rpcStruct["categories"]

			// TODO : implémenter ici le code permettant la modifcation d'un post existant
            return true;
        }

        [XmlRpcMethod("metaWeblog.setTemplate")]
        public Boolean setTemplate(String appKey, String blogid, String username, String password, String template, String templateType)
        {
            // TODO: A implémenter si vous souahiter permettre la modification du template de post
            return true;
        }

        [XmlRpcMethod("metaWeblog.getTemplate")]
        public string getTemplate(string appKey, string blogid, string username, string password, string templateType)
        {
            // TODO: A implémenter si vous souahiter permettre la récupération du template de post
            return String.Empty;
        }

    }
}

Avec ce canevas de code, il ne vous reste plus qu' à compléter les zone marqué "TODO", et le tour est joué.

Après pour paramètrer Live Writer, on renseigne l'url vers le fichier .ashx en indiquant qu'on utilise l'API MetWebLog et le compte de l' administrateur du blog.

Code testé + approuvé depuis plusieurs mois et utiliser pour rédigé ce post :).