6. Einführung in Plone-Templating und -Skripting für Fortgeschrittene
Artikelaktionen
Einführung in Plone-Templating und -Skripting für Fortgeschrittene
Das vorangegangene Kapitel behandelte die Funktionsweise des Zope Page Templates-Systems. Um Ihnen Page Templates nahe zu bringen, habe ich in Kapitel 5 auch die Objekthierarchie, Akquisition und TALES (Template Attribute Language Expression Syntax) behandelt. Mit dem dortigen Code konnten Sie dynamische Webseiten erzeugen. In dem Kapitel haben Sie ein Beispiel-Page Template gesehen, das den Code zusammengesetzt hat. Außerdem wurden darin die Bestandteile des Templating-Systems in Plone behandelt, d.h., Sie haben die Schlüsselinformationen erhalten, die Sie brauchen, um Plone zu benutzen.
Nun ist es Zeit, zu einigen der weitergehenden Eigenschaften von Page Templates und allgemein zum Templating in Plone überzugehen. Zuerst werde ich dazu METAL (Macro Expansion Template Attribute Language) und Internationalisierungs-(I18N-)Namespaces einführen. Wie der TAL-Namespace bieten diese dem Site-Entwickler einiges an Funktionalität. Diejenigen, die ganz genau wissen möchten, wie eine Plone-Seite zusammengesetzt wird, finden im Abschnitt "Sich mit METAL in Plone einklinken" viele Antworten auf ihre Fragen.
Bisher habe ich gezeigt, wie Sie einfache Python-Ausdrücke in Page Templates verwenden können. Natürlich ist manchmal ein einzeiliger Python-Ausdruck nicht ausreichend. Im Abschnitt "Plone mit Python skripten" werde ich zeigen, dass Sie Python eine Ebene höher einsetzen können und Ihre Skripten damit wesentlich mächtiger machen können.
Abschließend werde ich ein häufig vorkommendes Beispiel behandeln, das zeigt, wie man ein Formular in Plone zusammenbaut. Dieses Beispiel demonstriert Konzepte, die in den vorherigen Kapiteln vermittelt wurden, und kombiniert alles miteinander, während Sie genau sehen können, wie Plone mit Formularen umgeht.
Hintergrund zu fortgeschrittenem Plone Templating
Eine der hübschen Eigenschaften von Page Templates ist die, dass verschiedene Funktionen klar in verschiedenen Namespaces voneinander getrennt sind. Im vorigen Kapitel haben Sie die TAL-Namespaces gesehen. Das ist nicht der einzige Namespace, den Page Templates zur Verfügung stellen. Zwei weitere Namespaces sind sehr wichtig für Plone.
Der erste ist METAL. Wie aus dem ziemlich langen Namen hervorgeht, ist er insofern ähnlich zu TAL, als er eine Attributsprache ist und sich selbst in Elementattribute einfügt. Sein Hauptzweck ist jedoch sicherzustellen, dass Sie Codeteile von anderen Page Templates wiederverwenden können, und zwar mit Hilfe von Slots und Makro-Funktionen.
Der zweite ist I18N, mit dem Sie den Inhalt von Page Templates übersetzen können. In Plone wird das dazu verwendet, die Schnittstelle von Plone in über 30 Sprachen zu lokalisieren, was für viele Benutzer eines der Schlüsselmerkmale von Plone ist. Wie Sie sehen werden, ist die Möglichkeit, Texte zu lokalisieren, für alle Benutzer von Interesse, auch für jene, die eine einsprachige Site bauen. Beginnen wir mit METAL.
Sich mit METAL in Plone einklinken
Bisher haben Sie gesehen, wie Sie Teile von Webseiten mit TAL dynamisch generieren können. Damit können Sie allerdings nicht wirklich komplexes Templating betreiben. Es gibt einfach keinen Mechanismus, um oben auf jede Seite einen Standardkopf zu setzen, außer eine TAL-Anweisung zu benutzen. METAL ist eine Methode zur Vorverarbeitung der Templates und bietet einige mächtigere Funktionen als TAL. Alle METAL-Funktionen beginnen mit dem Präfix metal:.
metal:define-macro
Mit dem Befehl metal:define-macro können Sie ein Element so definieren, dass es von einem anderen Template referenziert werden kann. Der Name des referenzierten Teils ist der Name des Makros. Es folgt ein Beispiel, das boxA als etwas definiert, das Sie anderswo benutzen möchten:
<div metal:define-macro="boxA">
...
</div>
Das div-Element ist nun ein Makro, das aus anderen Templates referenziert werden kann. Das Makro bezieht sich nur auf den Teil der Seite, der von dem Element referenziert wird, was in diesem Fall der div-Tag ist. Es ist üblich, auf einer Seite mehrfach macro:defines zu verwenden, auch damit die Seite eine gültige HTML-Seite ist, z.B. so:
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
i18n:domain="plone">
<body>
<div metal:define-macro="boxA">
...
</div>
<div metal:define-macro="boxB">
...
</div>
</body>
</html>
Übereinstimmend mit den früheren Zielen von Page Templates ist diese Seite eine gültige HTML-Seite, die von einem Designer bearbeitet werden kann. Wenn das Makro aufgerufen wird, wird das HTML außerhalb der div-Tags weggeworfen.
metal:use-macro
Der Befehl metal:use-macro verwendet ein Makro, das mit define-macro definiert wird. Wenn ein Template ein Makro mit dem Befehl define-macro definiert, können andere Templates mit dem macros-Property darauf zugreifen. Wenn Sie z.B. das Makro portlet aus dem Template portlet_login verwenden möchten, können Sie Folgendes machen:
<div metal:use-macro="context/portlet_login/macros/portlet">
Der Einloggen-Slot kommt hierhin
</div>
Damit wird das Makro geholt und sein Ergebnis an dessen Stelle eingefügt. Wie Sie sehen, erwartet der Befehl use-macro einen Pfadausdruck, der auf das Template und auf das spezielle Makro darin zeigt.
Beispiel: Die Makros use-macro und define-macro verwenden
Folgendes Template namens time_template ist ein Beispiel hierfür. Dieses Template zeigt Datum und Zeit auf dem aktuellen Plone-Server an. Das ist eine recht nützliche Funktion, d.h., Sie können sie in ein Makro zur weiteren Wiederverwendung verpacken. Das Folgende ist ein Beispiel-Page-Template mit der Anweisung define-macro:
<html>
<body>
<div metal:define-macro="time">
<div tal:content="context/ZopeTime">
Die Zeit
</div>
</div>
</body>
</html>
Falls Ihr Template time_template heißt, können Sie dieses Makro in einem anderen Template referenzieren. Sie können es auch aus mehreren Templates referenzieren. Hier ist ein solches Beispiel-Template:
<html>
<body>
<div metal:use-macro="context/time_template/macros/time">
Falls es eine Message gibt, zeigt das Makro sie hier an.
</div>
</body>
</html>
Wenn dieses Template dargestellt wird, sieht der von Plone generierte HTML-Code dafür wie folgt aus:
<html>
<body>
<div>
<div>2004/04/15 17:18:18.312 GMT-7</div>
</div>
</body>
</html>
metal:define-slot
Ein Slot ist ein Makro-Abschnitt, von dem der Autor des Templates erwartet, dass er von einem anderen Template überschrieben wird. Sie können sich einen Slot als Loch in Ihrem Page Template vorstellen, bei dem Sie davon ausgehen, dass es von einem anderen gefüllt wird. Alle define-slot-Befehle müssen in einem define-macro vorkommen. Beispiel:
<div metal:define-macro="master"> <div metal:define-slot="main"> ... </div> </div>
metal:fill-slot
Hiermit wird ein Slot gefüllt, der mit dem Befehl define-slot definiert wurde. Ein fill-slot muss mit dem Befehl use-macro definiert werden. Wenn der define-macro-Teil aufgerufen wird, versucht das Makro, alle definierten Slots mit dem entsprechenden fill-slots zu füllen. Hier sehen Sie ein Beispiel für einen fill-slot:
<div metal:use-macro="master">
<div metal:fill-slot="main">
Der Haupt-Slot kommt hierhin
</div>
</div>
Beispiel: Makros und Slots verwenden
Kommen wir zum vorigen Beispiel zurück, das Sie nun ein wenig verbessern werden. Wenn Sie eine spezielle Meldung vor die Zeitangabe setzen möchten, würden Sie einen Slot am Anfang von time_template innerhalb von define-macro hinzufügen. Der Slot heißt time und sieht wie folgt aus:
<html>
<body>
<div metal:define-macro="time">
<div metal:define-slot="msg">Time slot</div>
<div tal:content="context/ZopeTime">
Die Zeit
</div>
</div>
</body>
</html>
Im aufrufenden Page Template können Sie nun fill-slot aufrufen:
<html>
<body>
<div metal:use-macro="context/time_template/macros/time">
<div metal:fill-slot="msg">Die Zeit ist:</div>
Falls es eine Message gibt, zeigt das Makro sie hier an.
</div>
</body>
</html>
Als Endergebnis sehen Sie den wie folgt gefüllten time-slot:
<html>
<body>
<div>
<div>Die Zeit ist: </div>
<div>2004/04/15 17:18:18.312 GMT-7</div>
</div>
</body>
</html>
Wie Plone Makros und Slots benutzt
Makros und Slots sind sich insofern ähnlich, als beide Inhalte aus einem anderen Template extrahieren und Inhalte einfügen, aber sie tun das auf unterschiedliche Weise. Der Unterschied besteht darin, wie sie benutzt werden. Makros sind Elemente eines Templates, die explizit aufgerufen werden, während Slots wie Löcher in einem Template sind, die von anderen Templates für Sie gefüllt werden sollen. Bei Plone selbst sind Portlets wie Kalender, Navigation etc. Makros, die explizit aufgerufen werden.
Wenn Sie sich im ZMI (Zope Management Interface) eine Datei anschauen, indem Sie hintereinander auf portal_skins, plone_templates und main_template klicken, werden Sie sogar sehen, dass die gesamte Seite aus Makros und Slots besteht. In diesem Stadium ist sie vermutlich ein wenig verwirrend, aber wenn sie aufgerufen wird, läuft sie durch eine Reihe von Makros und trägt alles zusammen. Damit kann ein Benutzer ganz leicht alle Teile einer Plone-Site dadurch ändern, dass er das Makro überschreibt, was Sie im nächsten Kapitel sehen werden. Beispiel:
...
<div metal:use-macro="here/global_siteactions/macros/site_actions">
Site-wide actions (Contact, Sitemap, Help, Style Switcher etc)
</div>
<div metal:use-macro="here/global_searchbox/macros/quick_search">
The quicksearch box, normally placed at the top right
</div>
...
Beim weiteren Herunterscrollen in main_template werden Sie einige define-Slots sehen. Ich wiederhole kurz, wie eine Seite in Plone dargestellt wird. Wenn ein Objekt angezeigt wird, wird ein Template für die Ansicht dieses Inhalts angezeigt. Wenn Sie ein Bild anzeigen, wird das Template image_view angezeigt, und dieses Template bestimmt, wie das Bild angezeigt wird. Um diese Aufgabe zu erfüllen, füllt das Bild-Template den Slot main. Wenn Sie ins Template image_view schauen, sehen Sie darin den folgenden Code:
<div metal:fill-slot="main"> ... </div>
Wenn Sie zurück zum main_template springen, werden Sie sehen, dass es die Definition define-slot für den Slot main enthält:
<metal:bodytext metal:define-slot="main" tal:content="nothing">
Page body text
</metal:bodytext>
Zu jedem Inhaltstyp gibt es ein eigenes Template, und jedes Template definiert für sich, wie es den Slot main benutzt. Das heißt, jeder Inhaltstyp erhält sein eigenes Look-and-feel aus dem Template. Nur ein Teil fehlt noch in dieser Gleichung. Als Sie image_view aufgerufen haben, wusste das Template irgendwie, dass es main_template verwenden sollte. Im Template image_view benutzen Sie das Makro aus main_template. Dieses wird mit folgendem HTML-Code definiert:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"
lang="en-US"
metal:use-macro="here/main_template/macros/master"
i18n:domain="plone">
In diesem Fall wird in main_template der Slot main von dem als main definierten Slot in demjenigen Template gefüllt, das dargestellt wird. Folgendes ist die zeitliche Abfolge der Ereignisse bei der Darstellung eines Bildes:
- Der Benutzer verlangt das Template image_view eines Bildes.
- Das Makro main_template oben im Template image_view wird gefunden und aufgerufen.
- Das main_template wird verarbeitet.
- Das define-slot="main" wird gefunden, und das fill-slot wird in image_view gefunden.
- Das fill-slot wird aufgerufen, und die Ergebnisse werden in main_template eingefügt.
- Das main_template wird weiterverarbeitet.
- Das Ergebnis wird zum Browser zurückgeschickt.
Das ermöglicht Plone eine hohe Flexibilität bei der Definition einer Seite. So definiert z.B. main_template kaum mehr als diesen einen Slot. Es gibt auch einen Slot zum Einfügen von CSS-Code (Cascading Style Sheets):
<metal:cssslot fill-slot="css_slot">
<metal:cssslot define-slot="css_slot" />
</metal:cssslot>
Falls eine Ansicht einen speziellen CSS-Teil bräuchte, könnten Sie diesen Slot in der Ansicht definieren, und er würde bei der Darstellung gefüllt werden. Einige der Makros in main_template definieren darin auch Slots und füllen sie im main_template wieder. Das heißt, wenn Sie wirklich wollten, könnten Sie auch diese Slots füllen. Allerdings ist das eine Technik für Fortgeschrittene, d.h., Sie sollten erst die Grundlagen beherrschen, bevor Sie sich auf diesen Weg begeben.
Einführung in die Internationalisierung
Die Macher von Plone sind kontinuierlich bestrebt, eine große Zahl qualitativ hochwertiger Übersetzungen anzubieten. Die Tatsache, dass Plone eine zugängliche Benutzerschnittstelle in über 30 Sprachen bietet, ist ein großes Verkaufsargument für Plone. Das heißt auch, dass I18N ein Schlüsselmerkmal von Templates ist. Das wird dadurch unterstützt, dass der I18N-Namespace ein Extra-Namespace wie TAL oder METAL ist, der spezielle Anweisungen enthält.
Dieser Abschnitt enthält, was Benutzer in Bezug auf Templates wissen müssen. In einem Template können Sie einen i18n-Tag zu einem Element hinzufügen, der die Übersetzung eines Attributs oder seines Inhalts erlaubt. Es sind sechs Anweisungen vorhanden: attributes, data, domain, source, target und translate. Das Grundmuster besteht darin, den zu übersetzenden Text einzuwickeln und die passenden i18n-Attribute hinzuzufügen. Wenn Sie z.B. Folgendes übersetzen möchten:
<i>Irgendein Text</i>
würde daraus Folgendes:
<i i18n:translate="some_text_label">Irgendein Text</i>
Jede Lokalisierung enthält eine Übersetzung von Irgendein Text, und das Translation-Werkzeug sucht nach einer Übersetzung für den Benutzer. Bei der Übersetzung muss jeder zu übersetzende String eine eindeutige Message-ID haben, die das zu übersetzende Element identifiziert. Ein String wie Suchen kann z.B. die Message-ID search_widget_label haben. Mit dieser Message-ID kann der String eindeutig identifiziert werden, und die Übersetzung kann wiederholt werden.
i18n:translate
Folgendes übersetzt den Inhalt eines Elements, wobei eine optionale Message-ID als Anweisung übergeben werden kann. Zum Beispiel erzeugt dieser Code eine Message-ID namens title_string:
<h1 i18n:translate="title_string">Dies ist ein Titel</h1>
Dieses Beispiel steht für einen statischen Text, der sich nicht verändert. In manchen Situationen könnte dieser Textteil dynamisch aus einer Datenbank oder einem Objekt kommen. Dadurch, dass die Anweisung translate leer gelassen wird, setzt sich die Message-ID aus dem Wert im Feld zusammen. Wenn im folgenden Beispiel der Titel vom Pfadausdruck here/title z.B. Alice im Wunderland wäre, würde dieser Titel an das Translation-Werkzeug übergeben. Wenn keine Übersetzung vorhanden ist, wird der Originalwert eingesetzt:
<h1
tal:content="here/title"
i18n:translate="">
Dies ist ein Titel.
</h1>
Der translation-Befehl ist wahrscheinlich einer der häufigsten i18n-Tags, den Sie benutzen werden, und Sie werden ihn in allen Plone-Templates sehen. Damit können Sie nicht nur statische Teile Ihrer Site wie Feldnamen in Formularen, Hilfen und Beschreibungen übersetzen, sondern auch die dynamischen Teile Ihrer Site, die sich öfter verändern können, beispielsweise Titel.
i18n:domain
Hiermit wird die Domain für die Übersetzung gesetzt. Um Konflikte zu vermeiden, kann jede Site mehrere Domains oder Gruppen von Übersetzungen haben. Es kann z.B. eine Domain für Plone und eine für Ihre eigene Anwendung geben. Plone verwendet die Domain plone, was in Plone normalerweise die Standard-Domain ist:
<body i18n:domain="plone">
Diesen Tag sollten Sie nicht oft benutzen müssen. Wenn Sie jedoch eine eigene Anwendung schreiben, ist es für Sie vielleicht nützlich, eine Domain zu haben, die keine Konflikte mit anderen Domains hervorruft.
Ii8n:source
Hiermit wird die Ausgangssprache für den zu übersetzenden Text gesetzt. In Plone wird das nicht benutzt:
<p i18n:source="en" i18n:translate="">Irgendein Text</p>
i18n:name
Dies bietet eine Möglichkeit, Elemente in einem längeren Textblock beizubehalten, damit der Textblock umgeordnet werden kann. In vielen Sprachen sind nicht nur die Wörter andere, sondern auch ihre Reihenfolge variiert. Wenn Sie einen ganzen Absatz oder einen Satz übersetzen müssen, der kleinere Teile enthält, die nicht übersetzt werden sollen, können diese durchgereicht werden:
<p i18n:translate="book_message">
Das
<span
tal:omit-tag=""
tal:content="book/color"
i18n:name="color">blaue</span>
Buch
</p>
Das produziert den folgenden String:
Das {color} Buch
Wenn in der Zielsprache verlangt wird, dass diese Wörter in einer anderen Reihenfolge stehen, könnten sie verschoben werden, und der dynamische Inhalt würde weiterhin am richtigen Platz eingesetzt. Auf Französisch müsste das wie folgt übersetzt werden:
Le Livre {color}
i18n:target
Hiermit wird die Zielsprache für den zu übersetzenden Text gesetzt. In Plone wird das nicht benutzt.
i18n:attributes
Dadurch ist es möglich, Attribute in einem Element statt deren Inhalt zu übersetzen. Ein image-Tag hat z.B. das Attribut alt, das eine alternative Textrepräsentation des Bildes enthält:
<img
href="/einBild.jpg"
alt="Irgendein Text"
i18n:attributes="alt alternate_image_label" />
Mehrere Attribute sollten durch ein Semikolon getrennt werden, genau wie bei tal:attributes.
i18n:data
Damit können andere Dinge als Strings übersetzt werden. Ein Beispiel dafür ist das Objekt DateTime. Eine i18n:data-Anweisung benötigt eine passende i18n:translate-Anweisung, damit eine gültige Message-ID verfügbar ist. Beispiel:
<span i18n:data="here/currentTime"
i18n:translate="timefmt"
i18n:name="time">2:32 pm</span>... Piep!
Übersetzungsdienst
Nachdem ich die Tags nun behandelt habe, werde ich jetzt den Mechanismus für die Durchführung der Übersetzung beschreiben. Plone enthält standardmäßig einen I18N-Mechanismus. Mit diesem können Sie die Benutzerschnittstelle so lokalisieren, dass Sie Meldungen, Reiter und Formulare übersetzen können. Der von Benutzern hinzugefügte Inhalt wird im Moment davon nicht erfasst. Wenn Sie ein Dokument in englischer Sprache hinzufügen und die Seite anzeigen, die es auf Französisch abruft, erhalten Sie das englische Dokument mit französischem Text drumherum (siehe Abbildung 6.1).
Abildung 6.1. Plone.org auf Französisch
Plone liest die HTTP-Header, die ein Browser an den Client sendet, um eine Sprache abzufragen. Wenn in Ihrem Browser Englisch eingestellt ist, werden Sie nicht viel sehen.
Tun Sie Folgendes, um die Spracheinstellungen im Internet Explorer zu ändern:
- Gehen Sie zu Werkzeuge - Internet-Optionen.
- Klicken Sie auf den Sprache-Button unten im Dialogfeld.
- Klicken Sie auf Hinzufügen, um eine neue Sprache hinzuzufügen, und wählen Sie eine Sprache, für die Plone eine Übersetzung hat, z.B. Französisch (Frankreich) [fr], und klicken Sie auf Hinzufügen.
- Stellen Sie dann sicher, dass diese Sprache oben ist, indem Sie den Nach oben-Button verwenden.
- Klicken Sie auf OK und noch einmal auf OK.
Wenn Sie damit fertig sind, wählen Sie Ihre bevorzugte Plone-Site, und besuchen Sie diese mit Ihrem Browser.
Die Übersetzungen für Plone werden mit einem Werkzeug namens Placeless Translation Service (PTS) vorgenommen. Das PTS-Werkzeug finden Sie im Zope-Control Panel. Unten auf der Seite sehen Sie eine Option für Placeless Translation Service. Klicken Sie darauf, und es werden alle vorhandenen Übersetzungen geöffnet. Diese Übersetzungen werden aus dem Dateisystem gelesen. Klicken Sie auf eine Übersetzung, um Angaben über die Sprache wie den Übersetzer, die Codierung und den Pfad zur Datei zu sehen. All diese Dateien werden im i18n-Unterverzeichnis vom CMFPlone-Verzeichnis gespeichert.
Übersetzungen werden jeweils mit zwei Dateien mit den Endungen .po und .mo vorgenommen. So enthält z.B. plone-de.po die deutschen Übersetzungen (de ist der Code für Deutsch). Die .mo-Datei ist die "kompilierte" Version der .po-Datei und wird aus Performance-Gründen von Plone benutzt. In die .mo-Datei müssen Sie nie hineinschauen, und Sie können sie einfach ignorieren. Die .po-Datei ist jene, die Sie bearbeiten können, um eine Übersetzung zu ändern. Wenn Sie diese Datei in einem Texteditor öffnen, sehen Sie eine Reihe von Zeilen, die mit msgid oder msgstr anfangen. Über dem msgid befindet sich der Code, wo der i18n-Befehl vorkommt, d.h., Sie sehen, welchen Teil einer Seite Sie übersetzen. Beispiel:
#: from plone_forms/content_status_history.pt #. <input attributes="tabindex tabindex/next;" value="Apply" class="context" name="workflow_action_submit" type="submit" /> #. #: from plone_forms/personalize_form.pt #. <input attributes="tabindex tabindex/next;" tabindex="" value="Apply" class="context" type="submit" /> #. msgid "Apply" msgstr "Anwenden"
In den zwei Teilen des vorigen Page Templates wird das Wort Apply für deutsche Benutzer in das Wort Anwenden übersetzt. Was übersetzt wird, bestimmen die i18n-Tags, die in die Page Templates eingefügt wurden, wie Sie zuvor schon gesehen haben. Wenn Sie diese Übersetzung ändern möchten oder wenn Sie eine eigene Variante davon hinzufügen möchten, müssen Sie lediglich die .po-Datei ändern. Falls kein msgstr gefunden wird, wird die englische Standardübersetzung gefunden. Nach einer solchen Änderung müssen Sie Plone neu starten. Dabei wird Plone diese Datei in die Version .mo neu kompilieren und dann Ihre aktualisierte Übersetzung verwenden.
Was Plone angeht, so wird per Standardeinstellung immer die englische Übersetzungsdatei verwendet, falls keine Sprache angegeben oder keine Übersetzung verfügbar ist. Tatsächlich ist die Datei plone-en.po leer, d.h., es ist keine solche Übersetzung verfügbar. Deswegen greift Plone zum letzten Mittel, nimmt keine Übersetzung vor, und zeigt den Text im Page Template an. Der Text in allen Page Templates liegt in englischer Sprache vor, da die meisten Entwickler englisch sprechen. Zusammengefasst heißt das, dass es keine englische Übersetzung gibt.
Deswegen können Sie eine neue Übersetzung dadurch machen, dass Sie die Datei plone.pot in eine neue Datei namens plone-xx.po kopieren. Dabei sollte der Wert von xx dem Landescode Ihrer Übersetzung entsprechen. Eine Liste von Sprachcodes finden Sie unter http://www.unicode.org/onlinedat/languages.html. Sobald Sie mit der Übersetzung angefangen haben, setzen Sie die obigen Werte, inklusive Sprachcode, und übersetzen drauflos. Wenn Sie eine neue Sprachdatei vollendet haben, wird das Plone-I18N-Team diese glücklich entgegennehmen und Ihnen bei der Fertigstellung helfen. Die Mailing-Liste des Plone-Teams finden Sie unter http://sourceforge.net/mailarchive/forum.php?forum_id=11647.
Den von Benutzern hinzugefügten Inhalt zu übersetzen, ist eine wesentlich komplexere Angelegenheit, bei der die Tücke im Detail liegt. Bislang gab es hier den Ansatz mit I18NLayer, der aber aufgrund von Seiteneffekten nicht einfach anzuwenden war. Inzwischen wurde von PloneSolutions das Nachfolgeprodukt LinguaPlone entwickelt, das wesentlich weniger Seiteneffekte hat und dem Ersteller von Inhalten wesentlich weniger Kenntnis der Materie abverlangt. Als dieser Abschnitt geschrieben wurde, war LinguaPlone in der Version 0.7.2 auf der Webseite von PloneSoutions (http://www.PloneSolution.com) verfügbar. Beim Einrichten von LinguaPlone muss man allerdings noch einige manuelle Schritte vornehmen und sicherstellen, dass Archetypes 1.3 und ATContentTypes installiert sind. Erst bei der Plone-Version 2.1 wird LinguaPlone standardmäßig mit ausgeliefert und installiert werden.
Beispiel: Mehr Benutzerinformationen anzeigen
In Kapitel 5 haben Sie einfache TAL-Befehle benutzt, um detailliertere Angaben zu einem Benutzer anzuzeigen. Das dortige Template hat einige Nachteile. Einer davon ist, dass es nur jeweils einen Benutzer anzeigt. Wie Sie gesehen haben, können Sie mit einem einfachen tal:repeat Inhalt wiederholen, aber jetzt werden Sie ein Makro verwenden, um diese Seite modularer zu machen.
Sie werden das Page Template user_info so ändern, dass es alle Site-Mitglieder auflistet. Anstatt nach einem in der Anfrage übergebenen Benutzernamen zu suchen, werden Sie dabei die Funktion listMembers benutzen, die eine Liste aller Mitglieder der Site zurückgibt:
<div metal:fill-slot="main">
<tal:block
tal:define="
getPortrait nocall: here/portal_membership/getPersonalPortrait;
getFolder nocall: here/portal_membership/getHomeFolder
">
<table>
<tr tal:repeat="userObj here/portal_membership/listMembers">
<metal:block
metal:use-macro="here/user_section/macros/userSection" />
</tr>
</table>
</tal:block>
</div>
Sie werden bemerken, dass nun der Code für user_info wesentlich kürzer ist. Das von listMembers zurückgegebene Mitglied wird an tal:repeat weitergegeben. Für jedes Mitglied wird es eine Tabellenzeile geben und dann ein Makro, um dem Benutzer Informationen anzuzeigen. In dieser Tabellenzeile enthält die lokal definierte Variable userObj nun die Benutzerangaben. Natürlich müssen Sie nun ein Makro namens userSection in einem Page Template erstellen, d.h., Sie erstellen ein Page Template namens user_section, wie es vom Makro referenziert wird. Dieses Template enthält den gesamten Code zwischen den row-Tags der Tabelle. Auch hierfür finden Sie das vollständige Listing für dieses Page Template in Anhang B:
<div metal:define-macro="userSection"
tal:define="userName userObj/getUserName">
...
Die einzige wirkliche Änderung besteht darin, dass use-macro im Haupt-Template entfernt werden und ein neues Makro definiert werden muss, damit dieses Makro definiert werden kann. Weil der Benutzername nicht mehr explizit übergeben wird, müssen Sie ihn mit der Methode getUserName aus dem Benutzerobjekt holen. Um die Ergebnisseite zu testen, gehen Sie zu http://ihresite/user_info, wo Sie eine Liste der Benutzer sehen sollten.
Nun ist die Seite benutzerfreundlich, da sie mehrere Benutzer auf einmal anzeigt. Der Code ist modularer, da er die Benutzerangaben in einem separaten Makro darstellt, das unabhängig vom Rest verändert werden kann. Die Seite ist noch nicht perfekt, wird aber in späteren Kapiteln noch verbessert.
Beispiel: Ein neues Portlet mit Google Ads erstellen
In Kapitel 4 haben Sie gesehen, wie Sie die Portlets in einer Plone-Site einfach bearbeiten können. Ihr eigenes Portlet zu erstellen ist nicht viel schwerer. Um Ihren eignen Slot zu schreiben, müssen Sie ein neues Page Template mit einem Makro darin erstellen. Dann wird ein TALES-Ausdruck, der auf das Makro zeigt, zur Liste der Portlets hinzugefügt, wodurch das Portlet auf der Seite angezeigt wird.
Das Grund-Template für ein Portlet sieht wie folgt aus:
<div metal:define-macro="portlet">
<div class="portlet">
<!-- Geben Sie hier Code ein -->
</div>
</div>
Alles, was Sie tun müssen, ist, den passenden Code ins Portlet einzufügen. Google hat 2003 ein textbasiertes Anzeigensystem erstellt, das Text auf Ihrer Site unterbringt. Die Anzeigen basieren auf Googles Annahmen darüber, worum es bei Ihrer Site geht, und diese Annahmen basieren auf den Suchergebnissen zu Ihrer Site. Das Google-System ist unter http://www.google.com/adsense verfügbar. Um Anzeigen anzuzeigen (und Geld dafür zu bekommen), müssen Sie sich bei Google registrieren. Auf der Google-Website werden Sie gebeten, einige Farben und Stile auszuwählen. Da Sie das in einen Slot einsetzen werden, empfehle ich die Größe "skyscraper", d.h. hoch und dünn. Machen Sie eine Kopie von dem JavaScript, das die Site produziert.
Als Nächstes müssen Sie ein Portlet erstellen:
- Erstellen Sie im Ordner portal_skins/custom ein Page Template namens googleAds.
- Nehmen Sie den vorigen Template-Grundcode, und ändern Sie den Portlet-Namen auf googleBox.
- Setzen Sie den Code von Google ein, wobei Sie den Abschnitt <!-- Geben Sie hier Code ein --> ersetzen.
Das Endergebnis sollte Listing 6.1 ähneln. Ihre Version wird allerdings einen gültigen Wert für google_ad_client enthalten statt yourUniqueValue. Dieser Wert sagt Google, welche Site diese Anzeige angefordert hat und wer dafür bezahlt werden soll. Merkwürdigerweise wird Google, wenn Sie keinen gültigen Wert dafür haben, die Anzeigen trotzdem anzeigen, ohne Sie dafür zu bezahlen!
Listing 6.1. Anzeigen von Google anzeigen
<div metal:define-macro="portlet">
<div class="portlet">
<script type="text/javascript"><!--
google_ad_client = "yourUniqueValue";
google_ad_width = 120;
google_ad_height = 600;
google_ad_format = "120x600_as";
//--></script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div>
Um das auf Ihrer Site einzufügen, fügen Sie folgendes Portlet zu Ihrer Portlet-Liste hinzu, wie es in Kapitel 4 beschrieben wurde:
here/googleAds/macros/portlet
Plone mit Python skripten
In Plone existieren mindestens vier verschiedene Ebenen, auf denen eine Programmlogik erstellt werden kann. Die einfachste Ebene, auf der Python in Plone verwendet werden kann, ist der Python-TALES-Ausdruck, den ich im vorigen Kapitel besprochen habe. Ein Python-Ausdruck darf allerdings nur eine Zeile Code enthalten, und oftmals werden Sie etwas Komplexeres machen wollen.
Noch häufiger kommt es vor, dass Sie nur ungern die ganze Logik in das Template hineinzwängen wollen. Ganz allgemein ist es keine gute Idee, Anwendungslogik in Ihr Template zu setzen. Immer, wenn Sie etwas, das nicht explizit Präsentationslogik ist, aus Ihrem Template hinaus verlegen können, haben Sie eine Menge Kopfschmerzen gespart. Die Trennung von Anwendungslogik und Präsentation ermöglicht es, dass verschiedene Leute an unterschiedlichen Projektteilen arbeiten, und sie verbessert die Wiederverwendung von Code. Die anderen Ebenen, auf denen Skripten zu Plone hinzugefügt werden können, kommen ungefähr in folgender Reihenfolge vor:
- Template-Attributausdrücke: Diese bieten Ausdrücke und eine Möglichkeit, kleine Schnipsel mit Logik oder einfachen Pfaden an vielen Orten einzufügen.
- Script (Python)-Objekte: Dies sind einfache Skripten, die in einer eingeschränkten Umgebung in Plone ausgeführt werden.
- Externe Methodenobjekte: Dies sind kompliziertere Module, die nicht in eingeschränkten Umgebungen ausgeführt werden.
- Python-Produkte: Daraus bestehen die wichtigsten Quellen, in denen das CMF und Plone geschrieben sind. Damit hat man Zugriff auf alles in Plone. Python-Produkte sind ein Thema für Fortgeschrittene und werden in Kapitel 14 behandelt.
Nach einem Ausdruck ist ein Script (Python)-Objekt die nächstkompliziertere Stufe. Dieses Objekt ermöglicht mehrere Zeilen Python-Code, und Sie können es aus einem Ausdruck aufrufen. Wenn Sie ein Script (Python)-Objekt aufrufen, fällt ein kleiner zusätzlicher Aufwand an, weil Plone zu diesem Objekt umschaltet. Der Aufwand ist jedoch minimal, da es einen Kompromiss zwischen Klarheit, Trennung und Performance gibt. Mein Rat ist der, so viel Logik wie möglich nach Python zu verlagern und Page Templates so einfach und sauber wie möglich zu halten. Wenn es ein Performance-Problem gibt, kann man sie später leicht wieder zurückverlegen, aber zumindest werden Sie später verstehen, was passiert.
Script (Python)-Objekte verwenden
Ein Script (Python)-Objekt ist das, woran Sie in Plone normalerweise bei einem Skript denken. Es ist ein Python-Schnipsel, den Sie schreiben und dann aus anderen Templates oder direkt über das Web aufrufen können. Tatsächlich verfügt Plone über eine große Anzahl dieser Skripten, um verschiedene wichtige Funktionen auszuführen. Was seine Mächtigkeit angeht, liegt ein Python-Skript etwa auf halbem Weg zwischen einem Ausdruck und einer externen Methode.
Um ein Script (Python)-Objekt hinzuzufügen, gehen Sie ins ZMI, wählen Script (Python) im Dropdown-Menü und klicken auf Add, wie in Abbildung 6.2 zu sehen ist.
Abbildung 6.2. Ein Script (Python)-Objekt hinzufügen
Geben Sie dem Skript z.B. die ID test_py, und klicken Sie dann auf Add and Edit. Danach wird die Bearbeiten-Seite für das Script (Python)-Objekt geöffnet, die aussieht wie in Abbildung 6.3.
Abbildung 6.3. Ein Script (Python)-Objekt bearbeiten
Sie können das Skript direkt über das Web bearbeiten. Falls Sie einen Syntaxfehler machen, erfahren Sie das, gleich nachdem Sie auf Save Changes geklickt haben (siehe Abbildung 6.4).
Abbildung 6.4. Ein absichtlicher Einrückungsfehler im Script (Python)-Objekt
Falls es keinen Fehler in Ihrem Python-Skript gibt, können Sie den Test-Reiter anklicken, um zu sehen, wie die Ausgabe aussieht. In diesem Fall ist das Beispiel recht langweilig und gibt folgenden Text aus:
This is the Script (Python) "test_py" in http://gloin:8080/Plone/portal_skins/custom
Ein Skript verfügt auch über die folgenden Optionen:
- Title: Das Bearbeiten-Formular hat eine Title-Option, mit der Sie dem Skript einen Titel geben können. Dieser wird im ZMI angezeigt, d.h., man erinnert sich dann leichter daran, was es macht.
- Parameter List: Dies ist eine Liste von Parametern, die das Skript erwartet, z.B. variableA oder variableB=None. Dies ist sogar die Standardparameterliste, die Sie in einer normalen Python-Funktion erwarten würden. Manche Parameter sind in diesem Objekt allerdings schon für Sie definiert. Sie können Sie sehen, wenn Sie auf den Reiter Bindings klicken. Dort sehen Sie eine Liste der Variablen, die bereits an das Objeckt gebunden sind. Mit ihren Namen sollten Sie schon vertraut sein.
Es gibt folgende an das Skript gebundene Variablen, auf die von einem Script (Python)-Objekt zugegriffen werden kann:
- context: Dies ist das Objekt, auf dem das Skript aufgerufen wird.
- container: Dies ist das Objekt, das dieses Skript enthält.
- script: Dies ist das Script (Python)-Objekt selbst. Das Äquivalent in Zope Page Templates ist template.
- namespace: Dies ist für den Fall da, dass das Skript aus DTML (Document Template Markup Language) heraus aufgerufen wird - etwas, was in Plone nicht passiert.
- traverse_subpath: Dies ist der URL-Pfad nach dem Skriptnamen, was eine Eigenschaft für Fortgeschrittene ist.
Ich werde nun ein einfaches Beispiel zeigen, das diese Themen mit dem Zope Page Templates-System verbindet, wobei der Python-Ausdruck zur Addition zweier Zahlen verwendet wird, den ich im vorigen Kapitel angegeben habe. Wie Sie gesehen haben, konnten Sie ein Page Template dafür anlegen, das wie folgt aussieht:
<p>1 + 2 = <em tal:content="python: 1 + 2" /></p>
Das Äquivalent mit einem Script (Python)-Objekt sieht wie folgt aus. Ändern Sie das Skript test_py auf die folgende Zeile:
return 1+2
Wie Sie am Anfang des vorigen Kapitels gesehen haben, rufen Sie ein Objekt auf, indem Sie seinen Pfad als Ausdruck angeben. In einem Page Template können Sie also Folgendes machen:
<p>1 + 2 = <em tal:content="here/test_py" /></p>
Durch den Pfadausdruck erhalten Sie das Objekt test_py und rufen es auf. Dann gibt es den Python-Code ans Template zurück und gibt es aus. Soeben haben Sie ein Skript aus einem Template aufgerufen! Dies ist ganz offensichtlich ein einfaches Beispiel, aber der Punkt ist der, dass Sie in einem Script (Python)-Objekt eine ganze Menge dessen tun können, was Sie in einem Page Template nicht können.
In einem Script (Python)-Objekt können Sie den Titel, Parameter und Bindungseinstellungen angeben, indem Sie die ##-Notation am Anfang eines Skripts verwenden. Wenn Sie ein Skript mit diesem Stück Text oben speichern, wird Plone diese Zeile entfernen und stattdessen den entsprechenden Wert im Objekt ändern. Diese Syntax wird sehr oft im Script (Python)-Objekt in diesem Buch verwendet, um sicherzugehen, dass Sie über den richtigen Titel und die richtigen Parameter verfügen. Das heißt, Sie könnten das vorige Skript wie folgt umschreiben:
##title=Ergibt 1+2 ##parameters= return 1+2
Plone skripten
Plone mit Skripten zu steuern ist ein recht kompliziertes Unterfangen, weil Sie, sobald Sie Plone skripten können, die API (Application Programming Interface) aller Objekte und Werkzeuge beachten müssen, die Sie verwenden möchten. Eine Erklärung der APIs würde den Rahmen des Buches sprengen, daher werde ich demonstrieren, wie man einige einfache Aufgaben mit Script (Python)-Objekten erledigt. Wenn Sie einmal etwas vertrauter damit sind, werde ich weitere API-spezifische Funktionen beschreiben.
Page Templates können recht schön über Python-Dictionaries und -Listen iterieren. Aber oftmals liegen Ihre Daten nicht in einem dieser bequemen Formate vor, d.h., Sie müssen zu einem Script (Python)-Objekt greifen, um die Daten hübsch zu formatieren und sie an das Page Template zurück zu übergeben.
Am bequemsten ist das Datenformat einer Liste von Dictionaries, bei der Sie die Mächtigkeit eines tal:repeat mit einem Pfadausdruck in einer Funktion kombinieren können. Als Beispiel werden Sie eine Funktion sehen, die eine Liste von Objekten erwartet. Jedes dieser Objekte ist ein Objekt in einem Ordner. Und jedes Objekt wird dann vorhanden sein, wenn es in den letzten fünf Tagen aktualisiert worden ist. Listing 6.2 zeigt ein nützliches kleines Portlet, das ich für eine Site erstellt habe, die genau diese Art von Informationen suchen und die entsprechenden Einträge hervorheben sollte.
Listing 6.2. Bis zu fünf Tage alte Objekte zurückgeben
##title=recentlyChanged
##parameters=objects
from DateTime import DateTime
now = DateTime()
difference = 5 # in Tagen
result = []
for object in objects:
diff = now - object.bobobase_modification_time()
if diff < difference:
dct = {"object":object,"diff":int(diff)}
result.append(dct)
return result
In diesem Script (Python)-Objekt habe ich ein paar neue Konzepte benutzt. Zuerst einmal importieren Sie Zopes DateTime-Modul mit der Anweisung import. Das Modul DateTime, das in Anhang C behandelt wird, bietet Zugriff auf Datumsangaben. Es ist ziemlich einfach, aber wenn Sie ein neues DateTime-Objekt ohne Parameter anlegen, erhalten Sie das aktuelle Datum und die aktuelle Zeit, und zwar in der Variablen now. Wenn Sie zwei DateTime-Objekte subtrahieren, erhalten Sie die Anzahl der Tage dazwischen. Diese können Sie mit der Anzahl vergleichen, die ein Benutzer überwachen möchte, und wenn sie für ein Objekt größer ist, können Sie dieses zur Ergebnisliste hinzufügen. Das Ergebnis ist eine Liste von Dictionary-Objekten, die wie in Listing 6.3 aussieht.
Listing 6.3. Das Ergebnis von Listing 6.2
[
{
'diff': 1,
'object': <PloneFolder instance at 02C0C110>
},
{
l 'diff': 4,
'object': <PloneFolder instance at 02FE3321>
},
...
Nun, da Sie die Ergebnisse in der richtigen Reihenfolge haben, benötigen Sie ein Page Template, das die Liste der Objekte übergibt und die Ergebnisse verarbeitet. Hier ist ein Beispiel dafür:
<ul> <li tal:repeat="updated python: context.updateScript(context.contentValues())">
Dieses Template enthält zu Beginn einen Aufruf tal:repeat, der das Skript aufruft (in diesem Fall wird updateScript aufgerufen). An diese Funktion übergibt es einen Wert, und zwar eine Liste von contentValues aus dem aktuellen Kontext. Vorher haben Sie das Script (Python)-Objekt mit einem Pfadausdruck aufgerufen, was Sie hier mit context/updateScript auch tun könnten. Allerdings können Sie mit dieser Syntax keine Parameter an das aufgerufene Skript übergeben, also nehmen Sie stattdessen einen Python-Ausdruck, python: context.updateScript(). Die Funktion contentValues gibt eine Liste aller Inhaltsobjekte in einem Ordner zurück. Betrachten Sie nun den Code für jede Iteration:
<a href="#"
tal:attributes="href updated/object/absolute_url"
tal:content="updated/object/title_or_id">
Der Titel des Artikels</a>
Vor <em tal:content="updated/diff" /> Tagen
</li>
</ul>
Wie Sie sehen, können Sie über diese Liste von Werten iterieren, und Sie können dann Pfadausdrücke benutzen, um zuerst auf den wiederholten Wert (updated), dann auf das Objekt (object) und dann auf eine Methode dieses Objekts (title_or_id) zuzugreifen. Dies ist ein Beispiel für die Auslagerung einer komplizierten Logik in ein Script (Python)-Objekt.
Eingeschränktes Python
Ich habe mehrfach erwähnt, dass Script (Python)-Objekte und Python-TAL-Ausdrücke alle in einem eingeschränkten Python-Modus ausgeführt werden. Eingeschränktes (Restricted) Python ist eine Umgebung, aus der einige Funktionen entfernt worden sind. Diese Funktionen sind in einer Web-Umgebung wie Plone potenziell gefährlich. Der ursprüngliche Grund ist der, dass Sie es zwar mit authentifizierten Benutzern zu tun haben, denen Sie aber nicht trauen können und die Python-Code auf Ihrer Site ausführen. Wenn Sie bei einem der vielen Zope-Webhosts einen Account eröffnen, werden Sie feststellen, dass Sie das können. Wenn Sie den Leuten jedoch das Recht geben, das zu tun, möchten Sie nicht, dass sie Zugriff auf bestimmte Dinge wie das Dateisystem haben.
In eingeschränktem Python wurden aus Sicherheitsgründen einige bekannte Python-Funktionen entfernt, insbesondere sind dir und open nicht verfügbar. Das heißt, dass wie bei Script (Python)-Objekten keine Introspektion darauf möglich ist und der Zugriff auf das Dateisystem beschränkt ist. Einige Python-Module stehen dem Benutzer aber zur Verfügung. Die meisten davon sind für erfahrene Entwickler gedacht. Weitere Informationen finden Sie in der Dokumentation oder im entsprechenden Modul-Code:
- string: Dies ist das Python-Modul string (http://python.org/doc/current/lib/module-string.html).
- random: Dies ist das Python-Modul random (http://python.org/doc/current/lib/module-random.html).
- whrandom: Dies ist das Python-Modul whrandom. Heute benutzen Sie meist besser das Modul random (http://python.org/doc/current/lib/module-whrandom.html).
- math: Dies ist das Python-Modul math (http://python.org/doc/current/lib/module-math.html).
- DateTime: Dies ist Zopes eigenes DateTime-Modul.
- sequence: Dies ist ein Zope-Modul zum einfachen Sortieren von Sequenzen.
- ZTUtils: Dies ist ein Zope-Modul mit verschiedenen Hilfsfunktionen.
- AccessControl: Hiermit haben Sie Zugriff auf Zopes Access-Modul.
- Products.PythonScripts.standard: Das ermöglicht den Zugriff auf die normalen String-verarbeitenden Funktionen von DTML, z.B. html_quote, thousands_commas usw.
Wenn Sie ein Modul importieren möchten, das in der obigen Liste nicht auftaucht, können Sie im Modul PythonScript eine sehr gute Anleitung dafür finden. Diese finden Sie unter Zope/lib/python/Products/PythonScripts/module_access_examples.py. Es steht Ihnen allerdings auch eine einfachere Methode zur Verfügung, nämlich die Verwendung einer externen Methode.
Externe Methodenobjekte verwenden
Eine externe Methode ist ein Python-Modul im Dateisystem, das von Plone aufgerufen wird. Weil sie im Dateisystem gespeichert ist, wird sie nicht im eingeschränkten Python-Modus ausgeführt und gehorcht daher den normalen Plone-Sicherheitseinstellungen.
Sie können also ein Skript schreiben, das tut, was immer Sie wollen, und es dann aus einem Page Template heraus aufrufen. Häufige Aufgaben hierfür sind das Öffnen und Schließen von Dateien, das Zugreifen auf andere Prozesse oder ausführbare Programme und das Ausführen von Aufgaben in Plone oder Zope, die Sie mit anderen Mitteln einfach nicht erledigen können. Aus offensichtlichen Gründen müssen Sie, wenn Sie ein solches Skript schreiben, sicher sein, dass Sie nichts Gefährliches tun, z.B. die Passwortdatei auf Ihrem Server lesen oder ungewollt eine Datei löschen.
Um eine externe Methode hinzuzufügen, gehen Sie im Dateisystem zur Wurzel Ihrer Plone-Instanz und finden darin das Verzeichnis Extensions. Darin fügen Sie eine neue Python-Skriptdatei hinzu. Abbildung 6.5 zeigt, dass ich z.B. test.py in das Verzeichnis auf meinem Windows-Rechner eingefügt habe.
Abbildung 6.5. Eine neue externe Methode, test.py
Nun können Sie test.py öffnen, nach Belieben bearbeiten und beliebigen Python-Code darin schreiben. Der einzige Haken ist der, dass Sie eine Eingangsfunktion haben müssen, die mindestens ein Argument erhält, nämlich self. Dieses Argument ist das externe Methodenobjekt in Plone, das Sie gleich hinzufügen werden. Folgendes ist ein Beispiel für eine Eingangsfunktion, die die Datei README.txt aus dem gleichen Verzeichnis Extensions liest und an den Benutzer zurückgibt (Sie müssen den Pfad so ändern, dass er auf Ihre Datei zeigt):
def readFile(self): fh = open(r'c:\Program Files\Plone\Data\Extensions\README.txt', 'rb') data = fh.read() return data
Nachdem Sie das getan haben, müssen Sie eine externe Methode auf dieses Skript abbilden. Da das ein Zope-Objekt ist, gehen Sie wieder zum ZMI, klicken auf portal_skins und dann auf custom. Wählen Sie schließlich in der Dropdown-Liste Add New Items den Eintrag External Method. Wenn Sie eine externe Methode hinzufügen, müssen Sie den Namen des Moduls (ohne .py) und die Eingangsfunktion angeben. In diesem Fall sieht das Add-Formular wie in Abbildung 6.6 aus.
Abbildung 6.6. Die neu hinzugefügte externe Methode
Nach einem Klick auf Save Changes können Sie den Test-Reiter anklicken, um zu sehen, was passiert, wenn die Methode ausgeführt wird. In diesem Fall sollten Sie ein oder zwei Zeilen Text erhalten. Da Ihr externes Methodenmodul in Plone ist, können Sie von einem Page Template genauso darauf zugreifen wie auf jedes andere Objekt. Ein Pfadausdruck auf here/test_external ist in dem Fall ausreichend. Beispiel:
<h1>README.txt</h1> <p tal:content="here/test_external" />
Die wirkliche Mächtigkeit besteht darin, dass Sie Code in den uneingeschränkten Python-Modus und von dort an eine beliebige Funktion auslagern können, ohne sich um die Sicherheit sorgen zu müssen. Auch wenn das nach einer coolen Funktion aussieht, werden externe Methoden in Plone nicht übermäßig benutzt, weil komplexe Logik normalerweise in ein Produkt verlagert wird, während einfache Logik in einem Script (Python)-Objekt belassen wird. Sollten Sie feststellen, dass Sie sehr ausgiebigen Gebrauch von externen Methodenobjekten machen, möchten Sie vielleicht einmal einen Blick auf die in Kapitel 12 beschriebenen Werkzeuge werfen.
Nützliche Hinweise
Da Page Templates gültiger XML-Code sind und unabhängig von Zope oder Plone benutzt werden können, gibt es mehrere nützliche Skripten zum Aufräumen von Template-Code und zur Durchführung von Syntax-Prüfungen. Dies sind zusätzliche Werkzeuge und Prüfungen. Wenn Sie ein Page Template hochladen, führt Zope tatsächlich alle notwendigen Prüfungen durch. Bei einem Projekt wie Plone kann es hilfreich sein, automatische Prüfungen über Ihren Code laufen zu lassen oder ihn lokal zu prüfen, bevor Änderungen vorgenommen werden.
Um diese Prüfungen durchzuführen müssen Sie in der Lage sein, diese Werkzeuge lokal zu bearbeiten, und Sie müssen Python auf Ihrem Rechner installiert haben. Weitere Informationen zum External Editor, einer Methode für die lokale Bearbeitung von entferntem Code, finden Sie in Kapitel 10.
Einführung in XML-Namespaces
Page Templates verwenden XML-Namespaces, um Code zu generieren. Programmierer können sich mit den Regeln von XML-Namespaces das Leben leichter machen. Am Anfang eines Page Templates sehen Sie im ersten Tag eine Deklaration des Namespaces:
<html xmlns="http://www.w3.org/1999/xhtml"...
Damit wird der Standard-Namespace auf XHTML (Extensible HTML) gesetzt. Für alle darin enthaltenen Elemente gilt, dass sie diesen Namespace verwenden, wenn kein anderer angegeben ist. Somit wissen Sie z.B., dass das nächste Element XHTML ist, weil es kein Namespace-Präfix hat:
<body>
Bei TAL- und METAL-Elementen und -Attributen haben Sie normalerweise das Präfix tal: und metal: hinzugefügt, um den Namespace anzugeben. Der folgende Code sollte Ihnen nun vertraut vorkommen:
<span tal:omit-tag="" tal:content="python: 1+2" />
Das gibt 3 aus. Folgendes ist jedoch eine Alternative dazu:
<tal:number content="python: 1+2" />
Dadurch, dass Sie das Präfix tal: im Element verwenden, haben Sie den Standard-Namespace für dieses ganze Element als tal angegeben. Ohne weiteres Präfix wird der Namespace tal benutzt. Im Beispiel mit den span-Tags ist der Standard-Namespace XHTML, d.h. Sie müssen das tal:-Präfix explizit angeben, wenn Sie den Content-Reiter benutzen.
Beachten Sie, dass der Elementname sprechend ist und alles sein darf, was noch nicht im tal-Namespace definiert worden ist (z.B. content oder replace). Weil tal:number kein gültiges XHTML-Element ist, wird dieser Tag nicht angezeigt, wohl aber sein Inhalt, was das omit-tag überflüssig macht. Diese Technik wird in Plone sehr oft eingesetzt, um kürzeren, prägnanteren Code zu erhalten, in dem man Fehler einfacher suchen kann.
Einführung in Code-Säuberung
HTML Tidy ist ein hervorragendes Werkzeug zum Testen und Säubern von HTML-Code, das einige nützliche Aufgaben erledigen kann. Es existieren Versionen von HTML Tidy für alle Betriebssysteme. Sie können es unter http://tidy.sourceforge.net herunterladen. Für Windows-Benutzer: Finden Sie das passende Paket für Ihre Windows-Version, packen Sie die Datei tidy.zip aus, und fügen Sie tidy.exe zu Ihrem PATH hinzu (normalerweise Ihr Windows-Verzeichnis, z.B. C:\WINNT).
HTML Tidy sagt Ihnen, wenn es irgendwelche XHTML-Fehler in Ihrem Page Template gibt. Zu diesem Zweck kann ein Flag einen großen Unterschied ausmachen: -xml. Damit weiß HTML Tidy, dass es die Datei als XML behandeln und alle XML-Fehler berichten soll. Am Beispiel eines "defekten" Templates in Listing 6.4 können Sie einige Fehler sehen. Der Code ist nicht nur nicht eingerückt, sondern es fehlen auch schließende Elemente, und auch die Verschachtelung stimmt nicht.
Listing 6.4. Ein Beispiel für ein defektes Page Template: bad_template.pt
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<p>
<div>
Das ist schlechtes HTML,
XHTML oder XML...<a tal:contents="string: someUrl"></a>
</p>
<img>
Außerdem ist er nicht eingerückt!
</body>
</html>
Wenn Sie HTML Tidy über das Listing 6.4 laufen lassen, werden Sie die Fehler im Template sehen und schön eingerückten Code erhalten, wie in Listing 6.5 zu sehen ist.
Listing 6.5. Ausgabe von HTML Tidy
$ tidy -q -i bad_template.pt
line 11 column 1 - Warning: <img> element not empty or not closed
line 10 column 1 - Warning: missing </div>
line 10 column 39 - Warning: <a> proprietary attribute "tal:contents"
line 11 column 1 - Warning: <img> lacks "alt" attribute
line 11 column 1 - Warning: <img> lacks "src" attribute
line 9 column 1 - Warning: trimming empty <p>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="generator" content=
"HTML Tidy for Linux/x86 (vers 1st August 2003), see http://www.w3.org" />
<title></title>
</head>
<body>
<div>
Das ist schlechtes HTML, XHTML oder XML...<a tal:contents=
"string: someUrl"></a> <img />Außerdem ist er nicht eingerückt!
</div>
</body>
</html>
Die Beschwerden über proprietäre Attribute können etwas nervig sein. Um zu prüfen, ob Ihr Page Template gültiges XML ist, übergeben Sie das Flag -xml. Die Ausgabe ist weniger ausführlich und weist lediglich auf die fehlenden Tags hin:
$ tidy -q -xml bad_template.pt line 15 column 1 - Error: unexpected </body> in <img> line 16 column 1 - Error: unexpected </html> in <img>
Syntax-Prüfungen durchführen
Wenn Sie ein Page Template im ZMI bearbeiten, führt Zope auf dem Dokument eine Syntax-Prüfung auf Dinge wie ungültige Tags durch. Falls ein Tag ungültig ist, wird ein Fehler bei dem Template angezeigt, während Sie es über das Web bearbeiten. Wenn Sie wie ich die meisten Ihrer Page Templates im Dateisystem schreiben (was ich in Kapitel 7 demonstrieren werde), so ist eine einfache Syntax-Prüfung des Page Templates wirklich sehr hilfreich. Listing 6.6 ist ein Python-Skript in Ihrem Dateisystem, das unabhängig von Zope läuft.
Um es auszuführen, müssen Sie einen Python-Interpreter haben, und das Python-Modul PageTemplate muss importiert werden können. Damit PageTemplate in Ihren Python-Interpreter importiert werden kann, müssen Sie das Verzeichnis Products Ihrer Zope-Installation zu Ihrem Python-Pfad hinzufügen. Das können Sie auf mehrere Arten tun (siehe Anhang B).
Listing 6.6. Fehlersuche bei Page Templates
#!/usr/bin/python
from Products.PageTemplates.PageTemplate import PageTemplate
import sys
def test(file):
raw_data = open(file, 'r').read()
pt = PageTemplate()
pt.write(raw_data)
if pt._v_errors:
print "*** Error in:", file
for error in pt._v_errors[1:]:
print error
if __name__=='__main__':
if len(sys.argv) < 2:
print "python check.py file [files...]"
sys.exit(1)
else:
for arg in sys.argv[1:]:
test(arg)
Für jede an das Skript übergebene Datei kompiliert das ZMI das Page Template und sucht nach irgendwelchen TAL-Fehlern. Für die Datei bad_template.pt aus Listing 6.4 erhalten Sie einen Fehler:
$ python zpt.py /tmp/bad_template.pt *** Error in: /tmp/bad_template.pt TAL.TALDefs.TALError: bad TAL attribute: 'contents', at line 10, column 39
In diesem Fall beschwert es sich über die unrichtige Buchstabierung von tal:content in Form von tal:contents. Dies ist ein Fehler, den HTML Tidy nicht findet. Dummerweise wird die Verarbeitung beim ersten Syntax-Fehler abgebrochen. Wenn es mehrere Fehler gibt, wird nur der erste genannt, d.h., manchmal müssen Sie die Syntax mehrfach überprüfen.
Formulare verwenden
Formulare sind ein wichtiger Bestandteil einer Site, und fast jeder muss eine Methode zum Erstellen und Ändern von Formularen in seiner Plone-Site benutzen. Mit dem Formular-Framework von Plone können Sie die Validierung von Formularen ändern. Dieses Framework ist nicht ausschließlich für isolierte Formulare gedacht, die eine einfache Aufgabe erfüllen, z.B. ein Passwort abfragen, eine Anmeldung vornehmen etc. Das Framework funktioniert auch für alle Inhaltstypen bei Aufgaben wie der Bearbeitung eines Inhaltstyps, was ich später in den Kapiteln 11 bis 13 behandeln werde.
Alle einfachen Formulare verfügen über mindestens zwei Komponenten, die Sie bereits gesehen haben: ein Page Template-Objekt, um das Formular dem Benutzer anzuzeigen, und ein Script (Python)-Objekt, um die Ergebnisse zu parsen und irgendeine Aktion darauf durchzuführen.
Das Form Controller-Framework von Plone führt ein paar neue Objekttypen ein, die äquivalent zu den Typen sind, die Sie in diesem Kapitel gesehen haben. Dies sind das Controller Page Template-Objekt, das Controller Script (Python)-Objekt und das Controller Validator-Objekt. Zu diesen neuen Objekten gibt es äquivalente Zope-Objekte, wie Sie in Tabelle 6.1 sehen. Diese neuen Objekte haben mehr Eigenschaften und agieren auf leicht andere Weise als die dazu äquivalenten Objekte.
Tabelle 6.1. Neue Objekttypen, die der Controller bietet
| Objekttyp | Äquivalentes Zope-Objekt |
|---|---|
| Controller Filesystem Page Template | Page Template |
| Controller Python Script | Python Script |
| Controller Validator | Python Script |
Um eines dieser Objekte mit dem ZMI zu hinzuzufügen, gehen Sie ins Dropdown-Menü und wählen seinen Namen aus.
Das Form Controller-Framework erzeugt eine Reihe von Ereignissen für ein Formular, die ein Benutzer dann definieren kann. Folgendes ist die Reihe der Ereignisse, wenn ein Formular ausgeführt wird:
- Dem Benutzer wird ein Controller Page Template-Objekt angezeigt.
- Der Benutzer füllt das Formular aus und schickt es ab.
- Eine eventuelle Validierung des abgeschickten Inhalts wird ausgeführt.
- Eine den Daten entsprechende Aktion (oft Erfolg oder Fehlschlag) wird ausgeführt.
Wenn diese Reihe von Ereignissen eintritt, wird ein Zustandsobjekt herumgereicht, das Informationen über den Objektstatus, den Erfolg irgendwelcher Validierungen und irgendwelche Meldungen enthält, die weitergegeben werden sollen.
Um zu zeigen, wie ein Formular validiert werden kann, gehen die folgenden Abschnitte diese Schritte durch. Anschließend werde ich ein vollständiges Beispiel im Abschnitt "E-Mail-Beispiel: E-Mail an den Webmaster schicken" zeigen.
Erstellen eines Beispielformulars und zugehörige Skripten
Am Anfang dieses Prozesses steht ein Formular. Obwohl es tatsächlich ein Controller Page Template-Objekt ist, ist es mit normalem TAL-Code geschrieben. Um ein Formular hinzuzufügen, wählen Sie im nun vertrauten Dropdown-Menü Controller Page Template und geben ihm die ID test_cpt.
Ein Formular in Plone besteht eigentlich aus ziemlich viel Code, falls Sie alle verfügbaren Optionen benutzen möchten. Dieser Codeteil ist in Anhang B vollständig enthalten und wird im späteren Beispiel benutzt:
<form method="post"
tal:define="errors options/state/getErrors"
tal:attributes="action template/id;">
...
<input type="hidden" name="form.submitted" value="1" />
</form>
Wenn Sie diesen Code betrachten, sollte Ihnen auffallen, dass einige kleine Unterschiede zu einer Art Standardformular existieren, damit der Code im Framework funktioniert. Zunächst ist das Formular so eingerichtet, dass es Daten an sich selbst schickt. Das ist nicht optional. Weiterhin existiert eine spezielle versteckte Variable namens form.submitted.
Das Controller Page Template-Objekt prüft die Anfragevariable auf den Wert form.submitted, um zu sehen, ob das Formular abgeschickt wurde. Wenn es stattdesen gerade erst aufgerufen worden ist, z.B. über einen Link, ist das nicht optional. Am Anfang des Formulars setzen Sie die Variable error. Dieses Dictionary kommt aus dem state-Objekt, das an die Templates übergeben wird. Das state-Objekt ist in diesem System das gleiche Objekt für alle Templates und Skripten.
Validierer erstellen
Sobald der Benutzer den Senden-Button auf Ihrem Formular anklickt, gehen die Daten durch die Validierer und werden von ihnen geprüft. Validierer sind optional. Daten müssen nicht validiert werden, aber eine Anwendung sollte das natürlich bei Bedarf tun. Der Validator-Reiter für ein Controller Page Template-Objekt enthält einen Link zu den möglichen Validierern.
Ein Validierungsskript ist identisch mit einem normalen Script (Python)-Objekt, das eine Extravariable, state, hat. Mit dieser Variablen können Sie Validierungsergebnisse übergeben. Listing 6.7 zeigt ein einfaches Validierungsskript, das überprüft, ob eine Zahl eingegeben wurde.
Listing 6.7. Validierung einer Zahleneingabe
##title=Ein Skript zum Überprüfen einer Zahleneingabe
##parameters=
num = context.REQUEST.get('num', None)
try:
int(num)
except ValueError:
state.setError("num", "Keine Zahl", new_status="failure")
except TypeError:
state.setError("num", "Keine Zahl eingegeben.", new_status="failure")
if state.getErrors():
state.set(portal_status_message="Bitte Fehler korrigieren.")
return state
Dieses state-Objekt enthält einfache Angaben darüber, was in der Validierungskette passiert ist. Es speichert für jedes Feld die Fehler, den Status und alle anderen Werte. Wenn z.B. die eingegebene Zahl nicht in eine Ganzzahl umgewandelt werden kann, setzen Sie den Status auf Fehlschlag und geben eine Fehlermeldung für das Feld mit der Methode setError aus. Später wird die Fehlermeldung für dieses Feld angezeigt. Am Ende des Skripts werden alle bisher zurückgegebenen Fehler mit der Methode getErrors übergeben.
Um das obige Skript hinzuzufügen, klicken Sie auf portal_skins, dann auf custom und wählen im Dropdown-Menü Controller Validator. Geben Sie ihm die ID test_validator. Nun können Sie zum Validation-Reiter Ihres Controller Page Template-Objekts zurückgehen und einen Zeiger auf dieses Validierungsskript hinzufügen, wie in Abbildung 6.7 zu sehen ist.
Abbildung 6.7. Hinzufügen von test_validator zum Controller Page Template-Objekt
Bei einer Validierung haben Sie einige Optionen zur Auswahl. Im Beispiel habe ich diese ignoriert, weil sie nicht relevant sind, aber hier ist die Liste dieser Optionen:
- contextType: Dies ist der Typ des context-Objekts, falls vorhanden, in dem das Template ausgeführt wird. Dies ist eine Abkürzung zum Inhaltstyp des context-Objekts. Wenn Sie wollten, dass nur diese Validierung bei einem Link ausgeführt werden soll, könnten Sie diesen Wert auf Link setzen.
- button: Dies ist der Button, falls vorhanden, der angeklickt wird, um das Formular abzuschicken. Sie könnten verschiedene Buttons in einem Formular haben (z.B. einen Senden- und einen Abbrechen-Button). Jeder dieser Buttons könnte dann auf eine andere Aktion abgebildet werden. Ein Klick auf Abbrechen würde Sie an einen Ort bringen und ein Klick auf Senden an einen anderen.
- validators: Dies ist eine mit Kommata getrennte Liste von Validierern, d.h. Controller Validator-Objekten, die das Template verwenden wird. Im vorigen Beispiel haben Sie die Validierer-ID test_validator benutzt.
Hinweis
Verwenden Sie Controller Validator-Objekte statt Script (Python)-Objekte, wenn Sie Validierungsskripten schreiben. Controller Validator-Objekte entsprechen gewöhnlichen Script (Python)-Objekten, haben aber einen zusätzlichen Actions-Reiter im ZMI.
Aktionen angeben
Aktionen werden ausgeführt, nachdem die Validierer beendet worden sind, und hängen vom Status ab, den die Validierer zurückgeben. Der Actions-Reiter für ein Controller Page Template-Objekt zeigt alle Aktionen des entsprechenden Page Templates an. Aktionen können Sie mit der gleichen Art von Spezialisierungsoptionen angeben, die zuvor schon mit einem Webformular beschrieben wurden (siehe Abbildung 6.8).
Abbildung 6.8. Eine Aktion hinzufügen
Für die eigentliche Ergebnisaktion haben Sie folgende vier Möglichkeiten:
- redirect_to: Damit wird eine Umleitung zu der im Argument (einem TALES-Ausdruck) angegebenen URL erzielt. Die URL kann entweder absolut oder relativ sein.
- redirect_to_action: Hier erfolgt die Umleitung auf die im Argument (einem TALES-Ausdruck) angegebene Aktion zu dem aktuellen Inhaltsobjekt (z.B. string:view). Zu diesem Zeitpunkt habe ich Aktionen noch nicht behandelt, aber jedes Inhaltsobjekt verfügt über Aktionen wie Anzeigen und Bearbeiten. Aktionen zu einem Objekt werden in Kapitel 11 behandelt.
- traverse_to: Führt eine Traversierung zu der im Argument (einem TALES-Ausdruck) angegebenen URL durch. Die URL kann entweder absolut oder relativ sein.
- traverse_to_action: Führt eine Traversierung zu der Aktion durch, die im Argument (einem TALES-Ausdruck) zu dem aktuellen Inhaltsobjekt angegeben ist (z.B. string:view).
Ein Beispiel hierfür ist, wenn Sie bei erfolgreichem Ausfüllen eines Formulars zu einem Controller Python Script-Objekt traversieren, das Sie geschrieben haben und das das Ergebnis des Formulars verarbeitet. Wenn die Seite fehlschlägt, traversieren Sie zum Template zurück und zeigen den Fehler an.
Der Unterschied zwischen einer Weiterleitung (Redirect) und einer Traversierung ist der, dass die Weiterleitung eine HTTP-Weiterleitung ist, die an den Browser des Benutzers gesendet wird. Der Browser verarbeitet sie und leitet den Benutzer zur nächsten Seite weiter. Daher verlieren die Weiterleitungsaktionen alle in der ursprünglichen Anfrage übergebenen Werte. Falls Sie den Inhalt des ursprünglichen Formulars untersuchen müssen, ist das nicht der beste Ansatz. Ich empfehle stattdessen die Traversierung zu den Optionen. Das Ergebnis ist das gleiche, aber die Traversierung erledigt das alles auf dem Server. Dabei werden die Anfragevariablen erhalten, die Sie dann in Skripten untersuchen können.
E-Mail-Beispiel: E-Mail an den Webmaster schicken
Nun werden Sie ein echtes Beispiel sehen, das Sie im Rest dieses Kapitels erstellen werden. Eine häufige Anforderung ist ein spezielles Formular, das E-Mails an den Webmaster schickt. Ein solches Formular werden Sie in den folgenden Abschnitten erstellen. Die vollständigen Skripten, das Page Template und der zugehörige Code sind in Anhang B verfügbar. Wenn Sie all das nicht eintippen möchten, können Sie das Beispiel online auf der Buch-Website sehen. Sie können es auch als komprimierte Datei von der Plone-Buch-Website (http://plone-book.agmweb.ca) und von der Apress-Website (http://www.apress.com) herunterladen. Das heißt, Sie können es einfach installieren und ausprobieren. Bei diesem Beispiel gibt es nur zwei Felder im Formular: die E-Mail-Adresse der Person, die das Formular abschickt, und ihren Kommentar. Bei diesem Formular ist die E-Mail-Adresse der Person notwendig, damit Sie auf ihre Kommentare antworten können.
Das Formular erstellen
Das Formular ist der größte und komplizierteste Teil dieser Prozedur, vor allem, weil man viel Arbeit in die Fehlerbehandlung investieren muss. Dieses Formular ist ein Controller Page Template-Objekt namens feedbackForm. Um sicher zu sein, dass es vom Haupt-Template umfasst wird, beginne ich das Formular mit der üblichen Methode:
<html
xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en-US"
lang="en-US"
i18n:domain="plone"
metal:use-macro="here/main_template/macros/master">
<body>
<div metal:fill-slot="main"
tal:define="errors options/state/getErrors;">
Ein Zusatz hierbei ist errors options/state/getErrors, womit alle eventuellen Fehler für eine spätere Verwendung in die lokale Variable errors abgelegt werden.
Wegen der Bedingung, dass das Formular sich selbst aufrufen soll, setzen Sie diese Aktion in TAL mit dem Ausdruck template/id. Dieser Pfad extrahiert die ID des Templates und fügt sie in die Aktion ein. Das heißt, dieser Pfad funktioniert immer, sogar dann, wenn Sie das Template umbenennen. Beachten Sie, dass Sie auch die zuvor gesehenen i18n-Tags hinzufügen, um zu gewährleisten, dass dieses Formular später lokalisiert werden kann:
<form method="post"
tal:attributes="action template/id;">
<legend i18n:translate="legend_feedback_form">
Website Feedback
</legend>
Der folgende Code ist der Anfang der Zeile für die E-Mail-Adresse. Hierbei definieren Sie eine Variable namens error_email_address, die auf einen Fehler-String gesetzt ist, falls es einen passenden String im Dictionary errors gibt. Dieser Fehlerwert wird vom Validierer erzeugt, falls ein Fehler vorkommen sollte:
<div class="field"
tal:attributes="class python:test(error_email_address,
'field error', 'field')">
tal:define="error_email_address errors/email_address|nothing;">
Folgendes ist die Bezeichnung für das E-Mail-Adressenfeld. Darin fügen Sie ein div für den Hilfetext ein. Das span-Element wird zu dem nun vertrauten roten Punkt neben der Bezeichnung, damit der Benutzer weiß, dass das Feld ausgefüllt werden muss:
<label i18n:translate="label_email_address">Your email address</label>
<span class="fieldRequired" title="Required">(Required)</span>
<div class="formHelp"
i18n:translate="label_email_address_help">
Enter your email address.
</div>
Als Nächstes fügen Sie das eigentliche Element hinzu:
<div tal:condition="error_email_address">
<tal:block i18n:translate=""
content="error_email_address">Error
</tal:block>
</div>
<input type="text" name="email_address"
tal:attributes="tabindex tabindex/next;
value request/email_address|nothing" />
</div>
Zu Beginn dieses Blocks testen Sie, ob es einen Fehler gibt. Wenn ja, hat die Klasse des Elements zu field error gewechselt. Diese Klasse zeigt einen hübschen orangefarbenen Kasten um das Feld herum. Wenn für dieses Feld ein Fehler aufgetreten ist (was Sie bereits getestet haben), wird die entsprechende Meldung angezeigt. Schließlich zeigen Sie das Formularelement an, und wenn es schon einen Wert für email_address in der Anfrage gibt, füllen Sie das Formularelement mit diesem Wert.
Das Werkzeug tabindex ist recht nützlich in Plone. Es enthält eine fortlaufende Nummer, die für jedes Element inkrementiert wird und einen neuen Wert für den HTML-tabindex setzt. Das ist eine nette Eigenschaft der Benutzerschnittstelle, denn dadurch kann jedes Formularelement gefahrlos herumgereicht werden, ohne dass man sich diese tabindex-Zahlen merken muss, weil das schon automatisch geschieht.
Das ist eine Menge Arbeit für ein Element, aber es ist überwiegend Code nach dem immer gleichen Schema, den Sie einfach kopieren oder ändern können. Den Rest des Formulars finden Sie in Anhang B.
Einen Validierer erstellen
In dem Beispiel gibt es nur ein notwendiges Element (die E-Mail-Adresse), d.h., es gibt ein einfaches Stück Python namens validEmail.vpy, das die Arbeit macht. Der Inhalt dieses Skripts sieht wie folgt aus:
email = context.REQUEST.get('email_address', None)
if not email:
state.setError('email_address', 'Email is required',
new_status='failure')
if state.getErrors():
state.set(portal_status_message='Please correct the errors.')
return state
Wenn keine E-Mail-Adresse gefunden wird, fügt dieses Skript einen Fehler mit dem Schlüssel email_address und einer Meldung zum Fehler-Dictionary hinzu. Dieser Schlüssel wird im Page Template benutzt, um zu prüfen, ob es in diesem speziellen Feld einen Fehler gab.
Das Skript verarbeiten
Dieses Beispiel verwendet ein einfaches E-Mail-Skript, das die (bereits validierten) Werte bekommt und daraus eine E-Mail macht. Das ist ein Controller Python Script-Objekt. Es ähnelt dem normalen Script (Python)-Objekt, mit dem Unterschied, dass es eine zusätzliche Variable namens state hat und Sie ihm, wie beim Controller Page Template, Aktionen angeben können, wenn es Erfolg hat:
mhost = context.MailHost
emailAddress = context.REQUEST.get('email_address')
administratorEmailAddress = context.email_from_address
comments = context.REQUEST.get('comments')
# the message format, %s will be filled in from data
message = """
From: %s
To: %s
Subject: Website Feedback
%s
URL: %s """
# format the message
message = message % (
emailAddress,
administratorEmailAddress,
comments,
context.absolute_url())
mhost.send(message)
Nun haben Sie ein einfaches Skript zum Verschicken von E-Mail gesehen. Das ist ein gängiges Skript, das Sie immer wieder sehen werden. Im Prinzip bekommt dabei das MailHost-Objekt in Plone eine E-Mail-Adresse als String, sofern es die RFC-Spezifikation (Request for Comment) für E-Mails erfüllt, die From- und To-Adressen enthält.
In dieser E-Mail nehmen Sie die Administrator-Adresse, die Sie in den Portal-Einstellungen angegeben haben, und schicken die E-Mail an diese Person. Der einzige zusätzliche Teil in diesem Skript besteht darin, dass außerdem auch der Zustand gesetzt wird. Damit wird eine Meldung gesetzt, die dem Benutzer ein Feedback gibt:
screenMsg = "Comments sent, thank you."
state.setKwargs( {'portal_status_message':screenMsg} )
return state
Verknüpfen aller drei Teile
Im Moment existieren jedoch drei separate Einheiten: ein Formular, ein Validierer und ein Aktionsskript. Diese müssen miteinander verbunden werden, um die ganze Kette zu bilden, damit Sie wieder zum Controller Template-Objekt zurückkommen. Klicken Sie auf den Validator-Reiter, und geben Sie einen neuen Validierer ein, der auf das Skript validEmail zeigt. Sie werden auch eine Erfolgsaktion für den Fall hinzufügen, dass die Verarbeitung korrekt ist, um zu dem Skript sendEmail zu traversieren. Bei dem sendEmail-Skript können Sie nun eine weitere Traversierung zurück zu feedbackForm hinzufügen, damit der Benutzer nach einem erfolgreichen sendEmail wieder zur ursprünglichen Seite zurückgelangt.
Hinweis
In Plone gibt es ein wesentlich vollständigeres E-Mail-Validierungsskript namens validate_emailaddr, das prüft, ob die E-Mail das richtige Format hat. Wenn Sie lieber dieses Skript benutzen möchten, können Sie den Validierer auf dieses Skript zeigen lassen.
Das war's, Sie sind fertig! Nun sollten Sie in der Lage sein, das Formular auf der Buch-Website zu testen. Um es noch einfacher zu machen, habe ich einen Feedback-Reiter erstellt, der auf das Template feedbackForm zeigt, und von dort können Sie mir nun Feedback zu diesem Buch geben!
Andy McKay: Plone. Addison-Wesley 2005
Es wurde zuletzt von ctheune am 30.04.2006 14:15 aus der local Quelle via
/tmp/plonebook/PloneBook/de/ aktualisiert.







