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:
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.