Mowson.org/karl

This website was built with Ikiwiki (v2.44, last time I checked) and AsciiDoc

I cobbled together an asciidoc plugin (my first ever perl programming) for ikiwiki.

To use it, this plugin goes in your plugin directory (a local one specified in ikiwiki.setup, or in the system plugin directory) and then you need to add "asciidoc" to the list of plugins specified in ikiwiki.setup - if you don't get this right, then when you try to pass an asciidoc conf to ikiwiki then it'll complain about "Unknown option". Note that asciidoc is probably not suitable for online wiki editing by untrusted users - asciidoc lets you do lots of things which could be used as exploits.

#!/usr/bin/perl
# AsciiDoc (*.adoc) handler for ikiwiki.
# Inspired by Felix Obenhuber's work with asciidoc.
#
# Karl Mowatt-Wilson, May 2007.   http://mowson.org/karl
# (crudely hacked from otl.pm)

# Accepts one command-line parameter to ikiwiki:
#   ikiwiki --asciidoc-conf=/path/to/asciidoc.conf
#
# Or you can add it to ikiwiki.setup with a line like this:
#   asciidoc_conf => "/path/to/asciidoc.conf",
#
# Note that ikiwiki-generated html needs to be escaped so that asciidoc
# will pass it through untouched.  In general, this means prefixing and
# postfixing with '+++'
#
#  eg.    +++[[map pages="*"]]+++
#
# Things will get complicated if anything in your ikiwiki code generates
# a '+++' sequence...       (or anything else which asciidoc considers special)
#
# You can't use [[ .. ]] constructs in the asciidoc sections - they'll
# just get interpreted as being ikiwiki constructs, and probably make
# a mess.  You need to use asciidoc equivalents.
#  eg. instead of asciidoc [[anchor]] use [id="anchor"]

package IkiWiki::Plugin::asciidoc;

use warnings;
use strict;
use IkiWiki;
use File::Basename;

sub import {    #{{{
    hook( type => "htmlize", id => "adoc", call => \&htmlize );
    hook( type => "getopt",  id => "adoc", call => \&getopt );

}    # }}}

sub getopt () {    #{{{
    eval q{use Getopt::Long};
    error($@) if $@;
    Getopt::Long::Configure('pass_through');
    GetOptions( "asciidoc-conf=s" => \$config{asciidoc_conf} );
}    #}}}

sub htmlize (@) {    #{{{
    my %params = @_;

    # cd into dir of source file so that any asciidoc file-includes
    # (eg. including a script listing in a page) have the correct dir to base
    # relative paths from.
    # Normally, asciidoc would get this info from the source file path, but we
    # are feeding asciidoc through a pipe, so it doesn't know the path.
    chdir dirname( $config{srcdir} . "/" . $params{page} );

    # This forking business may not be necessary with asciidoc, but I don't know:
    # Can't use open2 since otl2html doesn't play nice with buffering.
    # Instead, fork off a child process that will run otl2html and feed
    # it the content. Then read otl2html's response.

    my $tries = 10;
    my $pid;
    do {
        $pid = open( KID_TO_READ, "-|" );
        unless ( defined $pid ) {
            $tries--;
            if ( $tries < 1 ) {
                debug("failed to fork: $@");
                return $params{content};
            }
        }
    } until defined $pid;

    if ( !$pid ) {
        $tries = 10;
        $pid   = undef;

        do {
            $pid = open( KID_TO_WRITE, "|-" );
            unless ( defined $pid ) {
                $tries--;
                if ( $tries < 1 ) {
                    debug("failed to fork: $@");
                    print $params{content};
                    exit;
                }
            }
        } until defined $pid;

        my @asciidoc_args = (
            defined $config{asciidoc_conf}
            ? ( '--conf-file', "$config{asciidoc_conf}" )
            : (),
            '--no-header-footer',
            '--out-file', '-',
            '-'
        );

        if ( !$pid ) {
            if ( !exec 'asciidoc', @asciidoc_args ) {
                debug("failed to run asciidoc: $@");
                print $params{content};
                exit;
            }
        }

        print KID_TO_WRITE $params{content};
        close KID_TO_WRITE;
        waitpid $pid, 0;
        exit;
    }

    local $/ = undef;
    my $ret = <KID_TO_READ>;
    close KID_TO_READ;
    waitpid $pid, 0;

    return $ret;
}    # }}}

1

This is a close approximation of the ikiwiki.setup I use for this website. It needs tidying, later, sometime.

#!/usr/bin/perl
# Configuration file for ikiwiki.
# Passing this to ikiwiki --setup will make ikiwiki generate wrappers and
# build the wiki.
#
# Remember to re-run ikiwiki --setup any time you edit this file.

