Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes/registration with activation #589

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions example/myshop/templates/myshop/pages/password-reset-confirm.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@

{% block title %}{% trans "Password Reset Confirm" %}{% endblock %}

{% block breadcrumb %}{% endblock %}
{% block breadcrumb %}
<div class="container">
<div class="row">
<div class="col-xs-12">
<ol class="breadcrumb">
<li class="active"><a href="{{ request.path }}">{% trans "Set Account Password" %}</a></li>
</ol>
</div>
</div>
</div>
{% endblock %}

{% block main-content %}
<div class="row">
<div class="col-xs-12">

<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2">
{% if validlink %}
{% page_url 'shop-cart' as action %}
{% include "shop/auth/password-reset-confirm.html" %}
{% page_url 'shop-customer-details' as customer_details_url %}
{% include "shop/auth/password-reset-confirm.html" with action=customer_details_url|default:'DO_NOTHING' form_name="password_reset_confirm" %}
{% else %}
{% include "shop/auth/password-reset-decline.html" %}
{% endif %}

</div>
</div>
</div>
{% endblock main-content %}
4 changes: 2 additions & 2 deletions example/myshop/urls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ def render_robots(request):
permission = 'noindex' in settings.ROBOTS_META_TAGS and 'Disallow' or 'Allow'
return HttpResponse('User-Agent: *\n%s: /\n' % permission, content_type='text/plain')

i18n_urls = (
i18n_urls = [
url(r'^admin/', include(admin.site.urls)),
url(r'^password-reset-confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/?$',
PasswordResetConfirm.as_view(template_name='myshop/pages/password-reset-confirm.html'),
name='password_reset_confirm'),
url(r'^', include('cms.urls')),
)
]
urlpatterns = [
url(r'^robots\.txt$', render_robots),
url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, name='sitemap'),
Expand Down
7 changes: 4 additions & 3 deletions shop/cascade/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
('reset', _("Password Reset Form")),
('change', _("Change Password Form")),
('register-user', _("Register User"), 'shop.forms.auth.RegisterUserForm'),
('register-user-activate', _("Register User with Activation"), 'shop.forms.auth.RegisterUserActivateForm'),
('continue-as-guest', _("Continue as guest")),
)

Expand All @@ -41,7 +42,7 @@ def clean(self):
class ShopAuthenticationPlugin(ShopLinkPluginBase):
"""
A placeholder plugin which provides various authentication forms, such as login-, logout-,
register-, and other forms. They can be added any placeholder using the Cascade framework.
register-, and other forms. They can be added to any placeholder using the Cascade framework.
"""
name = _("Authentication")
parent_classes = ('BootstrapColumnPlugin',)
Expand Down Expand Up @@ -71,10 +72,10 @@ def render(self, context, instance, placeholder):
form_type = instance.glossary.get('form_type')
if form_type:
# prevent a malicious database entry to import an ineligible file
form_type = AUTH_FORM_TYPES[[ft[0] for ft in AUTH_FORM_TYPES].index(form_type)]
try:
form_type = AUTH_FORM_TYPES[[ft[0] for ft in AUTH_FORM_TYPES].index(form_type)]
FormClass = import_string(form_type[2])
except (ImportError, IndexError):
except (ImportError, IndexError, ValueError):
form_name = form_type[0].replace('-', '_')
context['form_name'] = '{0}_form'.format(form_name)
else:
Expand Down
35 changes: 35 additions & 0 deletions shop/forms/auth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.conf import settings
from django.contrib.auth import get_user_model, authenticate, login
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ValidationError
Expand All @@ -14,6 +15,7 @@

from shop.conf import app_settings
from shop.models.customer import CustomerModel
from shop.rest.auth import PasswordResetSerializer
from .base import UniqueEmailValidationMixin


Expand Down Expand Up @@ -99,6 +101,39 @@ def _send_password(self, request, user, password):
user.email_user(subject, body)


class RegisterUserActivateSerializer(PasswordResetSerializer):
base_template = 'register-user-activate'


class RegisterUserActivateForm(NgModelFormMixin, NgFormValidationMixin, UniqueEmailValidationMixin, Bootstrap3ModelForm):
form_name = 'register_user_form'
scope_prefix = 'form_data'
field_css_classes = 'input-group has-feedback'

email = fields.EmailField(label=_("Your e-mail address"))

