Skip to content

Metaweblog API

brehaut edited this page Oct 30, 2014 · 10 revisions

If you have discovered necessary-evil because you want to write a metaweblog implementation for your blog, you might find this block of code useful. I considered writing this as a library, but so much of the logic is handled by the specifics of the backend that it wasn't really worth it.

I've used (my specific variation of this) with Mars Edit and it seems to work fine. In Mars Edit I chose to set up my blog as a Movable Type blog with MetaWeblog as the API. If you wanted to support richer UI you would need to either implement movable type or wordpress specific api functions. Notable absent from the implementation below is handling of attachments.

The code implements a minimal metaweblog interface with a dummy database of a map in an atom.

Recipe:

(ns example.metaweblog
  "This namespace provides metaweblog handlers for posting blogs and links."
  (:use [necessary-evil.fault :only [fault]])
  (:require [necessary-evil.core :as xmlrpc]))

;; auth provider

(defn user-may-proceed? 
  "hardcoded the user and password."
  [user password]
  (and (= user "user")
       (= password "xyzzy")))

(defmacro when-auth 
   "A quick and dirty macro to make auth a bit less horrible."
   [user pass & body]
  `(if (user-may-proceed? ~user ~pass)
     (do ~@body)
     (fault -1 "Invalid username or password")))


;; storage backend

(defonce store (atom {}))

(defonce counter (let [c (atom 0)] (fn [] (str (swap! c inc)))))

;; the following functions implement a layer ontop of the database defined above. thus, if you adapt this
;; you probably want to rewrite these functions to not use atom and map functions.

;; the store just holds the basic data that we get from the editor, the only transformation we make is adding
;; the :postid field that is required by marsedit if you wish to edit posts.

(defn store->wire 
  "convert the store structure to wire structure"
  [post post-id]
  (assoc post :postid post-id))

(defn wire->store
  "convert the wire structure to store structure"
  [post]
  post)

(defn new-post
  [post]
  (let [id (counter)]
    (swap! store assoc id post)
    id))

(defn edit-post
  [post-id post]
  (swap! store assoc post-id post))

(defn get-post
  [post-id]
  (@store post-id))

(defn delete-post
  [post-id]
  (swap! store dissoc post-id))
    
(defn get-recent-post-ids
  [count]
  (map first(take count @store)))

(defn get-categories
  []
  ["funny-cats" "programming"])


;; endpoint

(defn metaweblog-endpoint
  "Creates a metaweblog XML RPC endpoint"
  []
  
  (xmlrpc/end-point
   {:metaWeblog.newPost
    (fn [blogid userid password post publish]
      (when-auth userid password
                 (new-post (wire->store post))))

    :metaWeblog.editPost
    (fn [postid username password post publish]
      (when-auth username password
                 (edit-post postid (wire->store post))
                 true))

    :metaWeblog.getPost
    (fn [postid username password]
      (when-auth username password
                 (store->wire (get-post postid) postid)))

    :metaWeblog.getRecentPosts
    (fn [blogid username password num-posts]
      (when-auth username password
                 (map #(store->wire (get-post %) %)
                      (get-recent-post-ids num-posts))))

    :metaWeblog.getCategories
    (fn [blogid username password]
      (when-auth username password
                 (get-categories)))
    
    :blogger.deletePost
    (fn [appkey postid username password publish]
      (when-auth username password
                 (delete-post postid)))}))

You might also find these two functions useful in writing wire->store and store->wire

(defn sanitize-custom
  "Custom fields come down the wire in a weird format. This function fixes that"
  [post]
  (update-in post [:custom_fields]
             #(into {} (map (fn [{:keys [key value]}] [key value]) %))))

(defn unsanitize-custom
  [post]
  (update-in post [:custom_fields]
             #(map (fn [[k v]] {:key k :value v}) %)))   

The Metaweblog API documentation has begun to suffer from substantial link rot, but here are the URLS I used:

If you use Mars Edit, Window > Network Log is an excellent tool for debugging this stuff.

See Also:

Clone this wiki locally