This text was written for a german python book. The idea was to provide an insight into python with examples of popular python projects. Two chapter of the book were about python learning and the last chapter describes first steps for three python projects. At last the publisher decided to drop the third chapter for several reasons.

English translation (google.com)

@copyright: 2007-2012 ReimarBauer

@license: GPL

1. MoinMoin (1.9) - Erweiterungen und Anpassungen (Reimar Bauer)

2. Einleitung

MoinMoin ist eine Wiki-Software, welche in Python geschrieben ist. Derzeit wird an der Version 2.0 entwickelt. -- auf der Homepage (http://moinmo.in) können Sie Aktuelles zum Entwicklungsstand erfahren. MoinMoin ist Freie Software (Open Source Software) unter der GNU General Public License. Diese Beschreibung setzt voraus, dass Sie die aktuelle Version dieser Software von der Homepage herunterladen.

Ein Wiki ist eine im World Wide Web verfügbare Seitensammlung, die von den Benutzern nicht nur gelesen, sondern auch in Echtzeit online geändert werden kann. Wie bei Hypertexten üblich, sind die einzelnen Seiten und Artikel eines Wikis durch Querverweise (Links) miteinander verbunden.

Seit der Version 1.6 enthält MoinMoin ein vorkonfiguriertes Wiki, das in Verbindung mit dem in der Python-Standardbibliothek enthaltenen Webserver gut für ein persönliches Wiki auf dem lokalem Desktop geeignet ist. Diese Kombination ermöglicht es ebenso, in einer Entwicklungsumgebung (z.B. Eclipse/PyDev), komfortabel zu debuggen. Folgen Sie daher bitte den Hinweisen in der Installationsanleitung für das vorkonfigurierte Wiki. Dieses Wiki enthält auch die Hilfeseiten;

Nahezu alles an MoinMoin kann durch ein ausgeklügeltes Plugin-Konzept verändert werden. Das Aussehen könnnen Sie durch ein eigenes Theme (siehe Kapitel theme) umgestalten. Mit einem Parser (siehe parser plugins) können Sie Inhalte interpretieren und diese dann darstellen. Ein Macro (siehe macro plugins) dient dazu, in Form eines Kommandos etwas auf einer Seite einzufügen, während Sie mit einer Action (siehe Kapitel action plugins) bestimmen, was auf "Knopfdruck" geschehen soll. Ein Formatter siehe Kapitel formatter plugins) und Kapitel formatter) erlaubt eine Ausgabe in einem beliebigen Format. Filter (filter plugins) werden zum Erstellen des Suchindex am Beispiel von Dateianhängen benutzt. Mit XMLRPC (siehe Kapitel xmlrpc und xmlrpc plugins) können Sie über eine Netzwerkverbindung mit MoinMoin kommunizieren.

Falls Sie Verbesserungswünsche haben, können Sie einen Feature Request 1 erstellen - eventuell sogar mit einem Patch, wenn Sie das Feature bereits implementiert haben.

Im Nachfolgenden beziehen sich alle Beschreibungen auf den Standalone-Server und das vorkonfigurierte Wiki. Um dieses zu starten, gehen Sie in das Verzeichnis moin-1.9 und rufen Sie das Programm wikiserver.py auf. Ihr lokales Wiki erreichen Sie dann über die URL http://localhost:8080/ (siehe Abbildung). Falls Einstellungen geändert werden müssen, werden diese in der Konfigurationsdatei wikiconfig.py im gleichen Verzeichnis vorgenommen

StartSeite.png

Das persönliche Wiki

3. Referenz der Klassen und Methoden von MoinMoin

Das nachfolgende Schaubild2 zeigt die wesentlichen Klassen, die beim Ablauf des Programms wikiserver.py eine Wiki-Seite in Ihrem Browser ausgeben. Die einzelnen Klassen stellen einzelne Schritte dar. Beispielsweise wird über eine Action eine Seite durch Page angefordert. Diese Seite wird durch das Theme mit weiteren Inhalten ausgestattet und dann als HTML-Seite auf ihrem Browser ausgegeben.

MoinMoinArchitecture.png

Prinzipieller Aufbau von MoinMoin

Diesen Klassen ist gemeinsam, dass sie über ein request-Objekt miteinander kommunizieren. Mit einem eigenem request-Objekt (siehe Kapitel request) können Sie aus einer anderen Applikation MoinMoin nutzen, um ein Wiki in Ihre Applikation zu integrieren.

Damit das Arbeiten mit dem MoinMoin-Code einfacher fällt, stelle ich Ihnen zunächst einige Klassen aus der Bibliothek vor, jedoch kann ich deren Methoden nicht komplett aufführen und auch nicht alle Parameter beschreiben. Ich beschränke mich hier auf jene, die wir dann auch in Beispielen verwenden werden. Da Sie den Sourcecode vorliegen haben, werden Sie noch viele andere interessante Klassen und Methoden finden. Gerne können Sie die Entwickler auch in ihrem chatroom #moin auf chat.freenode.net besuchen, wenn etwas unklar ist oder Sie über weitere Möglichkeiten diskutieren wollen.

Anhand des nachfolgenden Beispiels sehen Sie, wie Sie einige Funktionen zum Ausgeben einer Seite anwenden können.

   1 from MoinMoin.Page import Page
   2 from MoinMoin.web.contexts import ScriptContext
   3 request = ScriptContext()
   4 pagename = u'StartSeite'
   5 text = Page(request, pagename).get_raw_body()
   6 print text

Zunächst werden von MoinMoin.Page die Page-Klasse und von MoinMoin.web.contexts das Modul ScriptContext importiert. Die Page Klasse (siehe Kapitel Page) benötigen Sie für den lesenden Zugriff auf eine Wiki-Seite. Das request Object (siehe Kapitel request) benötigen Sie für alle Zugriffe auf das Wiki, die von der Python shell erfolgen.

Der Variablen request wird mit request = ScriptContext() ein frisch erzeugtes request-Objekt zugewiesen.

Die Variable pagename bekommt als unicode String z.B. mit u'StartSeite' den Namen der Seite zugewiesen, die Sie ausgeben möchten.

Die nachfolgende Zeile text = Page(request, pagename).get_raw_body() wendet aus der Klasse Page die Methode get_raw_body (siehe Kapitel page.get_raw_body) an. Diese liest den Inhalt der Seite und speichert deren Rückgabewert in der Variablen text.

Mit print text wird der Inhalt von text ausgegeben.

3.1. request

Den Klassen (siehe Abbildung) ist das zentrale request-Objekt gemeinsam. Dieses enthält Daten und Objekte, die für die Bearbeitung in den einzelnen Programmen benötigt werden; u.a. den Zugriff auf die Konfigurationsdatei.

Neben einem request-Objekt (ScriptContext), dass Sie zum Arbeiten in der Python-Shell verwenden können, unterstützt MoinMoin auch cgi, fcgi, standalone, twisted und wsgi.

MoinMoin ist seit 1.9 eine wsgi Applikation und verwendet dazu die werkzeug Bibliothek. Alle anderen deployment Methoden werden durch die Middleware flup umgesetzt.

Sehr praktisch ist, dass Sie mit diesem request-Objekt in der Python-Shell viele der Methoden, die für die Plugin-Entwicklung (siehe Kapitel Plugins) wichtig sind, ausprobieren und kennen lernen können.

Falls Sie bereits eine in Python entwickelte Applikation haben, können Sie unter anderem mit diesem request-Objekt auch mit einem MoinMoin-Wiki kommunizieren oder die Wiki- Funktionalitäten anwenden. Das nachfolgende Beispiel zeigt, wie Sie ein request-Objekt in der Python-Shell bilden können, und wie Sie von der Shell aus auf das Wiki zugreifen können.

   1 from MoinMoin.web.contexts import ScriptContext
   2 request = ScriptContext()

Sie können sich mit help(request) die Docstrings von request ausgeben lassen. Die Ausgabe beginnt mit dem folgendem Text:

Help on ScriptContext in module MoinMoin.web.contexts object:

class ScriptContext(AllContext)
 |  Context to act in scripting environments (e.g. former request_cli).
 |
 |  For input, sys.stdin is used as 'wsgi.input', output is written directly
 |  to sys.stdout though.
 |
 |  Method resolution order:
 |      ScriptContext
 |      AllContext
 |      HTTPContext
 |      BaseContext
 |      Context
 |      AuxilaryMixin
 |      __builtin__.object

3.1.1. request.getText

Die Methode request.getText ermöglicht die Übersetzung von Text (z.B. die Beschriftung der Navigationselemente) in die voreingestellte Sprache des Benutzers.

   1 request.lang = 'de'
   2 _ = request.getText
   3 print _("Mail me my account data")

E-Mail mit den Zugangsdaten senden

