Created signup view. Added login after signup.Added signup url to nosystem app urls.py. Added logout view, Added logout button on nabber, Added password reset form, Added password view , Added password reset html, Added password reset email for nosystemd app. Added confirm_reset_password.html, Added confirm_ reset password view, Added confirm reset password form, Fixed reset password token generation, Started donation view, Added donation view, Added donation.html, Added donation form, Adding donation.js lib in order to capture stripe payments for nosystem app.
This commit is contained in:
parent
da9169c4ce
commit
cb520f6b58
30 changed files with 645 additions and 86 deletions
|
@ -15,7 +15,7 @@ urlpatterns = [ url(r'^index.html$', LandingView.as_view()),
|
||||||
url(r'^railshosting/', RailsHostingView.as_view(), name="rails.hosting"),
|
url(r'^railshosting/', RailsHostingView.as_view(), name="rails.hosting"),
|
||||||
url(r'^nodehosting/', NodeJSHostingView.as_view(), name="node.hosting"),
|
url(r'^nodehosting/', NodeJSHostingView.as_view(), name="node.hosting"),
|
||||||
url(r'^djangohosting/', DjangoHostingView.as_view(), name="django.hosting"),
|
url(r'^djangohosting/', DjangoHostingView.as_view(), name="django.hosting"),
|
||||||
url(r'^nosystemd/', include('nosystemd.urls'), name="nosystemd"),
|
url(r'^nosystemd/', include('nosystemd.urls', namespace="nosystemd")),
|
||||||
url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')),
|
url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')),
|
||||||
url(r'^jsi18n/(?P<packages>\S+?)/$',
|
url(r'^jsi18n/(?P<packages>\S+?)/$',
|
||||||
'django.views.i18n.javascript_catalog'),
|
'django.views.i18n.javascript_catalog'),
|
||||||
|
|
|
@ -43,7 +43,7 @@ class HostingOrderAdmin(admin.ModelAdmin):
|
||||||
'to': obj.customer.user.email,
|
'to': obj.customer.user.email,
|
||||||
'context': context,
|
'context': context,
|
||||||
'template_name': 'vm_charged',
|
'template_name': 'vm_charged',
|
||||||
'template_path': 'emails/'
|
'template_path': 'hosting/emails/'
|
||||||
}
|
}
|
||||||
email = BaseEmail(**email_data)
|
email = BaseEmail(**email_data)
|
||||||
email.send()
|
email.send()
|
||||||
|
@ -87,7 +87,7 @@ class VirtualMachinePlanAdmin(admin.ModelAdmin):
|
||||||
'to': email,
|
'to': email,
|
||||||
'context': context,
|
'context': context,
|
||||||
'template_name': 'vm_status_changed',
|
'template_name': 'vm_status_changed',
|
||||||
'template_path': 'emails/'
|
'template_path': 'hosting/emails/'
|
||||||
}
|
}
|
||||||
email = BaseEmail(**email_data)
|
email = BaseEmail(**email_data)
|
||||||
email.send()
|
email.send()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "hosting/base_short.html" %}
|
{% extends "hosting/base_short.html" %}
|
||||||
{% load staticfiles bootstrap3 %}
|
{% load staticfiles bootstrap3 i18n %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div>
|
<div>
|
||||||
<div class="container virtual-machine-container dashboard-container ">
|
<div class="container virtual-machine-container dashboard-container ">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "hosting/base_short.html" %}
|
{% extends "hosting/base_short.html" %}
|
||||||
{% load staticfiles bootstrap3 %}
|
{% load staticfiles bootstrap3 i18n %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Credit card form -->
|
<!-- Credit card form -->
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{% extends "hosting/base_short.html" %}
|
{% extends "hosting/base_short.html" %}
|
||||||
{% load staticfiles bootstrap3%}
|
{% load staticfiles bootstrap3 i18n %}
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="intro-auth intro-signup">
|
<div class="intro-auth intro-signup">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "hosting/base_short.html" %}
|
{% extends "hosting/base_short.html" %}
|
||||||
{% load staticfiles bootstrap3 %}
|
{% load staticfiles bootstrap3 i18n %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div>
|
<div>
|
||||||
<div class="container virtual-machine-container dashboard-container ">
|
<div class="container virtual-machine-container dashboard-container ">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "hosting/base_short.html" %}
|
{% extends "hosting/base_short.html" %}
|
||||||
{% load staticfiles bootstrap3 %}
|
{% load staticfiles bootstrap3 i18n %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div>
|
<div>
|
||||||
<div class="container dashboard-container">
|
<div class="container dashboard-container">
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404, render,render_to_response
|
from django.shortcuts import render
|
||||||
from django.core.urlresolvers import reverse_lazy, reverse
|
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
|
||||||
|
@ -20,7 +16,8 @@ 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, PasswordResetRequestForm, SetPasswordForm
|
from utils.forms import BillingAddressForm, PasswordResetRequestForm
|
||||||
|
from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin
|
||||||
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
|
||||||
|
@ -188,75 +185,42 @@ class SignupView(CreateView):
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetView(FormView):
|
class PasswordResetView(PasswordResetViewMixin):
|
||||||
template_name = 'hosting/reset_password.html'
|
template_name = 'hosting/reset_password.html'
|
||||||
form_class = PasswordResetRequestForm
|
form_class = PasswordResetRequestForm
|
||||||
success_message = "The link to reset your email has been sent to your email"
|
|
||||||
success_url = reverse_lazy('hosting:login')
|
success_url = reverse_lazy('hosting:login')
|
||||||
# form_valid_message = 'Thank you for registering'
|
template_email_path = 'hosting/emails/'
|
||||||
|
|
||||||
def test_generate_email_context(self, user):
|
|
||||||
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())
|
|
||||||
|
|
||||||
}
|
|
||||||
return context
|
|
||||||
|
|
||||||
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 = self.test_generate_email_context(user)
|
|
||||||
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):
|
class PasswordResetConfirmView(PasswordResetConfirmViewMixin):
|
||||||
template_name = 'hosting/confirm_reset_password.html'
|
template_name = 'hosting/confirm_reset_password.html'
|
||||||
form_class = SetPasswordForm
|
|
||||||
success_url = reverse_lazy('hosting:login')
|
success_url = reverse_lazy('hosting:login')
|
||||||
|
|
||||||
def post(self, request, uidb64=None, token=None, *arg, **kwargs):
|
# def post(self, request, uidb64=None, token=None, *arg, **kwargs):
|
||||||
try:
|
# try:
|
||||||
uid = urlsafe_base64_decode(uidb64)
|
# uid = urlsafe_base64_decode(uidb64)
|
||||||
user = CustomUser.objects.get(pk=uid)
|
# user = CustomUser.objects.get(pk=uid)
|
||||||
except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
|
# except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
|
||||||
user = None
|
# user = None
|
||||||
|
|
||||||
form = self.form_class(request.POST)
|
# form = self.form_class(request.POST)
|
||||||
|
|
||||||
if user is not None and default_token_generator.check_token(user, token):
|
# if user is not None and default_token_generator.check_token(user, token):
|
||||||
if form.is_valid():
|
# if form.is_valid():
|
||||||
new_password = form.cleaned_data['new_password2']
|
# new_password = form.cleaned_data['new_password2']
|
||||||
user.set_password(new_password)
|
# user.set_password(new_password)
|
||||||
user.save()
|
# user.save()
|
||||||
messages.success(request, 'Password has been reset.')
|
# messages.success(request, 'Password has been reset.')
|
||||||
return self.form_valid(form)
|
# return self.form_valid(form)
|
||||||
else:
|
# else:
|
||||||
messages.error(request, 'Password reset has not been unsuccessful.')
|
# messages.error(request, 'Password reset has not been unsuccessful.')
|
||||||
form.add_error(None, 'Password reset has not been unsuccessful.')
|
# form.add_error(None, 'Password reset has not been unsuccessful.')
|
||||||
return self.form_invalid(form)
|
# return self.form_invalid(form)
|
||||||
|
|
||||||
else:
|
# else:
|
||||||
messages.error(request, 'The reset password link is no longer valid.')
|
# messages.error(request, 'The reset password link is no longer valid.')
|
||||||
form.add_error(None, 'Password reset has not been unsuccessful.')
|
# form.add_error(None, 'Password reset has not been unsuccessful.')
|
||||||
return self.form_invalid(form)
|
# return self.form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
class NotificationsView(LoginRequiredMixin, TemplateView):
|
class NotificationsView(LoginRequiredMixin, TemplateView):
|
||||||
|
@ -396,7 +360,7 @@ class PaymentVMView(LoginRequiredMixin, FormView):
|
||||||
'to': request.user.email,
|
'to': request.user.email,
|
||||||
'context': context,
|
'context': context,
|
||||||
'template_name': 'new_booked_vm',
|
'template_name': 'new_booked_vm',
|
||||||
'template_path': 'emails/'
|
'template_path': 'hosting/emails/'
|
||||||
}
|
}
|
||||||
email = BaseEmail(**email_data)
|
email = BaseEmail(**email_data)
|
||||||
email.send()
|
email.send()
|
||||||
|
@ -475,7 +439,7 @@ class VirtualMachineView(PermissionRequiredMixin, LoginRequiredMixin, UpdateView
|
||||||
'to': self.request.user.email,
|
'to': self.request.user.email,
|
||||||
'context': context,
|
'context': context,
|
||||||
'template_name': 'vm_status_changed',
|
'template_name': 'vm_status_changed',
|
||||||
'template_path': 'emails/'
|
'template_path': 'hosting/emails/'
|
||||||
}
|
}
|
||||||
email = BaseEmail(**email_data)
|
email = BaseEmail(**email_data)
|
||||||
email.send()
|
email.send()
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
from utils.forms import LoginFormMixin
|
from utils.forms import LoginFormMixin, SignupFormMixin
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(LoginFormMixin):
|
class LoginForm(LoginFormMixin):
|
||||||
email = forms.CharField(widget=forms.EmailInput())
|
email = forms.CharField(widget=forms.EmailInput())
|
||||||
password = forms.CharField(widget=forms.PasswordInput())
|
password = forms.CharField(widget=forms.PasswordInput())
|
||||||
|
|
||||||
|
|
||||||
|
class SignupForm(SignupFormMixin):
|
||||||
|
confirm_password = forms.CharField(widget=forms.EmailInput())
|
||||||
|
password = forms.CharField(widget=forms.PasswordInput())
|
||||||
|
|
|
@ -59,6 +59,9 @@
|
||||||
<li>
|
<li>
|
||||||
<a class="page-scroll" href="#contact">Contact</a>
|
<a class="page-scroll" href="#contact">Contact</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="page-scroll" href="{% url 'nosystemd:login' %}">Login</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.navbar-collapse -->
|
<!-- /.navbar-collapse -->
|
||||||
|
@ -83,6 +86,12 @@
|
||||||
<!-- Theme JavaScript -->
|
<!-- Theme JavaScript -->
|
||||||
<script src="{% static 'nosystemd/js/creative.min.js' %}"></script>
|
<script src="{% static 'nosystemd/js/creative.min.js' %}"></script>
|
||||||
|
|
||||||
|
<!-- Stripe Lib -->
|
||||||
|
<script type="text/javascript" src="//js.stripe.com/v2/"></script>
|
||||||
|
|
||||||
|
<!-- Proccess payment lib -->
|
||||||
|
<script type="text/javascript" src="{% static 'hosting/js/payment.js' %}"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
50
nosystemd/templates/nosystemd/confirm_reset_password.html
Normal file
50
nosystemd/templates/nosystemd/confirm_reset_password.html
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
{% extends "nosystemd/base.html" %}
|
||||||
|
{% load staticfiles bootstrap3%}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-content-inner">
|
||||||
|
<div class="col-md-3 col-md-offset-4">
|
||||||
|
|
||||||
|
<div class="intro-auth intro-reset-password">
|
||||||
|
<div class="container">
|
||||||
|
<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">{% trans "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">
|
||||||
|
{% trans "Reset"%}
|
||||||
|
</button>
|
||||||
|
{% endbuttons %}
|
||||||
|
</form>
|
||||||
|
<span>{% trans "Already have an account ?"%}<a class="unlink" href="{% url 'hosting:login' %}">{% trans "Log in"%}</a></span>
|
||||||
|
<ul class="list-inline intro-social-buttons">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% endblock %}
|
119
nosystemd/templates/nosystemd/donation.html
Normal file
119
nosystemd/templates/nosystemd/donation.html
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
{% extends "nosystemd/base.html" %}
|
||||||
|
{% load staticfiles bootstrap3 %}
|
||||||
|
{% block content %}
|
||||||
|
<!-- Credit card form -->
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-content-inner">
|
||||||
|
|
||||||
|
<div class="container payment-container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-md-4 col-md-offset-3" >
|
||||||
|
<h3><b>Monthly Donation</b></h3>
|
||||||
|
<hr>
|
||||||
|
<form role="form" novalidate>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-9 col-md-4 col-md-offset-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" class="form-control" placeholder="Amount to donate" name="donation_amount" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-md-4 col-md-offset-3 billing">
|
||||||
|
<h3><b>Billing Address</b></h3>
|
||||||
|
<hr>
|
||||||
|
<form role="form" id="billing-form" method="post" action="{% url 'hosting:payment' %}" novalidate>
|
||||||
|
{% for field in form %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_field field show_label=False type='fields'%}
|
||||||
|
{% endfor %}
|
||||||
|
{% bootstrap_form_errors form type='non_fields'%}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-sm-6 col-md-4 col-md-offset-3 creditcard-box">
|
||||||
|
<h3><b>Payment Details</b></h3>
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<form role="form" id="payment-form" novalidate>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-9 col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label" for="cardNumber">CARD NUMBER</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required autofocus data-stripe="number" />
|
||||||
|
<span class="input-group-addon"><i class="fa fa-credit-card"></i></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6 col-md-7">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="expMonth">EXPIRATION DATE</label><br/>
|
||||||
|
<div class="col-xs-6 col-lg-6 col-md-6 pl-ziro">
|
||||||
|
<input type="text" class="form-control" name="expMonth" placeholder="MM" required data-stripe="exp_month" />
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-6 col-lg-6 col-md-6 pl-ziro">
|
||||||
|
<input type="text" class="form-control" name="expYear" placeholder="YY" required data-stripe="exp_year" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-4 col-md-5 pull-right">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="cvCode">CV CODE</label>
|
||||||
|
<input type="password" class="form-control" name="cvCode" placeholder="CV" required data-stripe="cvc" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<button class="btn btn-success btn-lg btn-block" type="submit">Submit Donation</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row" style="display:none;">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<p class="payment-errors"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if paymentError %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<p>
|
||||||
|
{% bootstrap_alert paymentError alert_type='danger' %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- stripe key data -->
|
||||||
|
{% if stripe_key %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
(function () {window.stripeKey = "{{stripe_key}}";})();
|
||||||
|
</script>
|
||||||
|
{%endif%}
|
||||||
|
|
||||||
|
{%endblock%}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 'nosystemd:reset_password_confirm' uidb64=uid token=token %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% trans "Thanks for using our site!" %}
|
||||||
|
|
||||||
|
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||||
|
|
||||||
|
{% endautoescape %}
|
|
@ -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 'nosystemd:reset_password_confirm' uidb64=uid token=token %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% trans "Thanks for using our site!" %}
|
||||||
|
|
||||||
|
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||||
|
|
||||||
|
{% endautoescape %}
|
|
@ -59,6 +59,15 @@
|
||||||
<li>
|
<li>
|
||||||
<a class="page-scroll" href="#contact">Contact</a>
|
<a class="page-scroll" href="#contact">Contact</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if not request.user.is_authenticated %}
|
||||||
|
<li>
|
||||||
|
<a class="page-scroll" href="{% url 'nosystemd:login' %}">Login</a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
<a class="page-scroll" href=" {% url 'nosystemd:logout' %}">Logout</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.navbar-collapse -->
|
<!-- /.navbar-collapse -->
|
||||||
|
@ -72,7 +81,7 @@
|
||||||
<h1 id="homeHeading">No more SYSTEMD</h1>
|
<h1 id="homeHeading">No more SYSTEMD</h1>
|
||||||
<hr>
|
<hr>
|
||||||
<p>We want to remove systemd from the famous linux distros and create a good replacement</p>
|
<p>We want to remove systemd from the famous linux distros and create a good replacement</p>
|
||||||
<a href="#about" class="btn btn-primary btn-xl page-scroll">DONATE NOW</a>
|
<a href="{% url 'nosystemd:donation' %}" class="btn btn-primary btn-xl page-scroll">DONATE NOW</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -6,7 +6,29 @@
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<div class="header-content-inner">
|
<div class="header-content-inner">
|
||||||
<div class="col-md-4 col-md-offset-4">
|
<div class="col-md-4 col-md-offset-4">
|
||||||
<form action="{% url 'hosting:login' %}" method="post" class="form" novalidate>
|
|
||||||
|
|
||||||
|
{% 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 -->
|
||||||
|
<a class="close" data-dismiss="alert">×</a>
|
||||||
|
{% trans "You haven been logged out"%}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<form action="{% url 'nosystemd:login' %}" method="post" class="form" novalidate>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
{% bootstrap_field field show_label=False type='fields'%}
|
{% bootstrap_field field show_label=False type='fields'%}
|
||||||
|
@ -18,6 +40,11 @@
|
||||||
</button>
|
</button>
|
||||||
{% endbuttons %}
|
{% endbuttons %}
|
||||||
</form>
|
</form>
|
||||||
|
<span>{% trans "Don't have an account yet ? "%}<a class="unlink" href="{% url 'nosystemd:signup' %}">{% trans "Sign up"%}</a></span>
|
||||||
|
<br/>
|
||||||
|
<span> <a class="unlink" href="{% url 'nosystemd:reset_password' %}">{% trans "Forgot your password ? "%}</a></span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
42
nosystemd/templates/nosystemd/reset_password.html
Normal file
42
nosystemd/templates/nosystemd/reset_password.html
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{% extends "nosystemd/base.html" %}
|
||||||
|
{% load staticfiles bootstrap3%}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-content-inner">
|
||||||
|
<div class="col-md-3 col-md-offset-4">
|
||||||
|
|
||||||
|
<div class="intro-auth intro-reset-password">
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="intro-message">
|
||||||
|
<h2 class="section-heading">{% trans "Reset your password"%}</h2>
|
||||||
|
|
||||||
|
<form action="{% url 'nosystemd: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">
|
||||||
|
{% trans "Reset"%}
|
||||||
|
</button>
|
||||||
|
{% endbuttons %}
|
||||||
|
</form>
|
||||||
|
<span>{% trans "Already have an account ?"%} <a class="unlink" href="{% url 'nosystemd:login' %}">{% trans "Login"%} </a></span>
|
||||||
|
<ul class="list-inline intro-social-buttons">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% endblock %}
|
44
nosystemd/templates/nosystemd/signup.html
Normal file
44
nosystemd/templates/nosystemd/signup.html
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{% extends "nosystemd/base.html" %}
|
||||||
|
{% load staticfiles bootstrap3%}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-content-inner">
|
||||||
|
<div class="intro-auth intro-signup">
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-md-3 col-sm-4 col-xs-4"> </div>
|
||||||
|
<div class="col-md-4 col-sm-6 col-xs-6">
|
||||||
|
<div class="intro-message">
|
||||||
|
<h2 class="section-heading">{% trans "Sign up"%}</h2>
|
||||||
|
|
||||||
|
<form action="{% url 'nosystemd:signup' %}" 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">
|
||||||
|
{% trans "Sign up"%}
|
||||||
|
</button>
|
||||||
|
{% endbuttons %}
|
||||||
|
</form>
|
||||||
|
<span>{% trans "Already have an account ?"%} <a class="unlink" href="{% url 'nosystemd:login' %}">{% trans "Login"%}</a></span>
|
||||||
|
<ul class="list-inline intro-social-buttons">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -1,8 +1,18 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from .views import LandingView, LoginView
|
from .views import LandingView, LoginView, SignupView, PasswordResetView,\
|
||||||
|
PasswordResetConfirmView, DonationView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', LandingView.as_view(), name='landing'),
|
url(r'^$', LandingView.as_view(), name='landing'),
|
||||||
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'^logout/?$', 'django.contrib.auth.views.logout',
|
||||||
|
{'next_page': '/nosystemd/login?logged_out=true'}, name='logout'),
|
||||||
|
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'^donation/?$', DonationView.as_view(), name='donation'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,13 +1,169 @@
|
||||||
from django.shortcuts import render
|
from django.views.generic import TemplateView, CreateView, FormView
|
||||||
from django.views.generic import TemplateView, FormView
|
from django.http import HttpResponseRedirect
|
||||||
from .forms import LoginForm
|
from django.core.urlresolvers import reverse_lazy, reverse
|
||||||
|
from django.contrib.auth import authenticate, login
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
# Create your views here.
|
from membership.models import CustomUser
|
||||||
|
from utils.views import PasswordResetViewMixin, PasswordResetConfirmViewMixin
|
||||||
|
from utils.forms import PasswordResetRequestForm, BillingAddressForm
|
||||||
|
|
||||||
|
from .forms import LoginForm, SignupForm
|
||||||
|
|
||||||
|
|
||||||
class LandingView(TemplateView):
|
class LandingView(TemplateView):
|
||||||
template_name = "nosystemd/landing.html"
|
template_name = "nosystemd/landing.html"
|
||||||
|
|
||||||
|
|
||||||
class LoginView(FormView):
|
class LoginView(FormView):
|
||||||
template_name = "nosystemd/login.html"
|
template_name = "nosystemd/login.html"
|
||||||
form_class = LoginForm
|
form_class = LoginForm
|
||||||
|
success_url = reverse_lazy('nosystemd:landing')
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
email = form.cleaned_data.get('email')
|
||||||
|
password = form.cleaned_data.get('password')
|
||||||
|
auth_user = authenticate(email=email, password=password)
|
||||||
|
|
||||||
|
if auth_user:
|
||||||
|
login(self.request, auth_user)
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
if self.request.user.is_authenticated():
|
||||||
|
return HttpResponseRedirect(reverse('nosystemd:landing'))
|
||||||
|
return super(LoginView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SignupView(CreateView):
|
||||||
|
template_name = 'nosystemd/signup.html'
|
||||||
|
model = CustomUser
|
||||||
|
form_class = SignupForm
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
next_url = self.request.session.get('next', reverse_lazy('nosystemd:signup'))
|
||||||
|
return next_url
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
name = form.cleaned_data.get('name')
|
||||||
|
email = form.cleaned_data.get('email')
|
||||||
|
password = form.cleaned_data.get('password')
|
||||||
|
|
||||||
|
CustomUser.register(name, password, email)
|
||||||
|
auth_user = authenticate(email=email, password=password)
|
||||||
|
login(self.request, auth_user)
|
||||||
|
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetView(PasswordResetViewMixin):
|
||||||
|
template_name = 'nosystemd/reset_password.html'
|
||||||
|
success_url = reverse_lazy('nosystemd:login')
|
||||||
|
form_class = PasswordResetRequestForm
|
||||||
|
template_email_path = 'nosystemd/emails/'
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetConfirmView(PasswordResetConfirmViewMixin):
|
||||||
|
template_name = 'nosystemd/confirm_reset_password.html'
|
||||||
|
success_url = reverse_lazy('nosystemd:login')
|
||||||
|
|
||||||
|
|
||||||
|
class DonationView(LoginRequiredMixin, FormView):
|
||||||
|
template_name = 'nosystemd/donation.html'
|
||||||
|
login_url = reverse_lazy('nosystemd:login')
|
||||||
|
form_class = BillingAddressForm
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(DonationView, self).get_context_data(**kwargs)
|
||||||
|
context.update({
|
||||||
|
'stripe_key': settings.STRIPE_API_PUBLIC_KEY
|
||||||
|
})
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
# def post(self, request, *args, **kwargs):
|
||||||
|
# form = self.get_form()
|
||||||
|
|
||||||
|
# if form.is_valid():
|
||||||
|
# context = self.get_context_data()
|
||||||
|
# specifications = request.session.get('vm_specs')
|
||||||
|
# vm_type = specifications.get('hosting_company')
|
||||||
|
# vm = VirtualMachineType.objects.get(hosting_company=vm_type)
|
||||||
|
# final_price = vm.calculate_price(specifications)
|
||||||
|
|
||||||
|
# plan_data = {
|
||||||
|
# 'vm_type': vm,
|
||||||
|
# 'cores': specifications.get('cores'),
|
||||||
|
# 'memory': specifications.get('memory'),
|
||||||
|
# 'disk_size': specifications.get('disk_size'),
|
||||||
|
# 'configuration': specifications.get('configuration'),
|
||||||
|
# 'price': final_price
|
||||||
|
# }
|
||||||
|
# token = form.cleaned_data.get('token')
|
||||||
|
|
||||||
|
# # Get or create stripe customer
|
||||||
|
# customer = StripeCustomer.get_or_create(email=self.request.user.email,
|
||||||
|
# token=token)
|
||||||
|
# if not customer:
|
||||||
|
# form.add_error("__all__", "Invalid credit card")
|
||||||
|
# return self.render_to_response(self.get_context_data(form=form))
|
||||||
|
|
||||||
|
# # Create Virtual Machine Plan
|
||||||
|
# plan = VirtualMachinePlan.create(plan_data, request.user)
|
||||||
|
|
||||||
|
# # Create Billing Address
|
||||||
|
# billing_address = form.save()
|
||||||
|
|
||||||
|
# # Create a Hosting Order
|
||||||
|
# order = HostingOrder.create(vm_plan=plan, customer=customer,
|
||||||
|
# billing_address=billing_address)
|
||||||
|
|
||||||
|
# # Make stripe charge to a customer
|
||||||
|
# stripe_utils = StripeUtils()
|
||||||
|
# charge_response = stripe_utils.make_charge(amount=final_price,
|
||||||
|
# customer=customer.stripe_id)
|
||||||
|
# charge = charge_response.get('response_object')
|
||||||
|
|
||||||
|
# # Check if the payment was approved
|
||||||
|
# if not charge:
|
||||||
|
# context.update({
|
||||||
|
# 'paymentError': charge_response.get('error'),
|
||||||
|
# 'form': form
|
||||||
|
# })
|
||||||
|
# return render(request, self.template_name, context)
|
||||||
|
|
||||||
|
# charge = charge_response.get('response_object')
|
||||||
|
|
||||||
|
# # Associate an order with a stripe payment
|
||||||
|
# order.set_stripe_charge(charge)
|
||||||
|
|
||||||
|
# # If the Stripe payment was successed, set order status approved
|
||||||
|
# order.set_approved()
|
||||||
|
|
||||||
|
# # Send notification to ungleich as soon as VM has been booked
|
||||||
|
# # TODO send email using celery
|
||||||
|
|
||||||
|
# context = {
|
||||||
|
# 'vm': plan,
|
||||||
|
# 'order': order,
|
||||||
|
# 'base_url': "{0}://{1}".format(request.scheme, request.get_host())
|
||||||
|
|
||||||
|
# }
|
||||||
|
# email_data = {
|
||||||
|
# 'subject': 'New VM request',
|
||||||
|
# 'to': request.user.email,
|
||||||
|
# 'context': context,
|
||||||
|
# 'template_name': 'new_booked_vm',
|
||||||
|
# 'template_path': 'hosting/emails/'
|
||||||
|
# }
|
||||||
|
# email = BaseEmail(**email_data)
|
||||||
|
# email.send()
|
||||||
|
|
||||||
|
# return HttpResponseRedirect(reverse('hosting:orders', kwargs={'pk': order.id}))
|
||||||
|
# else:
|
||||||
|
# return self.form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,26 @@ from membership.models import CustomUser
|
||||||
# from utils.fields import CountryField
|
# from utils.fields import CountryField
|
||||||
|
|
||||||
|
|
||||||
|
class SignupFormMixin(forms.ModelForm):
|
||||||
|
|
||||||
|
confirm_password = forms.CharField(widget=forms.PasswordInput())
|
||||||
|
password = forms.CharField(widget=forms.PasswordInput())
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CustomUser
|
||||||
|
fields = ['name', 'email', 'password']
|
||||||
|
widgets = {
|
||||||
|
'name': forms.TextInput(attrs={'placeholder': 'Enter your name or company name'}),
|
||||||
|
}
|
||||||
|
|
||||||
|
def clean_confirm_password(self):
|
||||||
|
password = self.cleaned_data.get('password')
|
||||||
|
confirm_password = self.cleaned_data.get('confirm_password')
|
||||||
|
if not confirm_password == password:
|
||||||
|
raise forms.ValidationError("Passwords don't match")
|
||||||
|
return confirm_password
|
||||||
|
|
||||||
|
|
||||||
class LoginFormMixin(forms.Form):
|
class LoginFormMixin(forms.Form):
|
||||||
email = forms.CharField(widget=forms.EmailInput())
|
email = forms.CharField(widget=forms.EmailInput())
|
||||||
password = forms.CharField(widget=forms.PasswordInput())
|
password = forms.CharField(widget=forms.PasswordInput())
|
||||||
|
|
|
@ -1,3 +1,82 @@
|
||||||
from django.shortcuts import render
|
from django.views.generic import FormView
|
||||||
|
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from django.utils.encoding import force_bytes
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
|
from membership.models import CustomUser
|
||||||
|
|
||||||
|
from .mailer import BaseEmail
|
||||||
|
from .forms import SetPasswordForm
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetViewMixin(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')
|
||||||
|
|
||||||
|
def test_generate_email_context(self, user):
|
||||||
|
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())
|
||||||
|
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
|
||||||
|
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 = self.test_generate_email_context(user)
|
||||||
|
email_data = {
|
||||||
|
'subject': 'Password Reset',
|
||||||
|
'to': email,
|
||||||
|
'context': context,
|
||||||
|
'template_name': 'password_reset_email',
|
||||||
|
'template_path': self.template_email_path
|
||||||
|
}
|
||||||
|
email = BaseEmail(**email_data)
|
||||||
|
email.send()
|
||||||
|
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetConfirmViewMixin(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.')
|
||||||
|
form.add_error(None, 'Password reset has not been unsuccessful.')
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
else:
|
||||||
|
messages.error(request, 'The reset password link is no longer valid.')
|
||||||
|
form.add_error(None, 'Password reset has not been unsuccessful.')
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
Loading…
Reference in a new issue