Wikimarkup mit Django

Die Syntax, die MediaWiki (z.B. Wikipedia) verwendet, ist relativ weit verbreitet (Trac und Google Code benutzen eine daran angelehnte Syntax) und auch nicht unbedingt schlecht. Django bringt von Haus aus Parser für Restructured Text, Textile und Markdown mit, in diesem Blog-Eintrag möchte ich kurz zeigen, wie man sich noch einen 4. Parser für oben genanntes Wikimarkup hinzufügt.

Die Parser die Django mitbringt liegen in django.contrib.markup und sind dort als Template-Filter implementiert (templatetags/markup.py), deshalb soll der Wikimarup Parser nun auch erstmal als Template-Filter implementiert werden.

Als erstes brauchen wir eine Implementierung des Parsers, welcher Wikimarkup-Syntax in HTML konvertieren kann. Wenn man diesen Code nicht selbst implementieren will und die GPL Lizenz kompatibel zu dem Projekt ist an dem man grade arbeitet, kann man die Implementierung von David Cramer benutzen, diese ist auf Google Code verfügbar.

Der eigentliche Filter ist ein Einzeiler, mit allem drum und dran sieht der Code wie folgt aus:

from django import template
from django.template import TemplateSyntaxError
from django.conf import settings
from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode


register = template.Library()


def wiki_markup(value):
    try:
        from wikimarkup import parse
    except ImportError:
        if settings.DEBUG:
            raise TemplateSyntaxError, "Error importing wikimarkup"
        return force_unicode(value)
    return mark_safe(parse(value))


register.filter(wiki_markup)

Der Filter bekommt hier als einzigstes Argument (value) den String auf den er angewendet wird. Mit der Funktion parse aus dem wikimarkup-Modul, wird dieser String in HTML umgewandelt. Damit der HTML-Code im Template nicht noch einmal escaped wird, muss der String abschließend mit mark_safe markiert werden, dann weiß Djangos Autoescape-Mechanismus, dass nicht mehr escaped werden muss.

Hierbei sollte man beachten, dass man durch das Benutzen von mark_safe dem Parser ziemlich stark vertraut. Hat der Parser ein Feature oder einen Bug, der Cross-Site-Scripting o.ä. erlaubt kann Djangos Autoescape-Mechanismus dies nicht mehr verhindern. mark_safe teil Django mit, dass man als Entwickler der Ausgabe der Funktion vertraut und dieses als sicher einstuft.

Damit der Filter in Django-Templates verfügbar gemacht werden kann, muss er in einem Ordner namens templatetags innerhalb eines App-Ordners liegen.

Ein einfacher Weg wäre es den Template-Filter einfach dem Wikimarkup Modul hinzuzufügen, welches wir weiter oben heruntergeladen haben. Die Verzeichnisstruktur sollte am Ende so aussehen:

Wikimarkup Order mit Templatetags

Der Ordner wikimarkup mit der direkt darin befindlichen __init__.py Datei enthält den eigentlichen Parser. Dadurch, dass wir den Ordner templatetags mit einer leeren __init__.py Datei und die Datei wiki_markup.py hinzugefügt haben, können wir nun (vorausgesetzt der Ordner wikimarkup befindet sich im PYTHONPATH, z.B. im site-packages Ordner) in beliebigen Django-Projekten in der settings.py in den INSTALLED_APPS die App 'wikimarkup' hinzufügen und von dort an im Template den Filter mit:

{% load wiki_markup %}

laden und den Text mit:

{{ variable|wiki_markup }}

parsen lassen.

Der Name den man im load-Tag verwendet um den Filter zu laden ist nicht der Name der Funktion, sondern der Name der Datei in dem sich der Filter befindet. Das die Namen in diesem Fall gleich sind habe ich so gewählt, weil es nur einen Filter in der Datei gibt und man sich so nicht unnötig viele Namen merken muss. Die Django Markup-Filter werden z.B. mit {% load markup %} geladen, weil die Datei django/contrib/markup/templatetags/markup.py ` heißt. Die verschiedenen Filter heißen jedoch |restructuredtext, |textile und |markdown. Merkt man sich dieses Beispiel sollte man ` sich leicht merken können, wie man welche Komponenten nennen sollte und wie man welchen Filter im Template anspricht.

Der Filter heißt übrigens wiki_markup und nicht wikimarkup, weil schon das Modul an sich wikimarkup heißt, es würde sonst zu Python Import-Fehlern führen, wenn man in der Datei wikimarkup.py ein from wikimarkup import parser durchführen will.