(define page
`((p "Tekuti means \"I'm telling you\" in Oshiwambo. It is weblog software
written in Scheme, using Git as its persistent store.")
(h3 "Why")
(p "Tekuti hacks the web as it is meant to be hacked.")
(p (i "Tekuti uses tags, not categories.") " Put however many labels you like on
your posts, the more the merrier: with each tag you add meaning to
your tag cloud, related posts, and related tags.")
(p (i "Tekuti uses Git as its backing store.") " The data model scales, and you
get hot backups and rollback for free.")
(p (i "All operations on the data store are revertable.") " Try it: make a post,
add some comments, revert some comments, revert the reversions...")
(p (i "Tekuti is secure.") " There is one entry point, and the entirety of the
program is written in the functional style. It does not perform any
destructive modifications to data. It is very easy to audit its code.")
(p (i "Tekuti respects the meaning of the web's GET and POST operators.") "
Clicking on a link will not modify the data store.")
(p (i "There is a strict separation in types between XML and strings.") "You
won't see malformed XHTML come out of Tekuti, much less an XSS
vulnerability.")
(p (i "Output transformation and templating") " is easy and built-in to the
language via the powerful pre-post-order operator.")
(p "And, most importantly, it's written in Scheme, so it's fun to hack!")
(h3 "How")
(h4 "Installing prerequisites")
(ol
(li (p "Build and install Guile 1.9.14 or newer."
" (Or if you are reading this before 1.9.14 is out, build Guile from git.)"))
(li (p "Install git. Any modern git will work.")))
(h4 "Get and build tekuti")
(pre "git clone git://gitorious.org/tekuti/tekuti.git")
(pre "cd tekuti; autoreconf -vif; ./configure && make")
(h4 "Run it!")
(pre "./env src/tekuti")
(p "Now navigate to " (a (@ (href "http://localhost:8080/"))
"http://localhost:8080/")
" in your browser. Does it work? Sweet!")
(p "To add posts, visit the admin
interface at e.g. " (tt "http://localhost/blog/admin") ". The default
user is admin and the password is admin.")
(h3 "Serving tekuti behind apache")
(p "I hear the kids are all running nginx or mongrel2 or whatever, "
"but I haven't caught up yet. So " (tt "a2enmod") " mod_proxy, mod_rewrite,"
"and mod_proxy_http, then add this to an appropriate " (tt ".htaccess") " file:")
(pre "RewriteEngine On
RewriteBase /
RewriteRule ^archives/?(.*)$ http://localhost:8080/archives/$1 [P,QSA]
RewriteRule ^feed(/.*)?$ http://localhost:8080/feed/$1 [QSA,P]
RewriteRule ^search/?$ http://localhost:8080/search [QSA,P]
RewriteRule ^tags/?(.*)?$ http://localhost:8080/tags/$1 [QSA,P]
RewriteRule ^debug/*$ http://localhost:8080/debug [QSA,P]
RewriteRule ^admin/?(.*)$ http://localhost:8080/admin/$1 [QSA,NE,P]")
(p "You might need to set this somewhere else in your config:")
(pre
"AllowEncodedSlashes On")
(p "In any case, you'll have to restart apache.")
(h3 "Migrating from wordpress")
(p "Tekuti includes a script to import posts from wordpress into a
suitably-laid-out directory. Run it as:")
(pre "python ./wordpress-to-dir.py HOST USER PASSWORD DBNAME")
(p "Then in the directory that the script makes, run:")
(pre "git init
git add .
git commit -m 'initial import from wordpress'")
(p "Then replace ~/blog.git with the " (tt ".git") " subdirectory.")
(p "Tekuti's URL structure is almost the same as Wordpress' so the
only other thing you might want to do would be to redirect category
links to tag links, and the rss2 feed to the atom feed:")
(pre "RewriteRule ^archives/category/(.*)$ /tags/$1 [R=permanent]
RewriteRule ^feed/rss2$ /feed/atom [R=permanent]")
(p "Tekuti will emit valid XML. It will complain if the wordpress
posts are not valid. Common errors would be using
instead of
, and ampersands in URLs. Running xmllint on all files named
\"content\" in the directory might be useful.")
(p "The XML code doesn't understand all character entities, either
-- see config.scm's *character-entities* to add more.")
(h3 "Hacking tekuti")
(p "Run Tekuti with the " (tt "--listen") " option. Then telnet to
port 37146 on localhost, and voila, a REPL!")
(p "For me, the best way to hack in the repl is to enter \"inside\"
other modules. For example, to change the admin password, I would:")
(pre " ;; enter in the already-defined (tekuti config) module
(define-module (tekuti config))
;; replace the *admin-pass* definition
(define *admin-pass* \"foo\")")
(p "Of course the best way is to do all this from within Emacs. Just
install " (a (@ (href "http://www.nongnu.org/geiser/")) "Geiser") ",
then M-x geiser-connect to the running REPL. Open tekuti/config.scm in
Emacs, change the admin password, and press C-x C-e after the closing
parenthesis. Then go to your admin page and see if the password
changed. Nifty, eh?")
(h3 "Deploying")
(p "For deployment-specific variables like the CSS files and the
paths and such, you can run tekuti with the -c argument and pass it a
config file, which will be evaluated inside the (tekuti config) module.
For example, one of mine looks like this:")
(pre ";; -*- scheme -*-
(set! *navbar-links*
'((\"about\" . \"/about/\")
(\"software\" . \"/software/\")
(\"writings\" . \"/writings/\")
(\"photos\" . \"/photos/\")))
(set! *navbar-infix* \" | \")")
(p "I suggest taking the CSS file from my blog and customizing it
for your usage. Please make it look different. If you come up with
something that I should include as a default, I'd be interested in
seeing it.")
(p "I deploy tekuti in a screen session. Run " (tt "screen") ", then
run tekuti. Then press C-a d to detach from the session. Then when you
come back to your server you run " (tt "screen -r") " to jump back into
that tekuti session.")
(h3 "Feedback")
(p
"Send me an email at " (code "wingo at pobox.com") ".")))
;; Now output the HTML
(use-modules (sxml simple) (sxml transform))
(load "../../template.scm")
(define xhtml-doctype
(string-append
"\n"))
(define (make-index)
(display xhtml-doctype)
(sxml->xml
(templatize page
"tekuti"
"projects"
"../../")))