[silva.infrae.contact][Emiliano D'Alterio] Added session managem...

[email protected] Tue, 15 Oct 2013 15:26:10 +0200
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Emiliano D'Alterio
date:      Tue Oct 15 15:25:57 2013 +0200
revision:  6:fa0679abc51d in silva.infrae.contact
branch:    
details:   https://hg.infrae.com/silva.infrae.contact?cmd=changeset;node=fa0679abc51d
modified:  src/silva/infrae/contact/contactform.py src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt src/silva/infrae/contact/static/contactform.css
added:     
removed:   
log:       Added session management to fight spam-bots. Changed field names to
	be less identifiable. Small changes to CSS and template.


diffstat:

 src/silva/infrae/contact/contactform.py                                |  111 +++++++--
 src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt |   10 +-
 src/silva/infrae/contact/static/contactform.css                        |    5 +-
 3 files changed, 98 insertions(+), 28 deletions(-)

diffs (230 lines):

diff -r 6749166da5be -r fa0679abc51d src/silva/infrae/contact/contactform.py
--- a/src/silva/infrae/contact/contactform.py	Fri Oct 11 19:12:22 2013 +0200
+++ b/src/silva/infrae/contact/contactform.py	Tue Oct 15 15:25:57 2013 +0200
@@ -2,9 +2,14 @@
 # Copyright (c) 2013 Infrae. All rights reserved.
 # $Id$
 
+
+import uuid
+
 from five import grok
 from zope import interface, schema
 from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
+from zope.traversing.browser import absoluteURL
+from silva.core.cache import store
 from zope.publisher.interfaces.browser import IDefaultBrowserLayer
 from z3c.schema.email import RFC822MailAddress
 from zeam.form import silva as silvaforms
@@ -28,42 +33,56 @@
 
 ## Choices available for the subject in the contact form
 subjects = SimpleVocabulary([
-    SimpleTerm(value='Support Request', token='support',
+    SimpleTerm(value=u'Support Request',
+               token=u'support',
                title=u'Professional Support'),
-    SimpleTerm(value='Training Request', token='training',
+    SimpleTerm(value=u'Training Request',
+               token=u'training',
                title=u'Technical Training'),
-    SimpleTerm(value='Question', token='question',
+    SimpleTerm(value=u'Question',
+               token=u'question',
                title=u'Ask us a question'),
-    SimpleTerm(value='Feedback', token='feedback',
+    SimpleTerm(value=u'Feedback',
+               token=u'feedback',
                title=u'Give us your feedback'), ])
 
 
 class IContactFormFields(interface.Interface):
     """Fields present in the contact form.
     """
+    sid = schema.TextLine(
+        title=u'Sid',
+        required=True)
     name = schema.TextLine(
-        title=u'Full Name', required=True)
+        title=u'Full Name',
+        required=True)
     company = schema.TextLine(
-        title=u'Company', required=False)
+        title=u'Company',
+        required=False)
     email = RFC822MailAddress(
-        title=u'Email', required=True)
+        title=u'Email',
+        required=True)
     phone = schema.TextLine(
-        title=u'Phone', required=False)
+        title=u'Phone',
+        required=False)
     subject = schema.Choice(
-        title=u'Message subject', source=subjects, required=False)
+        title=u'Message subject',
+        source=subjects,
+        required=False)
     message = schema.Text(
-        title=u'Message', required=True)
+        title=u'Message',
+        required=True)
 
 
 EMAIL_TEMPLATE = u"""
-{subject} from {name}
+{st} from {ne}
 
-{message}
+{me}
 
-From: {name}
-Company: {company}
-Email: {email}
-Telephone: {phone}
+From: {ne}
+Company: {cy}
+Email: {el}
+Telephone: {pe}
 """
 
 
@@ -85,34 +104,74 @@
     grok.context(IViewableObject)
     grok.implements(IPublicContactForm)
 
-    label = u'Contact us'
+    label = _(u'Contact us')
+
     sent = False
+    expired = False
+    refresh_url = None
+    sstore = None
 
     fields = silvaforms.Fields(IContactFormFields)
+    fields['sid'].htmlAttributes['style'] = 'display: none;'
+    fields['sid'].mode = silvaforms.HIDDEN
+    fields['sid'].ignoreRequest = True
+
     fields['name'].htmlAttributes['placeholder'] = _(u'Your full name*')
     fields['company'].htmlAttributes['placeholder'] = _(u'Your company name')
     fields['email'].htmlAttributes['placeholder'] = _(u'Your email address*')
     fields['phone'].htmlAttributes['placeholder'] = _(u'Your phone number')
     fields['message'].htmlAttributes['placeholder'] = _(u'Your message to us*')
 