MoinMoin wird englischsprachig entwickelt und der Text dann in die jeweilige Benutzersprache übersetzt. Sie finden unter moin-1.9/MoinMoin/i18n in der Datei de.MoinMoin.po die deutschen Übersetzungen. Evtl. finden Sie in dieser Datei dort einen passenden Ausgabetext für Ihre Erweiterung. Dann können Sie direkt von den vorhandenen Übersetzungen profitieren. Die Übersetzungsarbeit wird gemeinschaftlich von vielen Nutzern auf dem Wiki MoinMaster 3 durchgeführt. Dieses Wiki dient auch der Entwicklung der Hilfeseiten, die Sie in Ihrem Wiki vorfinden. Wenn Sie bei Übersetzungen helfen wollen, sollten Sie einfach mal die Action CheckTranslation ausprobieren. Damit können Sie sich einen Überblick verschaffen, was noch übersetzt werden muß.

3.1.2. request.dicts

Mit den Methoden der Klasse wiki_dicts erhalten Sie Zugriff auf ein Dictionary, das Sie mit Wiki-Seiten anlegen können. So können Sie ein Dictionary für die Verarbeitung von Variablen, wie sie in dicts verwendet werden anlegen. Diese erlauben es Ihnen zu je einem Schlüssel einen oder mehrere Werte zu definieren.

Es gibt verschiedene Möglichkeiten dicts zu definieren. Derzeit kann man dicts als Wiki Seite anlegen oder in der wikiconfig als dictionary. Diese sind auch vereinigbar (CompositeDicts).

Nachfolgend ein Auszug aus moin-1.9/wiki/config/more_samples/dicts_wikiconfig_snippet. Dies ist ein Beispiel wie Sie in der wikiconfig die Dictionaries definieren können. Der default ist dictionaries aus Wiki-Seiten abzubilden.

   1     def dicts(self, request):
   2         from MoinMoin.datastruct import ConfigDicts
   3         dicts = {u'OneDict': {u'first_key': u'first item',
   4                               u'second_key': u'second item'},
   5                  u'NumbersDict': {u'1': 'One',
   6                                   u'2': 'Two'}}
   7         return ConfigDicts(request, dicts)

Der Zugriff auf die dicts erfolgt über das request.dicts.

In HelpOnConfiguration ist die Variable page_dict_regex beschrieben. Mit der Variablen page_dict_regex legen Sie fest, welche Seiten durch wiki_dicts auswertbar werden.

Die Voreinstellungen lautet:

page_dict_regex = ur'(?P<all>(?P<key>\S+)Dict)'

Wenn Sie z.B. eine Seite mit dem Namen http://localhost:8080/BeispielDict anlegen und dort als Beispiel eine Definitionsliste anlegen.

 var:: das ist ein Beispiel

schreiben, dann können Sie auf die Variable var in dem Dictionary wie folgt zugreifen:

   1 pagename = u"BeispielDict"
   2 d = request.dicts.get(pagename, {})
   3 print d.items()
   4 print d["var"]

Ausgabe:

[(u'var', u'das ist ein beispiel')]
das ist ein Beispiel

Das Macro GetVal wendet diese Methode an. Sie können den Wert überall dort einfügen, wo Sie das Macro <<GetVal(BeispielDict,var)>> in den Wiki-Text schreiben. Diese Variable lässt sich damit auch von einer anderen Wiki-Seite benutzen.

3.1.3. request.groups

Mit der Definition der Variablen page_group_regex legen Sie fest, welche Seiten als Gruppen auswertbar werden. In der Regel werden solche Seiten für die Definition von Benutzergruppen verwendet. Diese Gruppen werden dann verwendet um Rechte auf wiki Seiten einzuräumen. Jedoch ist das nicht darauf beschränkt. Alle Arten von items kann man mit Gruppenseiten zusammenfassen.

Die Voreinstellungen lautet:

page_group_regex = ur'(?P<all>(?P<key>\S+)Group)'

Neben der Default Einstellung das Wiki-Seiten verwendet werden, können Sie in der wikiconfig durch Definition eine andere Methode einstellen,. Nachfolgend ein Auszug aus moin-1.9/wiki/config/more_samples/groups_wikiconfig_snippet.

   1     # ConfigGroups uses groups defined in the configuration file.
   2     def groups(self, request):
   3         from MoinMoin.datastruct import ConfigGroups
   4         # Groups are defined here.
   5         groups = {u'EditorGroup': [u'AdminGroup', u'John', u'JoeDoe', u'Editor1'],
   6                   u'AdminGroup': [u'Admin1', u'Admin2', u'John']}
   7         return ConfigGroups(request, groups)

Mit CompositeGroups können Sie mehrere Methoden kombinieren.

Der Zugriff auf die Gruppen erfolgt mittels request.groups.

