GOPHERSPACE.DE - P H O X Y
gophering on gopher.beastieboy.net
_______________________

 HOW THIS SITE IS MADE

     Nicolas Herry
_______________________


       2017/11/10





1 How this site is made
=======================

  We are at the end of 2017, and blogging software is still a major
  topic of discussion among geeks. Well, not really, let's say I still
  find it interesting. Hmm... Actually, no, it's not even that; I guess
  I just wanted to brag a bit about my setup. Is there anything worth
  bragging about in my setup? I'm not even sure. Still, please keep
  reading!

  My objective with this site was to keep things simple: I wanted an
  easy and comfortable way to write the posts, minimal fuss when putting
  them online and as little server-side software as possible.


1.1 Writing posts
~~~~~~~~~~~~~~~~~

  I naturally use emacs to write the posts for this website. I actually
  use emacs for almost everything, as [I said here before]. I use [org
  mode] for that, and each post is a separate `.org' file. I only use a
  reduced set of properties, where I indicate the date the post was
  written, the author name, and the title. This means each post begins
  with the following preamble:

  ,----
  | #+TITLE: How this site is made
  | #+AUTHOR: Nicolas Herry
  | #+DATE: 2017/11/10
  | #+OPTIONS: toc:nil
  `----

  By default, org mode generates a table of contents, and although this
  might come in handy for some very long posts, it's generally useless
  to me here. The property `OPTIONS: toc:nil' disables this behaviour.

  Since I didn't want to type all this at the beginning of each and
  every file, I looked for a simple solution to insert it dynamically
  for me. There are many completion frameworks, and I could have written
  my own set of functions to do that, but there is already something
  called [yasnippet] which does an impressive job as a templating
  system. yasnippet is already part of my normal setup; all I had to do
  was create a template for my posts. This turned out to be very simple:

  ,----
  | # -*- mode: snippet -*-
  | # name: beastieboy
  | # key: BBP
  | # condition: (string-prefix-p "/home/kafka/org" (buffer-file-name))
  | # --
  | #+TITLE: $1
  | #+AUTHOR: Nicolas Herry
  | #+DATE: `(format-time-string "%Y/%m/%d")`
  | #+OPTIONS: toc:nil
  | 
  | * $1
  | 
  | $0
  `----

  In the above, I give this snippet a name, `beastieboy', as well as a
  key that I will be typing in the buffer to trigger the insertion of
  this template (`BBP', which seemed a rare enough combination of
  letters that any conflict with a real acronym should be avoided). I
  also control the insertion with a condition: yasnippet will only
  insert the template if the elisp code in the condition returns
  non-nil. Here, since I use org mode for many things, I wanted to
  contrain this template only to the posts for this website. An easy way
  to do this is to check the path of the buffer being edited. If it
  contains the directory where the posts are stored, then the template
  can be expanded, otherwise, yasnippet will just do nothing. The
  drawback here is that I am not being very subtle, and I have the path
  hardcoded and not even stored as a variable or anything. I will do the
  right thing; for now, it's good enough.

  The template continues with a separator, `--', which indicates that
  all that comes after it is the actual meat of the template. We find
  the preamble I presented above, with some code to dynamically generate
  the date. We also find three odd markers: one `$0' and two `$1'. The
  former indicates where the cursor should be put once the template has
  been inserted and filled, and the latter marks field the user must
  fill in. When the template is inserted, the cursor will first be
  positioned in the line `#+TITLE: _', instead of the `$1', and I will
  type in a title for the post. Since I have put more than one `$1'
  marker, yasnippet will automatically update the other markers with the
  same number with my typing. This trick allows me to store the title as
  a property for org mode as well as a header, without having to type it
  twice. When I'm done filling this field, pressing `TAB' takes me to
  the next field, or, if there aren't any, to where the `$0' is. I can
  then start typing the post.


[I said here before] file:stuffiuse2017.org

[org mode] http://orgmode.org/

[yasnippet] https://github.com/joaotavora/yasnippet


