Merge pull request #467 from pcoder/task/3740/contact_send_message

Task/3657 contact section new design
Task/3740/contact section send email message
This commit is contained in:
Pcoder 2017-08-29 20:29:59 +02:00 committed by GitHub
commit 989420d71f
16 changed files with 424 additions and 81 deletions

View file

@ -1,6 +1,6 @@
from django import forms from django import forms
from .models import BetaAccess from .models import BetaAccess, ContactUs
class BetaAccessForm(forms.ModelForm): class BetaAccessForm(forms.ModelForm):
@ -11,6 +11,13 @@ class BetaAccessForm(forms.ModelForm):
model = BetaAccess model = BetaAccess
class ContactForm(forms.ModelForm):
class Meta:
fields = ['name', 'email', 'message']
model = ContactUs
# class BetaAccessVMForm(forms.ModelForm): # class BetaAccessVMForm(forms.ModelForm):
# type = forms.CharField(widget=forms.EmailInput()) # type = forms.CharField(widget=forms.EmailInput())

View file

@ -82,6 +82,24 @@ msgstr "Bitte gib eine gültige E-Mailadresse ein."
msgid "Continue" msgid "Continue"
msgstr "Weiter" msgstr "Weiter"
msgid "Thank you for contacting us."
msgstr "Nachricht gesendet."
msgid "Your message was successfully sent to our team."
msgstr "Vielen Dank für Deine Nachricht."
msgid "Get in touch with us!"
msgstr "Sende uns eine Nachricht."
msgid "Message"
msgstr "Nachricht"
msgid "Sorry, there was an unexpected error. Kindly retry."
msgstr "Bitte entschuldige, es scheint ein unerwarteter Fehler aufgetreten zu sein. Versuche es doch bitte noch einmal."
msgid "SUBMIT"
msgstr "ABSENDEN"
msgid "Thank you for your request." msgid "Thank you for your request."
msgstr "Vielen Dank für Ihre Anfrage." msgstr "Vielen Dank für Ihre Anfrage."
@ -234,15 +252,12 @@ msgstr ""
msgid "Affordable VM hosting based in Switzerland" msgid "Affordable VM hosting based in Switzerland"
msgstr "Bezahlbares VM Hosting in der Schweiz" msgstr "Bezahlbares VM Hosting in der Schweiz"
msgid "Contact us"
msgstr "Kontaktiere uns"
msgid "Switzerland " msgid "Switzerland "
msgstr "Schweiz" msgstr "Schweiz"
msgid "Questions?"
msgstr "Fragen?"
msgid "Contact us!"
msgstr "Kontaktiere uns!"
msgid "Confirm Order" msgid "Confirm Order"
msgstr "Bestellung Bestätigen" msgstr "Bestellung Bestätigen"
@ -412,6 +427,9 @@ msgstr "ist kein gültiger Name"
msgid "is not a proper email" msgid "is not a proper email"
msgstr "ist keine gültige E-Mailadresse" msgstr "ist keine gültige E-Mailadresse"
#~ msgid "Questions?"
#~ msgstr "Fragen?"
#~ msgid "Please enter a value greater than or equal to 1." #~ msgid "Please enter a value greater than or equal to 1."
#~ msgstr "Bitte gib einen Wert größer oder gleich 1 ein." #~ msgstr "Bitte gib einen Wert größer oder gleich 1 ein."

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-08-19 21:08
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0006_vmtemplate'),
]
operations = [
migrations.CreateModel(
name='ContactUs',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=250)),
('email', models.CharField(max_length=250)),
('message', models.TextField()),
],
),
]

View file

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-08-23 13:06
from __future__ import unicode_literals
import datetime
from django.db import migrations, models
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0007_contactus'),
]
operations = [
migrations.AddField(
model_name='contactus',
name='field',
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2017, 8, 23, 13, 6, 24, 650869, tzinfo=utc)),
preserve_default=False,
),
]

View file

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-08-27 07:55
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0007_contactus'),
('datacenterlight', '0008_auto_20170821_2024'),
]
operations = [
]

View file

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2017-08-27 08:02
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('datacenterlight', '0009_merge'),
('datacenterlight', '0008_contactus_field'),
]
operations = [
]

View file

