1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-21 01:48:21 +02:00

Merge branch 'beta-soon' into 'master'

Pre-beta fixes

Closes #51

See merge request bde/nk20!86
This commit is contained in:
ynerant
2020-08-01 16:12:09 +02:00
100 changed files with 5020 additions and 2196 deletions

View File

@ -4,9 +4,12 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from note.templatetags.pretty_money import pretty_money
from note_kfet.admin import admin_site
from .forms import ProfileForm
from .models import Club, Membership, Profile, Role
from .models import Club, Membership, Profile
class ProfileInline(admin.StackedInline):
@ -17,6 +20,7 @@ class ProfileInline(admin.StackedInline):
can_delete = False
@admin.register(User, site=admin_site)
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline,)
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
@ -32,11 +36,33 @@ class CustomUserAdmin(UserAdmin):
return super().get_inline_instances(request, obj)
# Update Django User with profile
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
@admin.register(Club, site=admin_site)
class ClubAdmin(admin.ModelAdmin):
list_display = ('name', 'parent_club', 'email', 'require_memberships', 'pretty_fee_paid',
'pretty_fee_unpaid', 'membership_start', 'membership_end',)
ordering = ('name',)
search_fields = ('name', 'email',)
# Add other models
admin.site.register(Club)
admin.site.register(Membership)
admin.site.register(Role)
def pretty_fee_paid(self, obj):
return pretty_money(obj.membership_fee_paid)
def pretty_fee_unpaid(self, obj):
return pretty_money(obj.membership_fee_unpaid)
pretty_fee_paid.short_description = _("membership fee (paid students)")
pretty_fee_unpaid.short_description = _("membership fee (unpaid students)")
@admin.register(Membership, site=admin_site)
class MembershipAdmin(admin.ModelAdmin):
list_display = ('user', 'club', 'date_start', 'date_end', 'view_roles', 'pretty_fee',)
ordering = ('-date_start', 'club')
def view_roles(self, obj):
return ", ".join(role.name for role in obj.roles.all())
def pretty_fee(self, obj):
return pretty_money(obj.fee)
view_roles.short_description = _("roles")
pretty_fee.short_description = _("fee")

View File

@ -3,7 +3,7 @@
from rest_framework import serializers
from ..models import Profile, Club, Role, Membership
from ..models import Profile, Club, Membership
class ProfileSerializer(serializers.ModelSerializer):
@ -29,17 +29,6 @@ class ClubSerializer(serializers.ModelSerializer):
fields = '__all__'
class RoleSerializer(serializers.ModelSerializer):
"""
REST API Serializer for Roles.
The djangorestframework plugin will analyse the model `Role` and parse all fields in the API.
"""
class Meta:
model = Role
fields = '__all__'
class MembershipSerializer(serializers.ModelSerializer):
"""
REST API Serializer for Memberships.

View File

@ -1,7 +1,7 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from .views import ProfileViewSet, ClubViewSet, RoleViewSet, MembershipViewSet
from .views import ProfileViewSet, ClubViewSet, MembershipViewSet
def register_members_urls(router, path):
@ -10,5 +10,4 @@ def register_members_urls(router, path):
"""
router.register(path + '/profile', ProfileViewSet)
router.register(path + '/club', ClubViewSet)
router.register(path + '/role', RoleViewSet)
router.register(path + '/membership', MembershipViewSet)

View File

