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') | ... | ... |