@ -57,7 +57,8 @@ class VMTemplate(models.Model):
@classmethod @classmethod
def create(cls, name, opennebula_vm_template_id): def create(cls, name, opennebula_vm_template_id):
vm_template = cls(name=name, opennebula_vm_template_id=opennebula_vm_template_id) vm_template = cls(
name=name, opennebula_vm_template_id=opennebula_vm_template_id)
return vm_template return vm_template
@ -71,3 +72,10 @@ class StripePlan(models.Model):
def create(cls, stripe_plan_id): def create(cls, stripe_plan_id):
stripe_plan = cls(stripe_plan_id=stripe_plan_id) stripe_plan = cls(stripe_plan_id=stripe_plan_id)
return stripe_plan return stripe_plan
class ContactUs(models.Model):
name = models.CharField(max_length=250)
email = models.CharField(max_length=250)
message = models.TextField()
field = models.DateTimeField(auto_now_add=True)

View file

@ -323,9 +323,9 @@ button, input, optgroup, select, textarea {
padding-top: 50px; padding-top: 50px;
/* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */ /* If you're making other pages, make sure there is 50px of padding to make sure the navbar doesn't overlap content! */
padding-bottom: 50px; padding-bottom: 50px;
text-align: center; /* text-align: center; */
color: #f8f8f8; color: #f8f8f8;
background: url(../img/banner-bg.jpg) no-repeat center center; background: url(../img/pattern.jpg) no-repeat center center;
background-size: cover; background-size: cover;
position: relative; position: relative;
} }
@ -654,74 +654,161 @@ button, input, optgroup, select, textarea {
position: relative; position: relative;
} }
.full-contact-section {
background-image: -ms-linear-gradient(right, #29427A 50%, #4F6699 100%);
background-image: -moz-linear-gradient(right, #29427A 50%, #4F6699 100%);
background-image: -o-linear-gradient(right, #29427A 50%, #4F6699 100%);
background-image: -webkit-gradient(linear, right top, left top, color-stop(50, #29427A), color-stop(100, #4F6699));
background-image: -webkit-linear-gradient(right, #29427A 50%, #4F6699 100%);
background-image: linear-gradient(to left, #29427A 50%, #4F6699 100%);
}
.contact-section { .contact-section {
padding: 60px 0; padding: 80px 0;
color: #fff; color: rgba(255,255,255,0.9);
background-attachment: fixed; background-attachment: fixed;
} }
.contact-section .card { .contact-section .modal {
text-align: center; color: #333;
width: 350px;
margin: 0 auto;
background: #fff;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
padding-bottom: 40px;
border-radius: 7px;
color: #4c4444;
box-sizing: border-box;
padding: 45px;
margin-top: -115px;
} }
.contact-section .card .social a { .contact-details {
color: #29427A; padding-left: 5px;
}
.contact-section .description{
font-size: 20px;
}
.contact-section .social a {
color: #fff;
font-size: 45px; font-size: 45px;
} }
.contact-section .card .subtitle h3 { .contact-section .social .fa-facebook {
font-size: 30px; font-size: 40px;
margin-bottom: 23px; background: #fff;
border-radius: 100%;
color: #425d89;
width: 40px;
text-align: center;
top: -2px;
position: relative;
left: 10px;
}
.contact-section .social .fa-facebook:before {
font-size: 32px;
position: relative;
top: -1px;
left: -1px;
} }
.contact-section .card .social a:hover { .contact-section .social a:hover {
text-decoration: none; text-decoration: none;
} }
.contact-section .title { .contact-section .subtitle h3 {
margin-right: auto; font-size: 30px;
width: 80%; margin-bottom: 15px;
max-width: 468px; }
.contact-section .contact-form-success {
font-size: 18px;
text-align: center;
background-color: rgba(0,0,0,0.2);
padding: 0 15px 35px;
margin-top: 25px;
} }
.contact-section .title h2 { .contact-section .title h2 {
font-size: 65px; font-size: 65px;
margin: 0; margin: 0;
color: #fff;
padding-bottom: 25px;
position: relative; position: relative;
text-align: right; /* color: #eee;
padding-bottom: 25px;
text-align: right; */
} }
.contact-section .title h2::before { .contact-form .form-group {
content: ""; border: 0;
margin-bottom: 20px;
}
.contact-form .form-group label {
letter-spacing: 0.6px;
font-weight: 400;
}
.contact-form .btn {
min-width: 140px;
background: rgba(23, 23, 23, 0.18);
color: #fff;
border-radius: 4px;
border-width: 2px;
box-shadow: none;
letter-spacing: 2px;
border-color: #fff;
}
.contact-form .btn.sending {
cursor: wait;
}
@keyframes sending {
0% {content: '.';}
50% {content: '..';}
100% {content: '...';}
}
.contact-form .btn.sending:after {
content: '.';
position: absolute; position: absolute;
bottom: 0; display: inline-block;
background: #fff; text-align: left;
height: 7px; margin-left: 5px;
width: 70px; width: 20px;
right: 0; animation: sending 1s linear infinite;
} }
.contact-form .btn:hover,
.contact-form .btn:focus {
background: rgba(23, 23, 23, 0.28);
border-color: #fff;
box-shadow: none;
outline: 0;
}
.contact-form .form-control {
box-shadow: none;
border-color: #ccc;
}
.contact-form .errorlist {
list-style: none;
padding: 5px;
margin: 0;
color: rgb(255, 164, 164);
font-weight: 600;
letter-spacing: 0.4px;
}
.contact-form .form-error {
background: rgba(255,255,255,0.9);
color: #eb4d5c;
padding: 10px;
text-align: center;
margin-bottom: 20px;
border-radius: 5px;
}
.contact-form .has-error label {
color: #fff;
}
.contact-form .has-error .form-control {
border: 2px solid #e8534b;
box-shadow: none;
}
.contact-form .subtitle {
padding: 22px 0 15px;
}
.contact-form textarea {
resize: none;
}
/*Why DCL*/ /*Why DCL*/
@ -1311,9 +1398,9 @@ tech-sub-sec h2 {
margin: 0 auto; margin: 0 auto;
} }
.contact-section .title h2 { .contact-section .title h2 {
font-size: 35px; font-size: 45px;
line-height: 40px; line-height: 40px;
text-align: center; /* text-align: center; */
margin-top: 35px; margin-top: 35px;
} }
.contact-section .title h2::before { .contact-section .title h2::before {
@ -1558,4 +1645,4 @@ a.list-group-item-danger.active:focus {
} }
.panel-danger > .panel-heading .badge { .panel-danger > .panel-heading .badge {
background-color: #eb4d5c; background-color: #eb4d5c;
} }

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="57px" height="66px" viewBox="0 0 57 66" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Slice 20</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="contact-us" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<ellipse id="Oval-2" fill="#FFFFFF" cx="28.7865939" cy="33.4691264" rx="19.7865939" ry="19.4691264"></ellipse>
<path d="M35.3784886,34.6387051 L30.2336176,34.6387051 L30.2336176,50.2467762 L22.6226844,50.2467762 L22.6226844,34.6387051 L19,34.6387051 L19,29.1194625 L22.6226844,29.1194625 L22.6226844,25.5403791 C22.6226844,22.9849851 24.0459888,19 30.3115248,19 L35.9567996,19.0178699 L35.9567996,24.3762836 L31.8546864,24.3762836 C31.1894789,24.3762836 30.2426069,24.6596489 30.2426069,25.8824599 L30.2426069,29.1194625 L36.0436961,29.1194625 L35.3784886,34.6387051 Z" id="Shape" fill="#5E79AD" fill-rule="nonzero"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -39,7 +39,7 @@
_initScroll(); _initScroll();
_initNavUrl(); _initNavUrl();
_initPricing(); _initPricing();
ajaxForms();
}); });
$(window).resize(function() { $(window).resize(function() {
@ -157,4 +157,27 @@
$('#valueTotal').text(numbers * price * 31); $('#valueTotal').text(numbers * price * 31);
} }
})(jQuery); function ajaxForms() {
$('body').on('submit', '.ajax-form', function(e){
e.preventDefault();
var $form = $(this);
$form.find('[type=submit]').addClass('sending');
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
data: $form.serialize(),
success: function(response) {
var responseContain = $($form.attr('data-response'));
responseContain.html(response);
$form.find('[type=submit]').removeClass('sending');
},
error: function() {
$form.find('[type=submit]').removeClass('sending');
$form.find('.form-error').removeClass('hide');
}
});
})
}
})(jQuery);

View file

@ -0,0 +1,50 @@
{% load i18n %}
{% if success %}
<div class="contact-form-success">
<div class="subtitle text-center">
<h3>{% trans "Thank you for contacting us." %}</h3>
</div>
<p>
{% trans "Your message was successfully sent to our team." %}
</p>
</div>
{% else %}
<div class="row">
<div class="col-sm-offset-2 col-sm-10">
<div class="subtitle">
<h3>{% trans "Get in touch with us!" %}</h3>
</div>
</div>
</div>
<form class="form-horizontal ajax-form" method="POST" action="{% url 'datacenterlight:contact_us' %}" data-toggle="validator" data-response="#contact-form">
{% csrf_token %}
<div class="form-group">
<label class="control-label col-sm-2" for="name">{% trans "Name" %}</label>
<div class="col-sm-10">
<input type="text" name="name" class="form-control" data-minlength="3" data-error="{% trans 'Please enter your name.' %}" required>
{{contact_form.name.errors}}
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="email">{% trans "Email" %}</label>
<div class="col-sm-10">
<input name="email" type="email" pattern="^[^@\s]+@([^@\s]+\.)+[^@\s]+$" class="form-control" data-error="{% trans 'Please enter a valid email address.' %}" required>
{{contact_form.email.errors}}
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2" for="message">{% trans "Message" %}</label>
<div class="col-sm-10">
<textarea class="form-control" name="message" id="message" rows="6" required></textarea>
{{contact_form.message.errors}}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10 text-right">
<div class="form-error hide">{% trans "Sorry, there was an unexpected error. Kindly retry." %}</div>
<button type="submit" class="btn btn-default">{% trans "SUBMIT" %}</button>
</div>
</div>
</form>
{% endif %}

