Creating a simple feedback form¶
This section explains how to create a very basic feedback form.
The code for this example is available to checkout from the collective as the *example.formlib* package.
For all practical sense formlib based components are really regular Zope view components with some convenient base classes for auto-generating output based on schemas and other configuration info. You will see that in a moment.
First, define an interface class with the schema of the form:
from zope.interface import Interface from zope.schema import TextLine, Text class IFeedbackForm(Interface): """ A typical feedback schema """ customer = TextLine(title=u'Customer', description=u'Customer email', required=True) subject = TextLine(title=u'Subject', required=True) message = Text(title=u'Message', description=u'The message body', required=True)
The purpose of this interface is to define the fields of the form. The type of each schema field determines the type of widget that will be used by default for that field, so choose it carefully. To see all the schema fields available, read the zope.schema package's interfaces.
Next, create a form instance, which is a class that groups an ordered collection of fields and actions. To do that, simply subclass Five's PageForm class, a wrapper to the formlib Form class to keep Zope 2 happy. Type the following code into a Python file inside your product:
from Products.Five.formlib.formbase import PageForm
You will also need to make use of Five's strange hybrid between Zope 2 and Zope 3 page templates:
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
The simplest way to define a collection of form fields is using the
Fields constructor with the previous schema:
from zope.formlib import form class FeedbackForm(PageForm): """ A typical feedback form """ form_fields = form.Fields(IFeedbackForm)
By inheriting from the
PageForm class, the
inherit functionality from formlib itself. By default,
knows how to generate all the HTML that will make up of a finished form.
But in order to do this, formlib needs to know what fields are wanted.
This is done by providing the form_fields attribute. The
constructor is a formlib helper class that generates the appropriate
field items from any Zope 3 schema (in this case, the schema interface
In order to provide a complete form, you need to specify the action to
perform when the "submit" button of the form (or any other indicated) is
activated. To define the action, use the
form.action decorator with
a handler function for the submitted data. More on actions later.
# use a dummy MailHost tool here to keep it simple class MHost: def __init__(self): pass def Send(self, sender, to, subject, body): pass class FeedbackForm(PageForm): """ A typical feedback form """ form_fields = form.Fields(IFeedbackForm) result_template = ViewPageTemplateFile('feedback_result.pt') @form.action("send") def action_send(self, action, data): mhost = MHost() self.mFrom = data['customer'] self.mTo = "email@example.com" self.mSubject = data['subject'] self.mBody = data['message'] mhost.Send(self.mFrom, self.mTo, self.mSubject, self.mBody) return self.result_template()
This is where the real work takes place. In this example, the
feedback_result.pt page template is rendered and returned. All the
view's attributes will be available inside this template, which will be
An example result form is:
<html metal:use-macro="context/@@standard_macros/view"> <head> </head> <body> <div metal:fill-slot="body"> <h1 tal:content="view/label">Form label</h1> <p>Thank you for your request about <span tal:replace="view/mSubject">subject</span>, <span tal:replace="view/mFrom">customer@mail</span>.</p> <p>We will reply to it shortly.</p> </div> </body> </html>
zope.formlib already includes a default general page form template, with
the fields labels, the widgets structures and the submit buttons, so you
only have to register your form page with the appropiate ZCML snippet in
order to make it accesible from a browser. Assuming you've placed your
code into a file named
<browser:page name="feedback" for="Products.CMFPlone.Portal.PloneSite" class=".browser.FeedbackForm" permission="zope.Public" />
Let's explain what this ZCML snippet means:
forattribute indicates the class or interface this view will be available for; in this case, it will be shown only from the root of a Plone site. To see the interfaces provided by a certain object, fire up the ZMI, navigate up to your object and check the Interfaces tab.
nameattribute sets the name of the view, so the form will be available from a URL with the form
classattribute indicates the view class responsible for displaying the page form, in this case, the FeedbackForm class inside the
permissionattribute specify the permission needed to access the page.
Among the most used permissions you can find:
zope.Public- no restrictions, available to everyone.
zope.View- permission to view this component.
zope.ManageContent- add, edit and delete content objects.
Note: Keen readers will notice the special name for configuring the new view component, browser:page. This XML tag actually employs an XML namespace prefix which needs to be defined. Normally this is added right onto the configure tag like this:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" xmlns:five="http://namespaces.zope.org/five">
And that's all! Here's how the form and result pages will look like:** **