######## SYNTAX ########

  links: [[wikipage]], btw: [[use-dashes-instead-of-camel-case]]
         [[wikipage|text to show]]

  quoted text: "<`" text "`>"
  executable lua code (it will be run in place): "<?" code "?>"
  titles: use ascii art
  tables: use ascii art
  diagrams: use ascii art
  enumerations: use plain text
  list items: use plain text
  definition lists: use plain text
  quotations: use ascii art or plain text
  text color: use green text
  italics, bold text, underlining, variable width fonts: don't use them

Example: link to this page
(example code being run here)
(execution finished)
Output: 2

Only basic "safe" functions are allowed to the interpreter, please don't abuse
the precious cputime or memory. Btw, only executable code from authenticated
users is run.

######## IMAGE LIBRARY ########

There are only 16 available colors (proudly choosen by me):
 0.. 3: image white, yellow, orange, red
 4.. 7: image magenta, blue, teal, sky blue
 8..11: image green, dark green, brown, tan
12..15: image light gray, gray, dark gray, black

img = image(w, h)
  Returns a newly created image of size w*h (min 8x8, max 320x200).

  Sets the current foreground (pen) color. Must be an integer between 0 and 15.
  Returns this same image img.

img:get(x, y, w, h)
  Returns a string made of lines where each caracter corresponds to the hex
  representation of the respective pixel of the image in the given range.
  For instance a black 16x8 image with a red point at (3, 1), will serialize
  to the following text:
img:line(x1, y1, x2, y2)
  Draws a line between (x1, y1) and (x2, y2). Both source and destination
  points will be drawn.  Returns this same image img.

img:plot(x, y)
  Sets the point at x, y to the current foreground color, modifying the image.
  For the x,y outside their boundaries this function does nothing.
  Returns this same image img.

img:put(x, y, w, h, data)
  This method being the opposite of get, allows to load an image from dumped
  data.  Chars not in [0-9a-f] will be ignored, but the amount of valid pixels
  in the dumped data must be equal to w*h.
  Returns this same image img.

img:rect(x, y, w, h)
  Draws a w*h size filled rectangle, returning the same image img.

  Returns the image as a safe string that you may concatenate to other text
  (for inline display) or just return it.

img:text(x, y, text)
  Puts text into the image at position x, y (in pixels from top left corner, as
  usual). Text will be rendered with the traditional atari 8-bit font using the
  current color for foreground and transparent background, inverse video is not
  supported (you could however draw a rectangle behind the text and then change
  the color).  The parameter text must be encoded in utf-8 with only the
  following chars supported (equivalent to those of the 7 bit ATASCII), any
  other will be replaced to '●':

    '♥','├','▕','┘','┤','┐','╱','╲','◢','▗','◣','▝','▘', '▔','▂','▖',
    '♣','┌','─','┼','●','▄','▎','┬','┴','▌','└','␛','↑', '↓','←','→',
    ' ','!','"','#','$','%','&',"'",'(',')','*','+',',', '-','.','/',
    '0','1','2','3','4','5','6','7','8','9',':',';','<', '=','>','?',
    '@','A','B','C','D','E','F','G','H','I','J','K','L', 'M','N','O',
    '♦','a','b','c','d','e','f','g','h','i','j','k','l', 'm','n','o',
    'p','q','r','s','t','u','v','w','x','y','z','♠','|', '↰','◀','▶'




