mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-31 07:49:57 +01:00 
			
		
		
		
	Use a separate app for registration
This commit is contained in:
		| @@ -2,9 +2,8 @@ | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| 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.utils.translation import ugettext_lazy as _ | ||||
| from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput | ||||
| 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): | ||||
|     """ | ||||
|     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.http import urlsafe_base64_encode | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
|  | ||||
| from member.tokens import account_activation_token | ||||
| from note.models import MembershipTransaction | ||||
|  | ||||
|   | ||||
| @@ -7,11 +7,6 @@ from . import views | ||||
|  | ||||
| app_name = 'member' | ||||
| 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/create/', views.ClubCreateView.as_view(), name="club_create"), | ||||
|     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.db.models import Q | ||||
| 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.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, DetailView, UpdateView, TemplateView | ||||
| from django.views.generic.base import View | ||||
| from django.views.generic.edit import FormMixin | ||||
| @@ -30,10 +27,9 @@ from note.tables import HistoryTable, AliasTable | ||||
| from permission.backends import PermissionBackend | ||||
| 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 .tables import ClubTable, UserTable, MembershipTable | ||||
| from .tokens import account_activation_token | ||||
|  | ||||
|  | ||||
| class CustomLoginView(LoginView): | ||||
| @@ -44,98 +40,6 @@ class CustomLoginView(LoginView): | ||||
|         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): | ||||
|     model = User | ||||
|     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', | ||||
|  | ||||
|     # Note apps | ||||
|     'api', | ||||
|     'activity', | ||||
|     'logs', | ||||
|     'member', | ||||
|     'note', | ||||
|     'treasury', | ||||
|     'permission', | ||||
|     'api', | ||||
|     'logs', | ||||
|     'registration', | ||||
|     'treasury', | ||||
| ] | ||||
| LOGIN_REDIRECT_URL = '/note/transfer/' | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ urlpatterns = [ | ||||
|     # Include project routers | ||||
|     path('note/', include('note.urls')), | ||||
|     path('accounts/', include('member.urls')), | ||||
|     path('registration/', include('registration.urls')), | ||||
|     path('activity/', include('activity.urls')), | ||||
|     path('treasury/', include('treasury.urls')), | ||||
|  | ||||
| @@ -37,14 +38,7 @@ if "cas_server" in settings.INSTALLED_APPS: | ||||
|         # Include CAS Server routers | ||||
|         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: | ||||
|     import debug_toolbar | ||||
|     urlpatterns = [ | ||||
|   | ||||
| @@ -124,7 +124,7 @@ SPDX-License-Identifier: GPL-3.0-or-later | ||||
|                     </li> | ||||
|                 {% else %} | ||||
|                     <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 | ||||
|                         </a> | ||||
|                     </li> | ||||
| @@ -138,7 +138,7 @@ SPDX-License-Identifier: GPL-3.0-or-later | ||||
|         </div> | ||||
|     </nav> | ||||
|     <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"> | ||||
|                 {% trans "Your e-mail address is not validated. Please check your mail inbox and click on the validation link." %} | ||||
|             </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user