Make it possible to install custom contact forms anywhere

This commit is contained in:
Frédéric Péters 2011-08-01 16:25:45 +02:00
parent f44db052ba
commit f4032b7a69
7 changed files with 160 additions and 24 deletions

View File

@ -1,29 +1,33 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:grok="http://namespaces.zope.org/grok"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
xmlns:five="http://namespaces.zope.org/five"
xmlns:i18n="http://namespaces.zope.org/i18n"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="tabellio.contact">
<!-- Include configuration for dependencies listed in setup.py -->
<includeDependencies package="." />
<!-- Grok the package to initialise schema interfaces and content classes -->
<grok:grok package="." />
<five:registerPackage package="." initialize=".initialize" />
<browser:page
name="contact"
for="Products.CMFPlone.interfaces.siteroot.IPloneSiteRoot"
class=".form.ContactForm"
permission = "zope.Public" />
<!-- Captcha factory -->
<adapter
for="Products.CMFPlone.interfaces.siteroot.IPloneSiteRoot"
provides=".form.IContactForm"
factory=".form.Contact" />
<!-- Register an extension profile to make the product installable -->
<genericsetup:registerProfile
name="default"
title="Tabellio Contact Support"
description="..."
directory="profiles/default"
provides="Products.GenericSetup.interfaces.EXTENSION"
/>
<!-- Captcha validator -->
<adapter
factory=".form.CaptchaValidator"
/>
</configure>

View File

@ -1,20 +1,46 @@
from five import grok
from Acquisition import aq_inner
from zope.interface import implements
from zope import interface
from zope import schema
from zope.component import getMultiAdapter, provideAdapter
from Products.CMFCore.utils import getToolByName
from z3c.form import form, field, button, validator
from plone.z3cform.layout import wrap_form
from z3c.form.ptcompat import ViewPageTemplateFile
from plone.dexterity.content import Item
from plone.formwidget.captcha.widget import CaptchaFieldWidget
from plone.formwidget.captcha.validator import CaptchaValidator
from tabellio.agenda.interfaces import MessageFactory as _
class IContactForm(interface.Interface):
title = schema.TextLine(title=_(u'Title'))
description = schema.Text(title=_(u'Description'))
subjects = schema.Text(title=_(u'Available Subjects'))
class ContactForm(Item):
implements(IContactForm)
class View(grok.View):
grok.context(IContactForm)
grok.require('zope2.View')
def contact_form(self):
effective_contact = EffectiveContact(self.context)
effective_form = EffectiveContactForm(effective_contact, self.request)
effective_form.update()
return effective_form.render()
class IEffectiveContact(interface.Interface):
subject = schema.TextLine(title=_(u'Subject'), required=True)
name = schema.TextLine(title=_(u'Name'), required=True)
email = schema.TextLine(title=_(u'Name'), required=True)
@ -22,26 +48,35 @@ class IContactForm(interface.Interface):
message = schema.Text(title=_(u'Message'), required=True)
captcha = schema.TextLine(title=u'Captcha', required=False)
class Contact(object):
def __init__(self, context):
class EffectiveContact(object):
implements(IEffectiveContact)
def __init__(self, context=None):
self.context = context
class BaseForm(form.Form):
fields = field.Fields(IContactForm)
def absolute_url(self):
# this is used by the captcha
return self.context.absolute_url()
class EffectiveContactForm(form.Form):
fields = field.Fields(IEffectiveContact)
fields['captcha'].widgetFactory = CaptchaFieldWidget
template = ViewPageTemplateFile('form_templates/view_effectivecontact.pt')
@button.buttonAndHandler(u'Send')
def handleApply(self, action):
data, errors = self.extractData()
if data.has_key('captcha'):
if not errors and data.has_key('captcha'):
# Verify the user input against the captcha
captcha = CaptchaValidator(self.context, self.request, None, IContactForm['captcha'], None)
if data.has_key('subject') and captcha.validate(data['captcha']):
# if captcha validation passes, print the subject
print data['subject']
captcha = CaptchaValidator(self.context, self.request, None, IEffectiveContact['captcha'], None)
if captcha.validate(data['captcha']):
# if captcha validation passes, send the email.
plone_utils = getToolByName(self.context.context, 'plone_utils')
plone_utils.addPortalMessage(_('Your message has been sent successfully.'))
return self.request.response.redirect('./')
return
ContactForm = wrap_form(BaseForm)
# Register Captcha validator for the captcha field in the IContactForm
validator.WidgetValidatorDiscriminators(CaptchaValidator, field=IContactForm['captcha'])
validator.WidgetValidatorDiscriminators(CaptchaValidator, field=IEffectiveContact['captcha'])

View File

@ -0,0 +1,28 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
lang="en"
metal:use-macro="context/main_template/macros/master"
i18n:domain="tabellio.contact">
<body>
<metal:main fill-slot="main">
<tal:main-macro metal:define-macro="main"
tal:define="toLocalizedTime nocall:context/@@plone/toLocalizedTime">
<div tal:replace="structure provider:plone.abovecontenttitle" />
<h1 class="documentFirstHeading" tal:content="context/title" />
<div tal:replace="structure provider:plone.belowcontenttitle" />
<div tal:replace="structure view/contact_form"/>
<div tal:replace="structure provider:plone.belowcontentbody" />
</tal:main-macro>
</metal:main>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:tal="http://xml.zope.org/namespaces/tal">
<body>
<form method="post" action="." tal:attributes="action context/absolute_url">
<metal:use use-macro="context/@@ploneform-macros/fields" />
<metal:use use-macro="context/@@ploneform-macros/actions" />
</form>
</body>
</html>

View File

@ -0,0 +1,6 @@
<metadata>
<version>1</version>
<dependencies>
<dependency>profile-plone.app.dexterity:default</dependency>
</dependencies>
</metadata>

View File

@ -0,0 +1,3 @@
<object name="portal_types">
<object name="tabellio.contact.form" meta_type="Dexterity FTI" />
</object>

View File

@ -0,0 +1,50 @@
<?xml version="1.0"?>
<object name="tabellio.contact.form" meta_type="Dexterity FTI"
i18n:domain="tabellio.contact" xmlns:i18n="http://xml.zope.org/namespaces/i18n">
<!-- Basic metadata -->
<property name="title" i18n:translate="">Contact Form</property>
<property name="description" i18n:translate="">A contact form</property>
<property name="content_icon">document_icon.gif</property>
<property name="allow_discussion">False</property>
<property name="global_allow">True</property>
<property name="filter_content_types">False</property>
<property name="allowed_content_types"/>
<!-- schema interface -->
<property name="schema">tabellio.contact.form.IContactForm</property>
<!-- class used for content items -->
<property name="klass">tabellio.contact.form.ContactForm</property>
<!-- add permission -->
<property name="add_permission">cmf.AddPortalContent</property>
<!-- enabled behaviors -->
<property name="behaviors">
<element value="plone.app.content.interfaces.INameFromTitle" />
</property>
<!-- View information -->
<property name="default_view">view</property>
<property name="default_view_fallback">False</property>
<property name="view_methods">
<element value="view"/>
</property>
<!-- Method aliases -->
<alias from="(Default)" to="(dynamic view)"/>
<alias from="edit" to="@@edit"/>
<alias from="sharing" to="@@sharing"/>
<alias from="view" to="(selected layout)"/>
<!-- Actions -->
<action title="View" action_id="view" category="object" condition_expr=""
url_expr="string:${object_url}" visible="True">
<permission value="View"/>
</action>
<action title="Edit" action_id="edit" category="object" condition_expr=""
url_expr="string:${object_url}/edit" visible="True">
<permission value="Modify portal content"/>
</action>
</object>