1.2 Publishing
~~~~~~~~~~~~~~

  I publish the site in two steps, and everything here is once again
  done with emacs. I have defined two sets of projects for org mode in
  my configuration:

  ,----
  | (setq org-publish-project-alist
  |       `(
  |  ("org"
  |   :base-directory "~/org/beastieboy.net"
  |   :publishing-directory "~/beastieboy.net/"
  |   :publishing-function org-html-publish-to-html
  |   :recursive t
  |   :section-numbers nil
  |   :with-toc nil
  |   :base-extension "org"
  |   :html-head ,beastieboy-header
  |   :html-preamble ,beastieboy-preamble
  |   :html-postamble ,beastieboy-footer)
  |  ("org-images"
  |   :base-directory "~/org/beastieboy.net/images"
  |   :publishing-directory "~/beastieboy.net/images/"
  |   :base-extension "png\\|jpg\\|gif"
  |   :publishing-function org-publish-attachment
  |   :recursive t)
  |  ("org-js"
  |   :base-directory "~/org/beastieboy.net/js"
  |   :publishing-directory "~/beastieboy.net/js/"
  |   :base-extension "js"
  |   :publishing-function org-publish-attachment
  |   :recursive t)
  |  ("org-css"
  |   :base-directory "~/org/beastieboy.net/css"
  |   :publishing-directory "~/beastieboy.net/css/"
  |   :base-extension "css"
  |   :publishing-function org-publish-attachment
  |   :recursive t)
  |  ("org-remote"
  |   :base-directory "~/org/beastieboy.net"
  |   :publishing-directory "/ssh:beastieboy@beastieboy.net:/usr/local/www/beastieboy.net/"
  |   :publishing-function org-html-publish-to-html
  |   :recursive t
  |   :section-numbers nil
  |   :with-toc nil
  |   :base-extension "org"
  |   :html-head ,beastieboy-header
  |   :html-preamble ,beastieboy-preamble
  |   :html-postamble ,beastieboy-footer)
  |  ("org-images-remote"
  |   :base-directory "~/org/beastieboy.net/images"
  |   :publishing-directory "/ssh:beastieboy@beastieboy.net:/usr/local/www/beastieboy.net/images/"
  |   :base-extension "png\\|jpg\\|gif"
  |   :publishing-function org-publish-attachment
  |   :recursive t)
  |  ("org-js-remote"
  |   :base-directory "~/org/beastieboy.net/js"
  |   :publishing-directory "/ssh:beastieboy@beastieboy.net:/usr/local/www/beastieboy.net/js/"
  |   :base-extension "js"
  |   :publishing-function org-publish-attachment
  |   :recursive t)
  |  ("org-css-remote"
  |   :base-directory "~/org/beastieboy.net/css"
  |   :publishing-directory "/ssh:beastieboy@beastieboy.net:/usr/local/www/beastieboy.net/css/"
  |   :base-extension "css"
  |   :publishing-function org-publish-attachment
  |   :recursive t)
  |  ("beastieboy" :components ("org" "org-images" "org-js" "org-css"))
  |  ("beastieboy-remote" :components ("org-remote" "org-images-remote" "org-js-remote" "org-css-remote"))))
  `----

  The first set comprises `org', `org-images', `org-js' and
  `org-css'. They all point to different locations in the tree where I
  store all my data for this website. The first one deals with the
  posts, the next three configure org mode to just copy the files
  verbatim to their destination. All this is then regrouped under the
  composite project `beastieboy'. I use this first set of projects as a
  kind of pre-production: I generate the site locally on my PC, and I
  check how it looks, proofread the post, and so on.

  When I am happy with what I have, I trigger the publication of the
  second set of projects, those ending with `-remote' and grouped under
  `beastieboy-remote'. The configuration is identical, except for the
  fact that the destination folder is an ssh path to my server. org mode
  is then kind enough to call [tramp], the emacs mode for transparently
  accessing remote files, as the documentation says. In my case, tramp
  pushes everything recursively to my server, and the website is
  published.

  org mode allows the user to specify headers and footers for the
  generated pages, which is how I hook up the CSS bits to the
  articles. My configuration, in a dedicated `.el' file under
  `~/.emacs.d/', is straightforward:

  ,----
  | ;; custom header, footer, etc.
  | (defvar beastieboy-header
  |   "")
  | (defvar beastieboy-preamble
  |   "
  |      

BeastieBoy


  |      

FreeBSD, Lisp, Emacs, PostgreSQL & co.


  |    

  |    
  |     

      |       
  • Home

  •   |       
  • About

  •   |       
  • Contact

  •   |     

  |    
")
  | (defvar beastieboy-footer
  |   "
  |       © 2017 %a.

  |       Created %d.

  |       Last updated %C. 

  |       Built with %c.

  |       
  |    
")
  `----
  In the above, I set the author's name to that found in the properties
  of the post (the `#+AUTHOR:' bit, called with `%a' here), the creation
  date for the post (taken from `#+CREATED' in the preamble, called with
  `%C'), the date and time the page was generated (called with `%d'), I
  indicate the version of emacs and org mode that I used to generate the
  page and I put a nice little blinking Beastie to show my love to the
  daemon.

  The website itself is handled by nginx, as a set of static HTML
  files. Since there is no dynamic part or anything, the configuration
  is kept to a minimum: a single line in `nginx.conf' to point to the
  document root.


[tramp] https://www.gnu.org/software/tramp/


1.3 What's coming/missing
~~~~~~~~~~~~~~~~~~~~~~~~~

  In my trying to keep things as simple as possible, I also left aside a
  few things that I arguably should look into: HTML5 support, a proper
  mobile CSS, code-colouring in the examples, an index generated
  automatically (today, I still copy and paste all the titles in the
  index page), a better index with a split between short news and longer
  articles, an integration with [flyspell], the minor mode for
  spell-checking in emacs. I keep you posted.


[flyspell] https://www.emacswiki.org/emacs/FlySpell