From d0a62cbc651e7e5d35bd89c074ca23789220f06d Mon Sep 17 00:00:00 2001 From: Jonathan Weth <git@jonathanweth.de> Date: Thu, 23 Apr 2020 22:04:26 +0200 Subject: [PATCH] Add view for assigning child groups to groups --- aleksis/core/filters.py | 11 ++ aleksis/core/forms.py | 4 + aleksis/core/menus.py | 18 +- aleksis/core/models.py | 3 + aleksis/core/rules.py | 6 +- .../templates/core/groups_child_groups.html | 154 ++++++++++++++++++ aleksis/core/urls.py | 1 + aleksis/core/views.py | 40 +++++ 8 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 aleksis/core/filters.py create mode 100644 aleksis/core/templates/core/groups_child_groups.html diff --git a/aleksis/core/filters.py b/aleksis/core/filters.py new file mode 100644 index 000000000..6e813e888 --- /dev/null +++ b/aleksis/core/filters.py @@ -0,0 +1,11 @@ +from django_filters import FilterSet, CharFilter +from material import Layout, Row + + +class GroupFilter(FilterSet): + name = CharFilter(lookup_expr="icontains") + short_name = CharFilter(lookup_expr="icontains") + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.form.layout = Layout(Row("name", "short_name")) diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py index fd331592f..962766c4b 100644 --- a/aleksis/core/forms.py +++ b/aleksis/core/forms.py @@ -276,3 +276,7 @@ class AnnouncementForm(ExtensibleForm): class Meta: model = Announcement exclude = [] + + +class ChildGroupsForm(forms.Form): + child_groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all()) diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py index 33cb12c3b..5aea73640 100644 --- a/aleksis/core/menus.py +++ b/aleksis/core/menus.py @@ -147,10 +147,26 @@ MENUS = { "menu_generator.validators.is_superuser", ], }, + { + "name": _("Groups and child groups"), + "url": "groups_child_groups", + "icon": "group_add", + "validators": [ + ("aleksis.core.util.predicates.permission_validator", "core.assign_child_groups_to_groups") + ], + }, + ], + }, + ], + "DATA_MANAGEMENT_MENU": [ + { + "name": _("Assign child groups to groups"), + "url": "groups_child_groups", + "validators": [ + ("aleksis.core.util.predicates.permission_validator", "core.assign_child_groups_to_groups") ], }, ], - "DATA_MANAGEMENT_MENU": [], "SCHOOL_MANAGEMENT_MENU": [ {"name": _("Edit school information"), "url": "edit_school_information", }, {"name": _("Edit school term"), "url": "edit_school_term", }, diff --git a/aleksis/core/models.py b/aleksis/core/models.py index 0ea49774c..eb92cb5d7 100644 --- a/aleksis/core/models.py +++ b/aleksis/core/models.py @@ -255,6 +255,9 @@ class Group(ExtensibleModel): ordering = ["short_name", "name"] verbose_name = _("Group") verbose_name_plural = _("Groups") + permissions = ( + ("assign_child_groups_to_groups", _("Can assign child groups to groups")), + ) icon_ = "group" diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py index 234d6f801..9c8369222 100644 --- a/aleksis/core/rules.py +++ b/aleksis/core/rules.py @@ -83,6 +83,10 @@ edit_group_predicate = has_person & ( ) add_perm("core.edit_group", edit_group_predicate) +# Assign child groups to groups +assign_child_groups_to_groups_predicate = has_person & has_global_perm("core.assign_child_groups_to_groups") +add_perm("core.assign_child_groups_to_groups", assign_child_groups_to_groups_predicate) + # Edit school information edit_school_information_predicate = has_person & has_global_perm("core.change_school") add_perm("core.edit_school_information", edit_school_information_predicate) @@ -126,7 +130,7 @@ view_system_status_predicate = has_person & has_global_perm("core.view_system_st add_perm("core.view_system_status", view_system_status_predicate) # View people menu (persons + objects) -add_perm("core.view_people_menu", has_person & (view_persons_predicate | view_groups_predicate)) +add_perm("core.view_people_menu", has_person & (view_persons_predicate | view_groups_predicate | link_persons_accounts_predicate | assign_child_groups_to_groups_predicate)) # View admin menu view_admin_menu_predicate = has_person & (manage_data_predicate | manage_school_predicate | impersonate_predicate | view_system_status_predicate | view_announcements_predicate) diff --git a/aleksis/core/templates/core/groups_child_groups.html b/aleksis/core/templates/core/groups_child_groups.html new file mode 100644 index 000000000..f0b892947 --- /dev/null +++ b/aleksis/core/templates/core/groups_child_groups.html @@ -0,0 +1,154 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} + +{% load i18n material_form %} + +{% block browser_title %}{% blocktrans %}Assign child groups to groups{% endblocktrans %}{% endblock %} +{% block page_title %} + {% blocktrans %}Assign child groups to groups{% endblocktrans %} +{% endblock %} + + +{% block content %} + {% if not page %} + <div class="alert info"> + <p> + <i class="material-icons left">info</i> + {% blocktrans %} + You can use this to assign child groups to groups. Please use the filters below to select groups you want to + change and click at "Next". + {% endblocktrans %} + </p> + </div> + + <form method="get"> + {% csrf_token %} + {% form form=filter.form %}{% endform %} + + <button type="submit" class="btn green waves-effect waves-light"> + <i class="material-icons left">refresh</i> + {% trans "Update selection" %} + </button> + <a href="{% url "groups_child_groups" %}" class="btn red waves-effect waves-light"> + <i class="material-icons left">clear</i> + {% trans "Clear all filters" %} + </a> + </form> + + <h5>{% trans "Currently selected groups" %}</h5> + + {% for group in filter.qs %} + <div class="chip"> + {{ group }} + </div> + {% endfor %} + + {% if filter.qs %} + <p> + <form method="post"> + {% csrf_token %} + <button class="btn btn-primary waves-effect waves-light" type="submit" name="page" value="1"> + {% trans "Start assigning child groups for this groups" %} + <i class="material-icons right">arrow_forward</i> + </button> + </form> + </p> + {% else %} + <div class="alert warning"> + <p> + <i class="material-icons left">warning</i> + {% blocktrans %} + Please select some groups in order to go on with assigning. + {% endblocktrans %} + </p> + </div> + {% endif %} + {% else %} + <form method="post"> + <input type="hidden" name="old_page" value="{{ page.number }}"> + + <p class="flow-text"> + {% trans "Current group:" %} {{ group }} + </p> + + <div class="alert warning"> + <p> + <i class="material-icons left">warning</i> + {% blocktrans %} + <strong>Please be careful!</strong><br/> + If you click on "Back" or "Next" the current group assignments are not saved. + If you click on save, you will overwrite all existing child group relations for this group with what you + selected on this page. + {% endblocktrans %} + </p> + + </div> + + <div class="row"> + <p class="left"> + {% if page.has_previous %} + <button class="btn grey waves-effect waves-light" name="page" value="{{ page.previous_page_number }}"> + <i class="material-icons left">arrow_back</i> + {% trans "Back" %} + </button> + {% endif %} + {% if page.has_next %} + <button class="btn grey waves-effect waves-light" type="submit" name="page" + value="{{ page.next_page_number }}"> + {% trans "Next" %} + <i class="material-icons right">arrow_forward</i> + </button> + {% endif %} + </p> + <p class="right"> + <button class="btn green waves-effect waves-light" type="submit" name="save"> + {% trans "Save" %} + <i class="material-icons left">save</i> + </button> + {% if page.has_next %} + <button class="btn green waves-effect waves-light" type="submit" name="save" + value="{{ page.next_page_number }}"> + {% trans "Save and next" %} + <i class="material-icons left">save</i> + </button> + {% endif %} + </p> + </div> + + + {% csrf_token %} + + {% include "components/chips.html" with form_field=form.child_groups %} + + <p class="left"> + {% if page.has_previous %} + <button class="btn grey waves-effect waves-light" name="page" value="{{ page.previous_page_number }}"> + <i class="material-icons left">arrow_back</i> + {% trans "Back" %} + </button> + {% endif %} + {% if page.has_next %} + <button class="btn grey waves-effect waves-light" type="submit" name="page" + value="{{ page.next_page_number }}"> + {% trans "Next" %} + <i class="material-icons right">arrow_forward</i> + </button> + {% endif %} + </p> + <p class="right"> + <button class="btn green waves-effect waves-light" type="submit" name="save"> + {% trans "Save" %} + <i class="material-icons left">save</i> + </button> + {% if page.has_next %} + <button class="btn green waves-effect waves-light" type="submit" name="save" + value="{{ page.next_page_number }}"> + {% trans "Save and next" %} + <i class="material-icons left">save</i> + </button> + {% endif %} + </p> + </form> + {% endif %} +{% endblock %} diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py index 6c1648ee4..11b35e6b9 100644 --- a/aleksis/core/urls.py +++ b/aleksis/core/urls.py @@ -32,6 +32,7 @@ urlpatterns = [ path("person/<int:id_>", views.person, name="person_by_id"), path("person/<int:id_>/edit", views.edit_person, name="edit_person_by_id"), path("groups", views.groups, name="groups"), + path("groups/child_groups/", views.groups_child_groups, name="groups_child_groups"), path("group/create", views.edit_group, name="create_group"), path("group/<int:id_>", views.group, name="group_by_id"), path("group/<int:id_>/edit", views.edit_group, name="edit_group_by_id"), diff --git a/aleksis/core/views.py b/aleksis/core/views.py index 6c4fad570..29bff15c9 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -4,6 +4,7 @@ from typing import Optional from django.apps import apps from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import PermissionDenied +from django.core.paginator import Paginator from django.http import Http404, HttpRequest, HttpResponse from django.shortcuts import get_object_or_404, redirect, render from django.utils.translation import gettext_lazy as _ @@ -15,6 +16,7 @@ from haystack.query import SearchQuerySet from haystack.views import SearchView from rules.contrib.views import permission_required +from .filters import GroupFilter from .forms import ( EditGroupForm, EditPersonForm, @@ -22,6 +24,7 @@ from .forms import ( EditTermForm, PersonsAccountsFormSet, AnnouncementForm, + ChildGroupsForm, ) from .models import Activity, Group, Notification, Person, School, DashboardWidget, Announcement from .tables import GroupsTable, PersonsTable @@ -174,6 +177,43 @@ def persons_accounts(request: HttpRequest) -> HttpResponse: return render(request, "core/persons_accounts.html", context) +@permission_required("core.assign_child_groups_to_groups") +def groups_child_groups(request: HttpRequest) -> HttpResponse: + """ Assign child groups to groups (for matching by MySQL importer) """ + context = {} + + # Apply filter + filter = GroupFilter(request.GET, queryset=Group.objects.all()) + context["filter"] = filter + + # Paginate + paginator = Paginator(filter.qs, 1) + page_number = request.POST.get("page", request.POST.get("old_page")) + + if page_number: + page = paginator.get_page(page_number) + group = page[0] + + if "save" in request.POST: + # Save + form = ChildGroupsForm(request.POST) + form.is_valid() + + if "child_groups" in form.cleaned_data: + group.child_groups.set(form.cleaned_data["child_groups"]) + group.save() + messages.success(request, _("The child groups were successfully saved.")) + else: + # Init form + form = ChildGroupsForm(initial={"child_groups": group.child_groups.all()}) + + context["paginator"] = paginator + context["page"] = page + context["group"] = group + context["form"] = form + return render(request, "core/groups_child_groups.html", context) + + def get_person_by_id(request: HttpRequest, id_:int): return get_object_or_404(Person, id=id_) -- GitLab