Sith/core/views/group.py
2025-02-15 12:05:54 +01:00

197 lines
6.2 KiB
Python

#
# Copyright 2023 © AE UTBM
# ae@utbm.fr / ae.info@utbm.fr
#
# This file is part of the website of the UTBM Student Association (AE UTBM),
# https://ae.utbm.fr.
#
# You can find the source code of the website at https://github.com/ae-utbm/sith
#
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
"""Views to manage Groups."""
from django import forms
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.auth.models import Permission
from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from core.auth.mixins import CanEditMixin
from core.models import Group, User
from core.views import DetailFormView
from core.views.forms import PermissionGroupsForm
from core.views.widgets.select import AutoCompleteSelectMultipleUser
# Forms
class EditMembersForm(forms.Form):
"""Add and remove members from a Group."""
def __init__(self, *args, **kwargs):
self.current_users = kwargs.pop("users", [])
super().__init__(*args, **kwargs)
self.fields["users_added"] = forms.ModelMultipleChoiceField(
label=_("Users to add to group"),
help_text=_("Search users to add (one or more)."),
required=False,
widget=AutoCompleteSelectMultipleUser,
queryset=User.objects.exclude(id__in=self.current_users).all(),
)
self.fields["users_removed"] = forms.ModelMultipleChoiceField(
User.objects.filter(id__in=self.current_users).all(),
label=_("Users to remove from group"),
required=False,
widget=forms.CheckboxSelectMultiple,
)
# Views
class GroupListView(CanEditMixin, ListView):
"""Displays the Group list."""
model = Group
queryset = Group.objects.filter(is_manually_manageable=True)
ordering = ["name"]
template_name = "core/group_list.jinja"
class GroupEditView(CanEditMixin, UpdateView):
"""Edit infos of a Group."""
model = Group
queryset = Group.objects.filter(is_manually_manageable=True)
pk_url_kwarg = "group_id"
template_name = "core/group_edit.jinja"
fields = ["name", "description"]
class GroupCreateView(PermissionRequiredMixin, CreateView):
"""Add a new Group."""
model = Group
queryset = Group.objects.filter(is_manually_manageable=True)
template_name = "core/create.jinja"
fields = ["name", "description"]
permission_required = "core.add_group"
class GroupTemplateView(CanEditMixin, DetailFormView):
"""Display all users in a given Group
Allow adding and removing users from it.
"""
model = Group
queryset = Group.objects.filter(is_manually_manageable=True)
form_class = EditMembersForm
pk_url_kwarg = "group_id"
template_name = "core/group_detail.jinja"
def form_valid(self, form):
resp = super().form_valid(form)
data = form.clean()
group = self.get_object()
if data["users_removed"]:
for user in data["users_removed"]:
group.users.remove(user)
if data["users_added"]:
for user in data["users_added"]:
group.users.add(user)
group.save()
return resp
def get_success_url(self):
return reverse_lazy(
"core:group_detail", kwargs={"group_id": self.get_object().id}
)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["users"] = self.get_object().users.all()
return kwargs
class GroupDeleteView(CanEditMixin, DeleteView):
"""Delete a Group."""
model = Group
queryset = Group.objects.filter(is_manually_manageable=True)
pk_url_kwarg = "group_id"
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("core:group_list")
class PermissionGroupsUpdateView(
PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
"""Manage the groups that have a specific permission.
Notes:
This is an `UpdateView`, but unlike typical `UpdateView`,
it doesn't accept url arguments to retrieve the object
to update.
As such, a `PermissionGroupsUpdateView` can only deal with
a single hardcoded permission.
This is not a limitation, but an on-purpose design,
mainly for security matters.
Example:
```python
class SubscriptionPermissionView(PermissionGroupsUpdateView):
permission = "subscription.add_subscription"
```
"""
permission_required = "auth.change_permission"
template_name = "core/edit.jinja"
form_class = PermissionGroupsForm
permission = None
success_message = _("Groups have been successfully updated.")
def get_object(self, *args, **kwargs):
if not self.permission:
raise ImproperlyConfigured(
f"{self.__class__.__name__} is missing the permission attribute. "
"Please fill it with either a permission string "
"or a Permission object."
)
if isinstance(self.permission, Permission):
return self.permission
if isinstance(self.permission, str):
try:
app_label, codename = self.permission.split(".")
except ValueError as e:
raise ValueError(
"Permission name should be in the form "
"app_label.permission_codename."
) from e
return get_object_or_404(
Permission, codename=codename, content_type__app_label=app_label
)
raise TypeError(
f"{self.__class__.__name__}.permission "
f"must be a string or a permission instance."
)
def get_success_url(self):
# if children classes define a success url, return it,
# else stay on the same page
return self.success_url or self.request.path