reykfloeter – blog

Blog about my blog

Posted by
Reyk Floeter
on

Kristaps keeps on releasing very useful free software. Most well-known is mandoc, at least everyone is using it for manpages these days, but there is is also his BCHS (beaches) web stack which strongly advertises OpenBSD's httpd. Great. I also use kcgi whenever I have to write small CGIs. So sblg seemed like the right choice to me.

slbg, or "the simple off-line blog utility", generates a blog from XML pages. It is static because it generates the HTML pages without needing any dynamic content. This is exactly what I want as my simple blog doesn't need a database or any on-the-fly processing to generate the output. All it needs is to display articles and to take care about indexes, tags, links, and some page templates. Dynamic is over, static is cool!

But there was still a problem: XML. I don't want to write my articles in XML. Using languages with markup to write longer texts doesn't work for me - the text is hard to read and I very quickly get lost in "optimizing" the "source code" of it. Have you ever procrastinated by tweaking your LaTeX macros? Did you ever get disgusted by DocBook's syntax? That's why I decided to write my articles, including this one, in Markdown and use another tool such as lowdown to generate the XML pages for sblg.

The Makefile

If you read Kristaps' instructions on the sblg page, you will first get a bit confused about all the steps to generate the sblg blog. But there is one important statement after the examples: A Makefile makes this easy. The example looks very easy indeed:

XMLS = article1.xml article2.xml
ARTICLES = article1.html article2.html
all: index.html $(ARTICLES)
index.html: index.xml $(ARTICLES)
    sblg -o $@ -t index.xml $(ARTICLES)
.xml.html:
    sblg -o $@ -t article.xml $<

I copied this version and used it as a starting point for my Makefile. I'm not necessarily proud of it, because it quickly turned into more than 100 lines instead of the 7 lines example.

Let me quickly iterate over my current Makefile. I keep on tweaking this file, so it might have been changed by the time you are reading this article. Please note that the Makefile is written for OpenBSD's make, a distant derivative of pmake which is not like GNU make.

First there is the header:

.SUFFIXES: .xml .html .md