@ -4,8 +4,8 @@
from rest_framework.filters import SearchFilter
from api.viewsets import ReadProtectedModelViewSet
from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
from ..models import Profile, Club, Role, Membership
from .serializers import ProfileSerializer, ClubSerializer, MembershipSerializer
from ..models import Profile, Club, Membership
class ProfileViewSet(ReadProtectedModelViewSet):
@ -30,18 +30,6 @@ class ClubViewSet(ReadProtectedModelViewSet):
search_fields = ['$name', ]
class RoleViewSet(ReadProtectedModelViewSet):
"""
REST API View set.
The djangorestframework plugin will get all `Role` objects, serialize it to JSON with the given serializer,
then render it on /api/members/role/
"""
queryset = Role.objects.all()
serializer_class = RoleSerializer
filter_backends = [SearchFilter]
search_fields = ['$name', ]
class MembershipViewSet(ReadProtectedModelViewSet):
"""
REST API View set.

View File

@ -5,11 +5,11 @@ from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from note.models import NoteSpecial
from note.models import NoteSpecial, Alias
from note_kfet.inputs import Autocomplete, AmountInput, DatePickerInput
from permission.models import PermissionMask
from permission.models import PermissionMask, Role
from .models import Profile, Club, Membership, Role
from .models import Profile, Club, Membership
class CustomAuthenticationForm(AuthenticationForm):
@ -20,6 +20,18 @@ class CustomAuthenticationForm(AuthenticationForm):
)
class UserForm(forms.ModelForm):
def _get_validation_exclusions(self):
# Django usernames can only contain letters, numbers, @, ., +, - and _.
# We want to allow users to have uncommon and unpractical usernames:
# That is their problem, and we have normalized aliases for us.
return super()._get_validation_exclusions() + ["username"]
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.
@ -38,6 +50,15 @@ class ProfileForm(forms.ModelForm):
class ClubForm(forms.ModelForm):
def clean(self):
cleaned_data = super().clean()
if not self.instance.pk: # Creating a club
if Alias.objects.filter(normalized_name=Alias.normalize(self.cleaned_data["name"])).exists():
self.add_error('name', _("An alias with a similar name already exists."))
return cleaned_data
class Meta:
model = Club
fields = '__all__'
@ -56,8 +77,6 @@ class ClubForm(forms.ModelForm):
class MembershipForm(forms.ModelForm):
roles = forms.ModelMultipleChoiceField(queryset=Role.objects.filter(weirole=None).all())
soge = forms.BooleanField(
label=_("Inscription paid by Société Générale"),
required=False,
@ -96,7 +115,7 @@ class MembershipForm(forms.ModelForm):
class Meta:
model = Membership
fields = ('user', 'roles', 'date_start')
fields = ('user', 'date_start')
# Le champ d'utilisateur est remplacé par un champ d'auto-complétion.
# Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion
# et récupère les noms d'utilisateur valides
@ -112,3 +131,28 @@ class MembershipForm(forms.ModelForm):
),
'date_start': DatePickerInput(),
}
class MembershipRolesForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=User.objects,
label=_("User"),
disabled=True,
widget=Autocomplete(
User,
attrs={
'api_url': '/api/user/',
'name_field': 'username',
'placeholder': 'Nom ...',
},
),
)
roles = forms.ModelMultipleChoiceField(
queryset=Role.objects.filter(weirole=None).all(),
label=_("Roles"),
)
class Meta:
model = Membership
fields = ('user', 'roles')

View File

@ -3,8 +3,10 @@
import hashlib
from django.conf import settings
from django.contrib.auth.hashers import PBKDF2PasswordHasher
from django.utils.crypto import constant_time_compare
from note_kfet.middlewares import get_current_authenticated_user, get_current_session
class CustomNK15Hasher(PBKDF2PasswordHasher):
@ -20,8 +22,37 @@ class CustomNK15Hasher(PBKDF2PasswordHasher):
"""
algorithm = "custom_nk15"
def must_update(self, encoded):
if settings.DEBUG:
current_user = get_current_authenticated_user()
if current_user is not None and current_user.is_superuser:
return False
return True
def verify(self, password, encoded):
if settings.DEBUG:
current_user = get_current_authenticated_user()
if current_user is not None and current_user.is_superuser\
and get_current_session().get("permission_mask", -1) >= 42:
return True
if '|' in encoded:
salt, db_hashed_pass = encoded.split('$')[2].split('|')
return constant_time_compare(hashlib.sha256((salt + password).encode("utf-8")).hexdigest(), db_hashed_pass)
return super().verify(password, encoded)
class DebugSuperuserBackdoor(PBKDF2PasswordHasher):
"""
In debug mode and during the beta, superusers can login into other accounts for tests.
"""
def must_update(self, encoded):
return False
def verify(self, password, encoded):
if settings.DEBUG:
current_user = get_current_authenticated_user()
if current_user is not None and current_user.is_superuser\
and get_current_session().get("permission_mask", -1) >= 42:
return True
return super().verify(password, encoded)

View File