Legen Sie bitte zum Testen eine Seite mit dem Namen `http://localhost:8080/FruitsGroup' an und dort eine Punkte Liste mit den Items an, z.B.:

 * Äpfel
 * Birnen
 * Bananen

Nun können Sie leicht feststellen, was in der FruitsGroup ist und was nicht.

   1 groups = request.groups
   2 fruits = groups.get('FruitsGroup')
   3 print u'Äpfel' in fruits
   4 for item in fruits:
   5     print item

Ausgabe:

True
Äpfel
Birnen
Bananen

Bitte beachten Sie, dass Gruppen auch andere Gruppen und auch sehr viele Items enthalten können, daher ist ein Listen der items nicht unbedingt sinnvoll.

3.1.4. request.user.may

Mit den Methoden von request.user.may werden die Rechte des Benutzers für eine Seite überprüft.

   1 pagename = u'StartSeite'
   2 print request.user.may.read(pagename)
   3 print request.user.may.write(pagename)
   4 print request.user.may.delete(pagename)
   5 print request.user.may.revert(pagename)
   6 print request.user.may.admin(pagename)

3.2. Page

Die Klasse Page wird für den readonly-Zugriff auf eine Seite verwendet. Sie können damit auch auf ältere Versionen einer Seite als die aktuelle zugreifen. Bei Operationen, die auf Wiki-Seiten oder deren Attachments vorgenommen werden, werden die Zugriffsrechte des Benutzers überprüft

   1 from MoinMoin.Page import Page

3.2.1. Page.exists()

Mit der Methode Page.exists() stellen Sie fest, ob eine Seite bereits existiert, z.B.:

   1 pagename = u'StartSeite'
   2 print Page(request, pagename).exists()

Ausgabe:

True

3.2.2. Page.getRevList()

Die Methode Page.getRevList() gibt eine Liste der Revisionen einer Seite zurück. Die aktuelle Revision wird als erste angezeigt.

   1 pagename = u'StartSeite'
   2 print Page(request, pagename).getRevList()

Ausgabe:

[1]

3.2.3. Page.current_rev()

Die Methode Page.current_rev() zeigt die aktuelle Revision einer Seite an.

   1 pagename = u'StartSeite'
   2 print Page(request, pagename).current_rev()

Ausgabe:

1

3.2.4. Page.getPagePath()

Wenn Sie den Dateipfad zu einer Wiki-Seite wissen möchten, wenden Sie die Methode Page.getPagePath() an.

   1 pagename = u'StartSeite'
   2 print Page(request, pagename).getPagePath()

Ausgabe:

/home/workspace/moin-1.9/wiki/underlay/pages/StartSeite

3.2.5. Page.get_raw_body()

Um den Seiteninhalt auszulesen, wenden Sie Page.get_raw_body() an.

   1 pagename = u'StartSeite'
   2 text = Page(request, pagename).get_raw_body()
   3 print text

## Please edit system and help pages ONLY in the master wiki!
## For more information, please see MoinMoin:MoinDev/Translation.
##master-page:FrontPage
##master-date:2007-17-12 21:30:15
#format wiki
#language de
#pragma section-numbers off

= WikiName Wiki =

Worum geht es in diesem Wiki?

Wichtige Einstiegsseiten:
 * AktuelleÄnderungen: Sehen Sie, woran gerade gearbeitet wird
 * SeiteFinden: Durchsuchen Sie das Wiki
 * WegWeiser: Finden Sie sich im Wiki zurecht
 * WikiSandkasten: Probieren Sie das Editieren im Wiki aus
 * HilfeZurMoinWikiSyntax: Schnellübersicht der Darstellungs- und Textauszeichnungsmöglichkeiten

'''Beachten Sie auch die englische Startseite: FrontPage.'''

== Wie man diese Seiten benutzt ==

Ein Wiki wird von einer gemeinschaftlichen Idee getragen, jeder kann seinen Beitrag leisten und daran teilhaben:
 * Bearbeiten Sie jede Seite, indem Sie auf "<<GetText(Edit)>>" in der oberen oder unteren Leiste klicken
 * Erstellen Sie einen Link zu einer anderen Seite durch das Aneinanderreihen von groß geschriebenen Worten (z. B. AktuelleÄnderungen)
 * Suchen Sie nach Seiten mit dem Suchfeld oben auf der Seite (Titel- oder Volltextsuche)
 * Auf HilfeFürAnfänger stehen weitere Hinweise; HilfeInhalt verlinkt auf alle Hilfeseiten.

Um mehr darüber zu erfahren, was ein WikiWikiWeb ist, können Sie DseWiki:WarumWikiFunktioniert lesen. Antworten auf häufig gestellte Fragen finden Sie auch auf der Seite HilfeAllgemein/FragenUndAntworten.

Dieses Wiki basiert auf [[http://moinmo.in/|MoinMoin]].

3.2.6. Page.send_page()

Mit Page.send_page() senden Sie die formatierte Seite an das Ausgabegerät (meistens an den Browser). Optional können Sie eine Nachricht über den msg-Parameter ausgeben. Diese Nachricht wird dann in einer Box oberhalb der Seite angezeigt. Wird diese Methode nicht für einen Browser angewendet, sollte auch kein http_header verschickt werden.

   1 pagename = u'StartSeite'
   2 Page(request, pagename).send_page(emit_headers=False)

Die Ausgabe in der Konsole beginnt mit:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta name="robots" content="index,nofollow">

<title>StartSeite - MoinMoin DesktopEdition</title>
<script type="text/javascript" src="/moin_static190/common/js/common.js"></script>

<script type="text/javascript">
<!--
var search_hint = "Search";
//-->
</script>

Seit moin-1.9 ist die indizierte Suche mittels xapian in der Lage regulare expressions zu verarbeiten. Vor 1.9 ist dafür auf die langsame Suchmethode MoinSearch zurückgegriffen worden. Diese ist auch der default.

Um die Suche bei xapian einzuschalten, müssen Sie eine Version >= 1.0.6 verwenden und in der wikiconfig einkonfigurieren

xapian_search = True
xapian_stemming = True

Enthält das wiki schon Seiten oder attachments, sollte der index gebildet werden.

moin --config-dir=/where/your/configdir/is --wiki-url=wiki-url/ index build --mode=add

Das xapian bildet einen index der Worte in den Wiki-Seiten. Mittels Filter werden auch Textinformationen aus attachments extrahiert und die attachments sind daher dann auch durchsuchbar.

Anders als bei der default Suche können jedoch nur Worte gefunden werden, die auch im Index enthalten sind. Das Stemming lässt es zu, dass auch Wortstämme gefunden werden. Teilworte können nur mit Sicherheit mit Hilfe einer regex gefunden werden.

3.3.1. searchPages

Der nachfolgende Abschnitt beschreibt, wie Seiten anhand einer Category Zuweisung gefunden werden können.

   1 from MoinMoin.search import searchPages

Ein kleines Beispiel, wie Sie nach Seiten der Kategorie CategoryHomePage suchen können:

   1 from MoinMoin.web.contexts import ScriptContext
   2 request = ScriptContext()
   3 from MoinMoin.Page import Page
   4 from MoinMoin import search
   5 needle = "category:CategoryHomePage"
   6 search_result = searchPages(request, needle)
   7 for title in search_result.hits:
   8     print title.page_name

3.4. PageEditor

Die Klasse PageEditor wird für alle Schreiboperationen auf eine Seite des Wikis verwendet. Bei Operationen, die auf Wiki-Seiten oder deren Attachments vorgenommen werden, werden die Zugriffsrechte des Benutzers überprüft.

   1 from MoinMoin.PageEditor import PageEditor

3.4.1. PageEditor.saveText()

PageEditor.saveText() wird verwendet, um Text auf einer Seite zu speichern. Neben dem Text, den Sie speichern wollen, müssen Sie auch noch die Revision der Seite als Parameter angeben.

   1 pagename = u'TestSeite'
   2 text = u'Das ist ein Beispiel'
   3 print PageEditor(request, pagename).saveText(text, 0)

Die Bestätigung für das erfolgreiche Speichern lautet:

'Thank you for your changes. Your attention to detail is appreciated.'

Diese Seite befindet sich jetzt im Wiki, wie Sie leicht mit einem Web-Browser überprüfen können. http://localhost:8080/TestSeite

Wenn Sie den Aufruf PageEditor(request, pagename).saveText(text, 0) wiederholen, ohne eine Änderung am text durchzuführen. wird das Speichern verweigert und Sie erhalten eine Fehlermeldung ausgelöst durch eine Ausnahme.

MoinMoin.PageEditor.Unchanged: You did not change the page content, not saved!

Diese Fehlermeldung sollte immer mit einem try ... except:-Block abgefangen werden, siehe nachfolgendes Beispiel:

   1 pagename = u'TestSeite'
   2 page = PageEditor(request, pagename)
   3 text = u'Das ist ein Beispiel'
   4 try:
   5     page.saveText(text, 0)
   6 except page.Unchanged:
   7     print "You did not change the page content, not saved!"

3.4.2. PageEditor.deletePage()

Die Methode PageEditor.deletePage wird verwendet, um eine Seite zu löschen. Wird eine Seite gelöscht, wird eine neue Revision ohne Datendatei erzeugt -- die alten Versionen der Seite bleiben aber erhalten und können auch wieder hergestellt werden.

   1 pagename = u'TestSeite'
   2 print PageEditor(request, pagename).deletePage()

Die Bestätigung für das erfolgreiche Löschen lautet:

(True, u'Page "TestSeite" was successfully deleted!')

3.5. AttachFile

Das Modul AttachFile enthält die Funktionen, die benötigt werden, um Attachments auf Wiki-Seiten abzulegen oder von dort zu laden. Es ist geplant in einer zukünftigen MoinMoin-Version, dass Attachments ähnlich wie Wiki-Seiten behandelt werden (und damit dann auch versionierbar werden). Dadurch wird dann diese Klasse entfallen.

   1 from MoinMoin.action import AttachFile

3.5.1. AttachFile.exists()

Die Methode AttachFile.exists() stellt fest, ob ein bestimmter Dateianhang schon auf der Seite existiert.

   1 pagename = u'WikiSandkasten'
   2 attachment = u'dateianhang.png'
   3 print AttachFile.exists(request, pagename, attachment)

Ausgabe:

True

3.5.2. AttachFile.getAttachDir()

Die Methode AttachFile.getAttachDir() gibt den Pfad zu dem Attachment zurück.

   1 pagename = u'WikiSandkasten'
   2 attachment = u'dateianhang.png'
   3 print AttachFile.getAttachDir(request, pagename)

Ausgabe:

/home/workspace/moin-1.9/wiki/underlay/pages/WikiSandkasten/attachments

3.5.3. AttachFile.getAttachUrl()

Mit der Methode AttachFile.getAttachUrl() erhalten Sie die URL Ihres Attachments.

   1 pagename = u'WikiSandkasten'
   2 attachment = u'dateianhang.png'
   3 print AttachFile.getAttachUrl(pagename, attachment, request)

Ausgabe:

./WikiSandkasten?action=AttachFile&do=get&target=dateianhang.png

3.6. logfile

MoinMoin unterscheidet zwei Logging-Systeme: Mit eventlog werden hauptsächlich Ereignisse, wie mit dem Wiki umgegangen wird, für statistische Auswertungen festgehalten, z.B. welcher Browser eingesetzt wird oder welche Seiten an einem Tag angeschaut wurden. Das editlog speichert Ereignisse, wann eine Seite erstellt, bearbeitet oder gelöscht wurde, und diese Information werden auf der Seite Aktuelle Änderungen oder im Info Bereich der Seite angezeigt.

3.6.1. logfile.eventlog

Die Klasse eventlog verwaltet Zugriffe auf Wiki-Seiten. Sie haben die Möglichkeit zwischen zwei Filtern zu wählen. 'VIEWPAGE' kennzeichnet die angesehenen Seiten und 'SAVEPAGE' die gespeicherten. Das nachfolgende Beispiel zählt die Zugriffe auf die StartSeite. Bevor die Seitenzugriffe gezählt werden, wird überprüft, ob der Benutzer die zu zählende Seite lesen darf. Ist dies nicht der Fall, wird der Wert 0 ausgegeben.

   1 from MoinMoin.logfile import eventlog
   2 event_type = u'VIEWPAGE'
   3 pagename = u'LanguageSetup'
   4 event_log = eventlog.EventLog(request)
   5 count = 0
   6 if request.user.may.read(pagename):
   7     test = filter(lambda line: line[1] in event_type and line[2]['pagename'] == pagename, event_log)
   8     count = len(test)
   9 
  10 print count

Ausgabe:

8

3.6.2. logfile.editlog

Mit Hilfe der Klasse logfile.editlog erfahren Sie etwas über die Bearbeitungsgeschichte einer Wiki-Seite. Wiki-Seiten können erstellt, umbenannt und gelöscht werden. Dies wird im editlog vermerkt. u'SAVENEW' für neu angelegte Seiten, u'SAVE/RENAME' für Seiten die umbenannt wurden, u'SAVE' für gelöschte Seiten, (siehe Kapitel PageEditor.deletePage). Das nachfolgende Beispiel zeigt die neu erstellten Wiki-Seiten in Ihrem Wiki und das Erstellungsdatum der Seiten. Es werden durch den Vergleich line.action == 'SAVENEW' nur Seiten ausgegeben, die in Ihrem Wiki neu angelegt wurden.

   1 from MoinMoin import wikiutil
   2 from MoinMoin.logfile import editlog
   3 edit_log = editlog.EditLog(request)
   4 for line in edit_log.reverse():
   5     if (request.user.may.read(line.pagename) and line.action == 'SAVENEW'):
   6         timestamp = request.user.getTime(wikiutil.version2timestamp(line.ed_time_usecs))
   7         year, month, day = timestamp[0:3]
   8         print "%s %s.%s.%s" % (line.pagename, day, month, year)

Beispielhafte Ausgabe:

ExamplePage1 8.9.2009
ExamplePage2 8.9.2009
ExamplePage3 9.9.2009

3.7. wikiutil

Die Klasse wikiutil enthält diverse Utility-Funktionen.

   1 from MoinMoin import wikiutil

3.7.1. wikiutil.escape()

Mit der Methode wikiutil.escape() maskieren Sie mögliche HTML-Sonderzeichen. Alles, was HTML-Sonderzeichen enthält, muss vor der Ausgabe escaped werden. Diese Methode wendet auch der Formatter (siehe Seite formatter) an.

   1 print wikiutil.escape(u'<html>')

Ausgabe:

&lt;html&gt

3.7.2. wikiutil.createTicket()

Durch Anwendung der Methode wikiutil.createTicket() erstellen Sie ein Ticket, das Sie zur Verifikation von Formularen verwenden können. Dadurch können Sie überprüfen, ob die eingehenden Formulardaten aus einem von Ihnen erstellten Formular stammen (siehe Beispiel).

   1 print wikiutil.createTicket(request)

Ausgabe:

0046e8506e.None.show.8ea03fb9d6e50bf60721aa

3.7.3. wikiutil.checkTicket()

Mit der Methode wikiutil.checkTicket() wird überprüft, ob das Ticket dem System bekannt ist (siehe Beispiel).

   1 print wikiutil.checkTicket(request,
   2    '0046e8506e.None.show.8ea03fb9d6e50bf60721aa')

Ausgabe:

True

3.7.4. wikiutil.invoke_extension_function()

Die Anwendung der Methode wikiutil.invoke_extension_function() ermöglicht es Ihnen, komfortabel Parameter Ihrer Macros (siehe Kapitel Plugins macro) auszuwerten. Durch die Definition eines Defaultwerts wird der Datentyp des Parameters festgelegt. Durch Vorgabe einer Liste möglicher Eingaben werden nur Eingaben aus dieser Liste zugelassen. Auf abweichende Eingaben wird mit einer Fehlermeldung hingewiesen.

3.7.5. wikiutil.version2timestamp()

Mit der Methode wikiutil.version2timestamp() wird die Zeitinformation, die in MoinMoin verwendet wird in die UNIX Zeitinformation umgerechnet.

3.7.6. wikiutil.timestamp2version()

Mit der Methode wikiutil.timestamp2version() wird die UNIX Zeitinformation, in die in MoinMoin verwendete Zeit umgerechnet.

3.7.7. wikiutil.renderText()

Mit der Methode wikiutil.renderText() wird wiki Text geparsed und in HTML ausgegeben.

   1 from MoinMoin import wikiutil
   2 from MoinMoin.Page import Page
   3 from MoinMoin.web.contexts import ScriptContext
   4 request = ScriptContext()
   5 pagename = u'NeverExistingPage'
   6 request.formatter.page = Page(request, pagename)
   7 from MoinMoin.parser.text_moin_wiki import Parser as WikiParser
   8 text = "WikiName"
   9 wikiutil.renderText(request, WikiParser, text)

Ausgabe:

u'<span class="anchor" id="line-1"></span><a href="/WikiName">WikiName</a> '

3.8. user

user enthält eine Reihe nützlicher Funktionen für das User Klassen Object. Die Klasse User repräsentiert einen Benutzer und gibt Zugriff auf sein Profil.

   1 from MoinMoin import user

3.8.1. user.User(request, auth_method="new-user")

Das nachfolgende Beispiel legt einen user mit dem Namen "ExampleUser" an.

   1 import os
   2 from MoinMoin.web.contexts import ScriptContext
   3 request = ScriptContext()
   4 from MoinMoin import user, wikiutil
   5 the_user = user.User(request, auth_method="new-user")
   6 the_user.name = "ExampleUser"
   7 the_user.email = "ExampleUser@localhost"
   8 the_user.enc_password = user.encodePassword("zoo0Eifi")
   9 if (user.isValidName(request, the_user.name) and
  10     not user.getUserId(request, the_user.name) and
  11     not user.get_by_email_address(request, the_user.email)):
  12     print "This user with the userid %s doesn't exist yet."  % the_user.id
  13     the_user.save()
  14     filename = os.path.join(request.cfg.user_dir, the_user.id)
  15     if os.path.exists(filename):
  16         print "The user file %s was created" % filename
  17 else:
  18     print "user credential already used - nothing done"

Ausgabe:

This user with the userid 1257617552.0.33542 doesn't exist yet.
The user file /home/workspace/moin-1.9/wiki/data/user/1257617552.0.33542 was created

3.8.2. user.getUserList()

Um alle numerische uids der Benutzer zu erhalten verwenden Sie user.getUserList.

   1 uids = user.getUserList(request)
   2 print uids

Ausgabe:

['1252446015.52.43538', '1252448264.85.4326', '1257617552.0.33542']

3.8.3. user.getUserId()

Die uid des Benutzernamens wird benötigt, wenn auf einen Benutzer zugegriffen werden soll. Dies geht einfach mit der Abfrage

   1 uid = user.getUserId(request, u'ExampleUser')
   2 print uid

Ausgabe:

1257617552.0.33542

3.8.4. user.User.name

Auf der Variablen user.User.name ist der Benutzername gespeichert. Wenn Sie einen Benutzer in ihrem Wiki mit dem Namen ExampleUser haben, wird dieser Name im nachfolgendem Beispiel ausgegeben.

   1 uid = user.getUserId(request, u'ExampleUser')
   2 user_object = user.User(request, uid)
   3 print user_object.name

Ausgabe:

ExampleUser

3.8.5. user.User.exists()

Die Methode user.User.exists() wird verwendet, um festzustellen ob der Benutzer existiert.

   1 uid = user.getUserId(request, u'ExampleUser')
   2 print user.User(request, uid).exists()

Ausgabe:

True

3.8.6. user.User.isSuperUser()

Die Methode user.User.isSuperUser() Überprüft, ob der Benutzer ein Superuser ist. Superuser haben einige besondere Rechte.

   1 uid = user.getUserId(request, u'ExampleUser')
   2 print user.User(request, uid).isSuperUser()

Ausgabe:

False

3.8.7. user.User.save()

Mit der Methode user.User.save() wird das Benutzer-Profil gespeichert.

   1 uid = user.getUserId(request, u'ExampleUser')
   2 this_user = user.User(request, uid)
   3 if this_user.exists():
   4     this_user.save()

3.8.8. user.User.getSubscriptionList()

Die Methode user.User.getSubscriptionList() gibt die abonnierten Seiten des Benutzers aus.

   1 uid = user.getUserId(request, u'ExampleUser')
   2 print user.User(request, uid).getSubscriptionList()

Ausgabe:

[]

3.8.9. user.User.subscribe()

Die Methode user.User.subscribe() wird verwendet, um eine Seite zu abonnieren. Mit True wird das erfolgreiche Abonnement einer Seite bestätigt. Ändert ein anderer Benutzer diese Seite bekommt der Abonnement eine Benachrichtigung per email der Änderungen.

   1 uid = user.getUserId(request, u'ExampleUser')
   2 this_user = user.User(request, uid)
   3 pagename = u'StartSeite'
   4 print this_user.subscribe(pagename)

Ausgabe:

True

3.8.10. user.User.unsubscribe()

Die Methode user.User.unsubscribe() wird verwendet, um das Abonnement einer Seite zu beenden.

   1 uid = user.getUserId(request, u'ExampleUser')
   2 this_user = user.User(request, uid)
   3 pagename = u'StartSeite'
   4 print this_user.unsubscribe(pagename)

Ausgabe:

True

3.8.11. user.User.getTime()

Die Methode user.User.getTime() wird verwendet, um die Zeit in der Zeitzone des Benutzers umzurechnen. Wenn Sie den Wert 0 angeben, wird die aktuelle Zeit ausgegeben.

   1 uid = user.getUserId(request, u'ExampleUser')
   2 this_user = user.User(request, uid)
   3 print this_user.getTime(0)

Ausgabe:

time.struct_time(tm_year=2009, tm_mon=11, tm_mday=7, tm_hour=18, tm_min=21, tm_sec=56, tm_wday=5, tm_yday=311, tm_isdst=0)

3.9. formatter

Ein Formatter ist für die Ausgabe-Erzeugung zuständig. Für die HTML-Ausgabe ist eine Formatter-Instanz von formatter.text_html im request-Objekt enthalten.

Das nachfolgende Beispiel importiert den HTML-Formatter und weist eine Instanz des Formatters der Variablen html_formatter zu:

   1 from MoinMoin.formatter.text_html import Formatter
   2 html_formatter = Formatter(request)

3.9.1. formatter.text()

Wenn Sie Text mit einem Macro ausgeben, sollten Sie darauf achten, dass HTML-Steuerzeichen maskiert werden. Die Formatter-Methode text berücksichtigt dies, wie Sie am zweiten Beispiel sehen können.

   1 print html_formatter.text('Hello World')

Ausgabe:

Hello World

   1 print html_formatter.text('<BR>Hello World<BR>')

Ausgabe:

&lt;BR&gt;Hello World&lt;BR&gt;

3.9.2. formatter.img()

Wenn Sie mit dem formatter ein Bild ausgeben möchten, verwenden Sie die Methode formatter.img().

   1 src = "http://static.moinmo.in/logos/moinmoin.png"
   2 print html_formatter.image(src=src)

Ausgabe:

<img src="http://static.wikiwikiweb.de/logos/moinmoin.png" />

3.9.3. formatter.number_list()

Die Methode formatter.number_list() ermöglicht es, eine Aufzählung auszugeben (siehe Beispiel).

3.9.4. formatter.bullet_list()

Die Methode formatter.bullet_list() ermöglicht es, eine Punkte-Liste auszugeben (siehe Beispiel).

3.9.5. formatter.listitem()

Die Methode formatter.listitem() ermöglicht es, in einer Nummerierten- oder Punkte- Liste Elemente auszugeben.

   1 txt = html_formatter.bullet_list(True)
   2 txt += html_formatter.listitem(True)
   3 txt += 'erstes bullet der Zeile'
   4 txt += html_formatter.listitem(False)
   5 txt += html_formatter.bullet_list(False)
   6 print txt

Ausgabe:

<ul><li>erstes bullet der Zeile</li></ul>

   1 txt = html_formatter.number_list(True)
   2 txt += html_formatter.listitem(True)
   3 txt += 'erste nummerierte Zeile'
   4 txt += html_formatter.listitem(False)
   5 txt += html_formatter.number_list(False)
   6 print txt

Ausgabe:

<ol><li>erste nummerierte Zeile</li></ol>

3.10. xmlrpc

In den bisherigen Beispielen sind Ihnen einige Klassen und Methoden von MoinMoin vorgestellt worden. Sie haben gesehen, wie Sie interaktiv in der Python-Kommandozeile oder aus anderen Programmen heraus Wiki-Seiten erstellen können. Diese Programme müssen auf der gleichen Maschine ausgeführt werden, auf der sich das Wiki befindet. Diese Einschränkung wird durch die Anwendung des xmlrpc-Protokolls aufgehoben. Damit können Daten von anderen Rechnern in ein Wiki eingefügt werden. Die Default Konfiguration eines wikis verbietet u.a. die Action xmlrpc, damit nicht automatisch ein Schreib- und Lesezugriff erfolgen kann. Diese Einstellung muss von Ihnen geändert werden, wenn Sie dies wünschen. Damit Sie xmlrpc nutzen können, empfiehlt es sich neben der nötigen Konfiguration, in der Konfigurationsdatei wikiconfig.py die Rechte für den Schreibzugriff auf die Wiki Seiten auf eine bekannte Gruppe von Benutzern zu begrenzen. Die Zugriffsverwaltung geschieht durch die Anwendung von Access Control Lists (ACLs). Wenn Sie z.B. einen Benutzer TestUser in Ihrem Wiki angelegt haben, könnte der Eintrag in wikiconfig.py wie folgt lauten:

actions_excluded = multiconfig.DefaultConfig.actions_excluded[:]
actions_excluded.remove('xmlrpc')
acl_rights_default = u"TestUser:read,write,delete All:read"

Damit Sie mit xmlrpc auf Ihr Wiki zugreifen können, muss das Wiki gestartet werden (siehe Beispiel). Derzeit wird das Wiki nur standalone betrieben. Ist es auf einem Server installiert (siehe HilfeZurInstallation), verwenden Sie als wikiurl die Adresse des wikis das Sie ansprechen wollen. Mit dem nachfolgendem Beispiel wird gezeigt, wie die Anmeldung des Benutzers TestUser mit Benutzername und Passwort erfolgt:

name = "TestUser"
password = "geheimeswort"
wikiurl = "http://localhost:8080"
homewiki = xmlrpclib.ServerProxy(wikiurl + "?action=xmlrpc2", allow_none=True)
auth_token = homewiki.getAuthToken(name, password)

Durch das getAuthToken wird das User Object initialisiert.

3.10.1. xmlrpc.putPage()

Mit der xmlrpc.putPage()-Methode übertragen Sie neue Inhalte auf eine Seite. Das nachfolgende Beispiel erstellt die Seite "TestSeite" in Ihrem Wiki. Bitte verwenden Sie einen Benutzer, der in Ihrem Wiki durch die Konfiguration in wikiconfig.py Schreibrechte hat. Im Beispiel ist es der User TestUser. Mit der xmlrpclib.MultiCall()-Methode können Sie das Verarbeiten der xmlrpc-Befehle dadurch beschleunigen, dass Sie xmlrpc-Befehle bündeln, um diese dann als einen xmlrpc-Befehl abzuschicken.

   1 import xmlrpclib
   2 name = "TestUser"
   3 password = "geheimeswort"
   4 wikiurl = "http://localhost:8080"
   5 homewiki = xmlrpclib.ServerProxy(wikiurl + "?action=xmlrpc2", allow_none=True)
   6 auth_token = homewiki.getAuthToken(name, password)
   7 mc = xmlrpclib.MultiCall(homewiki)
   8 mc.applyAuthToken(auth_token)
   9 pagename = 'TestSeite'
  10 text = 'Dies ist eine Zeile Text'
  11 mc.putPage(pagename, text)
  12 result = mc()

3.10.2. xmlrpc.getPage()

Ebenso wie Sie Seiten in das Wiki schreiben können, können Sie auch Seiten auslesen. Anstelle der xmlrpc.putPage()-Methode verwenden Sie dann die xmlrpc.getPage()-Methode.

   1 import xmlrpclib
   2 wikiurl = "http://localhost:8080"
   3 homewiki = xmlrpclib.ServerProxy(wikiurl + "?action=xmlrpc2", allow_none=True)
   4 pagename = 'TestSeite'
   5 raw_text = homewiki.getPage(pagename)
   6 print raw_text

oder

   1 import xmlrpclib
   2 name = "TestUser"
   3 password = "geheimeswort"
   4 wikiurl = "http://localhost:8080/"
   5 homewiki = xmlrpclib.ServerProxy(wikiurl + "?action=xmlrpc2", allow_none=True)
   6 auth_token = homewiki.getAuthToken(name, password)
   7 mc = xmlrpclib.MultiCall(homewiki)
   8 mc.applyAuthToken(auth_token)
   9 pagename = u'TestSeite'
  10 mc.getPage(pagename)
  11 result = mc()
  12 success, raw = tuple(result)
  13 if success:
  14     print raw

3.10.3. xmlrpc.putAttachment()

Mit xmlrpc.putAttachment() können Sie eine Datei als Attachment auf eine Wiki-Seite laden.

   1 import xmlrpclib
   2 name = "TestUser"
   3 password = "geheimeswort"
   4 wikiurl = "http://localhost:8080"
   5 homewiki = xmlrpclib.ServerProxy(wikiurl + "?action=xmlrpc2", allow_none=True)
   6 mc = xmlrpclib.MultiCall(homewiki)
   7 auth_token = homewiki.getAuthToken(name, password)
   8 mc.applyAuthToken(auth_token)
   9 attachname = 'beispiel.txt'
  10 text = file(attachname, 'rb+').read()
  11 data = xmlrpclib.Binary(text)
  12 mc.putAttachment(pagename, attachname, data)
  13 result = mc()

3.10.4. xmlrpc.getAttachment()

Mit xmlrpc.getAttachment() können Sie ein Attachment einer Wiki-Seite als Datei auslesen (und dann z.B. auf Ihren Rechner speichern). Mit der vorgegebenen ACL-Definition darf ein anonymer Benutzer Wiki-Seiten und Attachments lesen. Das Beispiel beschreibt das Auslesen einer Datei "Beispiel.txt" auf der Seite TestSeite.

   1 import xmlrpclib
   2 wikiurl = "http://localhost:8080"
   3 homewiki = xmlrpclib.ServerProxy(wikiurl + "?action=xmlrpc2", allow_none=True)
   4 pagename = 'TestSeite'
   5 attachname = 'beispiel.txt'
   6 data = homewiki.getAttachment(pagename, attachname)
   7 file(attachname, 'wb+').write(data.data)

4. Plugins (currently for 1.8, needs to be refactored for 1.9)

Reicht der Umfang von MoinMoin nicht aus, kann man ihn sehr einfach durch ein eigenes Plugin erweitern. Wiki Seiten von MoinMoin werden in Form von HTML in einem Cache system zwischengespeichert. Solange Seiten sich nicht ändern können komplexe Seiten daher sehr schnell nach der ersten Generierung wieder dargestellt werden. Machmal ist das aber nicht gewünscht. Für alle Plugins können daher Abhängigkeiten definiert werden, wenn sich die Ausgabe des Plugins abhängig von diesen ändert. Mit der Variable Dependencies = [] können Sie bestimmen, welche Abhängigkeiten bestehen (wenn keine bestehen, wird die Ausgabe als statisch angesehen). Neben einem einfachem [], werden folgende Abhängigkeiten ["time"], ["user"], ["language"], ["pages"], ["namespace"] verwendet.

Dem Typ eines Plugins entsprechend gibt es unterhalb von moin-1.9/wiki/data/plugin jeweils ein zugeordnetes Verzeichnis, in dem Sie das Plugin für die Verwendung speichern müssen.

Z.B. ein Action-Plugin gehört in das Verzeichnis moin-1.9/wiki/data/plugin/action.

Achten Sie bei der Ausgabe bitte darauf, dass Sie diese mit dem formatter bewerkstelligen, um Anfälligkeit gegen XSS vorzubeugen (siehe formatter).

Bei Plugins kann es nötig sein, Berechtigungen über das request-Objekt abzufragen (siehe Beispiel).

Die nachfolgenden Sektion-Überschriften beschreiben den Ort, wo Sie Ihre plugins unterhalb von moin-1.9/wiki/data/plugin ablegen müssen, damit diese von MoinMoin erkannt werden.

4.1. macro

Ein Macro wird im Wiki-Text durch zwei Kleiner-Zeichen und zwei Größer-Zeichen dem Parser kenntlich gemacht. z.B.: <<HelloWorld>>

4.1.1. Hello World

Das nachfolgende Beispiel beschreibt ein typisches HelloWorld Programm zum Kennenlernen eines Macro Aufrufs. <<HelloWorld>>

   1 def execute(macro, args):
   2     return macro.request.formatter.text('Hello World')

Das obige Beispiel verwendet keine Argumente. Seit der Version 1.6 ist es möglich, Argumente durch einen Argumentparser auszuwerten. Damit können Sie explizit in der Definition des Makros festlegen, welche Eingabe erlaubt ist (siehe Kapitel wikiutil.invoke_extension_function). Seit der Version 1.7 entfällt die excecute Funktion in der Macro Definition.

   1 def macro_HelloWorld(macro, color=(u'red', u'blue')):
   2     return macro.request.formatter.text('Hello World', style="color:%s" % color)

Mit <<HelloWorld>> wird der erste Wert der Definition von color angewandt und der Text in rot geschrieben. <<HelloWorld(color="blue")>> schreibt Hello World in blau. Wogegen <<HelloWorld(color="green")>> die Fehlermeldung ausgibt:

<<HelloWorld: Argument "color" muss eins von "red", "blue"  sein, nicht "green">>

4.1.2. Attachments verarbeiten

Das nachfolgende Beispiel zeigt Ihnen die Möglichkeit Wiki-Text im Programm zu verwenden um daraus dann HTML für die Darstellung zu generieren. Es verarbeitet dabei CSV-Daten, die als Dateianhänge auf einer Seite abgespeichert sind.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - ShowCSV shows csv file as table
   4     @copyright: 2007 MoinMoin:ReimarBauer
   5     @license: GNU GPL, see COPYING for details.
   6 """
   7 Dependencies = ['time'] # do not cache
   8 
   9 import codecs, os
  10 from MoinMoin import config, wikiutil
  11 from MoinMoin.action import AttachFile
  12 from MoinMoin.parser.text_moin_wiki import Parser as wiki_Parser
  13 from MoinMoin.parser.text_csv import Parser as CSV_Parser
  14 
  15 def execute(macro, args):
  16     request = macro.request
  17     formatter = macro.formatter
  18     extension = '.csv'
  19     pagename = formatter.page.page_name
  20     files = AttachFile._get_files(request, pagename)
  21     attach_dir = AttachFile.getAttachDir(request, pagename)
  22     for file in files:
  23         if file.lower().endswith(extension):
  24             file_id = codecs.open(os.path.join(attach_dir, file), 'rb', config.charset)
  25             parser = CSV_Parser(file_id.read(), request, line_anchors=False)
  26             parser.format(request.formatter)
  27             result = '. \n[[attachment:%s]]' % file
  28             result = wikiutil.escape(result)
  29             wiki_Parser(result, request).format(request.formatter)
  30     return ""

Dieses Programm stellt nun bei der Verwendung als <<ShowCSV>> auf einer Wiki-Seite alle attachments, die auf dieser Seite gespeichert sind und auf .csv enden als Tabellen dar. Unterhalb der Tabelle befindet sich ein Link zu der Datei.

ShowCSV.png

Ausgabe des Makros ShowCSV.py

4.1.3. Logeintragungen auf einer Seite verwenden

Makros sind auch hervorragend geeignet, um z.B. Information aus der event-log Datei auf einer Seite auszugeben. Das nachfolgende Beispiel zeigt die Zugriffe auf der Seite an. Sobald es als <<Hits>> auf die Seite geschrieben wird. Wird die filter Variable verwendet, können Sie u'VIEWPAGE' bzw. u'SAVEPAGE' verwenden (siehe Kapitel eventlog. Mit der Variablen all können Sie festlegen, ob die Ausgabe der Analyse nur für die aktuelle Seite oder alle Seiten des Wikis erfolgen soll. Sie können dort nur boolsche Variablen eingeben, z.B.: <<HIT(all=True)>>, <<HITS(filter='SAVEPAGE')>>.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - Hits Macro shows hits of a page
   4     @copyright: 2004-2007 MoinMoin:ReimarBauer,
   5                 2005 BenjaminVrolijk
   6     @license: GNU GPL, see COPYING for details.
   7 """
   8 Dependencies = ['time'] # do not cache
   9 from MoinMoin import wikiutil
  10 from MoinMoin.logfile import eventlog
  11 
  12 def macro_Hits(macro, all=False, event_type=(u'VIEWPAGE', u'SAVEPAGE')):
  13     pagename = macro.formatter.page.page_name
  14     event_log = eventlog.EventLog(macro.request)
  15     count = 0
  16     if not all:
  17         test = filter(lambda line: line[1] in event_type and line[2]['pagename'] == pagename, event_log)
  18     else:
  19         test = filter(lambda line: line[1] in event_type, event_log)
  20 
  21     return u'%d' % len(test)

4.2. action

Eine Action wird über das "Weitere Aktionen"-Menü angeboten, sofern Sie den Dateinamen mit einem Grossbuchstaben beginnen. Eine Aktion ist durch die Anordnung im Theme nicht an eine Wiki-Seite gebunden im Unterschied zu einem Macro. Das nachfolgende Beispiel stellt die Save-Action dar. Wählt man diese Aktion für eine Seite aus, wird der Quelltext dieser Seite gespeichert.

z.B.: http://localhost:8080/FrontPage?action=Save.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - Action for saving a page
   4     @copyright: 2007 MoinMoin:ReimarBauer
   5     @license: GNU GPL, see COPYING for details.
   6 """
   7 
   8 from MoinMoin.Page import Page
   9 
  10 def execute(pagename, request):
  11     if not request.user.may.read(pagename):
  12         Page(request, pagename).send_page()
  13     else:
  14         rev = request.rev or 0
  15         Page(request, pagename,rev=rev).send_raw(content_disposition='attachment')

In diesem Beispiel sehen Sie auch die Überprüfung des Lese-Zugriffsrechts, das durch ACLs eingeschränkt sein kann: if not request.user.may.read(pagename):

Diese Abfrage hat den Zweck, dass Sie einen Hinweis mit einer Fehlermeldung bekommen, wenn Sie versuchen, eine Seite abzuspeichern, die Sie nicht lesen dürfen. Eine Aktion muss mit einer Message abgeschlossen werden. Wenn Sie einen eigenen Wert zurückgeben wollen, verwenden Sie den msg Parameter, z.B.:

   1 msg = _("Done")
   2 request.theme.add_msg(msg, "info")
   3 Page(request, pagename).send_page()

Neben dem read-Recht können Sie auch die anderen ACL-Rechte, wie: write, delete, revert und admin abfragen (siehe Kapitel request.user.may).

4.2.1. Mit einer Action POST oder GET Parameter verarbeiten

Eine Action kann Variablen verarbeiten, die über die Formularmethode GET oder POST verschickt werden. Das nachfolgende Beispiel zeigt, wie Sie eine Action benutzen können, um eine Seite mit Informationen zu ergänzen. Z.B. kann das Verwendung finden, wenn Sie ein Programm in einer beliebigen Programmiersprache haben, das Status-Informationen in ein Wiki schreiben soll.

Damit nicht jeder diese Action ausführen kann, muss noch eine geheime Information mitübertragen werden. Dies geschieht in diesem Beispiel durch eine Variable, deren Wert in dem Programm vorgegeben ist. Es ist auch abwandelbar, so dass diese Variable z.B. auf einer geschützten Wiki-Seite steht und ausgelesen wird (siehe Kapitel wikidicts).

Die gesendeten Daten können von einem anderen Rechner stammen. Die Action kann sowohl POST- als auch GET-Parameter verarbeiten. Die URL mit GET-Parametern für die Benutzung lautet: http://localhost:8080?action=log&date=200701129&comment=System on&ticket=Geheim! Sie sollten nur POST verwenden, wenn die Daten über das internet geschickt werden, je nachdem wie schützenswert Ihnen die Daten sind sollten Sie diese dann auch verschlüsseln.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     log - add log entry
   4     @license: GNU GPL, see COPYING for details.
   5     @copyright:  2007-2008 MoinMoin:ReimarBauer
   6 """
   7 from MoinMoin import wikiutil
   8 from MoinMoin.PageEditor import PageEditor
   9 from MoinMoin.Page import Page
  10 
  11 def execute(pagename, request):
  12     _ = request.getText
  13     ticket = 'Geheim!' # you better keep this password in wikiconfig
  14     date = wikiutil.escape(request.form.get('date', ['None'])[0])
  15     comment = wikiutil.escape(request.form.get('comment', ['None'])[0])
  16     secret = wikiutil.escape(request.form.get('ticket', ['wrong'])[0])
  17     if (ticket != secret or  date == 'None' or comment == 'None'):
  18         msg = _("A severe error occured:")
  19         request.theme.add_msg(msg, "error")
  20         Page(request, pagename).send_page()
  21         return
  22 
  23     pagename = u'LogPage' # the name of the output page
  24     page = Page(request, pagename)
  25     raw = u''
  26 
  27     if page.exists():
  28         raw = page.get_raw_body()
  29     result = "|| %s || %s ||" % (date, comment)
  30     newtext = "%s%s\n" % (raw, result)
  31     try:
  32         PageEditor(request, pagename).saveText(newtext, 0)
  33         info = (_(u"OK!"), "info")
  34     except:
  35         info = (_(u"A severe error occured:"), "error")
  36     request.theme.add_msg(info[0], info[1])
  37     Page(request, pagename).send_page()

Das nachfolgende Beispiel zeigt Ihnen, wie Sie Daten mit dieser Action aus einem anderen Python-Programm übertragen können. Auf result bekommen Sie die HTML-Ausgabe der Seite zurück.

   1 import urllib
   2 params = urllib.urlencode({'date':'20070928', 'comment': 'System on', 'ticket': 'Geheim!'})
   3 f = urllib.urlopen('http://localhost:8080/StartSeite?action=log&%s' % params)
   4 result = f.read()

4.2.2. Ausschließlich Formulare, die vom eigenem Server stammen, verarbeiten

Sollen Daten ausschließlich von dem Wiki-Server verarbeitet werden, sollten Sie wikiutil.createTicket() und wikiutil.checkTicket() (siehe Kapitel wikiutil.createTicket) verwenden. Dadurch können Sie auf dem Server eine geheime Information ablegen, die das Programm für die Eingabe der Daten angeben muss.

Sinnvollerweise verwendet man diese Daten dann nur mit der Methode POST. Wenn Sie ein Formular verwenden, dann können Sie in einem versteckten Textfeld das Ticket mit übertragen. Sobald das Formular ausgefüllt ist und die Daten an den Server geschickt werden, überprüft ihr Programm dann mit wikiutil.checkTicket(), ob das richtige Ticket enthalten ist. Ist dies der Fall, wird die Action ausgeführt. Auch bei diesem Beispiel ist es sinnvoll zu überprüfen, ob der Benutzer berechtigt ist, die Action auf der Seite auszuführen. Das nachfolgende Beispiel erstellt ein Listenfeld, in dem die einzelnen Revisionen der Seite gelistet werden. Mit Auswahl einer Revision wird die zugehörige Seite dargestellt.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     ShowRev - action to select a page revision
   4     @license: GNU GPL, see COPYING for details.
   5     @copyright:  2007-2008 MoinMoin:ReimarBauer
   6 """
   7 from MoinMoin import wikiutil
   8 from MoinMoin.Page import Page
   9 
  10 def form_html(ticket, revlist):
  11     html = []
  12     for rev in revlist:
  13         html.append("<OPTION>%d</OPTION>" % rev)
  14     return '''
  15 <form method="post" >
  16 <p>select a revision</P>
  17 <select name="revision" size="%(len)s">
  18 %(option)s
  19 </select>
  20 <input type="hidden" name="action" value="ShowRev">
  21 <input type="submit" name="button" value="Select">
  22 <input type="hidden" name="ticket" value="%(ticket)s">
  23 </form>''' % {
  24     'ticket' : ticket,
  25     'option': ''.join(html),
  26     'len': min(len(revlist), 5)}
  27 
  28 def execute(pagename, request):
  29     _ = request.getText
  30     page = Page(request, pagename)
  31     if not request.user.may.read(pagename):
  32         msg= _('''You are not allowed to read this page.''')
  33         request.theme.add_msg(msg, "error")
  34         page.send_page()
  35         return
  36 
  37     if (request.form.has_key('button')
  38         and request.form.has_key('ticket')):
  39         if not wikiutil.checkTicket(request, request.form['ticket'][0]):
  40             msg = _('''Please use the interactive user interface!''')
  41             request.theme.add_msg(msg, "error")
  42             page.send_page()
  43             return
  44         rev =  long((request.form.get('revision', ['-1'])[0]))
  45         return Page(request, pagename, rev=rev).send_page()
  46     ticket = wikiutil.createTicket(request)
  47     revlist = page.getRevList()
  48     msg = form_html(ticket, revlist)
  49     request.theme.add_msg(msg)
  50     page.send_page()

4.3. parser

Ein Parser analysiert die Eingabedaten und gibt sie in einer gewünschten Form aus. Zum einen werden Parser verwendet, um Quellcode farbig darzustellen und zum anderen, um z.B. Inhalte zu analysieren, damit sie dann strukturiert ausgegeben werden können. Werden die Parser-Programm-Dateien in Kleinbuchstaben geschrieben, können Sie für den kompletten Inhalt einer Seite angewendet werden, z.B. #format plain am Seitenanfang. Ansonsten muss der Text, der geparsed werden soll, mit drei offenen und drei geschlossenen geschweiften Klammern umschlossen werden. Der Name des Parsers muss dann mit dem Hash-Bang #! kenntlich gemacht werden, z.B.:

{{{#!plain
Das ist ein Beispiel
    Zeile 2
}}}

Das nachfolgende Beispiel zeigt die Anwendung des #format Befehls.

#format plain

Alles
was
jetzt
auf
dieser
Seite
geschrieben
wird,
wird
genauso
ausgegeben.

Der Parser, der die plain Text Ausgabe bewerkstelligt, ist wie folgt aufgebaut.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - Plain Text Parser, fallback for text/*
   4     @copyright: 2000-2002 Juergen Hermann <jh@web.de>
   5     @license: GNU GPL, see COPYING for details.
   6 """
   7 
   8 Dependencies = []
   9 
  10 class Parser:
  11     """
  12         Send plain text in a HTML <pre> element.
  13     """
  14     extensions = '*'
  15     Dependencies = []
  16 
  17     def __init__(self, raw, request, **kw):
  18         self.raw = raw
  19         self.request = request
  20         self.form = request.form
  21         self._ = request.getText
  22 
  23     def format(self, formatter):
  24         """ Send the text. """
  25         self.request.write(formatter.preformatted(1))
  26         self.request.write(formatter.text(self.raw.expandtabs()))
  27         self.request.write(formatter.preformatted(0))

Eigene Parser sollten mit dem Namen text_x_ beginnen, sonst werden sie nicht erkannt. Die Namensgebung ist angelehnt an den MIME-Type der Daten, die geparsed werden sollen.

4.4. formatter

Ein Formatter hat den Zweck, den Inhalt einer Seite in einer anderen Form auszugeben. Der Wiki-Text kann z.B. anstatt im HTML- auch in DocBook-Format ausgegeben werden, oder jedem anderem Format, das Sie anwenden möchten (und für das ein Formatter existiert).

Der Aufruf erfolgt über die url http://localhost:8080/FrontPage?mimetype=text/plain

Dazu müssen Sie die Ausgaben für die jeweiligen Methoden definieren.

In der folgenden Auflistung sehen Sie einen Auszug einiger möglicher Definitionen. Sehen Sie bitte in die Datei MoinMoin/formatter/__init__.py für eine komplette Liste.

def startDocument(self, pagename):
def startContent(self, content_id="content", **kw):
def endContent(self):
def text(self, text, **kw):
def table(self, on, attrs={}, **kw):
def table_row(self, on, attrs={}, **kw):
def table_cell(self, on, attrs={}, **kw):

Das nachfolgende Beispiel zeigt einige der Methoden und die Ableitung der FormatterBase von dem text_plain formatter. Es zeigt die Abwandlung der Ausgabe von Tabellen als Komma separierte Liste. Der Dateiname Ihres Formatters sollte demnach text_csv.py lauten und dann im fromatter Verzeichnis gespeichert werden.

   1 from MoinMoin.formatter.text_plain import Formatter as FBase
   2 
   3 class Formatter(FBase):
   4     def __init__(self, request, **kw):
   5         FBase.__init__(self, request, **kw)
   6         self._text = []
   7         self._table_rows = []
   8 
   9     def text(self, text, **kw):
  10         if text:
  11             self._text.append(text)
  12         return ""
  13 
  14     def table_row(self, on, attrs={}, **kw):
  15         if len(self._text) > 0:
  16             self._table_rows.append(u','.join(self._text))
  17             self._text = []
  18         return ""
  19 
  20     def table(self, on, attrs={}, **kw):
  21         if len(self._table_rows) > 0:
  22             result = u'\n'.join(self._table_rows)
  23             return "%s\n\n" % result
  24         return ""

Evtl. möchten Sie noch weitere Methoden anpassen. Die Ausgabe einer Wiki-Seite wird mit http://localhost:8080/FrontPage?mimetype=text/csv aufgerufen. Falls Sie eine Applikation in Ihrem Browser für diesen MIME-Type definiert haben, wird die Ausgabe dahin umgeleitet.

4.5. filter

Filter werden zur Indizierung von Datei-Anhängen benutzt, d.h. sie extrahieren die Text-Informationen aus Datei-Anhängen.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - PDF filter
   4     Depends on: pdftotext command from
   5                 xpdf-utils package
   6 
   7     @copyright: 2006 MoinMoin:ThomasWaldmann
   8     @license: GNU GPL, see COPYING for details.
   9 """
  10 
  11 from MoinMoin.filter import execfilter
  12 
  13 def execute(indexobj, filename):
  14      return execfilter("pdftotext -enc UTF-8 '%s' -", filename)

4.6. xmlrpc

Für den Fall, dass Sie weitere xmlrpc-Methoden benötigen, können Sie auch diese erweitern. Das nachfolgende Beispiel gibt aus, wer die Abfrage ausgeführt hat.

   1 """
   2     MoinMoin - Tells who you are
   3           and whether the wiki trusts you.
   4 
   5     @copyright: 2005 MoinMoin:ThomasWaldmann
   6     @license: GNU GPL, see COPYING for details.
   7 """
   8 
   9 def execute(xmlrpcobj, *args):
  10     request = xmlrpcobj.request
  11     username = request.user.name
  12     if not username:
  13         username = "<unknown user>"
  14     valid = request.user.valid
  15     result = "You are %s. valid=%d." % (username.encode("utf-8"), valid)
  16     return xmlrpcobj._outstr(result)

4.7. theme

Mit einem Theme bestimmen Sie, wie die Benutzeroberfläche von MoinMoin dargestellt wird. Derzeit werden mit MoinMoin standardmäßig drei Themes (modern, classic und rightsidebar) zur Verfügung gestellt. Eine ganze Reihe von Benutzern entwickelte Themes finden Sie unter http://moinmo.in/ThemeMarket.

Ein Theme besteht aus einem Python Programm, CSS und Bildern. Der prinzipielle Aufbau des Themes modern.py wird durch nachfolgendes Beispiel wiedergegeben. Bitte schauen Sie in die eigentliche Programmdatei für eine vollständige Listung des Aufbaus. Mit Hilfe der Variablen name wird bekannt gegeben, wo die zu dem Theme zugehörigen CSS Dateien und Bilder liegen.

Für das modern-Theme liegen diese daher unter moin-1.9/wiki/htdocs/modern in den Verzeichnissen css und img.

Im css Verzeichnis gibt es die Dateien

common.css

legt die Default styles fest

msie.css

MoinMoin MS Internet explorer bug workarounds

print.css

legt die Styles für eine Druckausgabe fest

projection.css

legt die Styles für eine Slide-Show fest

screen.css

legt die Styles für die Darsellung im Browser fest

Im img Verzeichnis liegen alle Bilder die im modern Theme Verwendung finden.

Das Theme modern ist von der ThemeBase abgeleitet. Es verändert header, editorheader und footer durch die Anordnung einzelner Elemente.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - modern theme
   4     @copyright: 2003-2005 Nir Soffer, Thomas Waldmann
   5     @license: GNU GPL, see COPYING for details.
   6 """
   7 from MoinMoin.theme import ThemeBase
   8 
   9 class Theme(ThemeBase):
  10     name = "modern"
  11 
  12     def header(self, d, **kw):
  13         html = [
  14             self.emit_custom_html(self.cfg.page_header1),]
  15 
  16 
  17     def editorheader(self, d, **kw):
  18         html = [
  19             self.emit_custom_html(self.cfg.page_header1),]
  20 
  21 
  22     def footer(self, d, **keywords):
  23         page = d['page']
  24         html = [
  25             self.pageinfo(page),]
  26 
  27 
  28 def execute(request):
  29     """ Generate and return a theme object """
  30     return Theme(request)

Das nachfolgende Beispiel ist von dem modern_cms-Theme abgeleitet, das von Nir Soffer entwickelt wurde.

   1 """
   2 @copyright (c) 2005 Nir Soffer <nirs@freeshell.org>
   3 @copyright (c) 2006 Thomas Waldmann
   4 @license: GNU GPL, see COPYING for details
   5 """
   6 from MoinMoin.theme import modern
   7 
   8 class Theme(modern.Theme):
   9 
  10     name = "modern" # uses "modern" CSS and images
  11 
  12     def shouldShowEditbar(self, page):
  13         """ Hide the edit bar if you can't edit """
  14         if self.request.user.may.write(page.page_name):
  15             return modern.Theme.shouldShowEditbar(
  16                    self, page)
  17         return False

Dieses Theme unterdrückt für nicht angemeldete Benutzer die Anzeige des editbars, wenn in wikiconfig.py folgendes eingetragen ist:

acl_rights_default = u"Known:read,write,delete,revert"
theme_default = 'modern_cms'
theme_force = True

Wenn Sie ein neues Theme erstellen wollen, gehen Sie am besten so vor, dass Sie eines der mitgelieferten Themes kopieren und ihren Wünschen nach abändern. Sie finden die vorhandenen Themes unter moin-1.9/MoinMoin/theme/.

Das nachfolgende Beispiel basiert auf einer Kopie des modern Themes. Es ändert das Aussehen des headers. Alle anderen Einstellungen von modern werden übernommen.

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     exampletheme
   4     @license: GNU GPL, see COPYING for details.
   5 """
   6 from MoinMoin.theme import modern
   7 
   8 class Theme(modern.Theme):
   9 
  10     name = "modern"
  11 
  12     def header(self, d, **kw):
  13         html = [
  14             u'<div id="header">',
  15             self.searchform(d),
  16             self.username(d),
  17             self.navibar(d),
  18             u'<div id="pageline">',
  19             u'<hr style="display:none;"></div>',
  20             u'</div>',
  21             self.startPage(),
  22         ]
  23 
  24 
  25 def execute(request):
  26     return Theme(request)

Diese Änderungen geben dem Wiki ein anderes Aussehen( zum Vergleich siehe Abbildung perswiki.

exampletheme.png

ein modifiziertes Theme auf Basis des modern Themes

Im nachfolgenden einige Beispiel Themes (SimpleMente 4 , classic_dark 5 , Explorer 6 , Green Mist 7 ), die Gestaltungsmöglichkeiten aufzeigen. Diese Themes finden Sie auf ThemeMarket.

simplemente-chico.png

SimpleMente

classic_dark.jpg

classic dark

explorer_theme.png

Explorer

greenmist.png

Green Mist

  1. FeatureRequest (1)

  2. mit freundlicher Genehmigung von Nir Soffer (2)

  3. EditingOnMoinMaster (3)

  4. Mit freundlicher Genehmigung von Oliver Siemoneit (4)

  5. Mit freundlicher Genehmigung von Heather Stern (5)

  6. Mit freundlicher Genehmigung von Wolfgang Fischer (6)

  7. Mit freundlicher Genehmigung von Radomir Dopieralski (7)

MoinMoin: MoinAPI/Beispiele (last edited 2013-06-04 16:48:33 by dslb-092-074-184-048)