Merge pull request #88 from levivm/develop

Develop
This commit is contained in:
Levi Velázquez 2016-06-21 00:17:55 -05:00 committed by GitHub
commit 05b8493fe1
24 changed files with 263 additions and 32 deletions

View file

@ -8,6 +8,8 @@ ADMINS = (
)
# ('Sanghee Kim', 'sanghee.kim@ungleich.ch'),
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
#MANAGERS = ADMINS
REGISTRATION_MESSAGE['message'] = REGISTRATION_MESSAGE['message'].format(host='digitalglarus.ungleich.ch',slug='{slug}')

View file

@ -25,7 +25,7 @@ urlpatterns += i18n_patterns('',
url(r'^digitalglarus/login/', include(membership_urls)),
url(r'^digitalglarus/', include('digitalglarus.urls',
namespace="digitalglarus")),
url(r'^blog/', include('ungleich.urls', namespace='ungleich')),
# url(r'^blog/', include('ungleich.urls', namespace='ungleich')),
url(r'^ungleich_page/',
include('ungleich_page.urls', namespace='ungleich_page'),
name='ungleich_page'),

View file

@ -32,10 +32,11 @@ class HostingOrderAdmin(admin.ModelAdmin):
context = {
'order': obj,
'vm': obj.vm_plan
'vm': obj.vm_plan,
'base_url': "{0}://{1}".format(request.scheme, request.get_host())
}
email_data = {
'subject': 'New VM request',
'subject': 'Your VM plan has been charged',
'to': obj.customer.user.email,
'context': context,
'template_name': 'vm_charged',
@ -75,7 +76,8 @@ class VirtualMachinePlanAdmin(admin.ModelAdmin):
email = self.email(obj)
if 'status' in form.changed_data:
context = {
'vm': obj
'vm': obj,
'base_url': "{0}://{1}".format(request.scheme, request.get_host())
}
email_data = {
'subject': 'Your VM has been activated',

View file

@ -4,7 +4,7 @@ from django.contrib.auth import authenticate
from utils.stripe_utils import StripeUtils
from .models import HostingOrder
from .models import HostingOrder, VirtualMachinePlan
class HostingOrderAdminForm(forms.ModelForm):
@ -17,6 +17,10 @@ class HostingOrderAdminForm(forms.ModelForm):
customer = self.cleaned_data.get('customer')
vm_plan = self.cleaned_data.get('vm_plan')
if vm_plan.status == VirtualMachinePlan.CANCELED_STATUS:
raise forms.ValidationError("""You can't make a charge over
a canceled virtual machine plan""")
# Make a charge to the customer
stripe_utils = StripeUtils()
charge_response = stripe_utils.make_charge(customer=customer.stripe_id,
@ -53,7 +57,7 @@ class HostingUserLoginForm(forms.Form):
CustomUser.objects.get(email=email)
return email
except CustomUser.DoesNotExist:
raise forms.ValidationError("User does not exists")
raise forms.ValidationError("User does not exist")
else:
return email

View file

@ -83,6 +83,12 @@ h6 {
height: 100%;
}
.intro-reset-password {
background: url(../img/signup-bg.png) no-repeat center center;
background-size: cover;
height: 100%;
}
.intro-message > h1 {
margin: 0;
font-weight: 400;

View file

@ -114,7 +114,7 @@
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a href="{% url 'hosting:orders' order.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
<![endif]--><a href="{{ base_url }}{% url 'hosting:orders' order.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
</div>
</td>
</tr>

View file

@ -101,7 +101,7 @@
<td class="free-text" style="border-collapse: collapse; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; color: #777777; text-align: center; line-height: 21px; width: 100% !important; padding: 10px 60px 0px;" align="center">
Your virtual machine {{vm.name}} subscription has been charged,
<br/>
We are going to contact you as soon your virtual machine has been activated.
we are going to contact you as soon your virtual machine has been activated.
<br/>
You can view your invoice clicking on the button below.
</td>
@ -114,7 +114,7 @@
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a href="{% url 'hosting:orders' order.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
<![endif]--><a href="{{ base_url }}{% url 'hosting:orders' order.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
</div>
</td>
</tr>

View file

@ -0,0 +1,13 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ base_url }}{% url 'hosting:reset_password_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}

View file

@ -0,0 +1,13 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ base_url }}{% url 'hosting:reset_password_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}

View file

@ -110,7 +110,7 @@
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a href="{% url 'hosting:orders' order.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
<![endif]--><a href="{{ base_url }}{% url 'hosting:orders' order.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
</div>
</td>
</tr>

View file

@ -110,7 +110,7 @@
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a href="{% url 'hosting:orders' order.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
<![endif]--><a href="{{ base_url }}{% url 'hosting:orders' order.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">View Invoice</a>
</div>
</td>
</tr>

View file

@ -111,7 +111,7 @@
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a href="{{request.HOS}}{% url 'hosting:virtual_machines' vm.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">VM Dashboard</a>
<![endif]--><a href="{{base_url}}{% url 'hosting:virtual_machines' vm.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">VM Dashboard</a>
</div>
</td>
</tr>

View file

@ -111,7 +111,7 @@
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a href="{{request.HOS}}{% url 'hosting:virtual_machines' vm.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">VM Dashboard</a>
<![endif]--><a href="{{base_url}}{% url 'hosting:virtual_machines' vm.id %}" style="border-radius: 5px; color: #ffffff; display: inline-block; font-family: 'Oxygen', 'Helvetica Neue', 'Arial', 'sans-serif' !important; font-size: 14px; font-weight: regular; line-height: 45px; text-align: center; text-decoration: none !important; width: 155px; -webkit-text-size-adjust: none; mso-hide: all; background: #ff6f6f;">VM Dashboard</a>
</div>
</td>
</tr>

View file

@ -53,7 +53,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand topnav" href="{% url 'ungleich_page:landing' %}"><img src="{% static 'hosting/img/logo_black.svg' %}"></a>
<a class="navbar-brand topnav" href="{{ request.session.hosting_url}}"><img src="{% static 'hosting/img/logo_black.svg' %}"></a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">

View file

@ -0,0 +1,38 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3%}
{% block content %}
<div class="intro-auth intro-reset-password">
<div class="container">
<div class="col-md-4">&nbsp;</div>
<div class="col-md-4">
<div class="intro-message">
{% if messages %}
<ul class="list-unstyled">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<h2 class="section-heading">Set your new password</h2>
<form action="" method="post" class="form" novalidate>
{% csrf_token %}
{% for field in form %}
{% bootstrap_field field show_label=False %}
{% endfor %}
{% buttons %}
<button type="submit" class="btn btn-default">
Reset
</button>
{% endbuttons %}
</form>
<span>Already have an account ? <a class="unlink" href="{% url 'hosting:login' %}">Log in</a></span>
<ul class="list-inline intro-social-buttons">
</ul>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -6,6 +6,14 @@
<div class="container">
<div class="col-md-4 col-md-offset-4">
{% if messages %}
<ul class="list-unstyled">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% block messages %}
{% if request.GET.logged_out %}
<div class="alert"> <!-- singular -->
@ -31,6 +39,8 @@
{% endbuttons %}
</form>
<span>Don't have an account yet ? <a class="unlink" href="{% url 'hosting:signup' %}">Sign up</a></span>
<br/>
<span> <a class="unlink" href="{% url 'hosting:reset_password' %}">Forgot your password ?</a></span>
<ul class="list-inline intro-social-buttons">

View file

@ -0,0 +1,29 @@
{% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3%}
{% block content %}
<div class="intro-auth intro-reset-password">
<div class="container">
<div class="col-md-4">&nbsp;</div>
<div class="col-md-4">
<div class="intro-message">
<h2 class="section-heading">Reset your password</h2>
<form action="{% url 'hosting:reset_password' %}" method="post" class="form" novalidate>
{% csrf_token %}
{% for field in form %}
{% bootstrap_field field show_label=False %}
{% endfor %}
{% buttons %}
<button type="submit" class="btn btn-default">
Reset
</button>
{% endbuttons %}
</form>
<span>Already have an account ? <a class="unlink" href="{% url 'hosting:login' %}">Log in</a></span>
<ul class="list-inline intro-social-buttons">
</ul>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -3,8 +3,8 @@
{% block content %}
<div class="intro-auth intro-signup">
<div class="container">
<div class="col-md-4">&nbsp;</div>
<div class="col-md-4">
<div class="col-md-4 col-sm-4 col-xs-4">&nbsp;</div>
<div class="col-md-4 col-sm-6 col-xs-6">
<div class="intro-message">
<h2 class="section-heading">Sign up</h2>

View file

@ -10,7 +10,7 @@ from model_mommy import mommy
from membership.models import CustomUser, StripeCustomer
from .models import VirtualMachineType, HostingOrder, VirtualMachinePlan
from .views import DjangoHostingView, RailsHostingView, NodeJSHostingView, LoginView, SignupView, \
PaymentVMView, OrdersHostingDetailView, OrdersHostingListView, VirtualMachineDetailView, \
PaymentVMView, OrdersHostingDetailView, OrdersHostingListView, VirtualMachineView, \
VirtualMachinesPlanListView
from utils.tests import BaseTestCase
@ -172,16 +172,16 @@ class PaymentVMViewTest(BaseTestCase):
settings.STRIPE_API_PUBLIC_KEY)
class VirtualMachineDetailViewTest(BaseTestCase):
class VirtualMachineViewTest(BaseTestCase):
def setUp(self):
super(VirtualMachineDetailViewTest, self).setUp()
super(VirtualMachineViewTest, self).setUp()
self.stripe_customer = mommy.make(StripeCustomer, user=self.customer)
self.vm = mommy.make(VirtualMachinePlan)
self.order = mommy.make(HostingOrder, customer=self.stripe_customer, vm_plan=self.vm)
self.url = reverse('hosting:virtual_machines', kwargs={'pk': self.vm.id})
self.view = VirtualMachineDetailView()
self.view = VirtualMachineView()
self.expected_template = 'hosting/virtual_machine_detail.html'
def url_resolve_to_view_correctly(self):