class Meta:
model = CustomerModel
fields = ['email']

def save(self, request=None, commit=True):
self.instance.user.is_active = True
self.instance.user.email = self.cleaned_data['email']
password = get_user_model().objects.make_random_password(20)
self.instance.user.set_password(password)
self.instance.user.save()
self.instance.recognize_as_guest(request, commit=False)

serializer = RegisterUserActivateSerializer(data=request.data, context={'request': request})
if serializer.is_valid():
msg = _("A link to activate this account has been sent to '{email}'.")
self.data.update(success=msg.format(**self.cleaned_data))
serializer.save()

customer = super(RegisterUserActivateForm, self).save(commit)
return customer


class ContinueAsGuestForm(ModelForm):
"""
Handles Customer's decision to order as guest.
Expand Down
10 changes: 6 additions & 4 deletions shop/rest/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@


class PasswordResetSerializer(serializers.PasswordResetSerializer):
base_template = 'reset-password'

def save(self):
subject_template = select_template([
'{}/email/reset-password-subject.txt'.format(app_settings.APP_LABEL),
'shop/email/reset-password-subject.txt',
'{}/email/{}-subject.txt'.format(app_settings.APP_LABEL, self.base_template),
'shop/email/{}-subject.txt'.format(self.base_template),
])
body_template = select_template([
'{}/email/reset-password-body.txt'.format(app_settings.APP_LABEL),
'shop/email/reset-password-body.txt',
'{}/email/{}-body.txt'.format(app_settings.APP_LABEL, self.base_template),
'shop/email/{}-body.txt'.format(self.base_template),
])
opts = {
'use_https': self.context['request'].is_secure(),
Expand Down
35 changes: 25 additions & 10 deletions shop/templates/shop/auth/password-reset-confirm.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,33 @@
{{ block.super }}
<div ng-hide="success_message">
<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
<label class="control-label">{% trans "New Password" %}</label>
<div style="margin-bottom: 10px" class="input-group">
<span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span>
<input type="password" class="form-control" name="new_password1" ng-model="form_data.new_password1" placeholder="{% trans 'New password' %}" />

<div class="has-feedback form-group" style="margin-bottom: 10px">
<label class="control-label">{% trans "New Password" %}</label>
<ul ng-messages="password_reset_confirm.new_password1.$error" class="shop-form-error" ng-cloak>
<li ng-show="password_reset_confirm.new_password1.$pristine" ng-message="rejected">
<span ng-bind="password_reset_confirm.new_password1.$message"></span>
</li>
</ul>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span>
<input type="password" class="form-control" name="new_password1" ng-model="form_data.new_password1" placeholder="{% trans 'New password' %}" />
</div>
</div>

<label class="control-label">{% trans "Confirm Password" %}</label>
<div style="margin-bottom: 20px" class="input-group">
<span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span>
<input type="password" class="form-control" name="new_password2" ng-model="form_data.new_password2" placeholder="{% trans 'Confirm password' %}" />

<div class="has-feedback form-group" style="margin-bottom: 20px">
<label class="control-label">{% trans "Confirm Password" %}</label>
<ul ng-messages="password_reset_confirm.new_password2.$error" class="shop-form-error" ng-cloak>
<li ng-show="password_reset_confirm.new_password2.$pristine" ng-message="rejected">
<span ng-bind="password_reset_confirm.new_password2.$message"></span>
</li>
</ul>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span>
<input type="password" class="form-control" name="new_password2" ng-model="form_data.new_password2" placeholder="{% trans 'Confirm password' %}" />
</div>
</div>

<div class="input-group">
<button type="button" ng-click="submitForm('{{ request.path }}', 5000)" class="btn btn-primary">{% trans 'Change my password' %}</button>
</div>
Expand Down
8 changes: 5 additions & 3 deletions shop/templates/shop/auth/password-reset-decline.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
{% load i18n %}
{% load i18n cms_tags %}
<div class="panel panel-danger">
<div class="panel-heading">
<div class="panel-title">{% trans 'Password reset unsuccessful' %}</div>
<div class="panel-title">{% trans "Password can not be reset" %}</div>
</div>
<div class="panel-body">
{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}
{% page_url 'shop-password-reset' as password_reset_url %}
<p>{% trans "The password reset link is invalid, possibly because it has already been used." %}</p>
<p>{% if password_reset_url %}<a href="{{ password_reset_url }}">{% endif %}{% trans "Please request a new password reset." %}{% if password_reset_url %}</a>{% endif %}</p>
</div>
</div>
23 changes: 23 additions & 0 deletions shop/templates/shop/auth/register-user-activate.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% extends "shop/auth/anonymous-base.html" %}
{% load i18n %}

{% block form_title %}{% trans "Register Yourself" %}{% endblock %}

{% block form_content %}
{{ block.super }}
<div class="form-group has-feedback" style="margin-bottom: 20px">
{{ register_user_form.email.label_tag }}
<div class="input-group" ng-class="hasError('email')">
<span class="input-group-addon"><i class="fa fa-envelope-o fa-fw"></i></span>
{{ register_user_form.email }}
</div>
<ul ng-messages="register_user_form.email.$error" class="shop-form-error" ng-cloak>
<li ng-show="register_user_form.email.$dirty" ng-message="email">{% trans "Please provide a valid email address" %}</li>
<li ng-show="register_user_form.email.$pristine" ng-message="rejected"><span ng-bind="register_user_form.email.$message"></span></li>
</ul>
</div>

<div class="form-group">
<button type="button" ng-click="submitForm('{% url 'shop:register-user-activate' %}', 5000)" class="btn btn-primary">{% trans "Register" %}</button>
</div>
{% endblock form_content %}
12 changes: 12 additions & 0 deletions shop/templates/shop/email/register-user-activate-body.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you registered a new account
for '{{ user }}' at {{ site_name }}.{% endblocktrans %}

{% trans "Please go to the following webpage to activate your new account:" %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

{% trans "Thanks for visiting our site!" %}

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

{% endautoescape %}
3 changes: 3 additions & 0 deletions shop/templates/shop/email/register-user-activate-subject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}Activate your new account on {{ site_name }}{% endblocktrans %}
{% endautoescape %}
6 changes: 3 additions & 3 deletions shop/templates/shop/email/reset-password-body.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user
{% blocktrans %}You're receiving this email because you requested to reset the password for your
account '{{ user }}' at {{ site_name }}.{% endblocktrans %}

{% trans "Please go to the following page and choose a new password:" %}
{% trans "Please go to the following webpage and choose a new password:" %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

{% trans "Thanks for using our site!" %}
{% trans "Thanks for visiting our site!" %}

{% blocktrans %}The {{ site_name }} team{% endblocktrans %}

Expand Down
4 changes: 3 additions & 1 deletion shop/urls/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from __future__ import unicode_literals
from django.conf.urls import url
from rest_auth.views import PasswordChangeView
from shop.forms.auth import RegisterUserForm, ContinueAsGuestForm
from shop.forms.auth import RegisterUserForm, RegisterUserActivateForm, ContinueAsGuestForm
from shop.views.auth import AuthFormsView, LoginView, LogoutView, PasswordResetView

urlpatterns = [
Expand All @@ -12,6 +12,8 @@
name='login'),
url(r'^register/?$', AuthFormsView.as_view(form_class=RegisterUserForm),
name='register-user'),
url(r'^register-activate/?$', AuthFormsView.as_view(form_class=RegisterUserActivateForm),
name='register-user-activate'),
url(r'^continue/?$', AuthFormsView.as_view(form_class=ContinueAsGuestForm),
name='continue-as-guest'),

Expand Down
13 changes: 11 additions & 2 deletions shop/views/auth.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.contrib.auth import logout, get_user_model
from django.contrib.auth import logout, get_user_model, authenticate, login
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth.tokens import default_token_generator
from django.utils.encoding import force_text
Expand Down Expand Up @@ -146,4 +146,13 @@ def post(self, request, uidb64=None, token=None):
serializer.errors, status=status.HTTP_400_BAD_REQUEST
)
serializer.save()
return Response({"success": _("Password has been reset with the new password.")})

# TODO: the following code can be simplified to ``customer = serializer.save().customer``
# whenever https://github.com/Tivix/django-rest-auth/pull/334 has been merged
customer = serializer.set_password_form.user.customer
customer.recognize_as_registered(request)
user = authenticate(username=customer.user.username, password=serializer.data['new_password1'])
login(request, user)

msg = _("The password for '{email}' has been reset by a new password.")
return Response({'success': msg.format(email=customer.email)})