Commit 1ea6cb0c0b1a8705c943a59fe28e541e15ef6e3d
1 parent
f1e1b1d3
Exists in
master
and in
3 other branches
Adding password recovering
Showing
7 changed files
with
203 additions
and
8 deletions
Show diff stats
amadeus/settings.py
... | ... | @@ -180,7 +180,7 @@ LOGS_URL = 'logs/' |
180 | 180 | |
181 | 181 | |
182 | 182 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' |
183 | -DEFAULT_FROM_EMAIL = 'admin@admin.com' | |
183 | +DEFAULT_FROM_EMAIL = 'admin@amadeus.com.br' | |
184 | 184 | |
185 | 185 | # Messages |
186 | 186 | from django.contrib.messages import constants as messages_constants | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +{% extends 'base.html' %} | |
2 | + | |
3 | +{% load i18n %} | |
4 | + | |
5 | +{% block nav %} | |
6 | +{% endblock %} | |
7 | + | |
8 | +{% block breadcrumbs %} | |
9 | +{% endblock %} | |
10 | + | |
11 | +{% block sidebar %} | |
12 | +{% endblock sidebar %} | |
13 | + | |
14 | +{% autoescape off %} | |
15 | + | |
16 | +{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %} | |
17 | + | |
18 | +{% trans "Please go to the following page and choose a new password:" %} | |
19 | + | |
20 | +{% block reset_link %} | |
21 | + {{ domain }}{% url 'users:reset_password_confirm' uidb64=uid token=token %} | |
22 | + <!--This is the only change from ` django/contrib/admin/templates/registration/password_reset_subject.html`. the url name is commented out in urls.py section. The view associated with the url is going to described later in this post. --> | |
23 | +{% endblock %} | |
24 | + | |
25 | +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} | |
26 | + | |
27 | +{% endautoescape %} | |
0 | 28 | \ No newline at end of file | ... | ... |
users/forms.py
... | ... | @@ -3,12 +3,25 @@ from django import forms |
3 | 3 | from django.utils.translation import ugettext_lazy as _ |
4 | 4 | from rolepermissions.shortcuts import assign_role |
5 | 5 | from django.contrib.auth import update_session_auth_hash |
6 | +from django.core.validators import validate_email | |
7 | +from django.core.exceptions import ValidationError | |
6 | 8 | from .models import User |
7 | 9 | |
8 | 10 | class Validation(forms.ModelForm): |
9 | 11 | MIN_PASS_LENGTH = 8 |
10 | 12 | MAX_UPLOAD_SIZE = 2*1024*1024 |
11 | 13 | |
14 | + def clean_email(self): | |
15 | + email = self.cleaned_data.get('email', '') | |
16 | + | |
17 | + try: | |
18 | + validate_email( email ) | |
19 | + return email | |
20 | + except ValidationError: | |
21 | + self._errors['email'] = [_('You must insert an email address')] | |
22 | + | |
23 | + return ValueError | |
24 | + | |
12 | 25 | def clean_image(self): |
13 | 26 | image = self.cleaned_data.get('image', False) |
14 | 27 | |
... | ... | @@ -170,4 +183,18 @@ class ChangePassForm(Validation): |
170 | 183 | } |
171 | 184 | widgets = { |
172 | 185 | 'password': forms.PasswordInput |
173 | - } | |
174 | 186 | \ No newline at end of file |
187 | + } | |
188 | + | |
189 | +class PassResetRequest(forms.Form): | |
190 | + email = forms.CharField(label = _('Email'), max_length = 254) | |
191 | + | |
192 | + def clean_email(self): | |
193 | + email = self.cleaned_data.get('email', '') | |
194 | + | |
195 | + try: | |
196 | + validate_email( email ) | |
197 | + return email | |
198 | + except ValidationError: | |
199 | + self._errors['email'] = [_('You must insert an email address')] | |
200 | + | |
201 | + return ValueError | |
175 | 202 | \ No newline at end of file | ... | ... |
... | ... | @@ -0,0 +1,84 @@ |
1 | +{% extends 'base.html' %} | |
2 | + | |
3 | +{% load static i18n %} | |
4 | +{% load widget_tweaks %} | |
5 | + | |
6 | +{% block nav %} | |
7 | +{% endblock %} | |
8 | + | |
9 | +{% block breadcrumbs %} | |
10 | +{% endblock %} | |
11 | + | |
12 | +{% block sidebar %} | |
13 | +{% endblock sidebar %} | |
14 | + | |
15 | +{% block content %} | |
16 | + <div class="row"> | |
17 | + <div class="col-sm-7 col-sm-offset-4 col-md-6 col-md-offset-4 col-xs-8 col-xs-offset-3 col-lg-6 col-lg-offset-4 col-xl-6 col-xl-offset-4 "> | |
18 | + <div class="col-sm-9 col-sm-offset-2 col-md-8 col-md-offset-2 col-xs-9 col-xs-offset-2 col-lg-8 col-lg-offset-2 col-xl-8 col-xl-offset-2"> | |
19 | + <img src="{% static 'img/amadeus.png' %}" class="img-responsive center-block logo-login " alt="logo amadeus"> | |
20 | + </div> | |
21 | + </div> | |
22 | + </div> | |
23 | + <div class="row"> | |
24 | + <div class="col-lg-8 col-lg-offset-3 col-md-8 col-md-offset-3 col-sm-9 col-sm-offset-3 col-xs-10 col-xs-offset-2"> | |
25 | + {% if messages %} | |
26 | + {% for message in messages %} | |
27 | + <div class="alert alert-{{ message.tags }} alert-dismissible" role="alert"> | |
28 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | |
29 | + <span aria-hidden="true">×</span> | |
30 | + </button> | |
31 | + <p>{{ message }}</p> | |
32 | + </div> | |
33 | + {% endfor %} | |
34 | + {% endif %} | |
35 | + <div class="card"> | |
36 | + <div class="card-block"> | |
37 | + <div class="row"> | |
38 | + <div class="col-md-12 text-center"> | |
39 | + <h2 style="color:#43a251"><strong> {% trans 'Forgot Password' %} </strong></h2> | |
40 | + <p>{% trans 'Enter your email below (the one used to access the platform) to recover your password' %}</p> | |
41 | + </div> | |
42 | + </div> | |
43 | + | |
44 | + <form id="form-reset" method="post" action=""> | |
45 | + {% csrf_token %} | |
46 | + {% for field in form %} | |
47 | + <div class="col-md-10 col-md-offset-1"> | |
48 | + <div class="form-group{% if form.has_error %} has-error {% endif %}"> | |
49 | + {% if field.field.required %} | |
50 | + <label for="{{ field.auto_id }}" class="control-label">{{ field.label }} <span>*</span></label> | |
51 | + {% else %} | |
52 | + <label for="{{ field.auto_id }}" class="control-label">{{ field.label }}</label> | |
53 | + {% endif %} | |
54 | + {% render_field field class='form-control' %} | |
55 | + <span id="helpBlock" class="help-block">{{ field.help_text }}</span> | |
56 | + {% if field.errors %} | |
57 | + <div class="alert alert-danger alert-dismissible" role="alert"> | |
58 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | |
59 | + <span aria-hidden="true">×</span> | |
60 | + </button> | |
61 | + <ul> | |
62 | + {% for error in field.errors %} | |
63 | + <li>{{ error }}</li> | |
64 | + {% endfor %} | |
65 | + </ul> | |
66 | + </div> | |
67 | + {% endif %} | |
68 | + </div> | |
69 | + </div> | |
70 | + {% endfor %} | |
71 | + </form> | |
72 | + <div class="row"> | |
73 | + <div class="col-md-5 col-xs-6 col-sm-6 col-lg-5 col-lg-offset-1 col-md-offset-1 text-center"> | |
74 | + <button type="submit" class="btn btn-success btn-raised btn-block" form="form-reset" style="position: initial;"> {% trans 'Recover' %} </button> | |
75 | + </div> | |
76 | + <div class="col-md-5 col-xs-6 col-sm-6 col-lg-5 text-center"> | |
77 | + <a class="btn btn-default btn-raised btn-block" href="{% url 'users:login' %}" formaction="#" style="position: initial;">{% trans 'Back' %}</a> | |
78 | + </div> | |
79 | + </div> | |
80 | + </div> | |
81 | + </div> | |
82 | + </div> | |
83 | + </div> | |
84 | +{% endblock %} | |
0 | 85 | \ No newline at end of file | ... | ... |
users/templates/users/login.html
... | ... | @@ -65,7 +65,7 @@ |
65 | 65 | </div> |
66 | 66 | <div class="row"> |
67 | 67 | <div class="col-md-offset-1 col-md-10 text-right forgotPassword"> |
68 | - <a href="#">{% trans 'Forgot your password?' %}</a> | |
68 | + <a href="{% url 'users:forgot_pass' %}">{% trans 'Forgot your password?' %}</a> | |
69 | 69 | </div> |
70 | 70 | </div> |
71 | 71 | </div> | ... | ... |
users/urls.py
... | ... | @@ -7,6 +7,8 @@ urlpatterns = [ |
7 | 7 | url(r'^login/$', views.login, name='login'), |
8 | 8 | url(r'^logout/$', auth_views.logout, {'next_page': 'users:login'}, name='logout'), |
9 | 9 | url(r'^signup/$', views.RegisterUser.as_view(), name = 'signup'), |
10 | + url(r'^forgot_password/$', views.ForgotPassword.as_view(), name = 'forgot_pass'), | |
11 | + url(r'^reset_password_confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', views.ForgotPassword.as_view(), name = 'reset_password_confirm'), | |
10 | 12 | url(r'^$', views.UsersListView.as_view(), name = 'manage'), |
11 | 13 | url(r'^create/$', views.CreateView.as_view(), name = 'create'), |
12 | 14 | url(r'^edit/(?P<email>[\w.%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})/$', views.UpdateView.as_view(), name='update'), | ... | ... |
users/views.py
1 | -from django.http import Http404 | |
2 | 1 | from django.shortcuts import get_object_or_404,redirect, render |
3 | -from django.db.models import Q | |
4 | 2 | from django.views import generic |
5 | 3 | from django.contrib import messages |
6 | 4 | from rolepermissions.mixins import HasRoleMixin |
... | ... | @@ -10,11 +8,17 @@ from django.core.urlresolvers import reverse, reverse_lazy |
10 | 8 | from django.utils.translation import ugettext_lazy as _ |
11 | 9 | from rolepermissions.shortcuts import assign_role |
12 | 10 | from rolepermissions.verifications import has_role |
13 | -from itertools import chain | |
14 | -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger | |
15 | 11 | |
16 | 12 | from .models import User |
17 | -from .forms import RegisterUserForm, ProfileForm, UserForm, ChangePassForm | |
13 | +from .forms import RegisterUserForm, ProfileForm, UserForm, ChangePassForm, PassResetRequest | |
14 | + | |
15 | +#RECOVER PASS IMPORTS | |
16 | +from django.contrib.auth.tokens import default_token_generator | |
17 | +from django.core.mail import send_mail | |
18 | +from django.conf import settings | |
19 | +from django.utils.encoding import force_bytes | |
20 | +from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode | |
21 | +from django.template import loader | |
18 | 22 | |
19 | 23 | #API IMPORTS |
20 | 24 | from rest_framework import viewsets |
... | ... | @@ -342,6 +346,57 @@ class RegisterUser(generic.edit.CreateView): |
342 | 346 | |
343 | 347 | return super(RegisterUser, self).form_valid(form) |
344 | 348 | |
349 | +class ForgotPassword(generic.FormView): | |
350 | + template_name = "users/forgot_password.html" | |
351 | + success_url = reverse_lazy('users:login') | |
352 | + form_class = PassResetRequest | |
353 | + | |
354 | + def get_context_data(self, **kwargs): | |
355 | + context = super(ForgotPassword, self).get_context_data(**kwargs) | |
356 | + context['title'] = _('Forgot Password') | |
357 | + | |
358 | + return context | |
359 | + | |
360 | + def post(self, request, *args, **kwargs): | |
361 | + form = self.get_form() | |
362 | + | |
363 | + if form.is_valid(): | |
364 | + email = form.cleaned_data['email'] | |
365 | + | |
366 | + users = User.objects.filter(email = email) | |
367 | + | |
368 | + if users.exists(): | |
369 | + for user in users: | |
370 | + c = { | |
371 | + 'email': user.email, | |
372 | + 'domain': 'amadeus.com.br', #or your domain | |
373 | + 'site_name': 'Amadeus', | |
374 | + 'uid': urlsafe_base64_encode(force_bytes(user.pk)), | |
375 | + 'user': user, | |
376 | + 'token': default_token_generator.make_token(user), | |
377 | + 'protocol': 'http', | |
378 | + } | |
379 | + | |
380 | + subject_template_name='registration/password_reset_subject.txt' | |
381 | + email_template_name = 'recover_pass_email_template.html' | |
382 | + | |
383 | + subject = loader.render_to_string(subject_template_name, c) | |
384 | + # Email subject *must not* contain newlines | |
385 | + subject = ''.join(subject.splitlines()) | |
386 | + email = loader.render_to_string(email_template_name, c) | |
387 | + | |
388 | + send_mail(subject, email, settings.DEFAULT_FROM_EMAIL , [user.email], fail_silently=False) | |
389 | + | |
390 | + result = self.form_valid(form) | |
391 | + messages.success(request, _("An email has been sent to the informed address. Please check its inbox to continue reseting password.")) | |
392 | + | |
393 | + return result | |
394 | + | |
395 | + result = self.form_invalid(form) | |
396 | + messages.error(request, _('No user is associated with this email address')) | |
397 | + | |
398 | + return result | |
399 | + | |
345 | 400 | def login(request): |
346 | 401 | context = {} |
347 | 402 | context['title'] = _('Log In') | ... | ... |