use IkiWiki::Setup::Standard {
        wikiname => "example.org",
        #adminuser => ["yourname", ],
        #adminemail => 'me@example.org',

        # Be sure to customise these..
        url => "http://mowson.org/",
        srcdir        => "/home/karl/prj/web/src.mowson.org/karl",
        destdir       => "/home/karl/prj/web/mowson.org/karl",
        templatedir   => "/home/karl/prj/web/src.mowson.org/_wikitemplates_",
        underlaydir   => "/home/karl/prj/web/src.mowson.org/_basewiki_",
        asciidoc_conf => "/home/karl/prj/web/src.mowson.org/_cfg_/mowson_asciidoc.conf",

        cgiurl => "",

        # Subversion stuff.
        #rcs => "svn",
        #historyurl => "http://svn.example.org/trunk/[[file]]",
        #diffurl => "http://svn.example.org/trunk/[[file]]?root=wiki&amp;r1=[[r1]]&amp;r2=[[r2]]",
        #svnrepo => "/svn/wiki",
        #svnpath => "trunk",

        # Git stuff.
        #rcs => "git",
        #historyurl => "http://git.example.org/gitweb.cgi?p=wiki.git;a=history;f=[[file]]",
        #diffurl => "http://git.example.org/gitweb.cgi?p=wiki.git;a=blobdiff;h=[[sha1_to]];hp=[[sha1_from]];hb=[[sha1_parent]];f=[[file]]",
        #gitorigin_branch => "origin",
        #gitmaster_branch => "master",

        # Tla stuff.
        #rcs => "tla"
        #historyurl => ??,
        #diffurl => ??,

        # Mercurial stuff.
        #rcs => "mercurial",
        #historyurl => "http://localhost:8000/", # hg serve'd local repository
        #diffurl => "http://localhost:8000/?fd=[[changeset]];file=[[file]]",

        # Monotone stuff
        #rcs => "monotone",
        #mtnkey => "web\@machine.company.com",
        # Set if you want the wiki to sync on update and commit.
        #mtnsync => 0,
        # The path to your workspace (defaults to the srcdir itself)
        # e.g. use if your srcdir is a subdirectory of the workspace.
        #mtnrootdir => "path/to/root/of/workspace",
        # This is a monotone lua hook file used by ikiwiki for
        # inserting conflict markers. By default it will use
        # mtnrootdir/_MTN/mergerc. This hook will be populated with
        # default code the first time you use ikiwiki.  You can
        # change it to alter how conflict markers are inserted.
        #mtnmergerc => "path/to/mergerc",

        wrappers => [
                #{
                #       # The cgi wrapper.
                #       cgi => 1,
                #       wrapper => "/var/www/wiki/ikiwiki.cgi",
                #       wrappermode => "06755",
                #},
                #{
                #       # The svn post-commit wrapper.
                #       # Note that this will overwrite any existing
                #       # post-commit hook script, which may not be
                #       # what you want.
                #       wrapper => "/svn/wikirepo/hooks/post-commit",
                #       wrappermode => "04755",
                #       # Enable mail notifications of commits.
                #       notify => 1,
                #       # Log to syslog since svn post-commit hooks
                #       # hide output and errors.
                #       syslog => 1,
                #},
                #{
                #       # The git post-update wrapper.
                #       # Note that this will overwrite any existing
                #       # post-update hook script, which may not be
                #       # what you want.
                #       wrapper => "/git/wiki.git/hooks/post-update",
                #       wrappermode => "04755",
                #       # Enable mail notifications of commits.
                #       notify => 1,
                #},
        ],

        # Generate rss feeds for blogs?
        rss => 0,
        # Generate atom feeds for blogs?
        atom => 0,
        # Urls to ping with XML-RPC when rss feeds are updated
        #pingurl => [qw{http://rpc.technorati.com/rpc/ping}],
        # Include discussion links on all pages?
        discussion => 0,
        # To exclude files matching a regexp from processing. This adds to
        # the default exclude list.
        #exclude => qr/*\.wav/,
        exclude => qr/(^_.*)|(.*\/_.*)|(.*~$)/,
        # To change the extension used for generated html files.
        #htmlext => 'htm',
        # Time format (for strftime)
        timeformat => '%T %Z %F',
        # Locale to use. Must be a UTF-8 locale.
        #locale => 'en_US.UTF-8',
        # Only send cookies over SSL connections.
        #sslcookie => 1,
        # Logging settings:
        verbose => 1,
        syslog => 0,
        # To link to user pages in a subdirectory of the wiki.
        #userdir => "users",
        # To create output files named page.html rather than page/index.html.
        usedirs => 1,
        # Simple spam prevention: require an account-creation password.
        #account_creation_password => "example",

        numbacklinks => 0,

        # To add plugins, list them here.
        #add_plugins => [qw{goodstuff search wikitext camelcase
        #                   htmltidy fortune sidebar map rst anonok}],
        add_plugins => [qw{ asciidoc brokenlinks inline map meta noconvert orphans }],

        # If you want to disable any of the default plugins, list them here.
        #disable_plugins => [qw{inline htmlscrubber passwordauth openid}],
        disable_plugins => [qw{ htmlscrubber }],

        # To add a directory to the perl searh path, use this.
        #libdir => "/home/me/.ikiwiki/",
        libdir => "/home/karl/prj/web/src.mowson.org/_plugins_/",

        # For use with the tag plugin, make all tags be located under a
        # base page.
        #tagbase => "tag",

        # For use with the search plugin if your estseek.cgi is located
        # somewhere else.
        #estseek => "/usr/lib/estraier/estseek.cgi",

        # For use with the openid plugin, to give an url to a page users
        # can use to signup for an OpenID.
        #openidsignup => "http://myopenid.com/",

        # For use with the mirrorlist plugin, a list of mirrors.
        #mirrorlist => {
        #       mirror1 => "http://hostname1",
        #       mirror2 => "http://hostname2/mirror",
        #},
}