View file

@ -4,7 +4,7 @@ from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\
NodeJSHostingView, LoginView, SignupView, IndexView, \
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \
MarkAsReadNotificationView
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView
urlpatterns = [
url(r'index/?$', IndexView.as_view(), name='index'),
@ -27,6 +27,9 @@ urlpatterns = [
name='read_notification'),
url(r'login/?$', LoginView.as_view(), name='login'),
url(r'signup/?$', SignupView.as_view(), name='signup'),
url(r'reset-password/?$', PasswordResetView.as_view(), name='reset_password'),
url(r'reset-password-confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',
PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
url(r'^logout/?$', 'django.contrib.auth.views.logout',
{'next_page': '/hosting/login?logged_out=true'}, name='logout')
]

View file

@ -4,6 +4,10 @@ from django.core.urlresolvers import reverse_lazy, reverse
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import View, CreateView, FormView, ListView, DetailView,\
DeleteView, TemplateView, UpdateView
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.contrib import messages
from django.utils.encoding import force_bytes
from django.http import HttpResponseRedirect
from django.contrib.auth import authenticate, login
from django.conf import settings
@ -16,7 +20,7 @@ from stored_messages.api import mark_read
from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils
from utils.forms import BillingAddressForm
from utils.forms import BillingAddressForm, PasswordResetRequestForm, SetPasswordForm
from utils.mailer import BaseEmail
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
from .forms import HostingUserSignupForm, HostingUserLoginForm
@ -164,6 +168,71 @@ class SignupView(CreateView):
return HttpResponseRedirect(self.get_success_url())
class PasswordResetView(FormView):
template_name = 'hosting/reset_password.html'
form_class = PasswordResetRequestForm
success_message = "The link to reset your email has been sent to your email"
success_url = reverse_lazy('hosting:login')
# form_valid_message = 'Thank you for registering'
def form_valid(self, form):
email = form.cleaned_data.get('email')
user = CustomUser.objects.get(email=email)
messages.add_message(self.request, messages.SUCCESS, self.success_message)
context = {
'user': user,
'token': default_token_generator.make_token(user),
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'site_name': 'ungleich',
'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host())
}
email_data = {
'subject': 'Password Reset',
'to': email,
'context': context,
'template_name': 'password_reset_email',
'template_path': 'emails/'
}
email = BaseEmail(**email_data)
email.send()
return HttpResponseRedirect(self.get_success_url())
class PasswordResetConfirmView(FormView):
template_name = 'hosting/confirm_reset_password.html'
form_class = SetPasswordForm
success_url = reverse_lazy('hosting:login')
def post(self, request, uidb64=None, token=None, *arg, **kwargs):
try:
uid = urlsafe_base64_decode(uidb64)
user = CustomUser.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
user = None
form = self.form_class(request.POST)
if user is not None and default_token_generator.check_token(user, token):
if form.is_valid():
new_password = form.cleaned_data['new_password2']
user.set_password(new_password)
user.save()
messages.success(request, 'Password has been reset.')
return self.form_valid(form)
else:
messages.error(request, 'Password reset has not been unsuccessful.')
return self.form_invalid(form)
else:
messages.error(request, 'The reset password link is no longer valid.')
return self.form_invalid(form)
class NotificationsView(TemplateView):
template_name = 'hosting/notifications.html'
@ -285,13 +354,16 @@ class PaymentVMView(LoginRequiredMixin, FormView):
# Send notification to ungleich as soon as VM has been booked
# TODO send email using celery
context = {
'vm': plan,
'order': order
'order': order,
'base_url': "{0}://{1}".format(request.scheme, request.get_host())
}
email_data = {
'subject': 'New VM request',
'to': 'info@ungleich.ch',
'to': request.user.email,
'context': context,
'template_name': 'new_booked_vm',
'template_path': 'emails/'
@ -299,11 +371,6 @@ class PaymentVMView(LoginRequiredMixin, FormView):
email = BaseEmail(**email_data)
email.send()
# request.session.update({
# 'charge': charge,
# 'order': order.id,
# 'billing_address': billing_address.id
# })
return HttpResponseRedirect(reverse('hosting:orders', kwargs={'pk': order.id}))
else:
return self.form_invalid(form)
@ -368,7 +435,8 @@ class VirtualMachineView(LoginRequiredMixin, UpdateView):
vm.cancel_plan()
context = {
'vm': vm
'vm': vm,
'base_url': "{0}://{1}".format(self.request.scheme, self.request.get_host())
}
email_data = {
'subject': 'Virtual machine plan canceled',

View file

@ -1,4 +1,4 @@
{% load cms_tags menu_tags sekizai_tags staticfiles bootstrap3 %}
{% load i18n cms_tags menu_tags sekizai_tags staticfiles bootstrap3 %}
<!doctype html>
<html>
<head>

View file

@ -3,9 +3,52 @@ from .models import ContactMessage, BillingAddress
from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives
from django.utils.translation import ugettext_lazy as _
from membership.models import CustomUser
# from utils.fields import CountryField
class PasswordResetRequestForm(forms.Form):
email = forms.CharField(widget=forms.EmailInput())
class Meta:
fields = ['email']
def clean_email(self):
email = self.cleaned_data.get('email')
try:
CustomUser.objects.get(email=email)
return email
except CustomUser.DoesNotExist:
raise forms.ValidationError("User does not exist")
else:
return email
class SetPasswordForm(forms.Form):
"""
A form that lets a user change set their password without entering the old
password
"""
error_messages = {
'password_mismatch': ("The two password fields didn't match."),
}
new_password1 = forms.CharField(label=("New password"),
widget=forms.PasswordInput)
new_password2 = forms.CharField(label=("New password confirmation"),
widget=forms.PasswordInput)
def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2')
if password1 and password2:
if password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',)
return password2
class BillingAddressForm(forms.ModelForm):
token = forms.CharField(widget=forms.HiddenInput())

View file

@ -21,7 +21,7 @@ class BaseEmail(object):
self.email = EmailMultiAlternatives(self.subject, text_content)
self.email.attach_alternative(html_content, "text/html")
self.email.to = ['info@digitalglarus.ch']
self.email.to = ['levinoelvm@gmail.com']
def send(self):
self.email.send()