The Maiko forum


Before starting, note that maiko has been mostly tested under Linux, runs on MacOS X, and has been tried under Windows (running with Apache remains untested in the latter two).

Quick start

To install, download and unzip (this creates a new directory maiko/). From a terminal, change into the new directory and run ./ (python under Windows). You can then click on http://localhost:9393 to start using the forum.

To add a topic click on http://localhost:9393 and in the Start a new topic section write the new topic's title and answer the simple math question presented (the MAPTCHA). Note that the answer must be a numeric integer e.g. 14 (not "fourteen"). After clicking on Create new topic a status page will appear with a link to the updated topic list.

To add a post, click on the topic and go to the Add a new post section. The content of the post can then be written in the text area therein. Note that no HTML is allowed, but blank lines will be parsed as paragraph breaks. Also, a line break can be forced using a " \" (a space followed by a backslash, which will not show up the post) at the end of a line, and two consecutive spaces will become two non-breaking HTML white spaces. After answering the MAPTCHA and clicking Submit post a status page with a link back to the post will appear. Posts can refer to previous entries by writing the appropriate post number in the "In response to message" field (no need to add the prefixed zeroes).


New topics and posts can be reviewed before allowing them to be displayed by setting the CONFIG['hold_ext'] variable (to, say, '.hld'). Once set, topics and posts will be created with the '.hld' extension and not shown (the status page will inform the submitter that the new entry has been sent to the moderator).

In order to simplify moderation maiko comes with a helper script called Running the program sifts through topics and posts, allowing the moderator to deal with entries in the desired manner (e.g. releasing a topic, or deleting a post). Note that the script expects '.hld' to be used as a hold extension. It furthermore uses '.spam' and '.deleted' to tag topics and posts as spam or deleted, respectively (no topic or post with any extension is served). To narrow moderation to only held entries use H.

Held posts do not instantly update the topic's modification time or post count. After releasing a post the topic's metadata will be updated only after the first visit henceforth.

A listing of every topic and post can be obtained by doing L. Running a script periodically to check for changes (and sending an email if any are found) can then be implemented as a notification system.

In principle it should be possible to automate the spam filtering process. Since posts are simple JSON files extracting the content can be readily done as so:

python -mjson.tool 00000.hld

Removing the HTML code and then piping it into a suitably-configured spam filter (such as bogofilter) should provide a score such that likely spam posts can be renamed with a, say, .spam extension (or deleted altogether), while the rest can have the .hld extension removed. Writing a shell script that runs as a cron job frequently would then perform the moderation task automatically.

Sticky topics and posts

The variable CONFIG['max_items'] sets the maximum number of topics which can be submitted to the forum, and the maximum number of posts submitted within each topic. It is not necessarily the maximum number of topics or posts, however, the difference being the number of entries reserved by the administrator for "stickies".

By default, CONFIG['max_items'] = 99000. What this means is that there can be at most 99000 user-submitted items (ranging from 00000 to 98999). The rest, however, are available to the administrator by simply creating a post or topic numbered anywhere between 99000 and 99999. This can be done, for example, by submitting an item the usual way and then renaming it on the filesystem. Since higher-numbered topics are at the top of the page they will remain there (and hence are sticky). By default newer posts are at the bottom of the page, so unless the sorting is reversed this functionality might not be useful in that case (unless the administrator takes over the first post — 00000).

Note that although maiko is quite robust when it comes to deleting/renaming items within the proper range (i.e. 0 to 99999 by default), care must be taken if referrals are present, and should be updated if necessary (by editing the response_to value in the metadata, see below).

Directory and file structure

Under the maiko/ directory is a subdirectory called topics/. Topics (or threads) are stored within it as subdirectories, and those in turn contain metadata and post files:

    | (optional)
    |       |-lock (transient)
    |       |--00000/ (first topic)
    |       |      |-topic.inf (topic metadata)
    |       |      |-00000 (first post)
    |       |      |-00001 (second post)
    |       |      +...
    |       |--00001/ (second topic)
    |       |      |-topic.inf
    |       |      |-00000
    |       |      |-00001
    |       |      +...
    |       +...
    + --static/ (templates (.tpl), CSS (.css) and other static files)

Topic metadata is contained within the topic.inf file in each topic directory and is a JSON dictionary consisting of the topic's modification timestamp, the type of entry the metadata refers to (topic), the sanitized title of the topic, the IP address of the author, the author's submitted name (sanitized), and the number of (non-held) posts within the topic e.g.:

{"count": 5, "timestamp": 1388965906.792156, "type": "topic",
 "title": "Hello world!", "author": "Joe", "ip": ""}

Note how in the above example the ! has been escaped to ! — all on-file data created by maiko is sanitized (posts included, of course).

Each post file is in JSON format and contains a dictionary with two keys: metadata and content. The former is a dictionary with the creation timestamp, the type of entry (post), the author and author's IP, and the response_to key indicating what post is being responded to (if any):

{"author": "Jane", "ip": "", "response_to": "00001",
 "type": "post", "timestamp": 1388966303.209694}

The second key contains a string with the sanitized content of the post enclosed in a paragraph tag (as defined by CONFIG['post_p_class'] so that, by default, it's saved as <p class="post_content">content</p>).

If the poster does not fill out the author field then the author's name is set to be the submitter's IP address. Note that the name is not authenticated, the technical reason for this being that REMOTE_USER is not passed over encrypted (HTTPS) form submissions (it is over HTTP connections, but since it's very poor practice to send passwords in cleartext this functionality is not present in maiko).

Previous: Introduction

Next: Advanced