This is my directory structure (with some stuff extra stuff chopped out or shortened). Directories (and files) starting and ending with "_" (or hidden dirs/files) don't get uploaded - they're hiding places for configs and suchlike. Note that I'm purely using ikiwiki for static generation of my website (no online diting by users trusted or otherwise). My config is probably completely inappropriate and insecure if you were to enable online editing.

.
|-- Makefile
|-- _basesite_
|   |-- Karl
|   |   `-- index.html
|   |-- index.html
|   `-- karl
|-- _basewiki_
|-- _cfg_
|   |-- ikiwiki.setup
|   |-- mowson_asciidoc.conf
|   `-- source-highlight-filter.conf
|-- _plugins_
|   `-- IkiWiki
|       `-- Plugin
|           |-- asciidoc.pm
|           `-- noconvert.pm
|-- _webcheck_
|   |-- about.html
|   |-- badlinks.html
|   |-- external.html
|       ...SNIP...
|-- _wikitemplates_
|   |-- aggregatepost.tmpl
|   |-- archivepage.tmpl
|   |-- atomitem.tmpl
|       ...SNIP...
`-- karl
    |-- changelog.adoc
    |-- colophon.adoc
    |-- colophon
    |   |-- Makefile
    |   |-- asciidoc.pm
    |   |-- ikiwiki.setup
    |   `-- tree.txt
    |-- contact.adoc
    |-- contact
    |   `-- addr.png
    |-- evo_t20.adoc
    |-- evo_t20
    |   |-- devosl-build
    |   |   |-- build-devosl.sh
    |   |   |-- devosl-build-old
    |   |   |   `-- build-devosl-old.sh
    |   |   `-- devosl-build-old.adoc
    |   |-- devosl-build.adoc
    |   |-- devosl-grub
    |   |   `-- devosl-grub.patch
    |   |-- devosl-grub.adoc
    |   |-- devosl-prep
    |   |   |-- dsc01883-DEvoSL.jpg
    |   |   `-- dsc01903-resources.jpg
    |   |-- devosl-prep.adoc
    |   |-- evo_t20_notes
    |   |   |-- dsc07599-T20-pcb-front.jpg
    |   |   `-- dsc07604-T20-pcb-back.jpg
    |   `-- evo_t20_notes.adoc
    |-- index.adoc
    |-- local.css
    `-- sitemap.adoc

This is the Makefile I use to tie everything together. It is a mutation of a previous system I used, and needs some tidying (but it works… well, it did work before I munged the secret bits). I suspect that ikiwiki has some concept of persistence of config and that I don't need to be specifying a setup all the time, but I haven't checked out how it works (and I might be completely wrong). I don't use git commits to trigger wiki processing, at the moment.

# Makefile for building and uploading website.
# Karl Mowatt-Wilson - http://mowson.org/karl
# October 2007.
#
#----------------------------------------------------------------------------
# make all      = refresh the wiki.
# make refresh  = refresh the wiki.
# make rebuild  = rebuild the wiki.
# make clean    = clean out the target directory completely.
# make preview  = start local web server (webfsd).
# make upload   = upload to web server.
# make test     = do some sort of test!
#----------------------------------------------------------------------------
# Define programs and commands.
SHELL   = sh
COPY    = cp
REMOVE  = rm -f
REMOVEDIR = rm -rf
MKDIR   = mkdir
IKIWIKI = /usr/bin/ikiwiki
WEBFSD  = /usr/bin/webfsd
WEBCHECK= /usr/bin/webcheck