View file

@ -1,5 +1,5 @@
{% extends "datacenterlight/base.html" %} {% extends "datacenterlight/base.html" %}
{% load staticfiles i18n%} {% load staticfiles i18n %}
{% block content %} {% block content %}
@ -149,32 +149,34 @@
</div> </div>
<!-- / contact section --> <!-- / contact section -->
<div class="full-contact-section"> <div class="full-contact-section">
<div class="intro-header-2 contact-section" id="contact"> <div class="intro-header-2 contact-section" id="contact">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-sm-6">
<div class="col-sm-6 col-md-6"> <div class="title">
<div class="card"> <h2>{% trans "Contact us" %}</h2>
</div>
<div class="contact-details">
<div class="subtitle"> <div class="subtitle">
<h3>ungleich GmbH </h3> <h3>ungleich GmbH</h3>
</div> </div>
<div class="description"> <div class="description">
<p><i class="fa fa-envelope-o"></i> info@datacenterlight.ch</p> <p>info@datacenterlight.ch</p>
<p>In der Au 7, Schwanden 8762</p> <p>In der Au 7, Schwanden 8762</p>
<p>{% trans "Switzerland " %}</p> <p>{% trans "Switzerland " %}</p>
</div> </div>
<div class="social"> </div>
<a target="_blank" class="" href="https://twitter.com/datacenterlight"><i class="fa fa-twitter fa-fw"></i></a> <div class="social">
<a target="_blank" class="" href="https://github.com/ungleich"><i class="fa fa-github fa-fw"></i></a> <a target="_blank" class="" href="https://twitter.com/datacenterlight"><i class="fa fa-twitter fa-fw"></i></a>
<a target="_blank" class="" href="https://www.facebook.com/ungleich.ch/"><i class="fa fa-facebook fa-fw"></i></a> <a target="_blank" class="" href="https://github.com/ungleich"><i class="fa fa-github fa-fw"></i></a>
</div> <a target="_blank" class="" href="https://www.facebook.com/ungleich.ch/"><i class="fa fa-facebook"></i></a>
</div> </div>
</div> </div>
<div class="col-sm-6 col-md-6"> <div class="col-sm-6">
<div class="title"> <div id="contact-form" class="contact-form">
<h2>{% trans "Questions?" %} {% trans "Contact us!" %}</h2> {% include "datacenterlight/contact_form.html" %}
</div> </div>
</div> </div>
</div> </div>

View file

@ -6,10 +6,8 @@ import stripe
from celery.result import AsyncResult from celery.result import AsyncResult
from django.conf import settings from django.conf import settings
from django.core.management import call_command from django.core.management import call_command
# Create your tests here.
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from model_mommy import mommy from model_mommy import mommy
from datacenterlight.models import VMTemplate from datacenterlight.models import VMTemplate
from datacenterlight.tasks import create_vm_task from datacenterlight.tasks import create_vm_task
from membership.models import StripeCustomer from membership.models import StripeCustomer

View file

