3 from django
import forms
4 from django
.core
.validators
import RegexValidator
5 from django
.core
.exceptions
import ValidationError
6 from django
.db
import IntegrityError
, transaction
7 from django
.http
import HttpResponseRedirect
8 from django
.views
.generic
.edit
import FormView
9 from django
.utils
.translation
import ugettext
as _
10 from django
.template
.loader
import render_to_string
11 from django
.core
.urlresolvers
import reverse
, reverse_lazy
12 from django
.views
.generic
import TemplateView
13 from django
.views
.generic
.base
import View
14 from django
.contrib
import messages
15 from django
.contrib
.auth
import get_user_model
16 from django
.contrib
.sites
.requests
import RequestSite
18 from mygpo
.utils
import random_token
19 from mygpo
.users
.models
import UserProxy
22 USERNAME_MAXLEN
= get_user_model()._meta
.get_field('username').max_length
24 USERNAME_REGEX
= re
.compile(r
'^\w[\w.+-]*$')
27 class DuplicateUsername(ValidationError
):
28 """ The username is already in use """
30 def __init__(self
, username
):
31 self
.username
= username
32 super().__init
__('The username {0} is already in use.'
36 class DuplicateEmail(ValidationError
):
37 """ The email address is already in use """
39 def __init__(self
, email
):
41 super().__init
__('The email address {0} is already in use.'
45 class UsernameValidator(RegexValidator
):
46 """ Validates that a username uses only allowed characters """
47 regex
= USERNAME_REGEX
48 message
= 'Invalid Username'
49 code
='invalid-username'
52 class RegistrationForm(forms
.Form
):
53 """ Form that is used to register a new user """
54 username
= forms
.CharField(max_length
=USERNAME_MAXLEN
,
55 validators
=[UsernameValidator()],
57 email
= forms
.EmailField()
58 password1
= forms
.CharField(widget
=forms
.PasswordInput())
59 password2
= forms
.CharField(widget
=forms
.PasswordInput())
62 cleaned_data
= super(RegistrationForm
, self
).clean()
63 password1
= cleaned_data
.get('password1')
64 password2
= cleaned_data
.get('password2')
66 if not password1
or password1
!= password2
:
67 raise forms
.ValidationError('Passwords do not match')
70 class RegistrationView(FormView
):
71 """ View to register a new user """
72 template_name
= 'registration/registration_form.html'
73 form_class
= RegistrationForm
74 success_url
= reverse_lazy('registration-complete')
76 def form_valid(self
, form
):
77 """ called whene the form was POSTed and its contents were valid """
80 user
= self
.create_user(form
)
82 except ValidationError
as e
:
83 messages
.error(self
.request
, '; '.join(e
.messages
))
84 return HttpResponseRedirect(reverse('register'))
86 except IntegrityError
:
87 messages
.error(self
.request
,
88 _('Username or email address already in use'))
89 return HttpResponseRedirect(reverse('register'))
91 send_activation_email(user
, self
.request
)
92 return super(RegistrationView
, self
).form_valid(form
)
95 def create_user(self
, form
):
96 User
= get_user_model()
98 username
= form
.cleaned_data
['username']
100 self
._check
_username
(username
)
101 user
.username
= username
103 email_addr
= form
.cleaned_data
['email']
104 user
.email
= email_addr
106 user
.set_password(form
.cleaned_data
['password1'])
107 user
.is_active
= False
113 except IntegrityError
as e
:
114 if 'django_auth_unique_email' in str(e
):
115 # this was not caught by the form validation, but now validates
116 # the DB's unique constraint
117 raise DuplicateEmail(email_addr
) from e
121 user
.profile
.activation_key
= random_token()
126 def _check_username(self
, username
):
127 """ Check if the username is already in use
129 Until there is a case-insensitive constraint on usernames, it is
130 necessary to check for existing usernames manually. This is not a
131 perfect solution, but the chance that two people sign up with the same
132 username at the same time is low enough. """
133 UserModel
= get_user_model()
134 users
= UserModel
.objects
.filter(username__iexact
=username
)
136 raise DuplicateUsername(username
)
139 class ActivationView(TemplateView
):
140 """ Activates an already registered user """
142 template_name
= 'registration/activation_failed.html'
144 def get(self
, request
, activation_key
):
145 User
= get_user_model()
148 user
= UserProxy
.objects
.get(
149 profile__activation_key
=activation_key
,
152 except UserProxy
.DoesNotExist
:
153 messages
.error(request
, _('The activation link is either not '
154 'valid or has already expired.'))
155 return super(ActivationView
, self
).get(request
, activation_key
)
158 messages
.success(request
, _('Your user has been activated. '
159 'You can log in now.'))
160 return HttpResponseRedirect(reverse('login'))
163 class ResendActivationForm(forms
.Form
):
164 """ Form for resending the activation email """
166 username
= forms
.CharField(max_length
=USERNAME_MAXLEN
, required
=False)
167 email
= forms
.EmailField(required
=False)
170 cleaned_data
= super(ResendActivationForm
, self
).clean()
171 username
= cleaned_data
.get('username')
172 email
= cleaned_data
.get('email')
174 if not username
and not email
:
175 raise forms
.ValidationError(_('Either username or email address '
179 class ResendActivationView(FormView
):
180 """ View to resend the activation email """
181 template_name
= 'registration/resend_activation.html'
182 form_class
= ResendActivationForm
183 success_url
= reverse_lazy('resent-activation')
185 def form_valid(self
, form
):
186 """ called whene the form was POSTed and its contents were valid """
189 user
= UserProxy
.objects
.all().by_username_or_email(
190 form
.cleaned_data
['username'],
191 form
.cleaned_data
['email'],
194 except UserProxy
.DoesNotExist
:
195 messages
.error(self
.request
, _('User does not exist.'))
196 return HttpResponseRedirect(reverse('resend-activation'))
198 if user
.profile
.activation_key
is None:
199 messages
.success(self
.request
, _('Your account already has been '
200 'activated. Go ahead and log in.'))
202 send_activation_email(user
, self
.request
)
203 return super(ResendActivationView
, self
).form_valid(form
)
206 class ResentActivationView(TemplateView
):
207 template_name
= 'registration/resent_activation.html'
210 def send_activation_email(user
, request
):
211 """ Sends the activation email for the given user """
213 subj
= render_to_string('registration/activation_email_subject.txt')
214 # remove trailing newline added by render_to_string
217 msg
= render_to_string('registration/activation_email.txt', {
218 'site': RequestSite(request
),
219 'activation_key': user
.profile
.activation_key
,
221 user
.email_user(subj
, msg
)