[silva.infrae.contact][Emiliano D'Alterio] First version of embe...

[email protected] Wed, 09 Oct 2013 10:23:30 +0200
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Emiliano D'Alterio
date:      Wed Oct 09 10:23:00 2013 +0200
revision:  0:93ba4ba697b2 in silva.infrae.contact
branch:    
details:   https://hg.infrae.com/silva.infrae.contact?cmd=changeset;node=93ba4ba697b2
modified:  .hgignore README.txt docs/HISTORY.txt setup.py src/silva/__init__.py src/silva/infrae/__init__.py src/silva/infrae/contact/__init__.py src/silva/infrae/contact/configure.zcml src/silva/infrae/contact/contactform.py src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt src/silva/infrae/contact/static/contactform.css src/silva/infrae/contact/static/contactform.js
added:     .hgignore README.txt docs/HISTORY.txt setup.py src/silva/__init__.py src/silva/infrae/__init__.py src/silva/infrae/contact/__init__.py src/silva/infrae/contact/configure.zcml src/silva/infrae/contact/contactform.py src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt src/silva/infrae/contact/static/contactform.css src/silva/infrae/contact/static/contactform.js
removed:   
log:       First version of embeddable "contact us" form for the Infrae site.


diffstat:

 .hgignore                                                              |    7 +
 README.txt                                                             |   18 +
 setup.py                                                               |   44 +++
 src/silva/__init__.py                                                  |    9 +
 src/silva/infrae/__init__.py                                           |    9 +
 src/silva/infrae/contact/__init__.py                                   |    9 +
 src/silva/infrae/contact/configure.zcml                                |   13 +
 src/silva/infrae/contact/contactform.py                                |  115 ++++++++++
 src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt |   58 +++++
 src/silva/infrae/contact/static/contactform.css                        |   25 ++
 src/silva/infrae/contact/static/contactform.js                         |   38 +++
 11 files changed, 345 insertions(+), 0 deletions(-)

diffs (390 lines):

diff -r 000000000000 -r 93ba4ba697b2 .hgignore
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,7 @@
+.pyo
+.pyc
+.egg-info
+.cpt.py
+.DS_Store
+~
+#
diff -r 000000000000 -r 93ba4ba697b2 README.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.txt	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,18 @@
+====================
+silva.infrae.contact
+====================
+
+Introduction
+============
+
+``silva.infrae.contact`` contains the public 
+"contact us" form for the Infrae site.
+You can embed this contact form in a template
+using: <tal:contactform tal:replace="structure provider:contactform" />
+
+Code repository
+===============
+
+This code can be found in Mercurial at:
+https://hg.infrae.com/silva.infrae.contact/.
+
diff -r 000000000000 -r 93ba4ba697b2 setup.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/setup.py	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2002-2010 Infrae. All rights reserved.
+# $Id$
+
+from setuptools import setup, find_packages
+import os
+
+version = '1.0'
+
+setup(name='silva.infrae.contact',
+      version=version,
+      description="Contact form for Infrae site",
+      long_description=open("README.txt").read() + "\n" + open(
+          os.path.join("docs", "HISTORY.txt")).read(),
+      classifiers=[
+          "Programming Language :: Python",
+          "Topic :: Software Development :: Libraries :: Python Modules",
+          ],
+      keywords='infrae contact form',
+      author='Infrae',
+      author_email='[email protected]',
+      url='http://infrae.com/products/silva',
+      license='BSD',
+      package_dir={'': 'src'},
+      packages=find_packages('src'),
+      namespace_packages=['silva'],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          'five.grok',
+          'zope.interface',
+          'zope.schema',
+          'zope.publisher',
+          'z3c.schema',
+          'zeam.form.silva',
+          'Products.Silva',
+          'silva.translations',
+          'silva.core.interfaces',
+          'silva.core.conf',
+          'silva.fanstatic',
+          'megrok.pagetemplate',
+          'js.jquery',
+          ],
+      )
diff -r 000000000000 -r 93ba4ba697b2 src/silva/__init__.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/__init__.py	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)
diff -r 000000000000 -r 93ba4ba697b2 src/silva/infrae/__init__.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/infrae/__init__.py	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)
diff -r 000000000000 -r 93ba4ba697b2 src/silva/infrae/contact/__init__.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/infrae/contact/__init__.py	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)
diff -r 000000000000 -r 93ba4ba697b2 src/silva/infrae/contact/configure.zcml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/infrae/contact/configure.zcml	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,13 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:grok="http://namespaces.zope.org/grok"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="silva">
+
+  <include package="five.grok" />
+
+  <!-- Grok package -->
+  <grok:grok package="." />
+
+</configure>
+
diff -r 000000000000 -r 93ba4ba697b2 src/silva/infrae/contact/contactform.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/infrae/contact/contactform.py	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 Infrae. All rights reserved.
+# $Id$
+
+from five import grok
+from zope import interface, schema
+from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+from z3c.schema.email import RFC822MailAddress
+from zeam.form import silva as silvaforms
+from zeam.form.viewlet.interfaces import IInlineForm
+from Products.Silva.mail import sendmail
+from silva.translations import translate as _
+from silva.core.interfaces import IViewableObject
+from silva.core import conf as silvaconf
+from silva.fanstatic import need
+from megrok import pagetemplate as pt
+from js.jquery import jquery
+
+
+class IContactFormResources(IDefaultBrowserLayer):
+    """CSS and JS for the contact form.
+    """
+    silvaconf.resource('contactform.css')
+    silvaconf.resource('contactform.js')
+
+
+## Choices available for the subject in the contact form
+subjects = SimpleVocabulary([
+    SimpleTerm(value='Support Request', token='support',
+               title=u'Professional Support'),
+    SimpleTerm(value='Training Request', token='training',
+               title=u'Technical Training'),
+    SimpleTerm(value='Question', token='question',
+               title=u'Ask us a question'),
+    SimpleTerm(value='Feedback', token='feedback',
+               title=u'Give us your feedback'), ])
+
+
+class IContactFormFields(interface.Interface):
+    """Fields present in the contact form.
+    """
+    name = schema.TextLine(
+        title=u'Full Name', required=True)
+    company = schema.TextLine(
+        title=u'Company', required=False)
+    email = RFC822MailAddress(
+        title=u'Email', required=True)
+    phone = schema.TextLine(
+        title=u'Phone', required=False)
+    subject = schema.Choice(
+        title=u'Message subject', source=subjects, required=False)
+    message = schema.Text(
+        title=u'Message', required=True)
+
+
+EMAIL_TEMPLATE = u"""
+{subject} from {name}
+
+{message}
+
+From: {name}
+Company: {company}
+Email: {email}
+Telephone: {phone}
+"""
+
+
+class IPublicContactForm(IInlineForm):
+    """The public embeddable contact form.
+    """
+
+
+class ContactFormTemplate(pt.PageTemplate):
+    """Template for the public contact form.
+    """
+    pt.view(IPublicContactForm)
+
+
+class ContactForm(silvaforms.PublicContentProviderForm):
+    """The public 'contact us' form.
+    """
+    grok.name('contactform')
+    grok.context(IViewableObject)
+    grok.implements(IPublicContactForm)
+
+    label = u'Contact us'
+    fields = silvaforms.Fields(IContactFormFields)
+    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')
+    def contact(self):
+        data, errors = self.extractData()
+        if errors:
+            self.status = u'There are errors, please check again.'
+            return silvaforms.FAILURE
+
+        msg = EMAIL_TEMPLATE.format(**data.getDictWithDefault())
+
+        sub = '%s from %s' % (data['subject'], data['name'])
+
+        sendmail(self.context, msg, mto='[email protected]',
+                 mfrom='[email protected]', subject=sub)
+
+        self.status = _(u'Thank you for your message.')
+        return silvaforms.SUCCESS
+
+    def update(self):
+        need(jquery)
+        need(IContactFormResources)
+        return super(ContactForm, self).update()
diff -r 000000000000 -r 93ba4ba697b2 src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/infrae/contact/contactform_templates/contactformtemplate.cpt	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,58 @@
+<div id="contactus-wrapper">
+<form action="#" tal:attributes="action request.URL;
+                                 name form.prefix or None;
+                                 id form.prefix or None"
+      method="post"
+      enctype="multipart/form-data"
+      i18n:domain="zeam.form.base">
+
+  <h3 tal:condition="form.label"
+      tal:content="form.label">Label</h3>
+
+  <p tal:condition="form.description"
+     tal:content="form.description">Description</p>
+
+  <p class="form-status"
+     tal:condition="form.status"
+     tal:content="form.status">Status</p>
+
+  <div class="form-error"
+       tal:condition="form.errors">
+    <ul tal:condition="form.formErrors"
+        tal:repeat="error form.formErrors">
+      <li> <span tal:replace="error.title" /> </li>
+    </ul>
+  </div>
+
+  <div class="fields"
+       tal:condition="form.fieldWidgets">
+    <div class="field" tal:repeat="widget form.fieldWidgets">
+
+
+      <br />
+      <tal:description tal:condition="widget.description">
+        <span class="field-description"
+              tal:content="widget.description">
+        </span><br />
+      </tal:description>
+      <tal:error tal:define="error widget.error"
+                 tal:condition="error">
+        <span class="field-error"
+              tal:content="error.title">
+          Error
+        </span>
+        <br />
+      </tal:error>
+      <tal:widget tal:content="structure widget.render()" />
+    </div>
+  </div>
+
+  <div class="actions"
+       tal:condition="form.actionWidgets">
+    <div class="action" tal:repeat="widget form.actionWidgets">
+      <tal:widget tal:content="structure widget.render()" />
+    </div>
+  </div>
+
+</form>
+</div>
\ No newline at end of file
diff -r 000000000000 -r 93ba4ba697b2 src/silva/infrae/contact/static/contactform.css
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/infrae/contact/static/contactform.css	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,25 @@
+#contactus-wrapper {
+    color: #fff;
+    background: #222;
+    padding: 10px;
+    border: 2px solid #ddd;
+    margin: 10px 0;
+    float: left;
+    font-size: 1.2em;
+    -moz-box-shadow: 0 0 20px #999;
+    -webkit-box-shadow: 0 0 20px #999;
+    box-shadow: 0 0 20px #999;
+    -moz-border-radius: 3px;
+    -webkit-border-radius: 3px;
+    border-radius: 3px;
+}
+
+#contactus-wrapper input,
+#contactus-wrapper textarea {
+    width: 98%;
+    margin: auto;
+}
+
+#contactus-wrapper h3 {
+    color: #fff;
+}
diff -r 000000000000 -r 93ba4ba697b2 src/silva/infrae/contact/static/contactform.js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/infrae/contact/static/contactform.js	Wed Oct 09 10:23:00 2013 +0200
@@ -0,0 +1,38 @@
+// We have to "emulate" placeholder attribute behaviour
+// for browser that don't support it (e.g. internet explorer less than version 10)
+(function(){
+    $(document).ready(function(){
+
+        var placeholder_support = (function has_placeholder_support() {
+            var input = document.createElement('input');
+            return ('placeholder' in input);
+        })();
+
+        if(placeholder_support === false){
+            var placeholder = '';
+            var value = '';
+            $('#contactus-wrapper input, #contactus-wrapper textarea').each(function(){
+                placeholder = $(this).attr('placeholder');
+                $(this).css('color', '#888');
+                $(this).attr('value', placeholder);
+
+                $(this).on('keypress', function(){
+                    placeholder = $(this).attr('placeholder');
+                    value = $(this).attr('value');
+                    if(value == placeholder){
+                        $(this).attr('value', '');
+                        $(this).css('color', '#222');
+                    }
+                });
+                $(this).on('focusout', function(){
+                    value = $(this).attr('value');
+                    if(value === ''){
+                        placeholder = $(this).attr('placeholder');
+                        $(this).css('color', '#888');
+                        $(this).attr('value', placeholder);
+                    }
+                });
+            });
+        }
+    });
+})();