@ -131,7 +131,7 @@ class Profile(models.Model):
return reverse('user_detail', args=(self.pk,))
def send_email_validation_link(self):
subject = "Activate your Note Kfet account"
subject = _("Activate your Note Kfet account")
message = loader.render_to_string('registration/mails/email_validation_email.html',
{
'user': self.user,
@ -247,24 +247,6 @@ class Club(models.Model):
return reverse_lazy('member:club_detail', args=(self.pk,))
class Role(models.Model):
"""
Role that an :model:`auth.User` can have in a :model:`member.Club`
"""
name = models.CharField(
verbose_name=_('name'),
max_length=255,
unique=True,
)
class Meta:
verbose_name = _('role')
verbose_name_plural = _('roles')
def __str__(self):
return str(self.name)
class Membership(models.Model):
"""
Register the membership of a user to a club, including roles and membership duration.
@ -284,7 +266,7 @@ class Membership(models.Model):
)
roles = models.ManyToManyField(
Role,
"permission.Role",
verbose_name=_("roles"),
)
@ -302,6 +284,7 @@ class Membership(models.Model):
verbose_name=_('fee'),
)
@property
def valid(self):
"""
A membership is valid if today is between the start and the end date.
@ -319,6 +302,14 @@ class Membership(models.Model):
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)
if self.pk:
for role in self.roles.all():
club = role.for_club
if club is not None:
if club.pk != self.club_id:
raise ValidationError(_('The role {role} does not apply to the club {club}.')
.format(role=role.name, club=club.name))
created = not self.pk
if created:
if Membership.objects.filter(

View File

@ -131,3 +131,31 @@ class MembershipTable(tables.Table):
template_name = 'django_tables2/bootstrap4.html'
fields = ('user', 'club', 'date_start', 'date_end', 'roles', 'fee', )
model = Membership
class ClubManagerTable(tables.Table):
"""
List managers of a club.
"""
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_roles(self, record):
roles = record.roles.all()
return ", ".join(str(role) for role in roles)
class Meta:
attrs = {
'class': 'table table-condensed table-striped table-hover',
'style': 'table-layout: fixed;'
}
template_name = 'django_tables2/bootstrap4.html'
fields = ('user', 'user.first_name', 'user.last_name', 'roles', )
model = Membership

View File

@ -16,6 +16,7 @@ urlpatterns = [
path('club/<int:pk>/update/', views.ClubUpdateView.as_view(), name="club_update"),
path('club/<int:pk>/update_pic/', views.ClubPictureUpdateView.as_view(), name="club_update_pic"),
path('club/<int:pk>/aliases/', views.ClubAliasView.as_view(), name="club_alias"),
path('club/<int:pk>/members/', views.ClubMembersListView.as_view(), name="club_members"),
path('user/', views.UserListView.as_view(), name="user_list"),
path('user/<int:pk>/', views.UserDetailView.as_view(), name="user_detail"),

View File

@ -6,12 +6,14 @@ from datetime import datetime, timedelta
from PIL import Image
from django.conf import settings
from django.contrib.auth import logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.auth.views import LoginView
from django.db.models import Q
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView
from django.views.generic.edit import FormMixin
@ -21,12 +23,14 @@ from note.forms import ImageForm
from note.models import Alias, NoteUser
from note.models.transactions import Transaction, SpecialTransaction
from note.tables import HistoryTable, AliasTable
from note_kfet.middlewares import _set_current_user_and_ip
from permission.backends import PermissionBackend
from permission.models import Role
from permission.views import ProtectQuerysetMixin
from .forms import ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm
from .models import Club, Membership, Role
from .tables import ClubTable, UserTable, MembershipTable
from .forms import ProfileForm, ClubForm, MembershipForm, CustomAuthenticationForm, UserForm, MembershipRolesForm
from .models import Club, Membership
from .tables import ClubTable, UserTable, MembershipTable, ClubManagerTable
class CustomLoginView(LoginView):
@ -36,6 +40,8 @@ class CustomLoginView(LoginView):
form_class = CustomAuthenticationForm
def form_valid(self, form):
logout(self.request)
_set_current_user_and_ip(form.get_user(), self.request.session, None)
self.request.session['permission_mask'] = form.cleaned_data['permission_mask'].rank
return super().form_valid(form)
@ -45,9 +51,11 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
Update the user information.
"""
model = User
fields = ['first_name', 'last_name', 'username', 'email']
form_class = UserForm
template_name = 'member/profile_update.html'
context_object_name = 'user_object'
extra_context = {"title": _("Update Profile")}
profile_form = ProfileForm
def get_context_data(self, **kwargs):
@ -62,7 +70,6 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form.fields['email'].help_text = _("This address must be valid.")
context['profile_form'] = self.profile_form(instance=context['user_object'].profile)
context['title'] = _("Update Profile")
return context
def form_valid(self, form):
@ -101,6 +108,7 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
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.save()
user.profile.send_email_validation_link()
return super().form_valid(form)
@ -117,6 +125,7 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
model = User
context_object_name = "user_object"
template_name = "member/profile_detail.html"
extra_context = {"title": _("Profile detail")}
def get_queryset(self, **kwargs):
"""
@ -129,7 +138,7 @@ class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
user = context['user_object']
history_list = \
Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note))\
.order_by("-created_at", "-id")\
.order_by("-created_at")\
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))
history_table = HistoryTable(history_list, prefix='transaction-')
history_table.paginate(per_page=20, page=self.request.GET.get("transaction-page", 1))
@ -150,12 +159,13 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
model = User
table_class = UserTable
template_name = 'member/user_list.html'
extra_context = {"title": _("Search user")}
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().distinct().filter(profile__registration_valid=True)
if "search" in self.request.GET:
pattern = self.request.GET["search"]
@ -175,13 +185,6 @@ class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
return qs[:20]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = _("Search user")
return context
class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
@ -190,6 +193,7 @@ class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
model = User
template_name = 'member/profile_alias.html'
context_object_name = 'user_object'
extra_context = {"title": _("Note aliases")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -203,6 +207,7 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det
Update profile picture of the user note.
"""
form_class = ImageForm
extra_context = {"title": _("Update note picture")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -260,6 +265,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
"""
model = Token
template_name = "member/manage_auth_tokens.html"
extra_context = {"title": _("Manage auth token")}
def get(self, request, *args, **kwargs):
if 'regenerate' in request.GET and Token.objects.filter(user=request.user).exists():
@ -287,6 +293,7 @@ class ClubCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
model = Club
form_class = ClubForm
success_url = reverse_lazy('member:club_list')
extra_context = {"title": _("Create new club")}
def form_valid(self, form):
return super().form_valid(form)
@ -298,12 +305,13 @@ class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
"""
model = Club
table_class = ClubTable
extra_context = {"title": _("Search club")}
def get_queryset(self, **kwargs):
"""
Filter the user list with the given pattern.
"""
qs = super().get_queryset().filter()
qs = super().get_queryset().distinct()
if "search" in self.request.GET:
pattern = self.request.GET["search"]
@ -322,6 +330,7 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
model = Club
context_object_name = "club"
extra_context = {"title": _("Club detail")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -330,9 +339,13 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
if PermissionBackend.check_perm(self.request.user, "member.change_club_membership_start", club):
club.update_membership_dates()
managers = Membership.objects.filter(club=self.object, roles__name="Bureau de club")\
.order_by('user__last_name').all()
context["managers"] = ClubManagerTable(data=managers, prefix="managers-")
club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\
.filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))\
.order_by('-created_at', '-id')
.order_by('-created_at')
history_table = HistoryTable(club_transactions, prefix="history-")
history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1))
context['history_list'] = history_table
@ -342,7 +355,7 @@ class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
).filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view"))
membership_table = MembershipTable(data=club_member, prefix="membership-")
membership_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1))
membership_table.paginate(per_page=5, page=self.request.GET.get('membership-page', 1))
context['member_list'] = membership_table
# Check if the user has the right to create a membership, to display the button.
@ -366,6 +379,7 @@ class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
model = Club
template_name = 'member/club_alias.html'
context_object_name = 'club'
extra_context = {"title": _("Note aliases")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -382,6 +396,7 @@ class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
context_object_name = "club"
form_class = ClubForm
template_name = "member/club_form.html"
extra_context = {"title": _("Update club")}
def get_queryset(self, **kwargs):
qs = super().get_queryset(**kwargs)
@ -415,6 +430,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
model = Membership
form_class = MembershipForm
template_name = 'member/add_members.html'
extra_context = {"title": _("Add new member to the club")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -425,7 +441,6 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
.get(pk=self.kwargs["club_pk"], weiclub=None)
form.fields['credit_amount'].initial = club.membership_fee_paid
form.fields['roles'].initial = Role.objects.filter(name="Membre de club").all()
# 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":
@ -444,7 +459,6 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
user = old_membership.user
form.fields['user'].initial = user
form.fields['user'].disabled = True
form.fields['roles'].initial = old_membership.roles.all()
form.fields['date_start'].initial = old_membership.date_end + timedelta(days=1)
form.fields['credit_amount'].initial = club.membership_fee_paid if user.profile.paid \
else club.membership_fee_unpaid
@ -560,7 +574,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
form.add_error('bank', _("This field is required."))
return self.form_invalid(form)
SpecialTransaction.objects.create(
transaction = SpecialTransaction(
source=credit_type,
destination=user.note,
quantity=1,
@ -571,9 +585,16 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
bank=bank,
valid=True,
)
transaction._force_save = True
transaction.save()
ret = super().form_valid(form)
member_role = Role.objects.filter(name="Membre de club").all()
form.instance.roles.set(member_role)
form.instance._force_save = True
form.instance.save()
# If Société générale pays, then we assume that this is the BDE membership, and we auto-renew the
# Kfet membership.
if soge:
@ -595,6 +616,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
date_start=old_membership.get().date_end + timedelta(days=1)
if old_membership.exists() else form.instance.date_start,
)
membership._force_save = True
membership._soge = True
membership.save()
membership.refresh_from_db()
@ -615,8 +637,9 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
Manage the roles of a user in a club
"""
model = Membership
form_class = MembershipForm
form_class = MembershipRolesForm
template_name = 'member/add_members.html'
extra_context = {"title": _("Manage roles of an user in the club")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@ -626,15 +649,61 @@ class ClubManageRolesView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
def get_form(self, form_class=None):
form = super().get_form(form_class)
# We don't create a full membership, we only update one field
form.fields['user'].disabled = True
del form.fields['date_start']
del form.fields['credit_type']
del form.fields['credit_amount']
del form.fields['last_name']
del form.fields['first_name']
del form.fields['bank']
club = self.object.club
form.fields['roles'].queryset = Role.objects.filter(Q(weirole__isnull=not hasattr(club, 'weiclub'))
& (Q(for_club__isnull=True) | Q(for_club=club))).all()
return form
def get_success_url(self):
return reverse_lazy('member:club_detail', kwargs={'pk': self.object.club.id})
return reverse_lazy('member:user_detail', kwargs={'pk': self.object.user.id})
class ClubMembersListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
model = Membership
table_class = MembershipTable
template_name = "member/club_members.html"
extra_context = {"title": _("Members of the club")}
def get_queryset(self, **kwargs):
qs = super().get_queryset().filter(club_id=self.kwargs["pk"])
if 'search' in self.request.GET:
pattern = self.request.GET['search']
qs = qs.filter(
Q(user__first_name__iregex='^' + pattern)
| Q(user__last_name__iregex='^' + pattern)
| Q(user__note__alias__normalized_name__iregex='^' + Alias.normalize(pattern))
)
only_active = "only_active" not in self.request.GET or self.request.GET["only_active"] != '0'
if only_active:
qs = qs.filter(date_start__lte=timezone.now().today(), date_end__gte=timezone.now().today())
if "roles" in self.request.GET:
if not self.request.GET["roles"]:
return qs.none()
roles_str = self.request.GET["roles"].replace(' ', '').split(',')
roles_int = map(int, roles_str)
qs = qs.filter(roles__in=roles_int)
qs = qs.order_by('-date_start', 'user__username')
return qs.distinct()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
club = Club.objects.filter(
PermissionBackend.filter_queryset(self.request.user, Club, "view")
).get(pk=self.kwargs["pk"])
context["club"] = club
applicable_roles = Role.objects.filter(Q(weirole__isnull=not hasattr(club, 'weiclub'))
& (Q(for_club__isnull=True) | Q(for_club=club))).all()
context["applicable_roles"] = applicable_roles
context["only_active"] = "only_active" not in self.request.GET or self.request.GET["only_active"] != '0'
return context