Created reset password email, Added forgot password view, Added forgot password form, Added set new password form, Added set new password view, fixed signup response issue, fixed main menu ungleich button redirect to django-hosting

This commit is contained in:
Levi 2016-06-21 00:10:38 -05:00
parent d7fc64258a
commit d8150b6593
13 changed files with 235 additions and 20 deletions

View file

@ -1,3 +0,0 @@
* * * * * (cd /home/app/app/; /usr/bin/python3 manage.py send_mail)
0,20,40 * * * * (cd /home/app/app/; /usr/bin/python3 manage.py retry_deferred)
0 0 * * * (cd /home/app/app/; /usr/bin/python3 manage.py purge_mail_log 7)

View file

@ -4,7 +4,7 @@ from django.contrib.auth import authenticate
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from .models import HostingOrder from .models import HostingOrder, VirtualMachinePlan
class HostingOrderAdminForm(forms.ModelForm): class HostingOrderAdminForm(forms.ModelForm):
@ -17,6 +17,10 @@ class HostingOrderAdminForm(forms.ModelForm):
customer = self.cleaned_data.get('customer') customer = self.cleaned_data.get('customer')
vm_plan = self.cleaned_data.get('vm_plan') 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 # Make a charge to the customer
stripe_utils = StripeUtils() stripe_utils = StripeUtils()
charge_response = stripe_utils.make_charge(customer=customer.stripe_id, charge_response = stripe_utils.make_charge(customer=customer.stripe_id,
@ -53,7 +57,7 @@ class HostingUserLoginForm(forms.Form):
CustomUser.objects.get(email=email) CustomUser.objects.get(email=email)
return email return email
except CustomUser.DoesNotExist: except CustomUser.DoesNotExist:
raise forms.ValidationError("User does not exists") raise forms.ValidationError("User does not exist")
else: else:
return email return email

View file

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

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

@ -53,7 +53,7 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </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> </div>
<!-- Collect the nav links, forms, and other content for toggling --> <!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <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="container">
<div class="col-md-4 col-md-offset-4"> <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 %} {% block messages %}
{% if request.GET.logged_out %} {% if request.GET.logged_out %}
<div class="alert"> <!-- singular --> <div class="alert"> <!-- singular -->
@ -31,6 +39,8 @@
{% endbuttons %} {% endbuttons %}
</form> </form>
<span>Don't have an account yet ? <a class="unlink" href="{% url 'hosting:signup' %}">Sign up</a></span> <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"> <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 %} {% block content %}
<div class="intro-auth intro-signup"> <div class="intro-auth intro-signup">
<div class="container"> <div class="container">
<div class="col-md-4">&nbsp;</div> <div class="col-md-4 col-sm-4 col-xs-4">&nbsp;</div>
<div class="col-md-4"> <div class="col-md-4 col-sm-6 col-xs-6">
<div class="intro-message"> <div class="intro-message">
<h2 class="section-heading">Sign up</h2> <h2 class="section-heading">Sign up</h2>

View file

@ -4,7 +4,7 @@ from .views import DjangoHostingView, RailsHostingView, PaymentVMView,\
NodeJSHostingView, LoginView, SignupView, IndexView, \ NodeJSHostingView, LoginView, SignupView, IndexView, \
OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\ OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\
VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \ VirtualMachineView, GenerateVMSSHKeysView, OrdersHostingDeleteView, NotificationsView, \
MarkAsReadNotificationView MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView
urlpatterns = [ urlpatterns = [
url(r'index/?$', IndexView.as_view(), name='index'), url(r'index/?$', IndexView.as_view(), name='index'),
@ -27,6 +27,9 @@ urlpatterns = [
name='read_notification'), name='read_notification'),
url(r'login/?$', LoginView.as_view(), name='login'), url(r'login/?$', LoginView.as_view(), name='login'),
url(r'signup/?$', SignupView.as_view(), name='signup'), 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', url(r'^logout/?$', 'django.contrib.auth.views.logout',
{'next_page': '/hosting/login?logged_out=true'}, name='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.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import View, CreateView, FormView, ListView, DetailView,\ from django.views.generic import View, CreateView, FormView, ListView, DetailView,\
DeleteView, TemplateView, UpdateView 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.http import HttpResponseRedirect
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.conf import settings from django.conf import settings
@ -16,7 +20,7 @@ from stored_messages.api import mark_read
from membership.models import CustomUser, StripeCustomer from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from utils.forms import BillingAddressForm from utils.forms import BillingAddressForm, PasswordResetRequestForm, SetPasswordForm
from utils.mailer import BaseEmail from utils.mailer import BaseEmail
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
from .forms import HostingUserSignupForm, HostingUserLoginForm from .forms import HostingUserSignupForm, HostingUserLoginForm
@ -164,6 +168,71 @@ class SignupView(CreateView):
return HttpResponseRedirect(self.get_success_url()) 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): class NotificationsView(TemplateView):
template_name = 'hosting/notifications.html' template_name = 'hosting/notifications.html'
@ -286,16 +355,6 @@ class PaymentVMView(LoginRequiredMixin, FormView):
# Send notification to ungleich as soon as VM has been booked # Send notification to ungleich as soon as VM has been booked
# TODO send email using celery # TODO send email using celery
from django.core.mail import send_mail
send_mail(
'Subject here',
'Here is the message.',
'levinoelvm@gmail.com',
['levinoelvm@gmail.com'],
fail_silently=False,
)
context = { context = {
'vm': plan, 'vm': plan,
'order': order, 'order': order,

View file

@ -3,9 +3,52 @@ from .models import ContactMessage, BillingAddress
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from membership.models import CustomUser
# from utils.fields import CountryField # 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): class BillingAddressForm(forms.ModelForm):
token = forms.CharField(widget=forms.HiddenInput()) token = forms.CharField(widget=forms.HiddenInput())