Generating a static version of my Orchard CMS blog - A Lazy Sunday Experiment

My blog, like most blogs, is pretty simple. There is only one interactive aspect, aside from the entire admin area, and that is comments. And let's be honest here, Orchard's comments are pretty lacklustre. Embeddable comment platforms like Disqus are hugely popular now and so that should solve the comments problem. I originally shied away from comments because I had lofty ideals about my blog being free from third party tracking and not contribute the gradual erosion of privacy across the internet. But I'm now skint so those have all gone out the window, I've added Google analytics, ads, AMP integration and Disqus comments are coming; full on pandering to my nosey overlords. I also cant justify the cost of a web server to host my blog when I could do it for nothing on a different platform.

I previously looked at some static generation platforms because they are the in thing, or they were, it all seems to change. But there are some seriously mature and great platforms out there, as I'm sure you know, and if you don't a quick Google will enlighten you no end. However, I have this problem where I really like to build useless stuff. So I woke up one lazy Sunday morning and decided to write a module for Orchard that would generate my static site.

I first planned to have a static part attached to content items, grab these items from the db and render them, capture the output using a result filter, have providers for customizing output for different content parts, overrides for handling pagination etc. After 10 minutes dreaming up this bonkers framework of static generating, I threw those ideas away as overkill. I wanted it to just work and be deployed by the end of the day. So I built a crawler.

This is all a bit weird because my own blog creates a crawler to crawl my own blog. Very meta. The entire website is downloaded into the App_Data folder where you can optionally sync it with a third party service. Currently it supports uploading to Netlify and GitHub Pages (technically if you are deploying to another service using CI and GitHub as your repo then this would work for that as there is nothing specific to GH pages). Naturally, this being Orchard, this set of sync targets is extensible and there are a couple of hooks during the generation process if you need to customize the output. And by the end of my slightly less-than-usual lazy Sunday, I'd thrown my blog up onto GitHub Pages.

I'll hopefully publish the code up on GitHub very soon (must remove hard coded GitHub password...) so anyone interested can take a look but here is a summary of what happens. When a page is fetched and the HTML is parsed by AngleSharp. All internal links on the page are found. All scripts and CSS are found. Any paginated links are converted into a paginated form that doesn't use querystring paramaters (e.g. /blog?page=5 becomes /blog/5). The homepage is the first page fetched and then it recursively calls all discovered urls. A setting allows you to add extra urls to fetch. The discovered HTML is saved in the App_Data/StaticSite/{tenant_name}/Output. Most of this is done in event handlers so you can plug additional handlers in. For example, I made one to check if a page had a corresponding AMP page (specified in the <link rel="amphtml" /> tag) and if so, fetch that page too. Another example would be one to add the rss feeds. The entire media folder for the current tenant is copied into the Output folder as well, based on a flag (no need to do this if using S3 or Azure blob storage). I didn't really want to do this but I needed it for working with git. Another issue is that the site's base url will be different once the static site is uploaded so when the site is being generated the base url is modified for that request. I cant remember what problems this was causing me but there were several. Once it is generated you can optionally sync the site with an external provider. I built two, one for GitHub Pages and one for Netlify. You don't need to use these, you can manually push to git, or upload as a zip to Netlify, or upload to S3 etc.

The ultimate question is how useful is this? To be honest, not very. As I have written about before, I really don't think Orchard's editor experience is good enough and it is missing some key plugins imho. There are also so many great static generators out there. However, it does demonstrate how versatile Orchard is and with OrchardCore on the horizon, I think it would be a great tool to have available. Static sites are often all that is needed for many cases, with the wonderful addition of being cheap to host and scale.