diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py index 98642c5f6d6d81b48fb004aa89bff4862f350d03..bebc8a5da5fd6db02607b20291ea6270bc410ff6 100644 --- a/aleksis/apps/alsijil/model_extensions.py +++ b/aleksis/apps/alsijil/model_extensions.py @@ -93,12 +93,29 @@ def get_personal_notes(self, persons: QuerySet, wanted_week: CalendarWeek): lesson_period=self, week=wanted_week.week ) -# Dynamically add extra permissions to Group and Person models in core, requires migration afterwards -Group.add_permission("view_week_class_register_group", _("Can view week overview of group class register")) -Group.add_permission("view_personalnote_group", _("Can view all personal notes of a group")) -Group.add_permission("edit_personalnote_group", _("Can edit all personal notes of a group")) -Group.add_permission("view_lessondocumentation_group", _("Can view all lesson documentation of a group")) -Group.add_permission("edit_lessondocumentation_group", _("Can edit all lesson documentation of a group")) + +# Dynamically add extra permissions to Group and Person models in core +# Note: requires migrate afterwards +Group.add_permission( + "view_week_class_register_group", + _("Can view week overview of group class register"), +) +Group.add_permission( + "view_personalnote_group", _("Can view all personal notes of a group") +) +Group.add_permission( + "edit_personalnote_group", _("Can edit all personal notes of a group") +) +Group.add_permission( + "view_lessondocumentation_group", _("Can view all lesson documentation of a group") +) +Group.add_permission( + "edit_lessondocumentation_group", _("Can edit all lesson documentation of a group") +) Group.add_permission("view_full_register_group", _("Can view full register of a group")) -Group.add_permission("register_absence_group", _("Can register an absence for all members of a group")) -Person.add_permission("register_absence_person", _("Can register an absence for a person")) +Group.add_permission( + "register_absence_group", _("Can register an absence for all members of a group") +) +Person.add_permission( + "register_absence_person", _("Can register an absence for a person") +) diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py index 0f29ffbb871afe44a8c511063d444fba1c6cc7ff..2503d852ca035d61aa739b1b073786548defca31 100644 --- a/aleksis/apps/alsijil/models.py +++ b/aleksis/apps/alsijil/models.py @@ -92,6 +92,7 @@ class PersonalNoteFilter(ExtensibleModel): verbose_name_plural = _("Personal note filters") ordering = ["identifier"] + class AlsijilGlobalPermissions(ExtensibleModel): class Meta: managed = False diff --git a/aleksis/apps/alsijil/rules.py b/aleksis/apps/alsijil/rules.py index 4c8cf021e02dcc194650927c8f7ee68e109ac138..54f3e789321af1fe91389e1f2eae171a7e64fd51 100644 --- a/aleksis/apps/alsijil/rules.py +++ b/aleksis/apps/alsijil/rules.py @@ -1,7 +1,6 @@ from rules import add_perm from aleksis.core.util.predicates import ( - has_any_object, has_global_perm, has_object_perm, has_person, @@ -9,91 +8,100 @@ from aleksis.core.util.predicates import ( ) from .util.predicates import ( - is_lesson_teacher, - is_lesson_participant, - is_lesson_parent_group_owner, has_lesson_group_object_perm, + has_person_group_object_perm, is_group_member, is_group_owner, - has_person_group_object_perm, + is_lesson_parent_group_owner, + is_lesson_participant, + is_lesson_teacher, is_person_group_owner, ) - # View lesson view_lesson_predicate = has_person & ( - has_global_perm("alsijil.view_lesson") | is_lesson_teacher | is_lesson_participant | is_lesson_parent_group_owner | has_lesson_group_object_perm("alsijil.view_lesson") + has_global_perm("alsijil.view_lesson") + | is_lesson_teacher + | is_lesson_participant + | is_lesson_parent_group_owner + | has_lesson_group_object_perm("alsijil.view_lesson") ) add_perm("alsijil.view_lesson", view_lesson_predicate) # View lesson personal notes view_lesson_personal_notes_predicate = has_person & ( - has_global_perm("alsijil.view_personalnote") | - has_lesson_group_object_perm("core.view_personalnote_group") | - is_lesson_teacher | - is_lesson_parent_group_owner + has_global_perm("alsijil.view_personalnote") + | has_lesson_group_object_perm("core.view_personalnote_group") + | is_lesson_teacher + | is_lesson_parent_group_owner ) add_perm("alsijil.view_lesson_personalnote", view_lesson_personal_notes_predicate) # Edit lesson personal notes edit_lesson_personal_notes_predicate = has_person & ( - has_global_perm("alsijil.change_personalnote") | - has_lesson_group_object_perm("core.edit_personalnote_group") | - is_lesson_teacher + has_global_perm("alsijil.change_personalnote") + | has_lesson_group_object_perm("core.edit_personalnote_group") + | is_lesson_teacher ) add_perm("alsijil.edit_personalnote", edit_lesson_personal_notes_predicate) # View lesson documentation view_lesson_documentation_predicate = has_person & ( - has_global_perm("alsijil.view_lessondocumentation") | - has_lesson_group_object_perm("core.view_lessondocumentation_group") | - is_lesson_teacher | - is_lesson_parent_group_owner | - is_lesson_participant + has_global_perm("alsijil.view_lessondocumentation") + | has_lesson_group_object_perm("core.view_lessondocumentation_group") + | is_lesson_teacher + | is_lesson_parent_group_owner + | is_lesson_participant ) add_perm("alsijil.view_lessondocumentation", view_lesson_documentation_predicate) # Edit lesson documentation edit_lesson_documentation_predicate = has_person & ( - has_global_perm("alsijil.change_lessondocumentation") | - has_lesson_group_object_perm("core.edit_lessondocumentation_group") | - is_lesson_teacher + has_global_perm("alsijil.change_lessondocumentation") + | has_lesson_group_object_perm("core.edit_lessondocumentation_group") + | is_lesson_teacher ) add_perm("alsijil.edit_lessondocumentation", edit_lesson_documentation_predicate) # View week overview view_week_predicate = has_person & ( - has_global_perm("alsijil.view_week") | has_object_perm("core.view_week_class_register_group") | is_group_member | is_group_owner | is_current_person + has_global_perm("alsijil.view_week") + | has_object_perm("core.view_week_class_register_group") + | is_group_member + | is_group_owner + | is_current_person ) add_perm("alsijil.view_week", view_week_predicate) # View week personal notes view_week_personal_notes_predicate = has_person & ( - has_global_perm("alsijil.view_personalnote") | - has_object_perm("alsijil.view_personalnote") | - is_group_owner + has_global_perm("alsijil.view_personalnote") + | has_object_perm("alsijil.view_personalnote") + | is_group_owner ) add_perm("alsijil.view_week_personalnote", view_week_personal_notes_predicate) # Register absence register_absence_predicate = has_person & ( - has_global_perm("alsijil.register_absence") | - has_person_group_object_perm("core.register_absence_group") | - has_global_perm("core.register_absence_person") | - is_person_group_owner + has_global_perm("alsijil.register_absence") + | has_person_group_object_perm("core.register_absence_group") + | has_global_perm("core.register_absence_person") + | is_person_group_owner ) add_perm("alsijil.register_absence", register_absence_predicate) # View full register for group view_full_register_predicate = has_person & ( - has_global_perm("alsijil.view_full_register") | - has_object_perm("core.view_full_register_group") | - is_group_owner + has_global_perm("alsijil.view_full_register") + | has_object_perm("core.view_full_register_group") + | is_group_owner ) add_perm("alsijil.view_full_register", view_full_register_predicate) # View all personal note filters -list_personal_note_filters_predicate = has_person & has_global_perm("alsijil.view_personal_note_filter") +list_personal_note_filters_predicate = has_person & has_global_perm( + "alsijil.view_personal_note_filter" +) add_perm("alsijil.view_personal_note_filters", list_personal_note_filters_predicate) # Edit personal note filter diff --git a/aleksis/apps/alsijil/util/alsijil_helpers.py b/aleksis/apps/alsijil/util/alsijil_helpers.py index dcb2bbf1fe5274bf5e3b32ab0bf650bb478de36e..ccdc13900fc722731f262ee2e18552b417f95669 100644 --- a/aleksis/apps/alsijil/util/alsijil_helpers.py +++ b/aleksis/apps/alsijil/util/alsijil_helpers.py @@ -1,15 +1,9 @@ from typing import Optional -from django.db.models import Count, Exists, OuterRef, Q, Sum -from django.http import HttpRequest, HttpResponseNotFound -from django.shortcuts import get_object_or_404 +from django.http import HttpRequest -from calendarweek import CalendarWeek - -from aleksis.apps.chronos.managers import TimetableType from aleksis.apps.chronos.models import LessonPeriod from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk -from ..models import LessonDocumentation def get_lesson_period_by_pk( @@ -23,9 +17,17 @@ def get_lesson_period_by_pk( lesson_period = LessonPeriod.objects.get(pk=period_id) elif hasattr(request, "user") and hasattr(request.user, "person"): if request.user.person.lessons_as_teacher.exists(): - lesson_period = LessonPeriod.objects.at_time().filter_teacher(request.user.person).first() + lesson_period = ( + LessonPeriod.objects.at_time() + .filter_teacher(request.user.person) + .first() + ) else: - lesson_period = LessonPeriod.objects.at_time().filter_participant(request.user.person).first() + lesson_period = ( + LessonPeriod.objects.at_time() + .filter_participant(request.user.person) + .first() + ) else: lesson_period = None return lesson_period @@ -42,4 +44,4 @@ def get_instance_by_pk( if type_ and id_: return get_el_by_pk(request, type_, id_) elif hasattr(request, "user") and hasattr(request.user, "person"): - return request.user.person \ No newline at end of file + return request.user.person diff --git a/aleksis/apps/alsijil/util/predicates.py b/aleksis/apps/alsijil/util/predicates.py index 80faf95f8ddbd3867c024a2e4a3b22d20f4dc2bd..f0abe866b2b1b422ac59b0ce71eb279cfcc759ec 100644 --- a/aleksis/apps/alsijil/util/predicates.py +++ b/aleksis/apps/alsijil/util/predicates.py @@ -11,36 +11,66 @@ from aleksis.core.util.predicates import check_object_permission @predicate def is_lesson_teacher(user: User, obj: LessonPeriod) -> bool: - """Predicate which checks whether the person linked to the user is a teacher in the lesson linked to the given LessonPeriod.""" + """Predicate for teachers of a lesson. + + Checks whether the person linked to the user is a teacher + in the lesson linked to the given LessonPeriod. + """ return user.person in obj.lesson.teachers + @predicate def is_lesson_participant(user: User, obj: LessonPeriod) -> bool: - """Predicate which checks whether the person linked to the user is a member in the groups linked to the given LessonPeriod.""" + """Predicate for participants of a lesson. + + Checks whether the person linked to the user is a member in + the groups linked to the given LessonPeriod. + """ return obj.lesson.groups.filter(members=user.person).exists() + @predicate def is_lesson_parent_group_owner(user: User, obj: LessonPeriod) -> bool: - """Predicate which checks whether the person linked to the user is the owner of any parent groups of any groups of the given LessonPeriods lesson.""" + """ + Predicate for parent group owners of a lesson. + + Checks whether the person linked to the user is the owner of + any parent groups of any groups of the given LessonPeriods lesson. + """ return obj.lesson.groups.filter(parent_groups__owners=user.person).exists() + @predicate def is_group_owner(user: User, obj: Union[Group, Person]) -> bool: - """Predicate which checks whether the person linked to the user is the owner of the given group.""" + """Predicate for group owners of a given group. + + Checks whether the person linked to the user is the owner of the given group. + If there isn't provided a group, it will return `False`. + """ if isinstance(obj, Group): if obj.owners.filter(pk=user.person.pk).exists(): return True return False + @predicate def is_person_group_owner(user: User, obj: Person) -> bool: - """Predicate which checks whether the person linked to the user is the owner of any group of the given person.""" + """ + Predicate for group owners of any group. + + Checks whether the person linked to the user is + the owner of any group of the given person. + """ return obj.filter(member_of__owners=user.person).exists() + @predicate def has_person_group_object_perm(perm: str): - """Predicate which checks whether a user has a permission on any group of a person.""" + """Predicate builder for permissions on a set of member groups. + + Checks whether a user has a permission on any group of a person. + """ name = f"has_person_group_object_perm:{perm}" @predicate(name) @@ -52,9 +82,14 @@ def has_person_group_object_perm(perm: str): return fn + @predicate def is_group_member(user: User, obj: Union[Group, Person]) -> bool: - """Predicate which checks whether the person linked to the user is a member of the given group.""" + """Predicate for group membership. + + Checks whether the person linked to the user is a member of the given group. + If there isn't provided a group, it will return `False`. + """ if isinstance(obj, Group): if obj.members.filter(pk=user.person.pk).exists(): return True @@ -63,7 +98,10 @@ def is_group_member(user: User, obj: Union[Group, Person]) -> bool: def has_lesson_group_object_perm(perm: str): - """Build predicate which checks whether a user has a permission on any group of a LessonPeriod.""" + """Predicate builder for permissions on lesson groups. + + Checks whether a user has a permission on any group of a LessonPeriod. + """ name = f"has_lesson_group_object_perm:{perm}" @predicate(name) diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 6ea8c2f451ed3a9132191b0b73109369cfbb0343..6ac35f3f59a4050770555563f95e0da16f091c2f 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -2,7 +2,7 @@ from datetime import date, datetime, timedelta from typing import Optional from django.core.exceptions import PermissionDenied -from django.db.models import Count, Exists, F, OuterRef, Q, Subquery, Sum +from django.db.models import Count, Exists, OuterRef, Q, Subquery, Sum from django.http import Http404, HttpRequest, HttpResponse, HttpResponseNotFound from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse @@ -14,10 +14,10 @@ from rules.contrib.views import permission_required from aleksis.apps.chronos.managers import TimetableType from aleksis.apps.chronos.models import LessonPeriod -from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk from aleksis.core.models import Group, Person, SchoolTerm from aleksis.core.util import messages from aleksis.core.util.core_helpers import objectgetter_optional + from .forms import ( LessonDocumentationForm, PersonalNoteFilterForm, @@ -27,7 +27,7 @@ from .forms import ( ) from .models import LessonDocumentation, PersonalNoteFilter from .tables import PersonalNoteFilterTable -from .util.alsijil_helpers import get_lesson_period_by_pk, get_instance_by_pk +from .util.alsijil_helpers import get_instance_by_pk, get_lesson_period_by_pk @permission_required("alsijil.view_lesson", fn=get_lesson_period_by_pk) @@ -102,10 +102,14 @@ def lesson( ) if request.method == "POST": - if lesson_documentation_form.is_valid() and request.user.has_perm("alsijil.edit_lessondocumentation", lesson_period): + if lesson_documentation_form.is_valid() and request.user.has_perm( + "alsijil.edit_lessondocumentation", lesson_period + ): lesson_documentation_form.save() - if personal_note_formset.is_valid() and request.user.has_perm("alsijil.edit_personalnote", lesson_period): + if personal_note_formset.is_valid() and request.user.has_perm( + "alsijil.edit_personalnote", lesson_period + ): instances = personal_note_formset.save() # Iterate over personal notes and carry changed absences to following lessons @@ -196,13 +200,13 @@ def week_view( # Aggregate all personal notes for this group and week lesson_periods_pk = lesson_periods.values_list("pk", flat=True) - persons = Person.objects.filter(is_active=True) if not request.user.has_perm("alsijil.view_week_personalnote", instance): persons = persons.filter(pk=request.user.pk) - persons = (persons.filter(member_of__lessons__lesson_periods__in=lesson_periods_pk) + persons = ( + persons.filter(member_of__lessons__lesson_periods__in=lesson_periods_pk) .distinct() .prefetch_related("personal_notes") .annotate( @@ -266,7 +270,9 @@ def week_view( return render(request, "alsijil/class_register/week_view.html", context) -@permission_required("alsijil.view_full_register", fn=objectgetter_optional(Group, None, False)) +@permission_required( + "alsijil.view_full_register", fn=objectgetter_optional(Group, None, False) +) def full_register_group(request: HttpRequest, id_: int) -> HttpResponse: context = {} @@ -355,7 +361,9 @@ def register_absence(request: HttpRequest) -> HttpResponse: register_absence_form = RegisterAbsenceForm(request.POST or None) if request.method == "POST": - if register_absence_form.is_valid() and request.user.has_perm("alsijil.register_absence", register_absence_form.cleaned_data["person"]): + if register_absence_form.is_valid() and request.user.has_perm( + "alsijil.register_absence", register_absence_form.cleaned_data["person"] + ): # Get data from form person = register_absence_form.cleaned_data["person"] start_date = register_absence_form.cleaned_data["date_start"] @@ -395,7 +403,10 @@ def list_personal_note_filters(request: HttpRequest) -> HttpResponse: return render(request, "alsijil/personal_note_filter/list.html", context) -@permission_required("alsijil.edit_personal_note_filter", fn=objectgetter_optional(PersonalNoteFilter, None, False)) +@permission_required( + "alsijil.edit_personal_note_filter", + fn=objectgetter_optional(PersonalNoteFilter, None, False), +) def edit_personal_note_filter( request: HttpRequest, id_: Optional["int"] = None ) -> HttpResponse: @@ -422,7 +433,10 @@ def edit_personal_note_filter( return render(request, "alsijil/personal_note_filter/manage.html", context) -@permission_required("alsijil.delete_personal_note_filter", fn=objectgetter_optional(PersonalNoteFilter, None, False)) +@permission_required( + "alsijil.delete_personal_note_filter", + fn=objectgetter_optional(PersonalNoteFilter, None, False), +) def delete_personal_note_filter(request: HttpRequest, id_: int) -> HttpResponse: context = {}