Manicai.net
To be honest this page has been rewritten more times than I can to remember. I don't think that my requirements have particularly changed over the years but I've never been able to find a solution that I'm entirely happy with. This describes the current state of play and also briefly mentions some of the previous methods in case the information there is of use to someone.
The requirements I have for generating this site are:
It's the last one that seems to kill most tools. Almost everything thinks that it's particular directory scheme is definitive and you fit to it. Fine for a new site (although descriptive URLs are irritatingly rare for those).
The bias towards static pages is based on two reasons. The first is efficiency, although I'll be the first to admit the chances of me writing anything interesting enough to get Slashdotted are vanishingly small. The second is that I've changed web hosting arrangements twice and webservers at least three times over the years, and I don't necessarily want to figure out the install process each time for a content management system on a new server. (It also gives me one less thing to watch for security holes in.)
I'd also like but don't require
Most fancy GUIs fall the last of those (and quite often the first too).
My current solution is comparatively low-tech: a text editor and webgen.
HRL (HTML redemption language) is a preprocessor so the output is plain HTML files that I can upload. It can be extended via Python. It's open source (ie. free) and runs anywhere Python runs. So that's what I use.
The way HRL works is you define you're own HTML type tags and then run the preprocessor to produce the HTML page. So I can write
<macro name="site_header"> <p class="header">Manicai.net</p> </macro>
and where ever I put
<site_header>it gets expanded to
<p class="header">Manicai.net</p>You also get the ability to add attributes to tags and have Python snippets run to specify the behaviour. As a slightly more complete example the global tags that are used to specify the common layout of all the pages on this site are:
<macro name="email">
<address>
<table border="0" cellpadding="0" cellspacing="0">
<tr><td>ian</td><td>@</td><td>manicai</td><td>.net</td>
</table>
</address>
</macro>
<macro name="site_header">
<p class="header">Manicai.net</p>
</macro>
<macro name="site_footer">
<hr><email>
</macro>
<macro name="site_head_details">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="generator" content="HRL, Emacs">
<meta name="author" content="ian -at- manicai -dot- net (Ian Glover)">
<link rel="stylesheet" href="/manicai.css" type="text/css">
</macro>
<macro name="document" req="title" opt="description" container>
<html>
<head>
<site_head_details>
<ifspec attr="description">
<meta name="description" content="((description))">
</ifspec>
<title><e val="((title))"></title>
</head>
<body>
<site_header>
<section_header>
<content>
<site_footer>
</body>
</html>
</macro>
These demonstrate but don't really stretch the features of HRL. The first three just do plain text substitution. The document macro is a bit more interesting. The ifspec tag allows you to check whether an optional attribute has been supplied. The ((...)) construct (and the associated <e val="..."> tag) are how you evaluate the value of an attribute.
At the minute the page that pushes the capabilities furthest is my wine tasting details page. Here a couple of example from there.
<macro name="wine" req="maker,wine,year,cost,supplier" opt="url">
<tr>
<td>
<ifspec attr="url">
<a href="(( 'http://%s' % url ))"><e val="((maker))"></a>
</ifspec>
<ifspec not attr="url">
<e val="((maker))">
</ifspec>
</td>
<td><e val="((wine))"></td>
<td><e val="((year))"></td>
<td align="right">
<if cond="((cost == ''))">Unknown</if>
<if cond="((cost != ''))">£<e val="((cost))"></if>
</td>
<td align="right"><supplier name="((supplier))"></td>
</tr>
</macro>
Here the maker's name is wrapped with a hyperlink if the url attribute is supplied. There's also a substitution of the cost depending on whether the cost has been specified.
This allows me to write the details as
<wine maker="Chateau Moulin Caresse" wine="Cuvee Prestige"
year="2000" cost="12.99" supplier="veritas">
The sharp eyed will have notice the supplier tag near the bottom of the definition of wine. This expands to the following
<macro name="supplier" req="name" verbatim>
<python>
mapping = { "bacc" : ( "Bacchanalia", None ),
"camb" : ( "Cambridge Wine Merchants",
"www.cambridgewine.com" ),
"majestic" : ( "Majestic",
"www.majestic.com" ),
"oddbins" : ( "Oddbins",
"www.oddbins.com" ),
"safeway" : ( "Safeway",
"www.safeway.co.uk" ),
"sainsburys" : ( "Sainsburys", None ),
"tesco" : ( "Tesco",
"www.tesco.com" ),
"veritas" : ( "Veritas",
"www.veritaswines.co.uk" ),
"winesoc" : ( "The Wine Society",
"www.thewinesociety.com/" ),
"young" : ( "Noel Young",
"www.nywines.co.uk" ) }
if name.lower() in mapping:
display, url = mapping[name.lower()]
if url is not None:
hrl.doc.write('<a href="http://%s">%s</a>' \
% (url, display))
else:
hrl.doc.write(display)
else:
hrl.doc.write(name)
</python>
</macro>
This shows to full power of HRL's embedded Python, allowing me to time a mnemonic for the shop name and get the fullname and if available to URL expanded for me.
A tag to convert an ANSI standard date (e.g. 2004-5-29) to an English text date (29th May 2004).
<macro name="date" verbatim container>
<python>
from string import strip, split
def write_date(day, month, year):
suffices = { '1' : 'st', '2' : 'nd', '3' : 'rd',
'21' : 'st', '22' : 'nd', '23' : 'rd',
'31' : 'st' }
default_suffix = 'th'
months = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December']
day = str(int(day))
if suffices.has_key(day):
suffix = suffices[day]
else:
suffix = default_suffix
return '%s<sup>%s</sup> %s %s' \
% (day, suffix, months[int(month) - 1], year)
(year, month, day) = tuple(map(strip,split(content,'-')))
hrl.doc.write(write_date(day, month, year))
</python>
</macro>
When producing a page like this there's one invaluable Emacs macro that I use to convert all the < and > instances to < and > respectively.
(defun html-quote-braces (begin end)
(interactive "r")
(save-excursion
(goto-char begin)
(while (search-forward ">" end t)
(replace-match ">" nil t)
(setq end (+ 3 end)))
(goto-char begin)
(while (search-forward "<" end t)
(replace-match "<" nil t)
(setq end (+ 3 end)))))
From memory this never quite worked properly but it gives the jist anyway.
When I first set this system up the biggest hassle was that I had to manually rebuild the html files from the HRLC files when I editted them. However shortly afterwards I moved from using Apache as the webserver at home to using AOLServer. As a side effect of this I was able to write a brief TCL function that did the work for me.
proc mn_check_hri { args why } {
ns_log notice "Check HRI: request [ns_conn url]"
set html "[ns_info pageroot][ns_conn url]"
set hri "[file rootname $html].hri"
if { [file exists $hri] } {
if { [file mtime $hri] > [file mtime $html] } {
ns_log notice "Need to update $html"
set dir [file dirname $html]
ns_log notice Executing /usr/bin/hrlc [ns_info pageroot]site_tags.hri $dir/section_tags.hri $hri $html
if { [ catch { exec -- /usr/bin/hrlc [ns_info pageroot]site_tags.hri $dir/section_tags.hri $hri $html } err ] } {
ns_log notice "HRLC returned $err"
}
}
}
ns_respond -file $html
}
ns_register_proc GET /*.html mn_check_hri
All of this would be pretty useless if I couldn't upload it all to the server. This is achieved via rsync, although since the command line gets quite long this is all wrapped into a shell script:
#!
rsync --archive --compress --rsh=ssh -n \
--exclude="*.hri" --exclude "*~" --exclude="Makefile" \
pages modules/tcl modules/nsperm \
user@example.com:/pageroot