@ -1,17 +1,20 @@
from django.conf.urls import url from django.conf.urls import url
from .views import IndexView, BetaProgramView, LandingProgramView, BetaAccessView, PricingView, SuccessView, \ from .views import IndexView, BetaProgramView, LandingProgramView, BetaAccessView, PricingView, SuccessView, \
PaymentOrderView, OrderConfirmationView, WhyDataCenterLightView PaymentOrderView, OrderConfirmationView, WhyDataCenterLightView, ContactUsView
urlpatterns = [ urlpatterns = [
url(r'^$', IndexView.as_view(), name='index'), url(r'^$', IndexView.as_view(), name='index'),
url(r'^whydatacenterlight/?$', WhyDataCenterLightView.as_view(), name='whydatacenterlight'), url(r'^whydatacenterlight/?$', WhyDataCenterLightView.as_view(),
name='whydatacenterlight'),
url(r'^beta-program/?$', BetaProgramView.as_view(), name='beta'), url(r'^beta-program/?$', BetaProgramView.as_view(), name='beta'),
url(r'^landing/?$', LandingProgramView.as_view(), name='landing'), url(r'^landing/?$', LandingProgramView.as_view(), name='landing'),
url(r'^pricing/?$', PricingView.as_view(), name='pricing'), url(r'^pricing/?$', PricingView.as_view(), name='pricing'),
url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'), url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'),
url(r'^order-confirmation/?$', OrderConfirmationView.as_view(), name='order_confirmation'), url(r'^order-confirmation/?$', OrderConfirmationView.as_view(),
name='order_confirmation'),
url(r'^order-success/?$', SuccessView.as_view(), name='order_success'), url(r'^order-success/?$', SuccessView.as_view(), name='order_success'),
url(r'^beta_access?$', BetaAccessView.as_view(), name='beta_access'), url(r'^beta_access?$', BetaAccessView.as_view(), name='beta_access'),
url(r'^contact/?$', ContactUsView.as_view(), name='contact_us'),
] ]

View file

@ -1,6 +1,6 @@
from django.views.generic import FormView, CreateView, TemplateView, DetailView from django.views.generic import FormView, CreateView, TemplateView, DetailView
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from .forms import BetaAccessForm from .forms import BetaAccessForm, ContactForm
from .models import BetaAccess, BetaAccessVMType, BetaAccessVM, VMTemplate from .models import BetaAccess, BetaAccessVMType, BetaAccessVM, VMTemplate
from django.contrib import messages from django.contrib import messages
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@ -21,6 +21,43 @@ from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import VirtualMachineTemplateSerializer, \ from opennebula_api.serializers import VirtualMachineTemplateSerializer, \
VMTemplateSerializer VMTemplateSerializer
from datacenterlight.tasks import create_vm_task from datacenterlight.tasks import create_vm_task
from utils.tasks import send_plain_email_task
class ContactUsView(FormView):
template_name = "datacenterlight/contact_form.html"
form_class = ContactForm
def get(self, request, *args, **kwargs):
return HttpResponseRedirect(reverse('datacenterlight:index'))
def form_invalid(self, form):
if self.request.is_ajax():
return self.render_to_response(
self.get_context_data(contact_form=form))
else:
return render(self.request,
'datacenterlight/index.html',
self.get_context_data(contact_form=form))
def form_valid(self, form):
form.save()
email_data = {
'subject': 'Request received on Data Center Light',
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': ['info@ungleich.ch'],
'body': "\n".join(
["%s=%s" % (k, v) for (k, v) in form.cleaned_data.items()]),
'reply_to': [form.cleaned_data.get('email')],
}
send_plain_email_task.delay(email_data)
if self.request.is_ajax():
return self.render_to_response(
self.get_context_data(success=True, contact_form=form))
else:
return render(self.request,
'datacenterlight/index.html',
self.get_context_data(success=True, contact_form=form))
class LandingProgramView(TemplateView): class LandingProgramView(TemplateView):
@ -314,8 +351,8 @@ class IndexView(CreateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs) context = super(IndexView, self).get_context_data(**kwargs)
context.update({ context.update({
'base_url': "{0}://{1}".format(self.request.scheme, 'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host()),
self.request.get_host()) 'contact_form': ContactForm
}) })
return context return context

20
utils/tasks.py Normal file
View file

@ -0,0 +1,20 @@
from celery.utils.log import get_task_logger
from django.conf import settings
from dynamicweb.celery import app
from django.core.mail import EmailMessage
logger = get_task_logger(__name__)
@app.task(bind=True, max_retries=settings.CELERY_MAX_RETRIES)
def send_plain_email_task(self, email_data):
"""
This is a generic celery task to be used for sending emails.
A celery wrapper task for EmailMessage
:param self:
:param email_data: A dict of all needed email headers
:return:
"""
email = EmailMessage(**email_data)
email.send()