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 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
class inherit functionality from formlib itself. By default,
PageForm 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
Fields constructor is a formlib helper class
that generates the appropriate field items from any Zope 3 schema
(in this case, the schema interface defined above).
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 = "firstname.lastname@example.org" 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 introduced later.
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: