Revision 890b0eaf astakos/im/backends/__init__.py
b/astakos/im/backends/__init__.py | ||
---|---|---|
33 | 33 |
|
34 | 34 |
from django.conf import settings |
35 | 35 |
from django.utils.importlib import import_module |
36 |
from django.core.exceptions import ImproperlyConfigured |
|
37 |
from django.core.mail import send_mail |
|
38 |
from django.template.loader import render_to_string |
|
39 |
from django.utils.translation import ugettext as _ |
|
40 |
from django.contrib.auth.forms import UserCreationForm |
|
41 |
from django.contrib import messages |
|
42 |
|
|
43 |
from smtplib import SMTPException |
|
44 |
from urllib import quote |
|
45 |
|
|
46 |
from astakos.im.util import get_or_create_user |
|
47 |
from astakos.im.models import AstakosUser, Invitation |
|
48 |
from astakos.im.forms import ExtendedUserCreationForm, InvitedExtendedUserCreationForm |
|
36 | 49 |
|
37 | 50 |
def get_backend(): |
38 | 51 |
""" |
39 | 52 |
Return an instance of a registration backend, |
40 |
according to the INVITATIONS_ENABLED setting. |
|
41 |
|
|
53 |
according to the INVITATIONS_ENABLED setting |
|
54 |
(if True returns ``astakos.im.backends.InvitationsBackend`` and if False |
|
55 |
returns ``astakos.im.backends.SimpleBackend``). |
|
56 |
|
|
57 |
If the backend cannot be located ``django.core.exceptions.ImproperlyConfigured`` |
|
58 |
is raised. |
|
42 | 59 |
""" |
43 |
module = 'invitations' if settings.INVITATIONS_ENABLED else 'simple'
|
|
44 |
module = 'astakos.im.backends.%s' %module
|
|
45 |
backend_class_name = 'Backend'
|
|
60 |
module = 'astakos.im.backends'
|
|
61 |
prefix = 'Invitations' if settings.INVITATIONS_ENABLED else 'Simple'
|
|
62 |
backend_class_name = '%sBackend' %prefix
|
|
46 | 63 |
try: |
47 | 64 |
mod = import_module(module) |
48 | 65 |
except ImportError, e: |
... | ... | |
51 | 68 |
backend_class = getattr(mod, backend_class_name) |
52 | 69 |
except AttributeError: |
53 | 70 |
raise ImproperlyConfigured('Module "%s" does not define a registration backend named "%s"' % (module, attr)) |
54 |
return backend_class() |
|
71 |
return backend_class() |
|
72 |
|
|
73 |
class InvitationsBackend(object): |
|
74 |
""" |
|
75 |
A registration backend which implements the following workflow: a user |
|
76 |
supplies the necessary registation information, if the request contains a valid |
|
77 |
inivation code the user is automatically activated otherwise an inactive user |
|
78 |
account is created and the user is going to receive an email as soon as an |
|
79 |
administrator activates his/her account. |
|
80 |
""" |
|
81 |
def get_signup_form(self, request): |
|
82 |
""" |
|
83 |
Returns the necassary registration form depending the user is invited or not |
|
84 |
|
|
85 |
Throws Invitation.DoesNotExist in case ``code`` is not valid. |
|
86 |
""" |
|
87 |
code = request.GET.get('code', '') |
|
88 |
formclass = 'ExtendedUserCreationForm' |
|
89 |
if request.method == 'GET': |
|
90 |
initial_data = None |
|
91 |
if code: |
|
92 |
formclass = 'Invited%s' %formclass |
|
93 |
self.invitation = Invitation.objects.get(code=code) |
|
94 |
if self.invitation.is_consumed: |
|
95 |
return HttpResponseBadRequest('Invitation has beeen used') |
|
96 |
initial_data.update({'username':self.invitation.username, |
|
97 |
'email':self.invitation.username, |
|
98 |
'realname':self.invitation.realname}) |
|
99 |
inviter = AstakosUser.objects.get(username=self.invitation.inviter) |
|
100 |
initial_data['inviter'] = inviter.realname |
|
101 |
else: |
|
102 |
initial_data = request.POST |
|
103 |
self.form = globals()[formclass](initial_data) |
|
104 |
return self.form |
|
105 |
|
|
106 |
def _is_preaccepted(self, user): |
|
107 |
""" |
|
108 |
If there is a valid, not-consumed invitation code for the specific user |
|
109 |
returns True else returns False. |
|
110 |
|
|
111 |
It should be called after ``get_signup_form`` which sets invitation if exists. |
|
112 |
""" |
|
113 |
invitation = getattr(self, 'invitation') if hasattr(self, 'invitation') else None |
|
114 |
if not invitation: |
|
115 |
return False |
|
116 |
if invitation.username == user.username and not invitation.is_consumed: |
|
117 |
return True |
|
118 |
return False |
|
119 |
|
|
120 |
def signup(self, request): |
|
121 |
""" |
|
122 |
Creates a incative user account. If the user is preaccepted (has a valid |
|
123 |
invitation code) the user is activated and if the request param ``next`` |
|
124 |
is present redirects to it. |
|
125 |
In any other case the method returns the action status and a message. |
|
126 |
""" |
|
127 |
kwargs = {} |
|
128 |
form = self.form |
|
129 |
user = form.save(commit=False) |
|
130 |
|
|
131 |
try: |
|
132 |
if self._is_preaccepted(user): |
|
133 |
user.is_active = True |
|
134 |
message = _('Registration completed. You can now login.') |
|
135 |
next = request.POST.get('next') |
|
136 |
if next: |
|
137 |
return redirect(next) |
|
138 |
else: |
|
139 |
message = _('Registration completed. You will receive an email upon your account\'s activation') |
|
140 |
status = messages.SUCCESS |
|
141 |
except Invitation.DoesNotExist, e: |
|
142 |
status = messages.ERROR |
|
143 |
message = _('Invalid invitation code') |
|
144 |
return status, message |
|
145 |
|
|
146 |
class SimpleBackend(object): |
|
147 |
""" |
|
148 |
A registration backend which implements the following workflow: a user |
|
149 |
supplies the necessary registation information, an incative user account is |
|
150 |
created and receives an email in order to activate his/her account. |
|
151 |
""" |
|
152 |
def get_signup_form(self, request): |
|
153 |
""" |
|
154 |
Returns the UserCreationForm |
|
155 |
""" |
|
156 |
initial_data = request.POST if request.method == 'POST' else None |
|
157 |
return UserCreationForm(initial_data) |
|
158 |
|
|
159 |
def signup(self, request, email_template_name='activation_email.txt'): |
|
160 |
""" |
|
161 |
Creates an inactive user account and sends a verification email. |
|
162 |
|
|
163 |
** Arguments ** |
|
164 |
|
|
165 |
``email_template_name`` |
|
166 |
A custom template for the verification email body to use. This is |
|
167 |
optional; if not specified, this will default to |
|
168 |
``activation_email.txt``. |
|
169 |
|
|
170 |
** Templates ** |
|
171 |
activation_email.txt or ``email_template_name`` keyword argument |
|
172 |
|
|
173 |
** Settings ** |
|
174 |
|
|
175 |
* ACTIVATION_LOGIN_TARGET: Where users should activate their local account |
|
176 |
* DEFAULT_CONTACT_EMAIL: service support email |
|
177 |
* DEFAULT_FROM_EMAIL: from email |
|
178 |
""" |
|
179 |
kwargs = {} |
|
180 |
form = self.form |
|
181 |
user = form.save(commit=False) |
|
182 |
status = messages.SUCCESS |
|
183 |
try: |
|
184 |
_send_verification(request, user, email_template_name) |
|
185 |
message = _('Verification sent to %s' % user.email) |
|
186 |
except (SMTPException, socket.error) as e: |
|
187 |
status = messages.ERROR |
|
188 |
name = 'strerror' |
|
189 |
message = getattr(e, name) if hasattr(e, name) else e |
|
190 |
return status, message |
|
191 |
|
|
192 |
def _send_verification(request, user, template_name): |
|
193 |
site = get_current_site(request) |
|
194 |
baseurl = request.build_absolute_uri('/').rstrip('/') |
|
195 |
url = settings.ACTIVATION_LOGIN_TARGET % (baseurl, |
|
196 |
quote(user.auth_token), |
|
197 |
quote(baseurl)) |
|
198 |
message = render_to_string(template_name, { |
|
199 |
'user': user, |
|
200 |
'url': url, |
|
201 |
'baseurl': baseurl, |
|
202 |
'site_name': site.name, |
|
203 |
'support': settings.DEFAULT_CONTACT_EMAIL}) |
|
204 |
sender = settings.DEFAULT_FROM_EMAIL |
|
205 |
send_mail('Pithos account activation', message, sender, [user.email]) |
|
206 |
logging.info('Sent activation %s', user) |
Also available in: Unified diff