After saving the desired image (whose size must not be bigger than 320x200) in
PNM color ascii format (identified by having the text P3 in the first line),
you may use the following command to convert it to the same format used by

  cat image.pnm | luajit -e 'P={[0]={255,255,255},{255,255,0},{255,102,0},
    e={68,68,68},f={0,0,0}} g=io.read"*a":gsub("#[^\n]*",""):gmatch"%S+";
    assert(g()=="P3","make sure the image is in pnm ascii color format (P3)");
    assert(0<w and w<=320 and 0<h and h<=200,"invalid size");
    rgb,i,buff={},1,{};for v in g do rgb[i],i=tonumber(v),i%3+1
    if i==1 then md=1e9 for c,p in pairs(P)do
    if d<md then md,mc=d,c end end buff[#buff+1]=mc if #buff==w then
    print(table.concat(buff));buff={}end end end'

######## PERFORMANCE ########

It's pretty obvious that running mod_lua with luajit makes a huge performance
improvement. However debian apache2's mod_lua comes with lua-5.2 by default.
So in order to fix that, I've recompiled the mod_lua.so library with the
following commands or other ones (I'm not sure though).

# apt-get source apache2 <- this is the first obvious one,
# dpkg-buildpackage <- to compile it.
* build/special.mk loads the config parameters from build/config_vars.mk,
  in the latter you may change MOD_LUA_LDADD = -lluajit-5.1
* The remaining variables can be set in modules/lua/modules.mk:
  MOD_INCLUDES = -I/usr/include/luajit-2.0
* The next step is to, in modules/lua/lua_vmprep.c comment the lines with calls
  to luaopen_jit and loadjitmodule, sort of "fixing" this bug:
* Under modules/lua run: make mod_lua.so
* Then you'll find in modules/lua/.libs/mod_lua.so the so desired library, with
  that in place (/usr/lib/apache2/modules/), after a restart, you'll finally be
  able to enjoy the speed boost.

######## DATABASE ########

CREATE TABLE pages(name text not null, version int not null, content text,
  rendered text, private boolean, created_on int not null,
  primary key(name, version));
-- name: name of the page, for instance 'lua/wiki'.
-- version: an integer starting with 1 incremented each time the page is saved.
-- content: the content of the page before rendering.
-- rendered: the html result of rendering content, if null it's updated once
--           someone requests the page.
-- private: if not null then this page will only be shown if the user has edit
--          access as well.
-- created_on: seconds from epoch when the version was created.

CREATE TABLE passwords(pattern text primary key, password_hash text, salt text);
-- on page save the wiki will only store the changes if there's at least one
-- row such that:
--   page_name:match(pattern), and
--   sha1(salt..':'..password) == password_hash.
-- This design allows different passwords for access to different page subsets.

######## REST API ########

In the current installation at fideo, <basePath> is "/wiki.lua/!rest/v0".
As for the <pageName> it referers the name of the wiki page without the
initial slash, ie: "/wiki.lua/foo/bar" will have <pageName> "foo/bar".

GET <basePath>/pages/<pageName>[?params]
params: format=(plain|html), version=<integer>

  Fetches a wiki page. Returning an error if the page doesn't exist.
  If the format is not specified, both html and plain text are returned.
  If the version is not specified, the last one is returned. If the page
  was "deleted", then empty bodies will be returned. If the page was never
  created or the requested version does not exist, a 404 error will be

response body schema:
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
        "body": {
            "type": "object",
            "properties": {
                "plain": {
                    "type": "string",
                    "description": "The source code of the requested page."
                "plain": {
                    "type": "string",
                    "description": "The requested page rendered into html."
            "description": "The body of the wiki page in at least one format.",
            "minProperties": 1
        "version": {
            "type": "integer",
            "description": "Requested or last version number."
    "required": [ "body", "version" ]

POST <basePath>/pages/<pageName>

  Creates or updates a wiki page.  To "delete" the page just update it with an
  empty body.

request body schema:
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
        "previousVersion": {
            "type": "integer",
            "description": "This request will fail if the page doesn't exist and this is defined, or if it exist and its value is not equal to the last version in the database.",
        "body": {
            "type": "string",
            "description": "Content of wiki page."
        "password": {
            "type": "string",
            "description": "Password used for access control."
    "required": [ "body" ]

response body schema:
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
        "version": {
            "type": "integer",
            "description": "Version of the page just created or updated."
    "required": [ "version" ]