Comment code

This commit is contained in:
Yohann D'ANELLO 2020-04-06 08:58:39 +02:00
parent 9d584ae87a
commit f833f1c46c
16 changed files with 201 additions and 40 deletions

View File

@ -250,12 +250,18 @@ class Membership(models.Model):
) )
def valid(self): def valid(self):
"""
A membership is valid if today is between the start and the end date.
"""
if self.date_end is not None: if self.date_end is not None:
return self.date_start.toordinal() <= datetime.datetime.now().toordinal() < self.date_end.toordinal() return self.date_start.toordinal() <= datetime.datetime.now().toordinal() < self.date_end.toordinal()
else: else:
return self.date_start.toordinal() <= datetime.datetime.now().toordinal() return self.date_start.toordinal() <= datetime.datetime.now().toordinal()
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""
Calculate fee and end date before saving the membership and creating the transaction if needed.
"""
if self.club.parent_club is not None: if self.club.parent_club is not None:
if not Membership.objects.filter(user=self.user, club=self.club.parent_club).exists(): if not Membership.objects.filter(user=self.user, club=self.club.parent_club).exists():
raise ValidationError(_('User is not a member of the parent club') + ' ' + self.club.parent_club.name) raise ValidationError(_('User is not a member of the parent club') + ' ' + self.club.parent_club.name)
@ -287,6 +293,9 @@ class Membership(models.Model):
self.make_transaction() self.make_transaction()
def make_transaction(self): def make_transaction(self):
"""
Create Membership transaction associated to this membership.
"""
if not self.fee or MembershipTransaction.objects.filter(membership=self).exists(): if not self.fee or MembershipTransaction.objects.filter(membership=self).exists():
return return

View File

@ -15,6 +15,9 @@ from .models import Club, Membership
class ClubTable(tables.Table): class ClubTable(tables.Table):
"""
List all clubs.
"""
class Meta: class Meta:
attrs = { attrs = {
'class': 'table table-condensed table-striped table-hover' 'class': 'table table-condensed table-striped table-hover'
@ -30,6 +33,9 @@ class ClubTable(tables.Table):
class UserTable(tables.Table): class UserTable(tables.Table):
"""
List all users.
"""
section = tables.Column(accessor='profile.section') section = tables.Column(accessor='profile.section')
balance = tables.Column(accessor='note.balance', verbose_name=_("Balance")) balance = tables.Column(accessor='note.balance', verbose_name=_("Balance"))
@ -51,6 +57,9 @@ class UserTable(tables.Table):
class MembershipTable(tables.Table): class MembershipTable(tables.Table):
"""
List all memberships.
"""
roles = tables.Column( roles = tables.Column(
attrs={ attrs={
"td": { "td": {
@ -59,7 +68,17 @@ class MembershipTable(tables.Table):
} }
) )
def render_user(self, value):
# If the user has the right, link the displayed user with the page of its detail.
s = value.username
if PermissionBackend.check_perm(get_current_authenticated_user(), "auth.view_user", value):
s = format_html("<a href={url}>{name}</a>",
url=reverse_lazy('member:user_detail', kwargs={"pk": value.pk}), name=s)
return s
def render_club(self, value): def render_club(self, value):
# If the user has the right, link the displayed club with the page of its detail.
s = value.name s = value.name
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_club", value): if PermissionBackend.check_perm(get_current_authenticated_user(), "member.view_club", value):
s = format_html("<a href={url}>{name}</a>", s = format_html("<a href={url}>{name}</a>",
@ -94,6 +113,7 @@ class MembershipTable(tables.Table):
return t return t
def render_roles(self, record): def render_roles(self, record):
# If the user has the right to manage the roles, display the link to manage them
roles = record.roles.all() roles = record.roles.all()
s = ", ".join(str(role) for role in roles) s = ", ".join(str(role) for role in roles)
if PermissionBackend.check_perm(get_current_authenticated_user(), "member.change_membership_roles", record): if PermissionBackend.check_perm(get_current_authenticated_user(), "member.change_membership_roles", record):

View File

@ -30,6 +30,9 @@ from .tables import ClubTable, UserTable, MembershipTable
class CustomLoginView(LoginView): class CustomLoginView(LoginView):
"""
Login view, where the user can select its permission mask.
"""
form_class = CustomAuthenticationForm form_class = CustomAuthenticationForm
def form_valid(self, form): def form_valid(self, form):
@ -38,6 +41,9 @@ class CustomLoginView(LoginView):
class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
"""
Update the user information.
"""
model = User model = User
fields = ['first_name', 'last_name', 'username', 'email'] fields = ['first_name', 'last_name', 'username', 'email']
template_name = 'member/profile_update.html' template_name = 'member/profile_update.html'
@ -93,6 +99,7 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
user.save() user.save()
if olduser.email != user.email: if olduser.email != user.email:
# If the user changed her/his email, then it is unvalidated and a confirmation link is sent.
user.profile.email_confirmed = False user.profile.email_confirmed = False
user.profile.send_email_validation_link() user.profile.send_email_validation_link()
@ -132,13 +139,16 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
""" """
Affiche la liste des utilisateurs, avec une fonction de recherche statique Display user list, with a search bar
""" """
model = User model = User
table_class = UserTable table_class = UserTable
template_name = 'member/user_list.html' template_name = 'member/user_list.html'
def get_queryset(self, **kwargs): def get_queryset(self, **kwargs):
"""
Filter the user list with the given pattern.
"""
qs = super().get_queryset().filter(profile__registration_valid=True) qs = super().get_queryset().filter(profile__registration_valid=True)
if "search" in self.request.GET: if "search" in self.request.GET:
pattern = self.request.GET["search"] pattern = self.request.GET["search"]
@ -150,6 +160,7 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
Q(first_name__iregex=pattern) Q(first_name__iregex=pattern)
| Q(last_name__iregex=pattern) | Q(last_name__iregex=pattern)
| Q(profile__section__iregex=pattern) | Q(profile__section__iregex=pattern)
| Q(profile__username__iregex="^" + pattern)
| Q(note__alias__name__iregex="^" + pattern) | Q(note__alias__name__iregex="^" + pattern)
| Q(note__alias__normalized_name__iregex=Alias.normalize("^" + pattern)) | Q(note__alias__normalized_name__iregex=Alias.normalize("^" + pattern))
) )
@ -167,6 +178,9 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
View and manage user aliases.
"""
model = User model = User
template_name = 'member/profile_alias.html' template_name = 'member/profile_alias.html'
context_object_name = 'user_object' context_object_name = 'user_object'
@ -179,6 +193,9 @@ class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView): class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView):
"""
Update profile picture of the user note.
"""
form_class = ImageForm form_class = ImageForm
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -278,6 +295,9 @@ class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
Display details of a club
"""
model = Club model = Club
context_object_name = "club" context_object_name = "club"
@ -298,6 +318,7 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
context['member_list'] = MembershipTable(data=club_member) context['member_list'] = MembershipTable(data=club_member)
# Check if the user has the right to create a membership, to display the button.
empty_membership = Membership( empty_membership = Membership(
club=club, club=club,
user=User.objects.first(), user=User.objects.first(),
@ -312,6 +333,9 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
Manage aliases of a club.
"""
model = Club model = Club
template_name = 'member/club_alias.html' template_name = 'member/club_alias.html'
context_object_name = 'club' context_object_name = 'club'
@ -324,6 +348,9 @@ class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
"""
Update the information of a club.
"""
model = Club model = Club
context_object_name = "club" context_object_name = "club"
form_class = ClubForm form_class = ClubForm
@ -334,6 +361,9 @@ class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
class ClubPictureUpdateView(PictureUpdateView): class ClubPictureUpdateView(PictureUpdateView):
"""
Update the profile picture of a club.
"""
model = Club model = Club
template_name = 'member/club_picture_update.html' template_name = 'member/club_picture_update.html'
context_object_name = 'club' context_object_name = 'club'
@ -343,6 +373,9 @@ class ClubPictureUpdateView(PictureUpdateView):
class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
"""
Add a membership to a club.
"""
model = Membership model = Membership
form_class = MembershipForm form_class = MembershipForm
template_name = 'member/add_members.html' template_name = 'member/add_members.html'
@ -352,10 +385,12 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form = context['form'] form = context['form']
if "club_pk" in self.kwargs: if "club_pk" in self.kwargs:
# We create a new membership.
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\ club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
.get(pk=self.kwargs["club_pk"]) .get(pk=self.kwargs["club_pk"])
form.fields['credit_amount'].initial = club.membership_fee_paid form.fields['credit_amount'].initial = club.membership_fee_paid
# If the concerned club is the BDE, then we add the option that Société générale pays the membership.
if club.name != "BDE": if club.name != "BDE":
del form.fields['soge'] del form.fields['soge']
else: else:
@ -366,6 +401,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
fee += kfet.membership_fee_paid fee += kfet.membership_fee_paid
context["total_fee"] = "{:.02f}".format(fee / 100, ) context["total_fee"] = "{:.02f}".format(fee / 100, )
else: else:
# This is a renewal. Fields can be pre-completed.
old_membership = self.get_queryset().get(pk=self.kwargs["pk"]) old_membership = self.get_queryset().get(pk=self.kwargs["pk"])
club = old_membership.club club = old_membership.club
user = old_membership.user user = old_membership.user
@ -378,6 +414,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form.fields['last_name'].initial = user.last_name form.fields['last_name'].initial = user.last_name
form.fields['first_name'].initial = user.first_name form.fields['first_name'].initial = user.first_name
# If this is a renewal of a BDE membership, Société générale can pays, if it is not yet done
if club.name != "BDE" or user.profile.soge: if club.name != "BDE" or user.profile.soge:
del form.fields['soge'] del form.fields['soge']
else: else:
@ -393,6 +430,10 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
return context return context
def form_valid(self, form): def form_valid(self, form):
"""
Create membership, check that all is good, make transactions
"""
# Get the club that is concerned by the membership
if "club_pk" in self.kwargs: if "club_pk" in self.kwargs:
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) \ club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view")) \
.get(pk=self.kwargs["club_pk"]) .get(pk=self.kwargs["club_pk"])
@ -404,6 +445,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form.instance.club = club form.instance.club = club
# Get form data
credit_type = form.cleaned_data["credit_type"] credit_type = form.cleaned_data["credit_type"]
credit_amount = form.cleaned_data["credit_amount"] credit_amount = form.cleaned_data["credit_amount"]
last_name = form.cleaned_data["last_name"] last_name = form.cleaned_data["last_name"]
@ -411,6 +453,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
bank = form.cleaned_data["bank"] bank = form.cleaned_data["bank"]
soge = form.cleaned_data["soge"] and not user.profile.soge and club.name == "BDE" soge = form.cleaned_data["soge"] and not user.profile.soge and club.name == "BDE"
# If Société générale pays, then we auto-fill some data
if soge: if soge:
credit_type = NoteSpecial.objects.get(special_type="Virement bancaire") credit_type = NoteSpecial.objects.get(special_type="Virement bancaire")
bde = club bde = club
@ -466,6 +509,9 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
.format(form.instance.club.membership_start)) .format(form.instance.club.membership_start))
return super().form_invalid(form) return super().form_invalid(form)
# Now, all is fine, the membership can be created.
# Credit note before the membership is created.
if credit_amount > 0: if credit_amount > 0:
if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"): if not last_name or not first_name or (not bank and credit_type.special_type == "Chèque"):
if not last_name: if not last_name:
@ -488,6 +534,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
valid=True, valid=True,
) )
# If Société générale pays, then we store the information: the bank can't pay twice to a same person.
if soge: if soge:
user.profile.soge = True user.profile.soge = True
user.profile.save() user.profile.save()
@ -495,6 +542,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
kfet = Club.objects.get(name="Kfet") kfet = Club.objects.get(name="Kfet")
kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
# Get current membership, to get the end date
old_membership = Membership.objects.filter( old_membership = Membership.objects.filter(
club__name="Kfet", club__name="Kfet",
user=user, user=user,
@ -522,6 +570,9 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
"""
Manage the roles of a user in a club
"""
model = Membership model = Membership
form_class = MembershipForm form_class = MembershipForm
template_name = 'member/add_members.html' template_name = 'member/add_members.html'
@ -534,6 +585,7 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
def get_form(self, form_class=None): def get_form(self, form_class=None):
form = super().get_form(form_class) form = super().get_form(form_class)
# We don't create a full membership, we only update one field
form.fields['user'].disabled = True form.fields['user'].disabled = True
del form.fields['date_start'] del form.fields['date_start']
del form.fields['credit_type'] del form.fields['credit_type']

View File

@ -45,6 +45,7 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl
.filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\ .filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\
.order_by("special_type").all() .order_by("special_type").all()
# Add a shortcut for entry page for open activities
if "activity" in settings.INSTALLED_APPS: if "activity" in settings.INSTALLED_APPS:
from activity.models import Activity from activity.models import Activity
context["activities_open"] = Activity.objects.filter(open=True).filter( context["activities_open"] = Activity.objects.filter(open=True).filter(
@ -56,7 +57,7 @@ class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTabl
class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView): class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
""" """
Create TransactionTemplate Create Transaction template
""" """
model = TransactionTemplate model = TransactionTemplate
form_class = TransactionTemplateForm form_class = TransactionTemplateForm
@ -65,7 +66,7 @@ class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, Cr
class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
""" """
List TransactionsTemplates List Transaction templates
""" """
model = TransactionTemplate model = TransactionTemplate
table_class = ButtonTable table_class = ButtonTable
@ -73,6 +74,7 @@ class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, Sing
class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
""" """
Update Transaction template
""" """
model = TransactionTemplate model = TransactionTemplate
form_class = TransactionTemplateForm form_class = TransactionTemplateForm

View File

@ -10,6 +10,9 @@ from note_kfet.inputs import AmountInput
class SignUpForm(UserCreationForm): class SignUpForm(UserCreationForm):
"""
Pre-register users with all information
"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.pop("autofocus", None) self.fields['username'].widget.attrs.pop("autofocus", None)
@ -25,6 +28,9 @@ class SignUpForm(UserCreationForm):
class ValidationForm(forms.Form): class ValidationForm(forms.Form):
"""
Validate the inscription of the new users and pay memberships.
"""
soge = forms.BooleanField( soge = forms.BooleanField(
label=_("Inscription paid by Société Générale"), label=_("Inscription paid by Société Générale"),
required=False, required=False,
@ -66,6 +72,7 @@ class ValidationForm(forms.Form):
initial=True, initial=True,
) )
# The user can join the Kfet club at the inscription
join_Kfet = forms.BooleanField( join_Kfet = forms.BooleanField(
label=_("Join Kfet Club"), label=_("Join Kfet Club"),
required=False, required=False,

View File

@ -6,6 +6,9 @@ from django.contrib.auth.models import User
class FutureUserTable(tables.Table): class FutureUserTable(tables.Table):
"""
Display the list of pre-registered users
"""
phone_number = tables.Column(accessor='profile.phone_number') phone_number = tables.Column(accessor='profile.phone_number')
section = tables.Column(accessor='profile.section') section = tables.Column(accessor='profile.section')

View File

@ -5,13 +5,12 @@ from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models import Q
from django.shortcuts import resolve_url, redirect from django.shortcuts import resolve_url, 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.http import urlsafe_base64_decode
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django.views.decorators.csrf import csrf_protect
from django.views.generic import CreateView, TemplateView, DetailView, FormView from django.views.generic import CreateView, TemplateView, DetailView, FormView
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from member.forms import ProfileForm from member.forms import ProfileForm
@ -46,11 +45,13 @@ class UserCreateView(CreateView):
""" """
If the form is valid, then the user is created with is_active set to False 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. so that the user cannot log in until the email has been validated.
The user must also wait that someone validate her/his account.
""" """
profile_form = ProfileForm(data=self.request.POST) profile_form = ProfileForm(data=self.request.POST)
if not profile_form.is_valid(): if not profile_form.is_valid():
return self.form_invalid(form) return self.form_invalid(form)
# Save the user and the profile
user = form.save(commit=False) user = form.save(commit=False)
user.is_active = False user.is_active = False
profile_form.instance.user = user profile_form.instance.user = user
@ -67,16 +68,15 @@ class UserCreateView(CreateView):
class UserValidateView(TemplateView): class UserValidateView(TemplateView):
"""
A view to validate the email address.
"""
title = _("Email validation") title = _("Email validation")
template_name = 'registration/email_validation_complete.html' template_name = 'registration/email_validation_complete.html'
@method_decorator(csrf_protect) def get(self, *args, **kwargs):
def dispatch(self, *args, **kwargs):
""" """
The dispatch method looks at the request to determine whether it is a GET, POST, etc, With a given token and user id (in params), validate the email address.
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 assert 'uidb64' in kwargs and 'token' in kwargs
@ -84,18 +84,23 @@ class UserValidateView(TemplateView):
user = self.get_user(kwargs['uidb64']) user = self.get_user(kwargs['uidb64'])
token = kwargs['token'] token = kwargs['token']
# Validate the token
if user is not None and email_validation_token.check_token(user, token): if user is not None and email_validation_token.check_token(user, token):
self.validlink = True self.validlink = True
# The user must wait that someone validates the account before the user can be active and login.
user.is_active = user.profile.registration_valid user.is_active = user.profile.registration_valid
user.profile.email_confirmed = True user.profile.email_confirmed = True
user.save() user.save()
user.profile.save() user.profile.save()
return super().dispatch(*args, **kwargs) return super().dispatch(*args, **kwargs)
else: else:
# Display the "Account Activation unsuccessful" page. # Display the "Email validation unsuccessful" page.
return self.render_to_response(self.get_context_data()) return self.render_to_response(self.get_context_data())
def get_user(self, uidb64): def get_user(self, uidb64):
"""
Get user from the base64-encoded string.
"""
try: try:
# urlsafe_base64_decode() decodes to bytestring # urlsafe_base64_decode() decodes to bytestring
uid = urlsafe_base64_decode(uidb64).decode() uid = urlsafe_base64_decode(uidb64).decode()
@ -118,16 +123,19 @@ class UserValidateView(TemplateView):
class UserValidationEmailSentView(TemplateView): class UserValidationEmailSentView(TemplateView):
"""
Display the information that the validation link has been sent.
"""
template_name = 'registration/email_validation_email_sent.html' template_name = 'registration/email_validation_email_sent.html'
title = _('Email validation email sent') title = _('Email validation email sent')
class UserResendValidationEmailView(LoginRequiredMixin, ProtectQuerysetMixin, DetailView): class UserResendValidationEmailView(LoginRequiredMixin, ProtectQuerysetMixin, DetailView):
"""
Rensend the email validation link.
"""
model = User model = User
def get_queryset(self, **kwargs):
return super().get_queryset(**kwargs).filter(profile__email_confirmed=False)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
user = self.get_object() user = self.get_object()
@ -139,14 +147,35 @@ class UserResendValidationEmailView(LoginRequiredMixin, ProtectQuerysetMixin, De
class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
""" """
Affiche la liste des utilisateurs, avec une fonction de recherche statique Display pre-registered users, with a search bar
""" """
model = User model = User
table_class = FutureUserTable table_class = FutureUserTable
template_name = 'registration/future_user_list.html' template_name = 'registration/future_user_list.html'
def get_queryset(self, **kwargs): def get_queryset(self, **kwargs):
return super().get_queryset().filter(profile__registration_valid=False) """
Filter the table with the given parameter.
:param kwargs:
:return:
"""
qs = super().get_queryset().filter(profile__registration_valid=False)
if "search" in self.request.GET:
pattern = self.request.GET["search"]
if not pattern:
return qs.none()
qs = qs.filter(
Q(first_name__iregex=pattern)
| Q(last_name__iregex=pattern)
| Q(profile__section__iregex=pattern)
| Q(username__iregex="^" + pattern)
)
else:
qs = qs.none()
return qs[:20]
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -158,7 +187,7 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi
class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, FormView): class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView, FormView):
""" """
Affiche les informations sur un utilisateur, sa note, ses clubs... Display information about a pre-registered user, in order to complete the registration.
""" """
model = User model = User
form_class = ValidationForm form_class = ValidationForm
@ -194,6 +223,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView,
def form_valid(self, form): def form_valid(self, form):
user = self.object = self.get_object() user = self.object = self.get_object()
# Get form data
soge = form.cleaned_data["soge"] soge = form.cleaned_data["soge"]
credit_type = form.cleaned_data["credit_type"] credit_type = form.cleaned_data["credit_type"]
credit_amount = form.cleaned_data["credit_amount"] credit_amount = form.cleaned_data["credit_amount"]
@ -204,6 +234,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView,
join_Kfet = form.cleaned_data["join_Kfet"] join_Kfet = form.cleaned_data["join_Kfet"]
if soge: if soge:
# If Société Générale pays the inscription, the user joins the two clubs
join_BDE = True join_BDE = True
join_Kfet = True join_Kfet = True
@ -218,6 +249,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView,
fee += kfet_fee fee += kfet_fee
if soge: if soge:
# Fill payment information if Société Générale pays the inscription
credit_type = NoteSpecial.objects.get(special_type="Virement bancaire") credit_type = NoteSpecial.objects.get(special_type="Virement bancaire")
credit_amount = fee credit_amount = fee
bank = "Société générale" bank = "Société générale"
@ -226,6 +258,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView,
form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club.")) form.add_error('join_Kfet', _("You must join BDE club before joining Kfet club."))
if fee > credit_amount: if fee > credit_amount:
# Check if the user credits enough money
form.add_error('credit_type', form.add_error('credit_type',
_("The entered amount is not enough for the memberships, should be at least {}") _("The entered amount is not enough for the memberships, should be at least {}")
.format(pretty_money(fee))) .format(pretty_money(fee)))
@ -241,14 +274,18 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView,
form.add_error('bank', _("This field is required.")) form.add_error('bank', _("This field is required."))
return self.form_invalid(form) return self.form_invalid(form)
# Save the user and finally validate the registration
# Saving the user creates the associated note
ret = super().form_valid(form) ret = super().form_valid(form)
user.is_active = user.profile.email_confirmed user.is_active = user.profile.email_confirmed
user.profile.registration_valid = True user.profile.registration_valid = True
# Store if Société générale paid for next years
user.profile.soge = soge user.profile.soge = soge
user.save() user.save()
user.profile.save() user.profile.save()
if credit_type is not None and credit_amount > 0: if credit_type is not None and credit_amount > 0:
# Credit the note
SpecialTransaction.objects.create( SpecialTransaction.objects.create(
source=credit_type, source=credit_type,
destination=user.note, destination=user.note,
@ -262,6 +299,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView,
) )
if join_BDE: if join_BDE:
# Create membership for the user to the BDE starting today
membership = Membership.objects.create( membership = Membership.objects.create(
club=bde, club=bde,
user=user, user=user,
@ -271,6 +309,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView,
membership.save() membership.save()
if join_Kfet: if join_Kfet:
# Create membership for the user to the Kfet starting today
membership = Membership.objects.create( membership = Membership.objects.create(
club=kfet, club=kfet,
user=user, user=user,
@ -287,10 +326,13 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView,
class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View): class FutureUserInvalidateView(ProtectQuerysetMixin, LoginRequiredMixin, View):
""" """
Affiche les informations sur un utilisateur, sa note, ses clubs... Delete a pre-registered user.
""" """
def dispatch(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
"""
Delete the pre-registered user which id is given in the URL.
"""
user = User.objects.filter(profile__registration_valid=False)\ user = User.objects.filter(profile__registration_valid=False)\
.filter(PermissionBackend.filter_queryset(request.user, User, "change", "is_valid"))\ .filter(PermissionBackend.filter_queryset(request.user, User, "change", "is_valid"))\
.get(pk=self.kwargs["pk"]) .get(pk=self.kwargs["pk"])

View File

@ -1267,7 +1267,7 @@ msgid "New user"
msgstr "" msgstr ""
#: templates/registration/future_user_list.html:17 #: templates/registration/future_user_list.html:17
msgid "There is no pending user." msgid "There is no pending user with this pattern."
msgstr "" msgstr ""
#: templates/registration/logged_out.html:8 #: templates/registration/logged_out.html:8

View File

@ -1274,8 +1274,8 @@ msgid "New user"
msgstr "Nouvel utilisateur" msgstr "Nouvel utilisateur"
#: templates/registration/future_user_list.html:17 #: templates/registration/future_user_list.html:17
msgid "There is no pending user." msgid "There is no pending user with this pattern."
msgstr "Il n'y a pas d'inscription en attente." msgstr "Il n'y a pas d'inscription en attente avec cette entrée."
#: templates/registration/logged_out.html:8 #: templates/registration/logged_out.html:8
msgid "Thanks for spending some quality time with the Web site today." msgid "Thanks for spending some quality time with the Web site today."

View File

@ -28,7 +28,6 @@ function reset() {
} }
$(document).ready(function() { $(document).ready(function() {
console.log(42);
autoCompleteNote("source_note", "source_alias_matched", "source_note_list", sources, sources_notes_display, autoCompleteNote("source_note", "source_alias_matched", "source_note_list", sources, sources_notes_display,
"source_alias", "source_note", "user_note", "profile_pic"); "source_alias", "source_note", "user_note", "profile_pic");
autoCompleteNote("dest_note", "dest_alias_matched", "dest_note_list", dests, dests_notes_display, autoCompleteNote("dest_note", "dest_alias_matched", "dest_note_list", dests, dests_notes_display,
@ -72,7 +71,6 @@ $(document).ready(function() {
$("label[for='type_credit']").attr('class', 'btn btn-sm btn-outline-primary'); $("label[for='type_credit']").attr('class', 'btn btn-sm btn-outline-primary');
$("label[for='type_debit']").attr('class', 'btn btn-sm btn-outline-primary'); $("label[for='type_debit']").attr('class', 'btn btn-sm btn-outline-primary');
console.log("#type_" + location.hash.substr(1));
if (location.hash) if (location.hash)
$("#type_" + location.hash.substr(1)).click(); $("#type_" + location.hash.substr(1)).click();
else else

View File

@ -118,7 +118,6 @@
}); });
$("#validate_activity").click(function () { $("#validate_activity").click(function () {
console.log(42);
$.ajax({ $.ajax({
url: "/api/activity/activity/{{ activity.pk }}/", url: "/api/activity/activity/{{ activity.pk }}/",
type: "PATCH", type: "PATCH",

View File

@ -36,7 +36,6 @@ function getInfo() {
if (asked.length >= 1) { if (asked.length >= 1) {
$.getJSON("/api/members/club/?format=json&search="+asked, function(buttons){ $.getJSON("/api/members/club/?format=json&search="+asked, function(buttons){
let selected_id = buttons.results.map((a => "#row-"+a.id)); let selected_id = buttons.results.map((a => "#row-"+a.id));
console.log(selected_id.join());
$(".table-row,"+selected_id.join()).show(); $(".table-row,"+selected_id.join()).show();
$(".table-row").not(selected_id.join()).hide(); $(".table-row").not(selected_id.join()).hide();

View File

@ -7,7 +7,13 @@
<hr> <hr>
<div id="user_table"> <div id="user_table">
{% render_table table %} {% if table.data %}
{% render_table table %}
{% else %}
<div class="alert alert-warning">
{% trans "There is no pending user with this pattern." %}
</div>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -37,7 +37,6 @@ function getInfo() {
if (asked.length >= 1) { if (asked.length >= 1) {
$.getJSON("/api/note/transaction/template/?format=json&search="+asked, function(buttons){ $.getJSON("/api/note/transaction/template/?format=json&search="+asked, function(buttons){
let selected_id = buttons.results.map((a => "#row-"+a.id)); let selected_id = buttons.results.map((a => "#row-"+a.id));
console.log(selected_id.join());
$(".table-row,"+selected_id.join()).show(); $(".table-row,"+selected_id.join()).show();
$(".table-row").not(selected_id.join()).hide(); $(".table-row").not(selected_id.join()).hide();

View File

@ -5,24 +5,49 @@
{% block content %} {% block content %}
<a href="{% url 'registration:signup' %}"><button class="btn btn-primary btn-block">{% trans "New user" %}</button></a> <a href="{% url 'registration:signup' %}"><button class="btn btn-primary btn-block">{% trans "New user" %}</button></a>
<hr>
<input id="searchbar" type="text" class="form-control" placeholder="Nom/prénom/note/section ...">
<hr> <hr>
{% if table.data %} <div id="user_table">
<div id="user_table"> {% if table.data %}
{% render_table table %} {% render_table table %}
</div> {% else %}
{% else %} <div class="alert alert-warning">
<div class="alert alert-warning"> {% trans "There is no pending user with this pattern." %}
{% trans "There is no pending user." %} </div>
</div> {% endif %}
{% endif %} </div>
{% endblock %} {% endblock %}
{% block extrajavascript %} {% block extrajavascript %}
<script type="text/javascript"> <script type="text/javascript">
$(".table-row").click(function() { $(document).ready(function() {
window.document.location = $(this).data("href"); let old_pattern = null;
let searchbar_obj = $("#searchbar");
function reloadTable() {
let pattern = searchbar_obj.val();
if (pattern === old_pattern || pattern === "")
return;
$("#user_table").load(location.href + "?search=" + pattern.replace(" ", "%20") + " #user_table", init);
$(".table-row").click(function() {
window.document.location = $(this).data("href");
});
}
searchbar_obj.keyup(reloadTable);
function init() {
$(".table-row").click(function() {
window.document.location = $(this).data("href");
});
}
init();
}); });
</script> </script>
{% endblock %} {% endblock %}