diff --git a/userena/forms.py b/userena/forms.py index dff9c0bf..92faf1dd 100644 --- a/userena/forms.py +++ b/userena/forms.py @@ -45,6 +45,10 @@ class SignupForm(forms.Form): render_value=False), label=_("Repeat password")) + def __init__(self, redirect=None, *args, **kwargs): + self.redirect = redirect + super(SignupForm, self).__init__(*args, **kwargs) + def clean_username(self): """ Validate that the username is alphanumeric and is not already in use. @@ -94,7 +98,8 @@ def save(self): email, password, not userena_settings.USERENA_ACTIVATION_REQUIRED, - userena_settings.USERENA_ACTIVATION_REQUIRED) + userena_settings.USERENA_ACTIVATION_REQUIRED, + redirect=self.redirect) return new_user class SignupFormOnlyEmail(SignupForm): diff --git a/userena/managers.py b/userena/managers.py index a4d91c09..dfc25d86 100644 --- a/userena/managers.py +++ b/userena/managers.py @@ -36,7 +36,7 @@ class UserenaManager(UserManager): """ Extra functionality for the Userena model. """ def create_user(self, username, email, password, active=False, - send_email=True): + send_email=True, redirect=None): """ A simple wrapper that creates a new :class:`User`. @@ -78,7 +78,7 @@ def create_user(self, username, email, password, active=False, userena_profile = self.create_userena_profile(new_user) if send_email: - userena_profile.send_activation_email() + userena_profile.send_activation_email(redirect=redirect) return new_user diff --git a/userena/models.py b/userena/models.py index a23d60be..3e7c50ed 100644 --- a/userena/models.py +++ b/userena/models.py @@ -156,11 +156,11 @@ def activation_key_expired(self): return True return False - def send_activation_email(self): + def send_activation_email(self, redirect=None): """ Sends a activation email to the user. - This email is send when the user wants to activate their newly created + This email is sent when the user wants to activate their newly created user. """ @@ -169,7 +169,8 @@ def send_activation_email(self): 'protocol': get_protocol(), 'activation_days': userena_settings.USERENA_ACTIVATION_DAYS, 'activation_key': self.activation_key, - 'site': Site.objects.get_current()} + 'site': Site.objects.get_current(), + 'redirect': redirect} mailer = UserenaConfirmationMail(context=context) mailer.generate_mail("activation") diff --git a/userena/templates/userena/emails/activation_email_message.html b/userena/templates/userena/emails/activation_email_message.html index 9f50e037..c3fcb6ae 100644 --- a/userena/templates/userena/emails/activation_email_message.html +++ b/userena/templates/userena/emails/activation_email_message.html @@ -5,7 +5,7 @@ {% blocktrans with site.name as site %}
Thank you for signing up at {{ site }}.
{% endblocktrans %}
{% trans "To activate your account you should click on the link below:" %}
- {{ protocol }}://{{ site.domain }}{% url 'userena_activate' activation_key %}
+ {{ protocol }}://{{ site.domain }}{% url 'userena_activate' activation_key %}{% if redirect %}?next={{redirect}}{% endif %}
{% trans "Thanks for using our site!" %}
diff --git a/userena/templates/userena/emails/activation_email_message.txt b/userena/templates/userena/emails/activation_email_message.txt
index 7bf5d631..e1a50888 100644
--- a/userena/templates/userena/emails/activation_email_message.txt
+++ b/userena/templates/userena/emails/activation_email_message.txt
@@ -5,7 +5,7 @@
{% trans "To activate your account you should click on the link below:" %}
-{{ protocol }}://{{ site.domain }}{% url 'userena_activate' activation_key %}
+{{ protocol }}://{{ site.domain }}{% url 'userena_activate' activation_key %}{% if redirect %}?next={{redirect}}{% endif %}
{% trans "Thanks for using our site!" %}
diff --git a/userena/templates/userena/signup_form.html b/userena/templates/userena/signup_form.html
index f10283b6..e2bc01fd 100644
--- a/userena/templates/userena/signup_form.html
+++ b/userena/templates/userena/signup_form.html
@@ -25,5 +25,6 @@
{% endfor %}
+ {% if next %}{% endif %}
{% endblock %}
diff --git a/userena/tests/tests_views.py b/userena/tests/tests_views.py
index 6860332a..53253370 100644
--- a/userena/tests/tests_views.py
+++ b/userena/tests/tests_views.py
@@ -1,7 +1,7 @@
import re
from datetime import datetime, timedelta
-from django.contrib.auth import get_user_model
+from django.contrib.auth import get_user_model, REDIRECT_FIELD_NAME
from django.core.urlresolvers import reverse
from django.core import mail
from django.contrib.auth.forms import PasswordChangeForm
@@ -36,6 +36,30 @@ def test_valid_activation(self):
user = User.objects.get(email='alice@example.com')
self.assertTrue(user.is_active)
+ def test_activation_redirect(self):
+ """ A ``GET`` to the activation view with redirect parameter """
+
+ redirect = '/some/url/'
+
+ # First, register an account.
+ self.client.post('%s?%s=%s' % (reverse('userena_signup'), REDIRECT_FIELD_NAME, redirect),
+ data={'username': 'alice',
+ 'email': 'alice@example.com',
+ 'password1': 'swordfish',
+ 'password2': 'swordfish',
+ 'tos': 'on'})
+ user = User.objects.get(email='alice@example.com')
+
+ # Send a GET request with the redirect parameter to the url
+ response = self.client.get(
+ '%s?%s=%s' % (reverse('userena_activate', kwargs={'activation_key': user.userena_signup.activation_key}),
+ REDIRECT_FIELD_NAME, redirect))
+
+ self.assertTrue(response.get('Location').endswith(redirect))
+
+ if hasattr(response, 'url'):
+ self.assertTrue(response.url.endswith(redirect))
+
def test_activation_expired_retry(self):
""" A ``GET`` to the activation view when activation link is expired """
# First, register an account.
diff --git a/userena/views.py b/userena/views.py
index a06d7214..064411f0 100644
--- a/userena/views.py
+++ b/userena/views.py
@@ -4,6 +4,7 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.views import logout as Signout
+from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import TemplateView
from django.views.generic.list import ListView
from django.contrib import messages
@@ -69,16 +70,19 @@ def get_queryset(self):
queryset = profile_model.objects.get_visible_profiles(self.request.user).select_related()
return queryset
+
+@sensitive_post_parameters('password1', 'password2')
@secure_required
def signup(request, signup_form=SignupForm,
template_name='userena/signup_form.html', success_url=None,
- extra_context=None):
+ extra_context=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
Signup of an account.
Signup requiring a username, email and password. After signup a user gets
an email with an activation link used to activate their account. After
- successful signup redirects to ``success_url``.
+ successful signup redirects to the ``REDIRECT_FIELD_NAME`` query parameter
+ if provided, otherwise ``success_url``.
:param signup_form:
Form that will be used to sign a user. Defaults to userena's
@@ -98,10 +102,16 @@ def signup(request, signup_form=SignupForm,
context. Defaults to a dictionary with a ``form`` key containing the
``signup_form``.
+ :param redirect_field_name:
+ String containing the name of the field that contains a redirect url.
+ Defaults to ``next``.
+
**Context**
``form``
Form supplied by ``signup_form``.
+ ``next``
+ Next supplied by a query parameter.
"""
# If signup is disabled, return 403
@@ -115,8 +125,11 @@ def signup(request, signup_form=SignupForm,
form = signup_form()
+ redirect_url = request.GET.get(redirect_field_name, request.POST.get(redirect_field_name, None))
+
if request.method == 'POST':
- form = signup_form(request.POST, request.FILES)
+ form = signup_form(redirect=redirect_url, data=request.POST, files=request.FILES)
+
if form.is_valid():
user = form.save()
@@ -124,7 +137,6 @@ def signup(request, signup_form=SignupForm,
userena_signals.signup_complete.send(sender=None,
user=user)
-
if success_url: redirect_to = success_url
else: redirect_to = reverse('userena_signup_complete',
kwargs={'username': user.username})
@@ -138,10 +150,18 @@ def signup(request, signup_form=SignupForm,
user = authenticate(identification=user.email, check_password=False)
login(request, user)
+ # since we signed in the user and have no more information to display,
+ # we can redirect the user to a requested page
+ if redirect_url:
+ redirect_to = redirect_url
+
return redirect(redirect_to)
if not extra_context: extra_context = dict()
- extra_context['form'] = form
+ extra_context.update({
+ 'form': form,
+ 'next': redirect_url,
+ })
return ExtraContextTemplateView.as_view(template_name=template_name,
extra_context=extra_context)(request)
@@ -149,17 +169,19 @@ def signup(request, signup_form=SignupForm,
def activate(request, activation_key,
template_name='userena/activate_fail.html',
retry_template_name='userena/activate_retry.html',
- success_url=None, extra_context=None):
+ success_url=None, extra_context=None,
+ redirect_field_name=REDIRECT_FIELD_NAME):
"""
Activate a user with an activation key.
The key is a SHA1 string. When the SHA1 is found with an
:class:`UserenaSignup`, the :class:`User` of that account will be
activated. After a successful activation the view will redirect to
- ``success_url``. If the SHA1 is not found, the user will be shown the
- ``template_name`` template displaying a fail message.
- If the SHA1 is found but expired, ``retry_template_name`` is used instead,
- so the user can proceed to :func:`activate_retry` to get a new activation key.
+ the ``REDIRECT_FIELD_NAME`` query parameter if provided, otherwise
+ ``success_url``. If the SHA1 is not found, the user will be shown the
+ ``template_name`` template displaying a fail message. If the SHA1 is found
+ but expired, ``retry_template_name`` is used instead, so the user can
+ proceed to :func:`activate_retry` to get a new activation key.
:param activation_key:
String of a SHA1 string of 40 characters long. A SHA1 is always 160bit
@@ -201,7 +223,10 @@ def activate(request, activation_key,
messages.success(request, _('Your account has been activated and you have been signed in.'),
fail_silently=True)
- if success_url: redirect_to = success_url % {'username': user.username }
+ redirect_to = request.GET.get(redirect_field_name, None)
+
+ if redirect_to: pass
+ elif success_url: redirect_to = success_url % {'username': user.username }
else: redirect_to = reverse('userena_profile_detail',
kwargs={'username': user.username})
return redirect(redirect_to)
@@ -386,6 +411,8 @@ def disabled_account(request, username, template_name, extra_context=None):
return ExtraContextTemplateView.as_view(template_name=template_name,
extra_context=extra_context)(request)
+
+@sensitive_post_parameters('password')
@secure_required
def signin(request, auth_form=AuthenticationForm,
template_name='userena/signin_form.html',
@@ -563,6 +590,8 @@ def email_change(request, username, email_form=ChangeEmailForm,
return ExtraContextTemplateView.as_view(template_name=template_name,
extra_context=extra_context)(request)
+
+@sensitive_post_parameters('old_password', 'new_password1', 'new_password2')
@secure_required
@permission_required_or_403('change_user', (get_user_model(), 'username', 'username'))
def password_change(request, username, template_name='userena/password_form.html',