ARTICLEMDS  != ls posts/*.md 
ARTICLES     = $(ARTICLEMDS:S/.md$/.html/g)
ARTICLEXMLS  = $(ARTICLEMDS:S/.md$/.xml/g)
ATOM         = atom.xml
HTMLS        = $(ARTICLES) index.html
TEMPLATEXMLS     = templates/index.xml templates/article.xml templates/tags.xml
TEMPLATES    = $(TEMPLATEXMLS:S/.xml$/.html/g)
TAGS         = tags/index.html posts/tag-cloud.html

CLEANFILES   = $(ATOM) $(ARTICLES) $(ARTICLEXMLS) $(HTMLS) $(TAGS)
CLEANFILES  += $(ARTICLES:S/.html//) $(TEMPLATES) posts/tag-cloud*
CLEANFILES  += tags/* *~ posts/*~ templates/*~ css/*~

The "source" of my blog articles is a number of Markdown files in the posts/ directory. Instead of listing each article by name, I decided to generate the $(ARTICLEMDS) lists with a simple ls. Variable modifiers are used to create the lists of the generated files, including $(ARTICLEXMLS) and $(ARTICLES).

all: $(HTMLS) $(ATOM) tags

install:
    # none

$(ARTICLES): $(ARTICLEXMLS) templates/article.html 

$(TEMPLATEXMLS): tags/index.html

index.html: templates/index.html $(ARTICLES) $(TAGS)
    sblg -o $@ -t templates/index.html $(ARTICLES)

atom.xml index.html $(ARTICLES) tags:

# Only include articles tagged with "blog" in the feed
# (it should be possible to filter this with sblg)
atom.xml: templates/atom.xml $(ARTICLES)
    BLOG=$$(grep -l "Tags:.*blog" $(ARTICLEMDS) | sed 's/\.md/.html/g'); \
    sblg -o $@ -t templates/atom.xml -a $

After some general rules I generate the Atom file for the blog's RSS feed. I only want to include articles in the feed that are tagged with blog, to allow hidden or drafted articles without the tag that don't appear in any of the indexes. Unlike other modes, sblg's -a mode doesn't seem to provide a built-in way to filter articles based on their tags. So I filter the articles manually by grep'ing a list of files that include the "blog" tag in the Markdown metadata. I'm talking with Kristaps about all the possible improvements in sblg, so maybe he will help to reduce such scripting hacks in the future.

# Generate list of known tags...
tags/index.txt: $(ARTICLEXMLS)
    mkdir -p tags
    sblg -o- -rl $(ARTICLEXMLS) | awk '{ print $$1 }' | sort | uniq > $@

# ...and a simple HTML "tag cloud" for the menu...
tags/index.html: tags/index.txt
    { (for i in $$(cat tags/index.txt); do \
        sblg -rl posts/*.xml | grep "^$$i" | wc -l | tr -d '\n'; \
        echo " $$i $$i"; \
    done) | sort -R | \
    xargs printf '<span class="tag-cloud tag-cloud-%d">\
        <a href="tags/%s">%s</a></span>\n'; } > $@

# ...and a page for each tag
tags: tags/index.txt templates/tags.html index.html
    for i in $$(cat tags/index.txt); do \
        sed "s/@NAVTAG@/$$i/g" templates/tags.html > tags/$$i.xml; \
        sblg -o tags/$$i.html -t tags/$$i.xml $(ARTICLES); \
        ln -sf $$i.html tags/$$i; \
    done

# ...and a fancy full-page tag cloud
posts/tag-cloud.html: $(TEMPLATES)
    sblg -o- -t templates/article.html templates/tag-cloud.xml | \
        sed -e '/@TAGS@/r tags/index.html' -e '/@TAGS@/d' > $@
    ln -sf tag-cloud.html posts/tag-cloud

The sblg -l option allows extracting all the tags from the articles. I use it to create a few files: tags/index.txt is the sorted list of tags, tags/index.html is an HTML version that is used by the "tag cloud", and posts/tag-cloud.html is an auto-generated post with a large version of the tag cloud.

I think that any serious blog needs a tag cloud ;-), so I decided to generate one with a simple shell script that is included in the tags/index.html target above: for each tag in tags/index.txt it counts the number of occurrences in the articles, prints a line with tag name and the count, sorts the output list of tags randomly, and prints an HTML line with the CSS class tag-cloud-%d where %d is the count. The css/style.css style sheet includes a number of different tag-cloud classes that adjust the opacity and scale the font-size up to 300%. It is all about the cloud.

$(TEMPLATEXMLS): templates/header.xml templates/footer.xml templates/navbar.xml

# Generate templates including a menu on the right side
# 1. Generate sidebar template from all articles
# 2. Mark sidebar nav as processed (data-sblg-nav="0")
# 3. Insert tag cloud
# 4. Insert sidebar into template
# 5. Insert other page elements (header, footer, navbar)
$(TEMPLATES): $(TEMPLATEXMLS) $(ARTICLESXMLS) templates/sidebar.xml
    sblg -o- -t templates/sidebar.xml $(ARTICLEXMLS) | \
    sed -e 's/data-sblg-nav="1"/data-sblg-nav="0"/g' \
        -e '/@TAGS@/r tags/index.html' -e '/@TAGS@/d' | \
    sed -e '/@SIDEBAR@/r /dev/stdin' -e '/@SIDEBAR@/d' \
        -e '/@HEADER@/r templates/header.xml' -e '/@HEADER@/d' \
        -e '/@FOOTER@/r templates/footer.xml' -e '/@FOOTER@/d' \
        -e '/@NAVBAR@/r templates/navbar.xml' -e '/@NAVBAR@/d' \
        -e '/@SOCIAL@/r templates/social.xml' -e '/@SOCIAL@/d' \
        $(@:S/.html/.xml/) > $@

I need different templates for various use cases: the index page (index.html), tag index pages (eg. tags/relayd.html), and a template for the posted articles. All these templates include common parts such as header and footer so I use my own "templating" to create the actual templates from a number of files. I never knew about sed's /r function to include files before I used it here, but I also think that sblg should support an include directive in templates directly.

.xml.html:
    sblg -o $@ -t templates/article.html -c $<
    ln -sf $(@:S/posts\///) $(@:S/.html//)

.md.xml: templates/social.xml
    TAGS=$$(lowdown -X Tags $< | tr -d '\t'); \
    TITLE=$$(lowdown -X Subject $< | tr -d '\t'); \
    AUTHOR=$$(lowdown -X From $< | tr -d '\t'); \
    DATE=$$(lowdown -X Date $< | tr -d '\t'); \
    { echo "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"; \
      printf "<article data-sblg-article=\"1\" data-sblg-tags=\"%s\">\n\
        <header>\n\
        <h1><a href=\"%s\">%s</a></h1>\n\
            <div>Posted by <address>%s</address> on <time datetime=\"%s\">%s</time></div>\n\
        </header>\n" "$$TAGS" "$(@:S/.xml//)" "$$TITLE" "$$AUTHOR" "$$DATE" "$$DATE"; \
      lowdown $< ; \
      cat templates/social.xml; \
      echo "</article>"; } | \
    sed -e '2,/<p>/s/<p>/<aside><p>/g' -e '1,/<hr\/>/s/<hr\/>/<\/aside>/g' >$@

The first target generates the final HTML pages from XML files, the second target generates such XML files from Markdown. There is an article on the sblg page that describes the steps to use Markdown with sblg and I extended it to include the necessary metadata for title, author, date, and tags. It also adds a templates/social.xml file that adds a common footer for all the articles. Once again, it would be nice to have an include directive in sblg directly. I even think that sblg could have built-in Markdown support by linking to liblowdown, but this is probably going a bit too far.

clean:
    rm -f $(CLEANFILES)

While make works to update everything with new articles, running make clean in my web server directory is currently the only option to remove dependencies to removed articles. This is not optimal as it temporarily purges the entire web content and I need a better "update" method in the future.

Permalinks

A blog needs permanent links for the articles that remain unchanged for many years in the future. And these links are ideally semantic URLs that are easy to remember and human-readable. Instead of using an URL like /article9.html or even a classic example like /index.php?id=9, a semantic URL consists of a path and a slug, a human-readable part of the URL that is typically generated from the title of the page, like /posts/blog-about-my-blog.

As sblg generates static pages, it is very easy to create semantic URLs by just naming the files accordingly. All sblg-generated files end with the .html extension but a typical slug doesn't have such an extension. It wouldn't work as a software-independent permalink - what if I ever dare to switch my blog to .php?.

My current trick is to create symbolic links in the Makefile, pointing from pages without the extension to the generated .html pages. The httpd.conf file includes a statement to change the default media type from application/octet-stream to text/html that will be used for any file without a (known) extension.

server "reykfloeter.com" {
    alias "www.reykfloeter.com"
    default type text/plain
    location "/posts/*" {
        default type text/html
    }
    location "/tags/*" {
        default type text/html
    }

As I previously hosted my "dead" blog on tumblr, I added the following statement to redirect requests to the old tumblr URLs to the new location of this blog. tumblr previously used the same slugs for my articles, but the path was under /post/ with an additional numeric identifier. I don't think that the ID part qualifies as a good permalink and I don't need it in my blog. The compatibility is solely based on the slug:

    # compatibility with old tumblr blog
    location match "/post/%d+/(.*)" {
        block return 301 "/posts/%1"
    }

Configuring httpd is out of the scope of this article, but I can sincerly recommend Michael W. Lucas' book Httpd and Relayd Mastery. I wrote most of the software that is explained in the book, but I even learned some things when I read the book and reviewed Michael's drafts (and now I got a topic for another blog post).

The blog theme

I'm not a designer or web developer, but I appreciate good looking web pages. I wanted to have something that is responsive, works on desktops and mobiles, looks somewhat modern, works without JavaScript, but doesn't disqualify me for all the eye candy from a geek point of view.

I bootstrapped the theme by creating a simple grid layout with a fairly typical blog style: banner, top menu, middle text, sidebar. In 2017, bootstrap is probably a vintage (or retro) framework but it makes it very easy to create responsive pages with a proper layout and without caring about all the CSS and HTML5 madness too much. I also use Font Awesome because it is awesome, provides some fancy icons, and was suggested in sblg's example templates (let's blame Kristaps for it). I do not include any JavaScript which prevents me from using bootstrap's responsive hamburger menu.

I was always concerned about adding external CSS or JavaScript dependencies to my web content, but the integrity attribute and the Subresource Integrity feature is very useful to make sure that we get what we expected.

    <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous" />
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" />

TODO

Two things that I'm missing are a search box and comments. An easy trick for a search box is the integration of DuckDuckGo (or Google, if you insist) with the "site:reykfloeter.com" option. But that reminds me that my Makefile, or sblg, should also generate an XML sitemap for the search engines. An internal search option would be much nicer.

Adding an option to comment is rather difficult as it conflicts with two of my concepts: no JavaScript and no dynamic content. I'm also not really fond of adding an external provider or even facebook for comments. A possible option to allow comments without the need for tracking my users with accounts would be a simple form and kcgi program that generates comment files from restricted input. But then I'd have to deal with potential spam and abuse myself. Maybe I should just refer to a platform like Lobsters or even Twitter for comments. Who reads my blog anyway?

What else? I'm using git to maintain the articles and this blog.

Every blog needs a name

I have to admit that "reykfloeter" is not an ideal name for a blog. My actual name is "Reyk Flöter", and I normally just use my first name "reyk" as a user- and nickname, but it was taken when I registered my Twitter account and the related domain. So I picked reykfloeter in a few places.

I'm aware that my German last name is nearly unpronounceable for others, so "reykfloeter" appears like a random concatenation of letters. As most of us, I own a number of domains and maybe I should move the blog to bsd.plumbing (which is used as a home for relayd and httpd), arc4random.com (but I intended to use it as a fine OpenBSD-powered Entropy-as-a-Service for poor Linuxers), or even copper.coffee?

In addition to the domain, I also need a good blog name or tag line. A very memorable example in the BSD world is Peter Hansteen's THAT GRUMPY BSD GUY blog. So what should I use?

  • Reyk Flöter's blog
  • OpenBSD hacker. Coffee nerd. Founder.
  • Ask Reyk (imaginary how-tos and 10 step guides)
  • Sewage, Drainage and BSD Plumbing (bsd.plumbing/blog)
  • A Replacement Call for Random (arc4random.com)
  • Coffee with Reyk (copper.coffee)
  • ...

For now it will just be reykfloeter - blog.


UPDATE: The initial version of the Makefile was done for OpenBSD 6.1-stable which included an older version of sblg. The output of -l has changed in the current version and needs an additional -r flag to be compatible. I also removed the unused CSSS variable.


Permalink, Source, Tags: blogmischttpd