From 73d3d6b48097e5d37add9b69e498ab672e5ed643 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 00:24:35 +0200 Subject: [PATCH] Start implementing user-facing confirmation email mechanics --- users/forms.py | 22 +++++++++++++ .../commands/disable_emailnotyetconfirmed.py | 3 +- users/models.py | 6 +++- users/templates/users/profil.html | 31 ++++++++++++++++++- users/urls.py | 1 + users/views.py | 14 +++++++++ 6 files changed, 74 insertions(+), 3 deletions(-) diff --git a/users/forms.py b/users/forms.py index bc88a1f4..5669b155 100644 --- a/users/forms.py +++ b/users/forms.py @@ -299,6 +299,11 @@ class ResetPasswordForm(forms.Form): email = forms.EmailField(max_length=255) +class ResendConfirmationEmailForm(forms.Form): + """Formulaire de renvoie du mail de confirmation""" + pass + + class MassArchiveForm(forms.Form): """Formulaire d'archivage des users inactif. Prend en argument du formulaire la date de depart avant laquelle archiver les @@ -344,6 +349,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields["room"].label = _("Room") self.fields["room"].empty_label = _("No room") self.fields["school"].empty_label = _("Select a school") + self.initial["email"] = kwargs["instance"].email class Meta: model = Adherent @@ -390,6 +396,22 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): remove_user_room(room) return + def save(self, commit=True): + """On met à jour l'état de l'utilisateur en fonction de son mail""" + user = super(AdherentForm, self).save(commit=False) + + if user.email != self.initial["email"]: + # Send a confirmation email + if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: + user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED + user.confirm_email_address_mail() + + # Always keep the oldest change date + if user.email_change_date is None: + user.email_change_date = timezone.now() + + return user + class AdherentCreationForm(AdherentForm): """Formulaire de création d'un user. diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index 404b5004..b2678c19 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -34,7 +34,8 @@ class Command(BaseCommand): days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_disable = ( User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) - .filter(registered__lte=timezone.now() - timedelta(days=days)) + .exclude(email_change_date__is_null=True) + .filter(email_change_date__lte=timezone.now() - timedelta(days=days)) .distinct() ) print("Disabling " + str(users_to_disable.count()) + " users.") diff --git a/users/models.py b/users/models.py index 12795a67..8671fba0 100755 --- a/users/models.py +++ b/users/models.py @@ -226,6 +226,7 @@ class User( shortcuts_enabled = models.BooleanField( verbose_name=_("enable shortcuts on Re2o website"), default=True ) + email_change_date = None USERNAME_FIELD = "pseudo" REQUIRED_FIELDS = ["surname", "email"] @@ -879,7 +880,10 @@ class User( def confirm_mail(self): """Marque l'email de l'utilisateur comme confirmé""" - # Let the "set_active" method handle + # Reset the email change date + self.email_change_date = None + + # Let the "set_active" method handle the rest self.state = self.STATE_NOT_YET_ACTIVE self.set_active() diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 0bd25f75..cb8358a8 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -38,7 +38,36 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% blocktrans with name=users.name surname=users.surname %}Profile of {{ name }} {{ surname }}{% endblocktrans %}

{% endif %} +
+ {% if users.state == Users.STATE_NOT_YET_ACTIVE %} +

{% blocktrans with name=users.name surname=users.surname %}Welcome {{ name }} {{ surname }}{% endblocktrans %}

+ {% else %} +

{% blocktrans with name=users.name surname=users.surname %}Profile of {{ name }} {{ surname }}{% endblocktrans %}

+ {% endif %} +
+ + + +
+ {% if users.state == Users.STATE_NOT_YET_ACTIVE %} +
+
+ {% blocktrans %}Please confirm your email address{% endblocktrans %} +
+ + {% blocktrans %}Resend the email{% endblocktrans %} + +
+
+ {% elif users.state == Users.STATE_DISABLED %} +
+
+ {% blocktrans %}Your account has been disabled{% endblocktrans %} +
+
+ {% endif %} +
{% if users.is_ban%} @@ -181,7 +210,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Email address" %}
-
{{ users.email }}
+
{{ users.email }}{% if users.email_change_date is not None %}{% trans "Pending confirmation..." %}{% endif %}
diff --git a/users/urls.py b/users/urls.py index 1cd303e6..8ab5253d 100644 --- a/users/urls.py +++ b/users/urls.py @@ -42,6 +42,7 @@ urlpatterns = [ url(r"^state/(?P[0-9]+)$", views.state, name="state"), url(r"^groups/(?P[0-9]+)$", views.groups, name="groups"), url(r"^password/(?P[0-9]+)$", views.password, name="password"), + url(r"^confirm_email/(?P[0-9]+)$", views.resend_confirmation_email, name="resend-confirmation-email"), url( r"^del_group/(?P[0-9]+)/(?P[0-9]+)$", views.del_group, diff --git a/users/views.py b/users/views.py index 4f4a62d3..266c0717 100644 --- a/users/views.py +++ b/users/views.py @@ -107,6 +107,7 @@ from .forms import ( PassForm, ConfirmMailForm, ResetPasswordForm, + ResendConfirmationEmailForm, ClubAdminandMembersForm, GroupForm, InitialRegisterForm, @@ -1023,6 +1024,19 @@ def process_passwd(request, req): ) +def resend_confirmation_email(request): + """ Renvoie du mail de confirmation """ + userform = ResendConfirmationEmailForm(request.POST or None) + if userform.is_valid(): + request.user.confirm_email_address_mail() + messages.success(request, _("An email to confirm your address was sent.")) + return redirect(reverse("index")) + + return form( + {"userform": userform, "action_name": _("Send")}, "users/user.html", request + ) + + def confirm_email(request, token): """Lien pour la confirmation de l'email""" valid_reqs = Request.objects.filter(expires_at__gt=timezone.now())