Form Controller Tool

Plone kommt mit dem CMF Form Controller-Produkt, mit dem die Abläufe zwischen Formularen und Skripten geregelt werden können. Gerade für komplexe Abläufe ist es sehr hilfreich, unterstützt jedoch keine Zope3-Views und kann daher nur in Skin-Layern definiert werden.

Schauen wir uns nun als Beispiel Plone’s Send this page to someone-Formular an, das in CMFPlone/skins/plone_forms/sendto_form.cpt definiert ist. Dabei steht der cpt-Suffix für Controller Page Template:

<div metal:fill-slot="main"
     tal:define="errors options/state/getErrors;">
  ...
  <form name="sendto_form"
        class="enableAutoFocus"
        action="sendto_form"
        method="post"
        enctype="multipart/form-data"
        tal:attributes="action string:$here_url/$template_id">
    ...
    <div class="field"
         tal:define="error errors/send_to_address|nothing;"
         tal:attributes="class python:test(error, ``field error``, ``field``)">
      ...
      <div class="formControls">
        <input class="context"
               type="submit"
               name="form.button.Send"
               value="Send"
               i18n:attributes="value label_send;"
               />
    </div>
    ...
  </form>

CMFFormController benötigt zur Auswertung des Formulars eine korrespondierende Datei sendto.cpy.metadata im selben Verzeichnis:

[default]
title=Send this page to somebody

[validators]
validators=validate_sendto

[actions]
action.success=traverse_to:string:sendto
action.failure=traverse_to:string:sendto_form

Schauen wir uns nun die Validatoren und Aktionen genauer an.

Validatoren angeben

Allgemein lassen sich für Für Controller Page Templates folgendermaßen Validatoren angeben:

[validators]
validators = validate_script1, validate_script2

Diese Angabe startet zwei Prüfskripte: zunächst validate_script1 , dann validate_script2 . Ein Prüfskript untersucht die Formulardaten wobei Fehler dem Form Controller Status hinzugefügt werden.

Objekttyp-spezifische Validierung

Soll die Validierung vom Objekttyp Document verschieden sein von der des Objekttyps Image sieht die Metaangabe so aus:

validators.Document = validate_script1
validators.Image = validate_script2

Button-spezifische Validierung

Kommen im Formular mehrere Buttons (Tasten) vor, z.B.:

<input type="submit" name="form.button.button1" value="Value1" />
<input type="submit" name="form.button.button2" value="Value2" />

können für diese auch unterschiedliche Validierungen angegeben werden:

validators.button1 = validate_script1
validators.button2 = validate_script2

Aktionen angeben

Für Controller Page Templates lassen sich auch Aktionen angeben, z.B.:

[actions]
action.success = traverse_to:string:script1

Haben die Prüfskripte den Status success ausgegeben, wird die Aktion traverse_to mit dem Argument string:script1 aufgerufen.

Schlägt ein Prüfskript fehl, wird gemäß den Standardeinstellungen das Formular erneut geladen.

Wie bei Validatoren kann auch bei Aktionen zwischen Dokumenttypen und Buttons unterschieden werden:

action.success.Document = traverse_to:string:document_script
action.success.Image = traverse_to:string:image_script
action.success.button1 = traverse_to:string:script1
action.success.button2 = traverse_to:string:script2

Folgende Aktionen sind möglich:

  • redirect_to

  • redirect_to_action

  • traverse_to

  • traverse_to_action.

Dabei rufen die traverse_to-Aktionen direkt ein Template oder Skript auf dem Server auf, wohingegen die redirect_to-Aktionen eine Weiterleitung des Browsers bewirken. Normalerweise werden die Zwischenschritte mit traverse_to-Aktionen und nur der letzte Schritt mit einer redirect_to-Aktion angegeben, sodass die Angabe der URL im Browser die aktuelle Seite wiedergibt. So ist z.B. in unserem Beispiel in sendto.cpy.metadata folgendes angegeben:

[validators]
validators=validate_sendto

[actions]
action.success = redirect_to_action:string:view
action.failure = redirect_to_action:string:view

Validator-Skripte schreiben

Schauen wir uns nun das Validator-Skript validate_sendto.vpy genauer an, auf das in sendto.cpy.metadata verwiesen wurde:

## Controller Script Python "validate_sendto"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind state=state
##bind subpath=traverse_subpath
##parameters=send_to_address='',send_from_address=''
##title=validates the email adresses

from Products.CMFPlone import PloneMessageFactory as _
plone_utils=context.plone_utils

if not send_to_address:
    state.setError('send_to_address', _(u'Please submit an email address.'), 'email_required')
...
if state.getErrors():
    context.plone_utils.addPortalMessage(_(u'Please correct the indicated errors.'), 'error')
    return state.set(status='failure')
else:
    return state

Aktionen schreiben

Ist die Validierung erfolgreich, fährt der CMFFormController, wie in sendto_form.cpt.metadata angegeben mit dem Skript sendto.cpy fort. Dieses Skript gibt schließlich den Wert für state aus:

## Controller Python Script "sendto"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind state=state
##bind subpath=traverse_subpath
##parameters=
##title=Send an URL to a friend
##
REQUEST=context.REQUEST

...

if not mtool.checkPermission(AllowSendto, context):
    context.plone_utils.addPortalMessage(_(u'You are not allowed to send this link.'), 'error')
    return state.set(status='failure')

...

context.plone_utils.addPortalMessage(_(u'Mail sent.'))
return state