mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-11-26 18:37:12 +00:00
Use a separate app for registration
This commit is contained in:
parent
0f77b9df9a
commit
49807d33d9
@ -2,9 +2,8 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.translation import ugettext_lazy as _
|
|
||||||
from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput
|
from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput
|
||||||
from permission.models import PermissionMask
|
from permission.models import PermissionMask
|
||||||
|
|
||||||
@ -19,21 +18,6 @@ class CustomAuthenticationForm(AuthenticationForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SignUpForm(UserCreationForm):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.fields['username'].widget.attrs.pop("autofocus", None)
|
|
||||||
self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
|
|
||||||
self.fields['first_name'].required = True
|
|
||||||
self.fields['last_name'].required = True
|
|
||||||
self.fields['email'].required = True
|
|
||||||
self.fields['email'].help_text = _("This address must be valid.")
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ('first_name', 'last_name', 'username', 'email', )
|
|
||||||
|
|
||||||
|
|
||||||
class ProfileForm(forms.ModelForm):
|
class ProfileForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
A form for the extras field provided by the :model:`member.Profile` model.
|
A form for the extras field provided by the :model:`member.Profile` model.
|
||||||
|
@ -12,7 +12,6 @@ from django.urls import reverse, reverse_lazy
|
|||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.http import urlsafe_base64_encode
|
from django.utils.http import urlsafe_base64_encode
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from member.tokens import account_activation_token
|
from member.tokens import account_activation_token
|
||||||
from note.models import MembershipTransaction
|
from note.models import MembershipTransaction
|
||||||
|
|
||||||
|
@ -7,11 +7,6 @@ from . import views
|
|||||||
|
|
||||||
app_name = 'member'
|
app_name = 'member'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('signup/', views.UserCreateView.as_view(), name="signup"),
|
|
||||||
path('accounts/activate/sent', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'),
|
|
||||||
path('accounts/activate/<uidb64>/<token>', views.UserActivateView.as_view(), name='account_activation'),
|
|
||||||
|
|
||||||
|
|
||||||
path('club/', views.ClubListView.as_view(), name="club_list"),
|
path('club/', views.ClubListView.as_view(), name="club_list"),
|
||||||
path('club/create/', views.ClubCreateView.as_view(), name="club_create"),
|
path('club/create/', views.ClubCreateView.as_view(), name="club_create"),
|
||||||
path('club/<int:pk>/', views.ClubDetailView.as_view(), name="club_detail"),
|
path('club/<int:pk>/', views.ClubDetailView.as_view(), name="club_detail"),
|
||||||
|
@ -12,12 +12,9 @@ from django.contrib.auth.views import LoginView
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.forms import HiddenInput
|
from django.forms import HiddenInput
|
||||||
from django.shortcuts import redirect, resolve_url
|
from django.shortcuts import redirect
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.decorators import method_decorator
|
|
||||||
from django.utils.http import urlsafe_base64_decode
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.decorators.csrf import csrf_protect
|
|
||||||
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView
|
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView
|
||||||
from django.views.generic.base import View
|
from django.views.generic.base import View
|
||||||
from django.views.generic.edit import FormMixin
|
from django.views.generic.edit import FormMixin
|
||||||
@ -30,10 +27,9 @@ from note.tables import HistoryTable, AliasTable
|
|||||||
from permission.backends import PermissionBackend
|
from permission.backends import PermissionBackend
|
||||||
from permission.views import ProtectQuerysetMixin
|
from permission.views import ProtectQuerysetMixin
|
||||||
|
|
||||||
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
|
from .forms import ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
|
||||||
from .models import Club, Membership
|
from .models import Club, Membership
|
||||||
from .tables import ClubTable, UserTable, MembershipTable
|
from .tables import ClubTable, UserTable, MembershipTable
|
||||||
from .tokens import account_activation_token
|
|
||||||
|
|
||||||
|
|
||||||
class CustomLoginView(LoginView):
|
class CustomLoginView(LoginView):
|
||||||
@ -44,98 +40,6 @@ class CustomLoginView(LoginView):
|
|||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class UserCreateView(CreateView):
|
|
||||||
"""
|
|
||||||
Une vue pour inscrire un utilisateur et lui créer un profil
|
|
||||||
"""
|
|
||||||
|
|
||||||
form_class = SignUpForm
|
|
||||||
success_url = reverse_lazy('member:login')
|
|
||||||
template_name = 'member/signup.html'
|
|
||||||
second_form = ProfileForm
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context["profile_form"] = self.second_form()
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
"""
|
|
||||||
If the form is valid, then the user is created with is_active set to False
|
|
||||||
so that the user cannot log in until the email has been validated.
|
|
||||||
"""
|
|
||||||
profile_form = ProfileForm(self.request.POST)
|
|
||||||
if not profile_form.is_valid():
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
user = form.save(commit=False)
|
|
||||||
user.is_active = False
|
|
||||||
user.profile = profile_form.save(commit=False)
|
|
||||||
user.save()
|
|
||||||
user.profile.save()
|
|
||||||
|
|
||||||
user.profile.send_email_validation_link()
|
|
||||||
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class UserActivateView(TemplateView):
|
|
||||||
title = _("Account Activation")
|
|
||||||
template_name = 'registration/account_activation_complete.html'
|
|
||||||
|
|
||||||
@method_decorator(csrf_protect)
|
|
||||||
def dispatch(self, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
The dispatch method looks at the request to determine whether it is a GET, POST, etc,
|
|
||||||
and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed
|
|
||||||
if not. We chose to check the token in the dispatch method to mimic PasswordReset from
|
|
||||||
django.contrib.auth
|
|
||||||
"""
|
|
||||||
assert 'uidb64' in kwargs and 'token' in kwargs
|
|
||||||
|
|
||||||
self.validlink = False
|
|
||||||
user = self.get_user(kwargs['uidb64'])
|
|
||||||
token = kwargs['token']
|
|
||||||
|
|
||||||
if user is not None and account_activation_token.check_token(user, token):
|
|
||||||
self.validlink = True
|
|
||||||
user.is_active = True
|
|
||||||
user.profile.email_confirmed = True
|
|
||||||
user.save()
|
|
||||||
return super().dispatch(*args, **kwargs)
|
|
||||||
else:
|
|
||||||
# Display the "Account Activation unsuccessful" page.
|
|
||||||
return self.render_to_response(self.get_context_data())
|
|
||||||
|
|
||||||
def get_user(self, uidb64):
|
|
||||||
print(uidb64)
|
|
||||||
try:
|
|
||||||
# urlsafe_base64_decode() decodes to bytestring
|
|
||||||
uid = urlsafe_base64_decode(uidb64).decode()
|
|
||||||
user = User.objects.get(pk=uid)
|
|
||||||
except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
|
|
||||||
user = None
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['login_url'] = resolve_url(settings.LOGIN_URL)
|
|
||||||
if self.validlink:
|
|
||||||
context['validlink'] = True
|
|
||||||
else:
|
|
||||||
context.update({
|
|
||||||
'title': _('Account Activation unsuccessful'),
|
|
||||||
'validlink': False,
|
|
||||||
})
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class UserActivationEmailSentView(TemplateView):
|
|
||||||
template_name = 'registration/account_activation_email_sent.html'
|
|
||||||
title = _('Account activation email sent')
|
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||||
model = User
|
model = User
|
||||||
fields = ['first_name', 'last_name', 'username', 'email']
|
fields = ['first_name', 'last_name', 'username', 'email']
|
||||||
|
4
apps/registration/__init__.py
Normal file
4
apps/registration/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
default_app_config = 'registration.apps.RegistrationConfig'
|
0
apps/registration/api/__init__.py
Normal file
0
apps/registration/api/__init__.py
Normal file
10
apps/registration/apps.py
Normal file
10
apps/registration/apps.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationConfig(AppConfig):
|
||||||
|
name = 'registration'
|
||||||
|
verbose_name = _('registration')
|
21
apps/registration/forms.py
Normal file
21
apps/registration/forms.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.contrib.auth.forms import UserCreationForm
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class SignUpForm(UserCreationForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['username'].widget.attrs.pop("autofocus", None)
|
||||||
|
self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
|
||||||
|
self.fields['first_name'].required = True
|
||||||
|
self.fields['last_name'].required = True
|
||||||
|
self.fields['email'].required = True
|
||||||
|
self.fields['email'].help_text = _("This address must be valid.")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ('first_name', 'last_name', 'username', 'email', )
|
0
apps/registration/migrations/__init__.py
Normal file
0
apps/registration/migrations/__init__.py
Normal file
13
apps/registration/urls.py
Normal file
13
apps/registration/urls.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'registration'
|
||||||
|
urlpatterns = [
|
||||||
|
path('signup/', views.UserCreateView.as_view(), name="signup"),
|
||||||
|
path('accounts/activate/sent', views.UserActivationEmailSentView.as_view(), name='account_activation_sent'),
|
||||||
|
path('accounts/activate/<uidb64>/<token>', views.UserActivateView.as_view(), name='account_activation'),
|
||||||
|
]
|
110
apps/registration/views.py
Normal file
110
apps/registration/views.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.shortcuts import resolve_url
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.utils.http import urlsafe_base64_decode
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.decorators.csrf import csrf_protect
|
||||||
|
from django.views.generic import CreateView, TemplateView
|
||||||
|
from member.forms import ProfileForm
|
||||||
|
from member.tokens import account_activation_token
|
||||||
|
|
||||||
|
from .forms import SignUpForm
|
||||||
|
|
||||||
|
|
||||||
|
class UserCreateView(CreateView):
|
||||||
|
"""
|
||||||
|
Une vue pour inscrire un utilisateur et lui créer un profil
|
||||||
|
"""
|
||||||
|
|
||||||
|
form_class = SignUpForm
|
||||||
|
success_url = reverse_lazy('member:login')
|
||||||
|
template_name = 'member/signup.html'
|
||||||
|
second_form = ProfileForm
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["profile_form"] = self.second_form()
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
"""
|
||||||
|
If the form is valid, then the user is created with is_active set to False
|
||||||
|
so that the user cannot log in until the email has been validated.
|
||||||
|
"""
|
||||||
|
profile_form = ProfileForm(self.request.POST)
|
||||||
|
if not profile_form.is_valid():
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
user = form.save(commit=False)
|
||||||
|
user.is_active = False
|
||||||
|
user.profile = profile_form.save(commit=False)
|
||||||
|
user.save()
|
||||||
|
user.profile.save()
|
||||||
|
|
||||||
|
user.profile.send_email_validation_link()
|
||||||
|
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class UserActivateView(TemplateView):
|
||||||
|
title = _("Account Activation")
|
||||||
|
template_name = 'registration/account_activation_complete.html'
|
||||||
|
|
||||||
|
@method_decorator(csrf_protect)
|
||||||
|
def dispatch(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
The dispatch method looks at the request to determine whether it is a GET, POST, etc,
|
||||||
|
and relays the request to a matching method if one is defined, or raises HttpResponseNotAllowed
|
||||||
|
if not. We chose to check the token in the dispatch method to mimic PasswordReset from
|
||||||
|
django.contrib.auth
|
||||||
|
"""
|
||||||
|
assert 'uidb64' in kwargs and 'token' in kwargs
|
||||||
|
|
||||||
|
self.validlink = False
|
||||||
|
user = self.get_user(kwargs['uidb64'])
|
||||||
|
token = kwargs['token']
|
||||||
|
|
||||||
|
if user is not None and account_activation_token.check_token(user, token):
|
||||||
|
self.validlink = True
|
||||||
|
user.is_active = True
|
||||||
|
user.profile.email_confirmed = True
|
||||||
|
user.save()
|
||||||
|
return super().dispatch(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
# Display the "Account Activation unsuccessful" page.
|
||||||
|
return self.render_to_response(self.get_context_data())
|
||||||
|
|
||||||
|
def get_user(self, uidb64):
|
||||||
|
print(uidb64)
|
||||||
|
try:
|
||||||
|
# urlsafe_base64_decode() decodes to bytestring
|
||||||
|
uid = urlsafe_base64_decode(uidb64).decode()
|
||||||
|
user = User.objects.get(pk=uid)
|
||||||
|
except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
|
||||||
|
user = None
|
||||||
|
return user
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['login_url'] = resolve_url(settings.LOGIN_URL)
|
||||||
|
if self.validlink:
|
||||||
|
context['validlink'] = True
|
||||||
|
else:
|
||||||
|
context.update({
|
||||||
|
'title': _('Account Activation unsuccessful'),
|
||||||
|
'validlink': False,
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class UserActivationEmailSentView(TemplateView):
|
||||||
|
template_name = 'registration/account_activation_email_sent.html'
|
||||||
|
title = _('Account activation email sent')
|
||||||
|
|
@ -54,13 +54,14 @@ INSTALLED_APPS = [
|
|||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
|
|
||||||
# Note apps
|
# Note apps
|
||||||
|
'api',
|
||||||
'activity',
|
'activity',
|
||||||
|
'logs',
|
||||||
'member',
|
'member',
|
||||||
'note',
|
'note',
|
||||||
'treasury',
|
|
||||||
'permission',
|
'permission',
|
||||||
'api',
|
'registration',
|
||||||
'logs',
|
'treasury',
|
||||||
]
|
]
|
||||||
LOGIN_REDIRECT_URL = '/note/transfer/'
|
LOGIN_REDIRECT_URL = '/note/transfer/'
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ urlpatterns = [
|
|||||||
# Include project routers
|
# Include project routers
|
||||||
path('note/', include('note.urls')),
|
path('note/', include('note.urls')),
|
||||||
path('accounts/', include('member.urls')),
|
path('accounts/', include('member.urls')),
|
||||||
|
path('registration/', include('registration.urls')),
|
||||||
path('activity/', include('activity.urls')),
|
path('activity/', include('activity.urls')),
|
||||||
path('treasury/', include('treasury.urls')),
|
path('treasury/', include('treasury.urls')),
|
||||||
|
|
||||||
@ -37,14 +38,7 @@ if "cas_server" in settings.INSTALLED_APPS:
|
|||||||
# Include CAS Server routers
|
# Include CAS Server routers
|
||||||
path('cas/', include('cas_server.urls', namespace="cas_server")),
|
path('cas/', include('cas_server.urls', namespace="cas_server")),
|
||||||
]
|
]
|
||||||
if "cas" in settings.INSTALLED_APPS:
|
|
||||||
from cas import views as cas_views
|
|
||||||
urlpatterns += [
|
|
||||||
# Include CAS Client routers
|
|
||||||
path('accounts/login/cas/', cas_views.login, name='cas_login'),
|
|
||||||
path('accounts/logout/cas/', cas_views.logout, name='cas_logout'),
|
|
||||||
|
|
||||||
]
|
|
||||||
if "debug_toolbar" in settings.INSTALLED_APPS:
|
if "debug_toolbar" in settings.INSTALLED_APPS:
|
||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -124,7 +124,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</li>
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="{% url 'member:signup' %}">
|
<a class="nav-link" href="{% url 'registration:signup' %}">
|
||||||
<i class="fa fa-user-plus"></i> S'inscrire
|
<i class="fa fa-user-plus"></i> S'inscrire
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -138,7 +138,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="container-fluid my-3" style="max-width: 1600px;">
|
<div class="container-fluid my-3" style="max-width: 1600px;">
|
||||||
{% if not user.profile.email_confirmed %}
|
{% if user.is_authenticated and not user.profile.email_confirmed %}
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
{% trans "Your e-mail address is not validated. Please check your mail inbox and click on the validation link." %}
|
{% trans "Your e-mail address is not validated. Please check your mail inbox and click on the validation link." %}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user