-    @silvaforms.action(u"Send", identifier='contact', accesskey='c')
+    ## Changing "name" attribute to something less understandable by spam-bots.
+    fields['name'].identifier = 'ne'   # Name.
+    fields['company'].identifier = 'cy'   # Company.
+    fields['email'].identifier = 'el'   # Email.
+    fields['phone'].identifier = 'pe'   # Phone.
+    fields['subject'].identifier = 'st'   # Subject.
+    fields['message'].identifier = 'me'   # Message.
+
+    def update(self):
+        need(IContactFormResources)
+        if self.sstore is None:
+            self.sstore = store.SessionStore(self.request)
+
+        return super(ContactForm, self).update()
+
+    def updateWidgets(self):
+        sid = uuid.uuid4()
+        self.sstore.set('sid', sid)
+        self.fields['sid'].defaultValue = sid
+
+        super(ContactForm, self).updateWidgets()
+
+    @silvaforms.action(_(u'Send'), identifier='contact', accesskey='c')
     def contact(self):
         data, errors = self.extractData()
         if errors:
-            self.status = u'There are errors, please check again.'
+            self.status = _(u'There are errors, please check again.')
             return silvaforms.FAILURE
 
-        msg = EMAIL_TEMPLATE.format(**data.getDictWithDefault())
+        data_dict = data.getDictWithDefault()
+        for key in data_dict:
+            if isinstance(data_dict[key], str):
+                data_dict[key] = unicode(data_dict[key], errors='replace')
 
-        sub = '%s from %s' % (data['subject'], data['name'])
+        sid_sent = data_dict['sid']
+        sid_expected = str(self.sstore.get('sid'))
+
+        if sid_sent != sid_expected:
+            self.status = _(u'Session expired.')
+            self.sent = True
+            self.expired = True
+            self.refresh_url = absoluteURL(self.context, self.request)
+            return silvaforms.FAILURE
+
+        msg = EMAIL_TEMPLATE.format(**data_dict)
+        sub = u'%s from %s' % (data['st'], data['ne'])
 
         sendmail(self.context, msg, mto='[email protected]',
                  mfrom='[email protected]', subject=sub)
 
+        self.status = _(u'Thank you for your message.')
         self.sent = True
-        self.status = _(u'Thank you for your message.')
         return silvaforms.SUCCESS
-
-    def update(self):
-        need(IContactFormResources)
-        return super(ContactForm, self).update()
diff -r 6749166da5be -r fa0679abc51d src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt
--- a/src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt	Fri Oct 11 19:12:22 2013 +0200
+++ b/src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt	Tue Oct 15 15:25:57 2013 +0200
@@ -15,6 +15,12 @@
   <p class="form-status"
      tal:condition="form.status"
      tal:content="form.status">Status</p>
+     
+ <p class="form-status">
+     <a tal:condition="form.expired"
+        tal:attributes="href python:'%s#contactus-wrapper' % (form.refresh_url)">Refresh page.
+     </a>
+ </p>
 
   <div class="form-error"
        tal:condition="form.errors">
@@ -27,7 +33,7 @@
   <div class="fields"
        tal:condition="form.fieldWidgets">
     <div class="field" tal:repeat="widget form.fieldWidgets">
-      <br />
+      
       <tal:error tal:define="error widget.error"
                  tal:condition="error">
         <span class="field-error"
@@ -36,7 +42,9 @@
         </span>
         <br />
       </tal:error>
+      
       <tal:widget tal:content="structure widget.render()" />
+      
     </div>
   </div>
 
diff -r 6749166da5be -r fa0679abc51d src/silva/infrae/contact/static/contactform.css
--- a/src/silva/infrae/contact/static/contactform.css	Fri Oct 11 19:12:22 2013 +0200
+++ b/src/silva/infrae/contact/static/contactform.css	Tue Oct 15 15:25:57 2013 +0200
@@ -17,7 +17,7 @@
 
 #contactus-wrapper .field {
     width: 95%;
-    margin: 0 5px;
+    margin: 5px;
 }
 
 #contactus-wrapper textarea {
@@ -45,3 +45,6 @@
     margin: 0 0.65em;
 }
 
+.form-status a:hover {
+    color: #fff;
+}