Continuing the story of replacing Windows Live Writer with some custom tools...
One of the best features of WLW was the ability to paste an image into a post and have the image magically appear on the server in an expected location. For my new workflow, I needed a second Markdig extension to look for images in a post and automatically upload the images through a WebAPI I built into this blog. The WebAPI returns the URL of the uploaded (or updated) image. This API, by the way, was a simple replacement for the ancient XML RPC API that WLW relied on.
For this extension I didn't need to custom render an image tag. Instead, I could wait until Markdig finished converting markdown into HTML and go looking for img
tags. One way to implement this style of extension for Markdig is to implement the low level IMarkdownExtension
interface.
public class ImageProcessingExtension : IMarkdownExtension { private readonly IFileSystem fileSystem; private readonly IBlogServer blogServer; public ImageProcessingExtension(IFileSystem fileSystem, IBlogServer blogServer) { this.fileSystem = fileSystem; this.blogServer = blogServer; } public void Setup(MarkdownPipelineBuilder pipeline) { pipeline.DocumentProcessed += Pipeline_DocumentProcessed; } public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) { } private void Pipeline_DocumentProcessed(MarkdownDocument document) { var damn = document.Descendants().ToArray(); foreach(var node in document.Descendants() .OfType<LinkInline>() .Where(l => l.IsImage)) { var localName = node.Url; if (!fileSystem.Exists(localName)) { throw new ArgumentException($"Cannot find file {localName}"); } var bytes = fileSystem.ReadBinary(localName); var newUrl = blogServer.UploadMedia(localName, bytes); node.Url = newUrl; } } }
This extension searches the rendered HTML nodes for images, uploads the image in the URL, and then updates the URL with the remote address.
Note that all this happens inside an event handler which must return void
. However, the UploadMedia
method uses HttpClient
behind the scenes, and must be async
. As you know, async
and void
return types mix together like potassium and water - an example of why I say we must always await the async letdown.