Gestion des adresses et du mot de passe
This commit is contained in:
parent
4e683f62e1
commit
90aa558896
17 changed files with 650 additions and 188 deletions
|
@ -1,11 +1,14 @@
|
||||||
# coding: utf-8
|
import unicodedata
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib.auth.forms import PasswordResetForm
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from simple_email_confirmation.models import EmailAddress
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage
|
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage, User
|
||||||
from .widgets import LatLonField
|
from .widgets import LatLonField
|
||||||
|
|
||||||
# Sur-classe utile
|
# Sur-classe utile
|
||||||
|
@ -107,3 +110,40 @@ class FeedbackForm(forms.Form):
|
||||||
objet = forms.CharField(label="Objet", required=True)
|
objet = forms.CharField(label="Objet", required=True)
|
||||||
message = forms.CharField(label="Message", required=True, widget=forms.widgets.Textarea())
|
message = forms.CharField(label="Message", required=True, widget=forms.widgets.Textarea())
|
||||||
|
|
||||||
|
# Nouvelle adresse mail
|
||||||
|
class AdresseEmailForm(forms.Form):
|
||||||
|
def __init__(self, _user, **kwargs):
|
||||||
|
self._user = _user
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
email = forms.EmailField(widget=forms.widgets.EmailInput(attrs={"placeholder": "Nouvelle adresse"}))
|
||||||
|
|
||||||
|
def clean_email(self):
|
||||||
|
email = self.cleaned_data["email"]
|
||||||
|
if EmailAddress.objects.filter(user=self._user, email=email).exists():
|
||||||
|
raise forms.ValidationError(
|
||||||
|
"Cette adresse est déjà associée à ce compte")
|
||||||
|
return email
|
||||||
|
|
||||||
|
|
||||||
|
def _unicode_ci_compare(s1, s2):
|
||||||
|
"""
|
||||||
|
Perform case-insensitive comparison of two identifiers, using the
|
||||||
|
recommended algorithm from Unicode Technical Report 36, section
|
||||||
|
2.11.2(B)(2).
|
||||||
|
"""
|
||||||
|
return unicodedata.normalize('NFKC', s1).casefold() == unicodedata.normalize('NFKC', s2).casefold()
|
||||||
|
|
||||||
|
|
||||||
|
# (Ré)initialisation du mot de passe
|
||||||
|
class ReinitMdpForm(PasswordResetForm):
|
||||||
|
def get_users(self, email):
|
||||||
|
"""Override default method to allow unusable passwords"""
|
||||||
|
email_field_name = User.get_email_field_name()
|
||||||
|
active_users = User._default_manager.filter(**{
|
||||||
|
'%s__iexact' % email_field_name: email,
|
||||||
|
'is_active': True,
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
u for u in active_users
|
||||||
|
if _unicode_ci_compare(email, getattr(u, email_field_name))
|
||||||
|
)
|
||||||
|
|
|
@ -77,8 +77,14 @@ def forwards(apps, schema_editor):
|
||||||
if "@" not in user.username:
|
if "@" not in user.username:
|
||||||
print(user.username)
|
print(user.username)
|
||||||
continue
|
continue
|
||||||
entrance_year = "20" + saccount.extra_data.get(
|
entrance_year = saccount.extra_data.get(
|
||||||
"entrance_year", user.username.split("@")[1])
|
"entrance_year", user.username.split("@")[1])
|
||||||
|
try:
|
||||||
|
entrance_year = 2000 + int(entrance_year)
|
||||||
|
except ValueError:
|
||||||
|
print(entrance_year)
|
||||||
|
continue
|
||||||
|
|
||||||
new_conns.append(CASAccount(user=user, cas_login=clipper,
|
new_conns.append(CASAccount(user=user, cas_login=clipper,
|
||||||
entrance_year=int(entrance_year)))
|
entrance_year=int(entrance_year)))
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,9 @@ class Normalien(models.Model):
|
||||||
promotion = models.CharField(u"Promotion", max_length=40, blank=True)
|
promotion = models.CharField(u"Promotion", max_length=40, blank=True)
|
||||||
mail = models.EmailField(u"Adresse e-mail permanente",
|
mail = models.EmailField(u"Adresse e-mail permanente",
|
||||||
max_length=200, blank=True)
|
max_length=200, blank=True)
|
||||||
contactez_moi = models.BooleanField(u"Inviter les visiteurs à me contacter",
|
contactez_moi = models.BooleanField(
|
||||||
default=True)
|
u"Inviter les visiteurs à me contacter",
|
||||||
|
default=True, help_text="Affiche votre adresse e-mail principale sur votre profil public")
|
||||||
bio = models.TextField(u"À propos de moi", blank=True, default="")
|
bio = models.TextField(u"À propos de moi", blank=True, default="")
|
||||||
en_scolarite = models.BooleanField(default=False, blank=True)
|
en_scolarite = models.BooleanField(default=False, blank=True)
|
||||||
|
|
||||||
|
|
|
@ -177,14 +177,14 @@
|
||||||
display: block;
|
display: block;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: 0.95em;
|
font-size: 0.95em;
|
||||||
color: $compl * 0.8;
|
color: darken($compl, 20%);
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.help_text {
|
.help_text {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
color: $fond * 0.4;
|
color: darken($fond, 60%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
|
|
|
@ -46,7 +46,7 @@ em, i {
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: $compl * 0.9;
|
color: darken($compl, 10%);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ header {
|
||||||
color: lighten($fond, 40%);
|
color: lighten($fond, 40%);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $barre * 0.6;
|
background: darken($barre, 40%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,6 @@ p.warning {
|
||||||
li {
|
li {
|
||||||
display: table;
|
display: table;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
//border: 1px solid $fond * 1.3;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
margin: 12px;
|
margin: 12px;
|
||||||
|
|
||||||
|
@ -363,6 +362,57 @@ section.profil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
section.two-cols {
|
||||||
|
display: flex;
|
||||||
|
display: flexbox;
|
||||||
|
align-items: center;
|
||||||
|
& > * {
|
||||||
|
flex: 1;
|
||||||
|
width: 50%;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.mes-emails {
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
background: #fff;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
min-height: 70px;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adresse {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmee {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supprimer {
|
||||||
|
flex: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
.field {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Détail d'un stage
|
// Détail d'un stage
|
||||||
|
@ -432,7 +482,7 @@ input[type="submit"], .btn {
|
||||||
font: $textfontsize $textfont;
|
font: $textfontsize $textfont;
|
||||||
background-color: $fond;
|
background-color: $fond;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: 1px solid $fond * 0.7;
|
border: 1px solid darken($fond, 30%);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
19
avisstage/templates/avisstage/compte/aconfirmer.html
Normal file
19
avisstage/templates/avisstage/compte/aconfirmer.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends "avisstage/base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block title %}Confirmation requise - ExperiENS{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Confirmation requise</h1>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
{% if object.confirmed_at %}
|
||||||
|
<p>L'adresse {{ object.email }} a déjà été confirmée.</p>
|
||||||
|
{% else %}
|
||||||
|
<p>Un mail de confirmation vous a été envoyé à l'adresse {{ object.email }} pour la vérifier.</p>
|
||||||
|
<p>Merci de cliquer sur le lien inclus pour confirmer qu'elle est correcte.</p>
|
||||||
|
<p>Si vous ne recevez rien, vérifier dans vos indésirables.</p>
|
||||||
|
{% endif %}
|
||||||
|
<p><a href="{% url "avisstage:parametres" %}">Retour</a></p>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
29
avisstage/templates/avisstage/compte/edit_mdp.html
Normal file
29
avisstage/templates/avisstage/compte/edit_mdp.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{% extends "avisstage/base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Définir un mot de passe</h1>
|
||||||
|
<form action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
<div class="field">
|
||||||
|
<label>Nom d'utilisateur</label>
|
||||||
|
<div class="input">
|
||||||
|
{{ user.username }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% for field in form %}
|
||||||
|
{{ field.errors }}
|
||||||
|
<div class="field">
|
||||||
|
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
|
||||||
|
<div class="input">
|
||||||
|
{{ field }}
|
||||||
|
{% if field.help_text %}
|
||||||
|
<p class="help_text">{{ field.help_text }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<input type="submit" value="Enregistrer" />
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
18
avisstage/templates/avisstage/compte/email_supprime.html
Normal file
18
avisstage/templates/avisstage/compte/email_supprime.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends "avisstage/base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block title %}Supprimer une adresse mail - ExperiENS{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Supprimer une adresse mail</h1>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<section class="profil">
|
||||||
|
<form action="" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>Êtes-vous sûr⋅e de vouloir supprimer l'adresse mail {{ object.email }} ?</p>
|
||||||
|
<p><a href="{% url "avisstage:parametres" %}">Retour</a> <input type="submit" value="Confirmer la suppression"></p>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
76
avisstage/templates/avisstage/compte/parametres.html
Normal file
76
avisstage/templates/avisstage/compte/parametres.html
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
{% extends "avisstage/base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block title %}Mes paramètres - ExperiENS{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Mes paramètres</h1>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>Adresses e-mail</h2>
|
||||||
|
<ul class="mes-emails">
|
||||||
|
{% for email in request.user.email_address_set.all %}
|
||||||
|
<li>
|
||||||
|
<span class="adresse">{{ email.email }}
|
||||||
|
<span class="confirmee">{{ email.confirmed_at|yesno:"✓,✗"|safe }}</span></span>
|
||||||
|
{% if email.confirmed_at %}
|
||||||
|
<span class="principale">
|
||||||
|
{% if email.email == user.email %}
|
||||||
|
Principale
|
||||||
|
{% else %}
|
||||||
|
<form action="{% url "avisstage:emails_principal" email.email %}" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="submit" value="Rendre principale">
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<span class="confirmer">
|
||||||
|
<form action="{% url "avisstage:emails_reconfirme" email.email %}" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="submit" value="Renvoyer le lien de confirmation">
|
||||||
|
</form>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
<span class="supprimer">
|
||||||
|
{% if not email.email == user.email %}
|
||||||
|
<a href="{% url "avisstage:emails_supprime" email.email %}">Supprimer</a>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
<li>
|
||||||
|
<form action="" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
{% for field in form %}
|
||||||
|
{{ field.errors }}
|
||||||
|
<div class="field">
|
||||||
|
<div class="input">
|
||||||
|
{{ field }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<input type="submit" value="Ajouter l'adresse">
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<h2>Mot de passe</h2>
|
||||||
|
<section class="profil">
|
||||||
|
{% if request.user.password and request.user.has_usable_password %}
|
||||||
|
<p>Un mot de passe interne est déjà défini pour ce compte.</p>
|
||||||
|
{% else %}
|
||||||
|
<p>Aucun mot de passe n'est défini pour ce compte. Créez-en un pour pouvoir vous connecter après la fin de votre scolarité à l'ENS.</p>
|
||||||
|
{% endif %}
|
||||||
|
<form action="{% url "avisstage:mdp_demande" %}" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="submit" value="Définir un nouveau mot de passe" />
|
||||||
|
</form>
|
||||||
|
<p>En cliquant sur ce bouton, un lien unique vous sera envoyé à votre adresse e-mail principale ({{ request.user.email }}) qui vous donnera accès au formulaire d'édition du mot de passe.</p>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
|
@ -18,6 +18,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<input type="submit" />
|
<div class="field">
|
||||||
|
<label>Adresse e-mail</label>
|
||||||
|
<div class="input">
|
||||||
|
{{ request.user.email }}
|
||||||
|
<p class="help_text">Allez dans <a href="{% url "avisstage:parametres" %}">les paramètres de connexion</a> pour modifier votre adresse principale</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Enregistrer" />
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
8
avisstage/templates/avisstage/mails/reinit_mdp.html
Normal file
8
avisstage/templates/avisstage/mails/reinit_mdp.html
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
Pour créer ou modifier le mot de passe associé à votre compte {{ user.get_username }}, merci de cliquer sur le lien suivant ou de le copier dans votre navigateur :
|
||||||
|
|
||||||
|
{{ protocol }}://{{ domain }}{% url 'avisstage:mdp_edit' uidb64=uid token=token %}
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
L'équipe ExperiENS
|
1
avisstage/templates/avisstage/mails/reinit_mdp.txt
Normal file
1
avisstage/templates/avisstage/mails/reinit_mdp.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
[ExperiENS] Définition du mot de passe
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<h2>Mon compte</h2>
|
<h2>Mon compte</h2>
|
||||||
|
<section class="two-cols">
|
||||||
<section class="profil">
|
<section class="profil">
|
||||||
{% if user.profil.en_scolarite %}
|
{% if user.profil.en_scolarite %}
|
||||||
<h3 class="scolarite">Statut : En scolarité</h3>
|
<h3 class="scolarite">Statut : En scolarité</h3>
|
||||||
|
@ -19,23 +20,26 @@
|
||||||
<p>Vous ne pouvez plus accéder qu'à vos propres expériences pour les modifier, et tenir à jour votre profil.</p>
|
<p>Vous ne pouvez plus accéder qu'à vos propres expériences pour les modifier, et tenir à jour votre profil.</p>
|
||||||
<p>Si vous êtes encore en scolarité, merci de vous <a href="{% url "authens:login.cas" %}">reconnecter en passant par le serveur d'authentification de l'ENS</a> pour mettre à jour votre statut.</p>
|
<p>Si vous êtes encore en scolarité, merci de vous <a href="{% url "authens:login.cas" %}">reconnecter en passant par le serveur d'authentification de l'ENS</a> pour mettre à jour votre statut.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<hr />
|
||||||
<p><i>Le statut est mis à jour automatiquement chaque année selon le mode de connexion que vous utilisez.</i></p>
|
<p><i>Le statut est mis à jour automatiquement chaque année selon le mode de connexion que vous utilisez.</i></p>
|
||||||
</section>
|
</section>
|
||||||
<section class="profil">
|
<section class="profil">
|
||||||
<h3>Adresse e-mail</h3>
|
<h3>Connexion</h3>
|
||||||
|
<p><b>Adresse e-mail principale :</b><br/> {{ user.email }}</p>
|
||||||
{% if not user.profil.has_nonENS_email %}<p align="center" class="warning">Vous n'avez pas renseigné d'adresse mail autre que celle de l'ENS. Pensez à le faire, pour que les générations futures puissent toujours vous contacter !</p>{% endif %}
|
{% if not user.profil.has_nonENS_email %}<p align="center" class="warning">Vous n'avez pas renseigné d'adresse mail autre que celle de l'ENS. Pensez à le faire, pour que les générations futures puissent toujours vous contacter !</p>{% endif %}
|
||||||
<p><a href="{% url "avisstage:perso" %}">Gérer les adresses e-mail liées à mon compte</a></p>
|
<hr/>
|
||||||
|
|
||||||
|
<p><b>Mot de passe interne :</b> {% if user.password and user.has_usable_password %}Défini{% else %}Non défini{% endif %}</p>
|
||||||
|
{% if not user.password or not user.has_usable_password %}<p class="warning" align="center">Pensez à définir un mot de passe propre à ExperiENS pour garder l'accès au site quand vous n'aurez plus de compte clipper !</p>{% endif %}
|
||||||
|
{% if user.profil.en_scolarite %}<p>En scolarité, utilisez le serveur central d'authentification pour vous connecter. Quand vous n'aurez plus de compte clipper, vous devrez vous connecter directement via l'accès archicubes, avec votre login <b>{{ user.username }}</b> et le mot de passe spécifique à ExperiENS que vous aurez défini.</p>{% endif %}
|
||||||
|
<hr/>
|
||||||
|
<p><a href="{% url "avisstage:parametres" %}">Gérer mes paramètres de connexion</a></p>
|
||||||
</section>
|
</section>
|
||||||
<section class="profil">
|
|
||||||
<h3>Mode de connexion</h3>
|
|
||||||
{% if user.profil.en_scolarite %}<p>En scolarité, utilisez le serveur central d'authentification pour vous connecter. Quand vous n'aurez plus de compte clipper, vous devrez vous connecter directement via l'accès archicubes, avec votre login {{ user.cas_account.cas_login }} et le mot de passe spécifique à ExperiENS que vous aurez défini.</p>{% endif %}
|
|
||||||
{% if not user.password or not user.has_usable_password %}<p class="warning" align="center">Vous n'avez pas créé de mot de passe interne à ExperiENS. Pensez-y pour garder l'accès au site quand vous n'aurez plus de compte clipper !</p>{% endif %}
|
|
||||||
<p><a href="{% url "authens:reset.pwd" %}">Créer / changer mon mot de passe ExperiENS</a></p>
|
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<article>
|
<article>
|
||||||
<h2><a href="{% url "avisstage:profil" user.username %}">Mon profil public</a> <a href="{% url "avisstage:profil_edit" %}" class="edit-btn btn">Modifier mes infos</a></h2>
|
<h2>Mon profil public <a href="{% url "avisstage:profil" user.username %}" class="btn">Voir</a> <a href="{% url "avisstage:profil_edit" %}" class="edit-btn btn">Modifier mes infos</a></h2>
|
||||||
{% with object=user.profil %}
|
{% with object=user.profil %}
|
||||||
<section class="profil">
|
<section class="profil">
|
||||||
<div class="infos">
|
<div class="infos">
|
||||||
|
|
|
@ -24,6 +24,23 @@ urlpatterns = [
|
||||||
path('profil/show/<str:username>/', views.ProfilView.as_view(),
|
path('profil/show/<str:username>/', views.ProfilView.as_view(),
|
||||||
name='profil'),
|
name='profil'),
|
||||||
path('profil/edit/', views.ProfilEdit.as_view(), name='profil_edit'),
|
path('profil/edit/', views.ProfilEdit.as_view(), name='profil_edit'),
|
||||||
|
path('profil/parametres/', views.MesParametres.as_view(), name='parametres'),
|
||||||
|
path('profil/emails/<str:email>/aconfirmer/',
|
||||||
|
views.AdresseAConfirmer.as_view(), name="emails_aconfirmer"),
|
||||||
|
path('profil/emails/<str:email>/supprime/', views.SupprimeAdresse.as_view(),
|
||||||
|
name="emails_supprime"),
|
||||||
|
path('profil/emails/<str:email>/reconfirme/',
|
||||||
|
views.ReConfirmeAdresse.as_view(),
|
||||||
|
name="emails_reconfirme"),
|
||||||
|
path('profil/emails/<str:email>/principal/',
|
||||||
|
views.RendAdressePrincipale.as_view(), name="emails_principal"),
|
||||||
|
path('profil/emails/confirme/<str:key>/', views.ConfirmeAdresse.as_view(),
|
||||||
|
name="emails_confirme"),
|
||||||
|
path('profil/mdp/demande/',
|
||||||
|
views.EnvoieLienMotDePasse.as_view(), name="mdp_demande"),
|
||||||
|
path('profil/mdp/<str:uidb64>/<str:token>/',
|
||||||
|
views.DefinirMotDePasse.as_view(), name="mdp_edit"),
|
||||||
|
|
||||||
path('recherche/', views.recherche, name='recherche'),
|
path('recherche/', views.recherche, name='recherche'),
|
||||||
path('recherche/resultats/', views.recherche_resultats,
|
path('recherche/resultats/', views.recherche_resultats,
|
||||||
name='recherche_resultats'),
|
name='recherche_resultats'),
|
||||||
|
|
|
@ -2,21 +2,31 @@
|
||||||
|
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
|
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import (
|
||||||
from django.views.generic.edit import UpdateView, CreateView
|
DetailView, ListView, UpdateView, CreateView, TemplateView, DeleteView,
|
||||||
|
FormView, View
|
||||||
|
)
|
||||||
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.urls import reverse
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth.tokens import default_token_generator
|
||||||
|
from django.contrib.auth.views import PasswordResetConfirmView
|
||||||
|
from django.contrib import messages
|
||||||
from braces.views import LoginRequiredMixin
|
from braces.views import LoginRequiredMixin
|
||||||
from django.http import JsonResponse, HttpResponseForbidden, Http404
|
from django.http import JsonResponse, HttpResponseForbidden, Http404
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.db.models import Q, Count
|
from django.db.models import Q, Count
|
||||||
from collections import Counter, defaultdict
|
from collections import Counter, defaultdict
|
||||||
|
from simple_email_confirmation.models import EmailAddress
|
||||||
|
|
||||||
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage
|
from .models import Normalien, Stage, Lieu, AvisLieu, AvisStage
|
||||||
from .forms import StageForm, LieuForm, AvisStageForm, AvisLieuForm, FeedbackForm
|
from .forms import (
|
||||||
|
StageForm, LieuForm, AvisStageForm, AvisLieuForm, FeedbackForm, AdresseEmailForm,
|
||||||
|
ReinitMdpForm
|
||||||
|
)
|
||||||
from .utils import en_scolarite
|
from .utils import en_scolarite
|
||||||
|
|
||||||
from .views_search import *
|
from .views_search import *
|
||||||
|
@ -343,3 +353,126 @@ def statistiques(request):
|
||||||
'num_lieux_utiles': nlieux,
|
'num_lieux_utiles': nlieux,
|
||||||
'num_par_longueur': nbylength,
|
'num_par_longueur': nbylength,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#
|
||||||
|
# Compte
|
||||||
|
#
|
||||||
|
|
||||||
|
class MesAdressesMixin(LoginRequiredMixin):
|
||||||
|
slug_url_kwarg = "email"
|
||||||
|
slug_field = "email"
|
||||||
|
confirmed_only = False
|
||||||
|
|
||||||
|
def get_queryset(self, *args, **kwargs):
|
||||||
|
qs = self.request.user.email_address_set.all()
|
||||||
|
if self.confirmed_only:
|
||||||
|
qs = qs.filter(confirmed_at__isnull=False)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
def _send_confirm_mail(email, request):
|
||||||
|
confirm_url = request.build_absolute_uri(
|
||||||
|
reverse("avisstage:emails_confirme", kwargs={"key": email.key}))
|
||||||
|
send_mail(
|
||||||
|
"[ExperiENS] Confirmez votre adresse a-mail",
|
||||||
|
"""Bonjour,
|
||||||
|
|
||||||
|
Vous venez d'ajouter cette adresse e-mail à votre compte ExperiENS.
|
||||||
|
|
||||||
|
Pour la vérifier, merci de cliquer sur le lien suivant, ou de copier l'adresse dans votre navigateur :
|
||||||
|
|
||||||
|
{confirm_url}
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
L'équipe ExperiENS""".format(confirm_url=confirm_url),
|
||||||
|
'experiens-nepasrepondre@eleves.ens.fr',
|
||||||
|
[email.email],
|
||||||
|
fail_silently=False,
|
||||||
|
)
|
||||||
|
return redirect(reverse("avisstage:emails_aconfirmer",
|
||||||
|
kwargs={"email": email.email}))
|
||||||
|
|
||||||
|
class MesParametres(LoginRequiredMixin, FormView):
|
||||||
|
model = EmailAddress
|
||||||
|
template_name = "avisstage/compte/parametres.html"
|
||||||
|
form_class = AdresseEmailForm
|
||||||
|
|
||||||
|
def get_form_kwargs(self, *args, **kwargs):
|
||||||
|
kwargs = super().get_form_kwargs(*args, **kwargs)
|
||||||
|
kwargs["_user"] = self.request.user
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
new = EmailAddress.objects.create_unconfirmed(
|
||||||
|
form.cleaned_data["email"], self.request.user)
|
||||||
|
return _send_confirm_mail(new, self.request)
|
||||||
|
|
||||||
|
class RendAdressePrincipale(MesAdressesMixin, SingleObjectMixin, View):
|
||||||
|
model = EmailAddress
|
||||||
|
confirmed_only = True
|
||||||
|
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
if not hasattr(self, "object"):
|
||||||
|
self.object = self.get_object()
|
||||||
|
self.request.user.email = self.object.email
|
||||||
|
self.request.user.save()
|
||||||
|
return redirect(reverse("avisstage:parametres"))
|
||||||
|
|
||||||
|
class AdresseAConfirmer(MesAdressesMixin, DetailView):
|
||||||
|
model = EmailAddress
|
||||||
|
template_name = "avisstage/compte/aconfirmer.html"
|
||||||
|
|
||||||
|
class ReConfirmeAdresse(MesAdressesMixin, DetailView):
|
||||||
|
model = EmailAddress
|
||||||
|
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
email = self.get_object()
|
||||||
|
if email.confirmed_at is None:
|
||||||
|
return _send_confirm_mail(email, self.request)
|
||||||
|
return redirect(reverse("avisstage:parametres"))
|
||||||
|
|
||||||
|
class ConfirmeAdresse(LoginRequiredMixin, View):
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
email = EmailAddress.objects.confirm(self.kwargs["key"],
|
||||||
|
self.request.user, True)
|
||||||
|
except Exception as e:
|
||||||
|
raise Http404()
|
||||||
|
messages.add_message(
|
||||||
|
self.request, messages.SUCCESS,
|
||||||
|
"L'adresse email {email} a bien été confirmée".format(email=email.email))
|
||||||
|
return redirect(reverse("avisstage:parametres"))
|
||||||
|
|
||||||
|
class SupprimeAdresse(MesAdressesMixin, DeleteView):
|
||||||
|
model = EmailAddress
|
||||||
|
template_name = "avisstage/compte/email_supprime.html"
|
||||||
|
success_url = reverse_lazy("avisstage:parametres")
|
||||||
|
|
||||||
|
def get_queryset(self, *args, **kwargs):
|
||||||
|
qs = super().get_queryset(*args, **kwargs)
|
||||||
|
return qs.exclude(email=self.request.user.email)
|
||||||
|
|
||||||
|
class EnvoieLienMotDePasse(LoginRequiredMixin, View):
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
form = ReinitMdpForm({"email": self.request.user.email})
|
||||||
|
form.is_valid()
|
||||||
|
form.save(
|
||||||
|
email_template_name = 'avisstage/mails/reinit_mdp.html',
|
||||||
|
from_email = 'experiens-nepasrepondre@eleves.ens.fr',
|
||||||
|
subject_template_name = 'avisstage/mails/reinit_mdp.txt',
|
||||||
|
)
|
||||||
|
messages.add_message(
|
||||||
|
self.request, messages.INFO,
|
||||||
|
"Un mail a été envoyé à {email}. Merci de vérifier vos indésirables si vous ne le recevez pas bientôt".format(email=self.request.user.email)
|
||||||
|
)
|
||||||
|
return redirect(reverse("avisstage:parametres"))
|
||||||
|
|
||||||
|
class DefinirMotDePasse(PasswordResetConfirmView):
|
||||||
|
template_name = "avisstage/compte/edit_mdp.html"
|
||||||
|
success_url = reverse_lazy("avisstage:perso")
|
||||||
|
|
||||||
|
def get_user(self, *args, **kwargs):
|
||||||
|
user = super().get_user(*args, **kwargs)
|
||||||
|
if self.request.user.is_authenticated and user != self.request.user:
|
||||||
|
raise Http404("Ce token n'est pas valide pour votre compte")
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,9 @@ INSTALLED_APPS = [
|
||||||
|
|
||||||
'django_elasticsearch_dsl',
|
'django_elasticsearch_dsl',
|
||||||
|
|
||||||
#'allauth',
|
#'allauth', # Uncomment that part when you
|
||||||
#'allauth.account', # Uncomment for transition
|
#'allauth.account', # apply migration
|
||||||
#'allauth.socialaccount', # Allauth -> Authens
|
#'allauth.socialaccount', # Allauth -> AuthENS
|
||||||
|
|
||||||
'widget_tweaks',
|
'widget_tweaks',
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue