Plone form example with z3c.form
First off there was a load of talk of plone.directives but I didn't use this as I had buildout version issues and no time to figure out a KGS.
This is a simple form that will (when completed) email its data. This is not
creating any objects.
In the theme's browser folder, a python module called alertforms.py
from Products.CMFPlone import PloneMessageFactory as _
from zope.interface import Interface
from zope import schema
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
from z3c.form import form, field, button
from plone.z3cform.layout import wrap_form
from z3c.form.browser.radio import RadioFieldWidget
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
import logging
logger = logging.getLogger("alert form")
# define vocabularies for singleselection fields
deal_types = SimpleVocabulary(
[SimpleTerm(value=u'E', title=_(u'Early booking deals')),
SimpleTerm(value=u'L', title=_(u'Last minute deals')),
SimpleTerm(value=u'S', title=_(u'All season deals')),
SimpleTerm(value=u'A', title=_(u'All deals'))]
)
who_is_going = SimpleVocabulary(
[SimpleTerm(value=u'F', title=_(u'Family - require childcare and/or kids programmes')),
SimpleTerm(value=u'A', title=_(u'Adults - no childcare or kids programmes necessary'))]
)
# the fields to be displayed
class IDealAlert(Interface):
tour_deals = schema.Choice(
vocabulary=deal_types,
title=_(u'label_tour_op_deals', default=u'Tour operator deals'),
description=_(u'help_tour_op_deals',
default=u"Tour operator deal info "
"..."),
required=True,
)
other_deals = schema.Choice(
vocabulary=deal_types,
title=_(u'other_deals', default=u'Other supplier deals'),
description=_(u'help_other_deals',
default=u"e.g. Lift passes etc. "
"..."),
required=True,
)
country = schema.TextLine(
title=_(u'label_country', default=u'Country'),
description=_(u'help_country', default=u"Leave blank for any."),
required=False)
resort = schema.TextLine(
title=_(u'label_resort', default=u'Resort'),
description=_(u'help_resort', default=u"Leave blank for any."),
required=False)
flexible = schema.Bool(title=_(u'label_flexible',
default=u'I am flexible'),
required=False,
default=False)
who = schema.Choice(
vocabulary=who_is_going,
title=_(u'label_who', default=u'Who is going?'),
required=True)
adults_no = schema.Int(
title=_(u'label_adultsno', default=u'No. of adults'),
required=True)
children_no = schema.Int(
title=_(u'label_childno', default=u'No. of children under 11'),
required=False)
departuredate = schema.Date(
title=_(u'label_departuredate', default=u'When would you like to go on holiday?'),
description=_(u'help_departuredate',
default=u"Departure date +/- 2days. Leave blank if flexible."),
required=False)
alertsfrom = schema.Date(
title=_(u'label_alertsfrom', default=u'When would you like to start receiving alerts?'),
description=_(u'help_alertsfrom',
default=u"Leave blank for as soon as possible."),
required=False)
# the form view
# override the drop-downs with radiobutton widgets.
# some docs used updateFields() method for this but...
class DealAlertForm(form.Form):
fields = field.Fields(IDealAlert)
fields['tour_deals'].widgetFactory = RadioFieldWidget
fields['other_deals'].widgetFactory = RadioFieldWidget
fields['who'].widgetFactory = RadioFieldWidget
ignoreContext = True # don't use context to get widget data
label = _(u"Send a deal alert")
description = _(u"At Pistebook we want to make sure you find the ski holiday deal that you are looking for. So we've tried not to over complicate the information we need from you, this means you don't miss out on a great deal by being too specific on your requirements.")
# the form will be generated automatically but I wanted more control
# for styling so a defined a template
# some docs used 'index =...' and also hinted that auto placement
# of a dealalertform.pt in alertforms_templates/ would do it
# but only 'template = ...' seemed to work
template = ViewPageTemplateFile('templates/dealalertform.pt')
# some minor widget adjustment
def updateWidgets(self):
super(DealAlertForm, self).updateWidgets()
# Widgets
self.widgets['adults_no'].size = 2
self.widgets['children_no'].size = 2
# the submit buitton handler
@button.buttonAndHandler(u'Send deals')
def handleApply(self, action):
data, errors = self.extractData()
if errors:
return
# form data - do what you need to with it
# ie it will be emailed...
# if data.has_key('text'):
# print data['text'] # ... or do stuff
logger.info('handleApply form data:%s', data)
# if you want form wrapped in a new page
#DealAlertView = wrap_form(DealAlertForm)
# but I'm using a template so I don't
DealAlertView = DealAlertForm
In browser/templates I created the form template dealalertform.pt
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="example.dexterityforms"
metal:use-macro="context/main_template/macros/master">
<metal:block fill-slot="main">
<h1 class="documentFirstHeading" tal:content="view/label | nothing" />
<p>Welcome to this form.</p>
<tal:x replace="nothing">
this diplays the whole form if the customisation is minimal.
</tal:x
<div id="content-core">
<metal:block use-macro="context/@@ploneform-macros/titlelessform" />
</div>
<tal:x replace="nothing">
Couldn't get the /field macro to work. Used various
values for widget??
</tal:x>
<div tal:define="widget string:formfield-form-widgets-tour_deals">
<metal:block use-macro="context/@@ploneform-macros/field" />
</div>
<tal:x replace="nothing">
<tal:field tal:replace="structure view/widgets/tour_deals/@@ploneform-render-widget" />
</metal:block>
</html>
Also defined the <FORM> in the template using method="POST" which is the default for auto-forms (no template). Comment out the template attribute on the form and see what the autoform form tags look like.
Use an Finally the plumbing in browser/configure.zcml
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:plone="http://namespaces.plone.org/plone"
i18n_domain="bb.mrg">
...
<!-- form views -->
<browser:page
for="*"
name="deal-alerts"
class=".alertforms.DealAlertView"
layer=".interfaces.IThemeSpecific"
permission="zope2.View"
/>
...
</configure>
http://my-site/deal-alerts
Adios
If you are in a hurry then you can call us on 01980 556432 and we'll see if we can help.

