Revision 890b0eaf astakos/im/views.py
b/astakos/im/views.py | ||
---|---|---|
54 | 54 |
from django.utils.http import urlencode |
55 | 55 |
from django.utils.translation import ugettext as _ |
56 | 56 |
from django.core.urlresolvers import reverse |
57 |
from django.contrib.auth.forms import AuthenticationForm |
|
58 |
from django.contrib.auth.models import AnonymousUser |
|
59 |
from django.contrib.auth.decorators import login_required |
|
60 |
from django.contrib.sites.models import get_current_site |
|
61 |
from django.contrib import messages |
|
62 |
from django.db import transaction |
|
63 |
from django.contrib.auth.forms import UserCreationForm |
|
57 | 64 |
|
58 | 65 |
#from astakos.im.openid_store import PithosOpenIDStore |
59 | 66 |
from astakos.im.models import AstakosUser, Invitation |
60 | 67 |
from astakos.im.util import isoformat, get_or_create_user, get_context |
61 |
from astakos.im.forms import * |
|
62 | 68 |
from astakos.im.backends import get_backend |
69 |
from astakos.im.forms import ProfileForm, FeedbackForm |
|
63 | 70 |
|
64 | 71 |
def render_response(template, tab=None, status=200, context_instance=None, **kwargs): |
72 |
""" |
|
73 |
Calls ``django.template.loader.render_to_string`` with an additional ``tab`` |
|
74 |
keyword argument and returns an ``django.http.HttpResponse`` with the |
|
75 |
specified ``status``. |
|
76 |
""" |
|
65 | 77 |
if tab is None: |
66 | 78 |
tab = template.partition('_')[0] |
67 | 79 |
kwargs.setdefault('tab', tab) |
68 | 80 |
html = render_to_string(template, kwargs, context_instance=context_instance) |
69 | 81 |
return HttpResponse(html, status=status) |
70 | 82 |
|
71 |
def requires_login(func): |
|
72 |
@wraps(func) |
|
73 |
def wrapper(request, *args): |
|
74 |
if not settings.BYPASS_ADMIN_AUTH: |
|
75 |
if not request.user: |
|
76 |
next = urlencode({'next': request.build_absolute_uri()}) |
|
77 |
login_uri = reverse(index) + '?' + next |
|
78 |
return HttpResponseRedirect(login_uri) |
|
79 |
return func(request, *args) |
|
80 |
return wrapper |
|
81 |
|
|
82 | 83 |
def index(request, template_name='index.html', extra_context={}): |
83 |
print '#', get_context(request, extra_context) |
|
84 |
""" |
|
85 |
Renders the index (login) page |
|
86 |
|
|
87 |
**Arguments** |
|
88 |
|
|
89 |
``template_name`` |
|
90 |
A custom template to use. This is optional; if not specified, |
|
91 |
this will default to ``index.html``. |
|
92 |
|
|
93 |
``extra_context`` |
|
94 |
An dictionary of variables to add to the template context. |
|
95 |
|
|
96 |
**Template:** |
|
97 |
|
|
98 |
index.html or ``template_name`` keyword argument. |
|
99 |
|
|
100 |
""" |
|
84 | 101 |
return render_response(template_name, |
85 |
form = LoginForm(),
|
|
102 |
form = AuthenticationForm(),
|
|
86 | 103 |
context_instance = get_context(request, extra_context)) |
87 | 104 |
|
88 | 105 |
def _generate_invitation_code(): |
... | ... | |
97 | 114 |
def _send_invitation(baseurl, inv): |
98 | 115 |
url = settings.SIGNUP_TARGET % (baseurl, inv.code, quote(baseurl)) |
99 | 116 |
subject = _('Invitation to Pithos') |
117 |
site = get_current_site(request) |
|
100 | 118 |
message = render_to_string('invitation.txt', { |
101 | 119 |
'invitation': inv, |
102 | 120 |
'url': url, |
103 | 121 |
'baseurl': baseurl, |
104 |
'service': settings.SERVICE_NAME,
|
|
122 |
'service': site_name,
|
|
105 | 123 |
'support': settings.DEFAULT_CONTACT_EMAIL}) |
106 | 124 |
sender = settings.DEFAULT_FROM_EMAIL |
107 | 125 |
send_mail(subject, message, sender, [inv.username]) |
108 | 126 |
logging.info('Sent invitation %s', inv) |
109 | 127 |
|
110 |
@requires_login |
|
128 |
@login_required |
|
129 |
@transaction.commit_manually |
|
111 | 130 |
def invite(request, template_name='invitations.html', extra_context={}): |
131 |
""" |
|
132 |
Allows a user to invite somebody else. |
|
133 |
|
|
134 |
In case of GET request renders a form for providing the invitee information. |
|
135 |
In case of POST checks whether the user has not run out of invitations and then |
|
136 |
sends an invitation email to singup to the service. |
|
137 |
|
|
138 |
The view uses commit_manually decorator in order to ensure the number of the |
|
139 |
user invitations is going to be updated only if the email has been successfully sent. |
|
140 |
|
|
141 |
If the user isn't logged in, redirects to settings.LOGIN_URL. |
|
142 |
|
|
143 |
**Arguments** |
|
144 |
|
|
145 |
``template_name`` |
|
146 |
A custom template to use. This is optional; if not specified, |
|
147 |
this will default to ``invitations.html``. |
|
148 |
|
|
149 |
``extra_context`` |
|
150 |
An dictionary of variables to add to the template context. |
|
151 |
|
|
152 |
**Template:** |
|
153 |
|
|
154 |
invitations.html or ``template_name`` keyword argument. |
|
155 |
|
|
156 |
**Settings:** |
|
157 |
|
|
158 |
The view expectes the following settings are defined: |
|
159 |
|
|
160 |
* LOGIN_URL: login uri |
|
161 |
* SIGNUP_TARGET: Where users should signup with their invitation code |
|
162 |
* DEFAULT_CONTACT_EMAIL: service support email |
|
163 |
* DEFAULT_FROM_EMAIL: from email |
|
164 |
""" |
|
112 | 165 |
status = None |
113 | 166 |
message = None |
114 | 167 |
inviter = request.user |
... | ... | |
129 | 182 |
if created: |
130 | 183 |
inviter.invitations = max(0, inviter.invitations - 1) |
131 | 184 |
inviter.save() |
132 |
status = 'success'
|
|
185 |
status = messages.SUCCESS
|
|
133 | 186 |
message = _('Invitation sent to %s' % username) |
187 |
transaction.commit() |
|
134 | 188 |
except (SMTPException, socket.error) as e: |
135 |
status = 'error'
|
|
189 |
status = messages.ERROR
|
|
136 | 190 |
message = getattr(e, 'strerror', '') |
191 |
transaction.rollback() |
|
137 | 192 |
else: |
138 |
status = 'error'
|
|
193 |
status = messages.ERROR
|
|
139 | 194 |
message = _('No invitations left') |
140 |
|
|
195 |
messages.add_message(request, status, message) |
|
196 |
|
|
141 | 197 |
if request.GET.get('format') == 'json': |
142 | 198 |
sent = [{'email': inv.username, |
143 | 199 |
'realname': inv.realname, |
... | ... | |
146 | 202 |
rep = {'invitations': inviter.invitations, 'sent': sent} |
147 | 203 |
return HttpResponse(json.dumps(rep)) |
148 | 204 |
|
149 |
kwargs = {'user': inviter, 'status': status, 'message': message}
|
|
205 |
kwargs = {'user': inviter} |
|
150 | 206 |
context = get_context(request, extra_context, **kwargs) |
151 | 207 |
return render_response(template_name, |
152 | 208 |
context_instance = context) |
153 | 209 |
|
154 |
def _send_password(baseurl, user): |
|
155 |
url = settings.PASSWORD_RESET_TARGET % (baseurl, |
|
156 |
quote(user.username), |
|
157 |
quote(baseurl)) |
|
158 |
message = render_to_string('password.txt', { |
|
159 |
'user': user, |
|
160 |
'url': url, |
|
161 |
'baseurl': baseurl, |
|
162 |
'service': settings.SERVICE_NAME, |
|
163 |
'support': settings.DEFAULT_CONTACT_EMAIL}) |
|
164 |
sender = settings.DEFAULT_FROM_EMAIL |
|
165 |
send_mail('Pithos password recovering', message, sender, [user.email]) |
|
166 |
logging.info('Sent password %s', user) |
|
167 |
|
|
168 |
def reclaim_password(request, template_name='reclaim.html', extra_context={}): |
|
169 |
if request.method == 'GET': |
|
170 |
return render_response(template_name, |
|
171 |
context_instance = get_context(request, extra_context)) |
|
172 |
elif request.method == 'POST': |
|
173 |
username = request.POST.get('username') |
|
174 |
try: |
|
175 |
user = AstakosUser.objects.get(username=username) |
|
176 |
try: |
|
177 |
_send_password(request.build_absolute_uri('/').rstrip('/'), user) |
|
178 |
status = 'success' |
|
179 |
message = _('Password reset sent to %s' % user.email) |
|
180 |
user.is_active = False |
|
181 |
user.save() |
|
182 |
except (SMTPException, socket.error) as e: |
|
183 |
status = 'error' |
|
184 |
name = 'strerror' |
|
185 |
message = getattr(e, name) if hasattr(e, name) else e |
|
186 |
except AstakosUser.DoesNotExist: |
|
187 |
status = 'error' |
|
188 |
message = 'Username does not exist' |
|
189 |
|
|
190 |
kwargs = {'status': status, 'message': message} |
|
191 |
return render_response(template_name, |
|
192 |
context_instance = get_context(request, extra_context, **kwargs)) |
|
193 |
|
|
194 |
@requires_login |
|
195 |
def users_profile(request, template_name='users_profile.html', extra_context={}): |
|
210 |
@login_required |
|
211 |
def edit_profile(request, template_name='profile.html', extra_context={}): |
|
212 |
""" |
|
213 |
Allows a user to edit his/her profile. |
|
214 |
|
|
215 |
In case of GET request renders a form for displaying the user information. |
|
216 |
In case of POST updates the user informantion. |
|
217 |
|
|
218 |
If the user isn't logged in, redirects to settings.LOGIN_URL. |
|
219 |
|
|
220 |
**Arguments** |
|
221 |
|
|
222 |
``template_name`` |
|
223 |
A custom template to use. This is optional; if not specified, |
|
224 |
this will default to ``profile.html``. |
|
225 |
|
|
226 |
``extra_context`` |
|
227 |
An dictionary of variables to add to the template context. |
|
228 |
|
|
229 |
**Template:** |
|
230 |
|
|
231 |
profile.html or ``template_name`` keyword argument. |
|
232 |
""" |
|
196 | 233 |
try: |
197 | 234 |
user = AstakosUser.objects.get(username=request.user) |
235 |
form = ProfileForm(instance=user) |
|
198 | 236 |
except AstakosUser.DoesNotExist: |
199 | 237 |
token = request.GET.get('auth', None) |
200 | 238 |
user = AstakosUser.objects.get(auth_token=token) |
239 |
if request.method == 'POST': |
|
240 |
form = ProfileForm(request.POST, instance=user) |
|
241 |
if form.is_valid(): |
|
242 |
try: |
|
243 |
form.save() |
|
244 |
msg = _('Profile has been updated successfully') |
|
245 |
messages.add_message(request, messages.SUCCESS, msg) |
|
246 |
except ValueError, ve: |
|
247 |
messages.add_message(request, messages.ERROR, ve) |
|
201 | 248 |
return render_response(template_name, |
249 |
form = form, |
|
202 | 250 |
context_instance = get_context(request, |
203 | 251 |
extra_context, |
204 | 252 |
user=user)) |
205 | 253 |
|
206 |
@requires_login |
|
207 |
def users_edit(request, template_name='users_profile.html', extra_context={}): |
|
208 |
try: |
|
209 |
user = AstakosUser.objects.get(username=request.user) |
|
210 |
except AstakosUser.DoesNotExist: |
|
211 |
token = request.POST.get('auth', None) |
|
212 |
#users = AstakosUser.objects.all() |
|
213 |
user = AstakosUser.objects.get(auth_token=token) |
|
214 |
user.first_name = request.POST.get('first_name') |
|
215 |
user.last_name = request.POST.get('last_name') |
|
216 |
user.affiliation = request.POST.get('affiliation') |
|
217 |
user.is_verified = True |
|
218 |
user.save() |
|
219 |
next = request.POST.get('next') |
|
220 |
if next: |
|
221 |
return redirect(next) |
|
222 |
|
|
223 |
status = 'success' |
|
224 |
message = _('Profile has been updated') |
|
225 |
return render_response(template_name, |
|
226 |
context_instance = get_context(request, extra_context, **kwargs)) |
|
254 |
@transaction.commit_manually |
|
255 |
def signup(request, template_name='signup.html', extra_context={}, backend=None): |
|
256 |
""" |
|
257 |
Allows a user to create a local account. |
|
258 |
|
|
259 |
In case of GET request renders a form for providing the user information. |
|
260 |
In case of POST handles the signup. |
|
261 |
|
|
262 |
The user activation will be delegated to the backend specified by the ``backend`` keyword argument |
|
263 |
if present, otherwise to the ``astakos.im.backends.InvitationBackend`` |
|
264 |
if settings.INVITATIONS_ENABLED is True or ``astakos.im.backends.SimpleBackend`` if not |
|
265 |
(see backends); |
|
266 |
|
|
267 |
Upon successful user creation if ``next`` url parameter is present the user is redirected there |
|
268 |
otherwise renders the same page with a success message. |
|
227 | 269 |
|
228 |
def signup(request, template_name='signup.html', extra_context={}, backend=None, success_url = None): |
|
270 |
On unsuccessful creation, renders the same page with an error message. |
|
271 |
|
|
272 |
The view uses commit_manually decorator in order to ensure the user will be created |
|
273 |
only if the procedure has been completed successfully. |
|
274 |
|
|
275 |
**Arguments** |
|
276 |
|
|
277 |
``template_name`` |
|
278 |
A custom template to use. This is optional; if not specified, |
|
279 |
this will default to ``signup.html``. |
|
280 |
|
|
281 |
``extra_context`` |
|
282 |
An dictionary of variables to add to the template context. |
|
283 |
|
|
284 |
**Template:** |
|
285 |
|
|
286 |
signup.html or ``template_name`` keyword argument. |
|
287 |
""" |
|
229 | 288 |
if not backend: |
230 |
backend = get_backend() |
|
231 |
if request.method == 'GET': |
|
232 |
try: |
|
233 |
form = backend.get_signup_form(request) |
|
234 |
return render_response(template_name, |
|
235 |
form=form, |
|
236 |
context_instance = get_context(request, extra_context)) |
|
237 |
except Exception, e: |
|
238 |
return _on_failure(e, template_name=template_name) |
|
239 |
elif request.method == 'POST': |
|
240 |
try: |
|
241 |
form = backend.get_signup_form(request) |
|
242 |
if not form.is_valid(): |
|
243 |
return render_response(template_name, |
|
244 |
form = form, |
|
245 |
context_instance = get_context(request, extra_context)) |
|
246 |
status, message = backend.signup(request, form, success_url) |
|
247 |
next = request.POST.get('next') |
|
248 |
if next: |
|
249 |
return redirect(next) |
|
250 |
return _info(status, message) |
|
251 |
except Exception, e: |
|
252 |
return _on_failure(e, template_name=template_name) |
|
253 |
|
|
254 |
def _info(status, message, template_name='base.html'): |
|
255 |
html = render_to_string(template_name, { |
|
256 |
'status': status, |
|
257 |
'message': message}) |
|
258 |
response = HttpResponse(html) |
|
259 |
return response |
|
289 |
backend = get_backend() |
|
290 |
try: |
|
291 |
form = backend.get_signup_form(request) |
|
292 |
if request.method == 'POST': |
|
293 |
if form.is_valid(): |
|
294 |
status, message = backend.signup(request) |
|
295 |
# rollback incase of error |
|
296 |
if status == messages.ERROR: |
|
297 |
transaction.rollback() |
|
298 |
else: |
|
299 |
transaction.commit() |
|
300 |
next = request.POST.get('next') |
|
301 |
if next: |
|
302 |
return redirect(next) |
|
303 |
messages.add_message(request, status, message) |
|
304 |
except (Invitation.DoesNotExist), e: |
|
305 |
messages.add_message(request, messages.ERROR, e) |
|
306 |
return render_response(template_name, |
|
307 |
form = form if 'form' in locals() else UserCreationForm(), |
|
308 |
context_instance=get_context(request, extra_context)) |
|
260 | 309 |
|
261 |
def _on_success(message, template_name='base.html'): |
|
262 |
return _info('success', message, template_name) |
|
310 |
@login_required |
|
311 |
def send_feedback(request, template_name='feedback.html', email_template_name='feedback_mail.txt', extra_context={}): |
|
312 |
""" |
|
313 |
Allows a user to send feedback. |
|
314 |
|
|
315 |
In case of GET request renders a form for providing the feedback information. |
|
316 |
In case of POST sends an email to support team. |
|
317 |
|
|
318 |
If the user isn't logged in, redirects to settings.LOGIN_URL. |
|
319 |
|
|
320 |
**Arguments** |
|
321 |
|
|
322 |
``template_name`` |
|
323 |
A custom template to use. This is optional; if not specified, |
|
324 |
this will default to ``feedback.html``. |
|
325 |
|
|
326 |
``extra_context`` |
|
327 |
An dictionary of variables to add to the template context. |
|
328 |
|
|
329 |
**Template:** |
|
263 | 330 |
|
264 |
def _on_failure(message, template_name='base.html'): |
|
265 |
return _info('error', message, template_name) |
|
331 |
signup.html or ``template_name`` keyword argument. |
|
332 |
|
|
333 |
**Settings:** |
|
334 |
|
|
335 |
* FEEDBACK_CONTACT_EMAIL: List of feedback recipients |
|
336 |
""" |
|
337 |
if request.method == 'GET': |
|
338 |
form = FeedbackForm() |
|
339 |
if request.method == 'POST': |
|
340 |
if not request.user: |
|
341 |
return HttpResponse('Unauthorized', status=401) |
|
342 |
|
|
343 |
form = FeedbackForm(request.POST) |
|
344 |
if form.is_valid(): |
|
345 |
subject = _("Feedback from Okeanos") |
|
346 |
from_email = request.user.email |
|
347 |
recipient_list = [settings.FEEDBACK_CONTACT_EMAIL] |
|
348 |
content = render_to_string(email_template_name, { |
|
349 |
'message': form.cleaned_data('feedback_msg'), |
|
350 |
'data': form.cleaned_data('feedback_data'), |
|
351 |
'request': request}) |
|
352 |
|
|
353 |
send_mail(subject, content, from_email, recipient_list) |
|
354 |
|
|
355 |
resp = json.dumps({'status': 'send'}) |
|
356 |
return HttpResponse(resp) |
|
357 |
return render_response(template_name, |
|
358 |
form = form, |
|
359 |
context_instance = get_context(request, extra_context)) |
Also available in: Unified diff