diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py index 888926e5871ea70d4cebd9d8b7b0eb33fb4aa8cd..8e5233c531193f1c9bbccea011ebdc3f4b765c66 100644 --- a/aleksis/core/forms.py +++ b/aleksis/core/forms.py @@ -10,7 +10,7 @@ from dynamic_preferences.forms import PreferenceForm from material import Fieldset, Layout, Row from .mixins import ExtensibleForm -from .models import Announcement, Group, Person +from .models import Announcement, Group, GroupType, Person from .registries import ( group_preferences_registry, person_preferences_registry, @@ -125,7 +125,7 @@ class EditGroupForm(ExtensibleForm): """Form to edit an existing group in the frontend.""" layout = Layout( - Fieldset(_("Common data"), "name", "short_name"), + Fieldset(_("Common data"), "name", "short_name", "group_type"), Fieldset(_("Persons"), "members", "owners", "parent_groups"), ) @@ -280,3 +280,11 @@ class GroupPreferenceForm(PreferenceForm): """Form to edit preferences valid for members of a group.""" registry = group_preferences_registry + + +class EditGroupTypeForm(forms.ModelForm): + """Form to manage group types.""" + + class Meta: + model = GroupType + exclude = [] diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py index c91287d016f24487163a332376b2cc95a9a78cf0..4767a8f3492c7401a22164fe25f54da4ec9fd702 100644 --- a/aleksis/core/menus.py +++ b/aleksis/core/menus.py @@ -153,6 +153,17 @@ MENUS = { ("aleksis.core.util.predicates.permission_validator", "core.view_groups") ], }, + { + "name": _("Group types"), + "url": "group_types", + "icon": "category", + "validators": [ + ( + "aleksis.core.util.predicates.permission_validator", + "core.view_group_type", + ) + ], + }, { "name": _("Persons and accounts"), "url": "persons_accounts", diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py index 5753bbc5374e03dccb97b935b1eba6ae65f04239..866b6a3ee08aa1e3fdb2e3366bcb4f26eaf07376 100644 --- a/aleksis/core/rules.py +++ b/aleksis/core/rules.py @@ -1,6 +1,6 @@ from rules import add_perm, always_allow -from .models import Announcement, Group, Person +from .models import Announcement, Group, GroupType, Person from .util.predicates import ( has_any_object, has_global_perm, @@ -194,3 +194,21 @@ change_group_preferences = has_person & ( | is_group_owner ) add_perm("core.change_group_preferences", change_group_preferences) + +# Edit group type +edit_group_type_predicate = has_person & ( + has_global_perm("core.change_group_type") | has_object_perm("core.change_group_type") +) +add_perm("core.edit_group_type", edit_group_type_predicate) + +# Delete group type +delete_group_type_predicate = has_person & ( + has_global_perm("core.delete_group_type") | has_object_perm("core.delete_group_type") +) +add_perm("core.delete_group_type", delete_group_type_predicate) + +# View group types +view_group_type_predicate = has_person & ( + has_global_perm("core.view_grouptype") | has_any_object("core.view_grouptype", GroupType) +) +add_perm("core.view_grouptype", view_group_type_predicate) diff --git a/aleksis/core/tables.py b/aleksis/core/tables.py index 7bc4e4e14741d7b5bc0f6c878749511a52e95252..ea5cf05d5470c6cf541c0b41f7d322d6091ae607 100644 --- a/aleksis/core/tables.py +++ b/aleksis/core/tables.py @@ -1,3 +1,5 @@ +from django.utils.translation import gettext_lazy as _ + import django_tables2 as tables from django_tables2.utils import A @@ -20,3 +22,16 @@ class GroupsTable(tables.Table): name = tables.LinkColumn("group_by_id", args=[A("id")]) short_name = tables.LinkColumn("group_by_id", args=[A("id")]) + + +class GroupTypesTable(tables.Table): + """Table to list group types.""" + + class Meta: + attrs = {"class": "table table-striped table-bordered table-hover table-responsive-xl"} + + name = tables.LinkColumn("edit_group_type_by_id", args=[A("id")]) + description = tables.LinkColumn("edit_group_type_by_id", args=[A("id")]) + delete = tables.LinkColumn( + "delete_group_type_by_id", args=[A("id")], verbose_name=_("Delete"), text=_("Delete") + ) diff --git a/aleksis/core/templates/core/edit_group_type.html b/aleksis/core/templates/core/edit_group_type.html new file mode 100644 index 0000000000000000000000000000000000000000..c857de98b893cf243c3c108f3c76b5aa142e6037 --- /dev/null +++ b/aleksis/core/templates/core/edit_group_type.html @@ -0,0 +1,17 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} +{% load material_form i18n %} + +{% block browser_title %}{% blocktrans %}Edit group type{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Edit group type{% endblocktrans %}{% endblock %} + +{% block content %} + + <form method="post"> + {% csrf_token %} + {% form form=edit_group_type_form %}{% endform %} + {% include "core/save_button.html" %} + </form> + +{% endblock %} diff --git a/aleksis/core/templates/core/group_types.html b/aleksis/core/templates/core/group_types.html new file mode 100644 index 0000000000000000000000000000000000000000..6344416b1a8292fd5400e57d63c056e1b2839ef8 --- /dev/null +++ b/aleksis/core/templates/core/group_types.html @@ -0,0 +1,18 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} + +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block browser_title %}{% blocktrans %}Group types{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Group types{% endblocktrans %}{% endblock %} + +{% block content %} + <a class="btn green waves-effect waves-light" href="{% url 'create_group_type' %}"> + <i class="material-icons left">add</i> + {% trans "Create group type" %} + </a> + + {% render_table group_types_table %} +{% endblock %} diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py index ad35fa8c5de7f5bf4045c28fe241aecec74235ed..47cb1d63c85c41b562e54909d553febac99c7625 100644 --- a/aleksis/core/urls.py +++ b/aleksis/core/urls.py @@ -37,6 +37,14 @@ urlpatterns = [ views.notification_mark_read, name="notification_mark_read", ), + path("groups/group_type/create", views.edit_group_type, name="create_group_type"), + path( + "groups/group_type/<int:id_>/delete", + views.delete_group_type, + name="delete_group_type_by_id", + ), + path("groups/group_type/<int:id_>/edit", views.edit_group_type, name="edit_group_type_by_id"), + path("groups/group_types", views.group_types, name="group_types"), path("announcements/", views.announcements, name="announcements"), path("announcement/create/", views.announcement_form, name="add_announcement"), path("announcement/edit/<int:id_>/", views.announcement_form, name="edit_announcement"), diff --git a/aleksis/core/views.py b/aleksis/core/views.py index 2b856a04846196b73190778c7eee71c02c3d4ad0..2a6a0ec6de5315f22fdd3a3fceb54dbed4aafe94 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -21,19 +21,20 @@ from .forms import ( AnnouncementForm, ChildGroupsForm, EditGroupForm, + EditGroupTypeForm, EditPersonForm, GroupPreferenceForm, PersonPreferenceForm, PersonsAccountsFormSet, SitePreferenceForm, ) -from .models import Announcement, DashboardWidget, Group, Notification, Person +from .models import Announcement, DashboardWidget, Group, GroupType, Notification, Person from .registries import ( group_preferences_registry, person_preferences_registry, site_preferences_registry, ) -from .tables import GroupsTable, PersonsTable +from .tables import GroupsTable, GroupTypesTable, PersonsTable from .util import messages from .util.apps import AppConfig from .util.core_helpers import objectgetter_optional @@ -444,3 +445,57 @@ def preferences( context["instance"] = instance return render(request, "dynamic_preferences/form.html", context) + + +@permission_required("core.edit_group_type", fn=objectgetter_optional(GroupType, None, False)) +def edit_group_type(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: + """View to edit or create a group_type.""" + context = {} + + group_type = objectgetter_optional(GroupType, None, False)(request, id_) + context["group_type"] = group_type + + if id_: + # Edit form for existing group_type + edit_group_type_form = EditGroupTypeForm(request.POST or None, instance=group_type) + else: + # Empty form to create a new group_type + edit_group_type_form = EditGroupTypeForm(request.POST or None) + + if request.method == "POST": + if edit_group_type_form.is_valid(): + edit_group_type_form.save(commit=True) + + messages.success(request, _("The group_type has been saved.")) + + return redirect("group_types") + + context["edit_group_type_form"] = edit_group_type_form + + return render(request, "core/edit_group_type.html", context) + + +@permission_required("core.view_grouptype") +def group_types(request: HttpRequest) -> HttpResponse: + """List view for listing all group types.""" + context = {} + + # Get all group types + group_types = get_objects_for_user(request.user, "core.view_grouptype", GroupType) + + # Build table + group_types_table = GroupTypesTable(group_types) + RequestConfig(request).configure(group_types_table) + context["group_types_table"] = group_types_table + + return render(request, "core/group_types.html", context) + + +@permission_required("core.delete_group_type", fn=objectgetter_optional(GroupType, None, False)) +def delete_group_type(request: HttpRequest, id_: int) -> HttpResponse: + """View to delete an group_type.""" + group_type = objectgetter_optional(GroupType, None, False)(request, id_) + group_type.delete() + messages.success(request, _("The group type has been deleted.")) + + return redirect("group_types")