#----------------------------------------------------------------------------
# define where I keep my source and destination directories for wiki building
PRJ = $(HOME)/prj
SRC = $(PRJ)/web/src.mowson.org
DST = $(PRJ)/web/mowson.org

# web host details for unison/ssh upload
HOST      = example.net
HOST_USER = MyUserName
HOST_DIR  = /html/example.net
HOST_PORT = 22
HOST_KEY  = $(HOME)/.ssh/private_key
HOST_KEY_FINGERPRINT = 00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff

# BASESITE gets copied verbatim to output dir when wiki is rebuilt
BASESITE    = $(SRC)/_basesite_
IKISRC      = $(SRC)
WEBCHECKDIR = $(SRC)/_webcheck_
IKIDST      = $(DST)

# WIPDIRS are WorkInProgress dirs which aren't linked from anywhere public,
# so need to be manually specified for webcheck
WIPDIRS = $(shell find "$(IKIDST)" -type d -name 'wip-*' -printf "\"%p\" " )

# Ikiwiki setup
IKICFG  = --setup $(SRC)/_cfg_/ikiwiki.setup
#
# these two arguments may have to be last!  (I think)
IKICFG += $(IKISRC)
IKICFG += $(IKIDST)
# add no more arguments after src/dst ?

WEBFSCFG = -r $(DST) -f index.html -e 10

WEBCHECKCFG = --output $(WEBCHECKDIR)

#=========================================================================
all: refresh

cfg: $(IKIWIKI) $(PRJ) $(IKISRC) $(BASESITE)

rebuild: cfg clean
        $(MKDIR) -p $(DST)
        $(COPY) -ap $(BASESITE)/* $(DST)/.
        $(MKDIR) -p $(IKIDST)
        $(IKIWIKI) $(IKICFG) --rebuild

refresh: cfg
        $(IKIWIKI) $(IKICFG) --refresh

preview: $(WEBFSD) $(DST)
        @echo "Serving pages on localhost:8000"
        $(WEBFSD) $(WEBFSCFG)

clean: clean_list

clean_list:
        @echo
        @echo "Cleaning project:"
        $(REMOVEDIR) $(IKIDST)
        $(REMOVEDIR) $(DST)
        $(MKDIR) -p $(DST)

upload:
        ##### Check/setup private key
        @ssh-add -l | egrep '^[0-9]+ $(HOST_KEY_FINGERPRINT) ' || { \
           ssh-add "$(HOST_KEY)" || { \
              echo "ERROR: problem loading key into agent"; \
              exit 1; \
           }; \
        }
        ##### Check for index.html in all uploadable dirs
        [ -z "$(shell find $(DST) -type d ! -regex '.*/[._].*' ! -execdir [ -e "{}/index.html" ] \; -exec echo FAILED {} \;)" ]
        ##### Fixing directory permissions so all can read
        @find $(DST) -type d     \
            ! -regex '.*/[._].*' \
                 -exec chmod -c a+rx "{}" \;
        ##### Fixing file permissions so all can read
        @find $(DST) -type f      \
            ! -regex '.*/[._].*'  \
            ! -regex '.*~'        \
                 -exec chmod -c a+r,go-w "{}" \;

        ##### Upload
        @unison $(DST) ssh://$(HOST_USER)@$(HOST)/$(HOST_DIR) \
         -sshargs '-p $(HOST_PORT)'          \
         -sortnewfirst=true          \
         -terse=true                 \
         -log=true                   \
         -auto=true                  \
         -ignore 'Path */_*'         \
         -ignore 'Path */.*'         \
         -ignore 'Name _*'           \
         -ignore 'Name .*'           \
         -ignore 'Name *~'
        #============ FINITO! ============#

webcheck: $(IKIDST) $(WEBCHECK)
        $(WEBCHECK) $(WEBCHECKCFG) --avoid-external $(WIPDIRS) $(IKIDST)

webcheckall: $(IKIDST) $(WEBCHECK)
        $(WEBCHECK) $(WEBCHECKCFG) $(WIPDIRS) $(IKIDST)

webchecklive: $(WEBCHECK)
        $(WEBCHECK) $(WEBCHECKCFG) http://www.$(HOST)

test:
        @echo "WIPDIRS: $(WIPDIRS)"


#=========================================================================
# Listing of phony targets.
.PHONY : all cfg clean preview rebuild refresh test upload webcheck webcheckall webchecklive