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