diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py index 888926e5871ea70d4cebd9d8b7b0eb33fb4aa8cd..3e5f0f60b97e3a7070540a4eb06d111a8a23d09c 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 AdditionalField, Announcement, Group, Person from .registries import ( group_preferences_registry, person_preferences_registry, @@ -127,6 +127,7 @@ class EditGroupForm(ExtensibleForm): layout = Layout( Fieldset(_("Common data"), "name", "short_name"), Fieldset(_("Persons"), "members", "owners", "parent_groups"), + Fieldset(_("Additional data"), "additional_fields"), ) class Meta: @@ -150,6 +151,7 @@ class EditGroupForm(ExtensibleForm): "parent_groups": ModelSelect2MultipleWidget( search_fields=["name__icontains", "short_name__icontains"] ), + "additional_fields": ModelSelect2MultipleWidget(search_fields=["title__icontains",]), } @@ -280,3 +282,11 @@ class GroupPreferenceForm(PreferenceForm): """Form to edit preferences valid for members of a group.""" registry = group_preferences_registry + + +class EditAdditionalFieldForm(forms.ModelForm): + """Form to manage group types.""" + + class Meta: + model = AdditionalField + exclude = [] diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py index c91287d016f24487163a332376b2cc95a9a78cf0..8bbc074c91d696f4128a88f98c867419af237f22 100644 --- a/aleksis/core/menus.py +++ b/aleksis/core/menus.py @@ -175,6 +175,17 @@ MENUS = { ) ], }, + { + "name": _("Additional fields"), + "url": "additional_fields", + "icon": "style", + "validators": [ + ( + "aleksis.core.util.predicates.permission_validator", + "core.view_additionalfield", + ) + ], + }, ], }, ], diff --git a/aleksis/core/migrations/0001_initial.py b/aleksis/core/migrations/0001_initial.py index 4dd6486f478a00e23a42876d302bbbe954648084..413fc88b1b698381fc9e7b0af1d7ffe42e3542ce 100644 --- a/aleksis/core/migrations/0001_initial.py +++ b/aleksis/core/migrations/0001_initial.py @@ -1,16 +1,19 @@ # Generated by Django 3.0.5 on 2020-05-04 14:16 -import aleksis.core.mixins -import aleksis.core.util.core_helpers import datetime -from django.conf import settings + import django.contrib.postgres.fields.jsonb import django.contrib.sites.managers -from django.db import migrations, models import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + import image_cropping.fields import phonenumber_field.modelfields +import aleksis.core.mixins +import aleksis.core.util.core_helpers + class Migration(migrations.Migration): diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py index 5753bbc5374e03dccb97b935b1eba6ae65f04239..1320e1410ff8a440ad82561b637d477ba4ebe4ca 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 AdditionalField, Announcement, Group, Person from .util.predicates import ( has_any_object, has_global_perm, @@ -194,3 +194,24 @@ change_group_preferences = has_person & ( | is_group_owner ) add_perm("core.change_group_preferences", change_group_preferences) + +# Edit additional field +edit_additional_field_predicate = has_person & ( + has_global_perm("core.change_additional_field") + | has_object_perm("core.change_additional_field") +) +add_perm("core.edit_additional_field", edit_additional_field_predicate) + +# Delete additional field +delete_additional_field_predicate = has_person & ( + has_global_perm("core.delete_additional_field") + | has_object_perm("core.delete_additional_field") +) +add_perm("core.delete_additional_field", delete_additional_field_predicate) + +# View additional fields +view_additional_field_predicate = has_person & ( + has_global_perm("core.view_additionalfield") + | has_any_object("core.view_additionalfield", AdditionalField) +) +add_perm("core.view_additionalfield", view_additional_field_predicate) diff --git a/aleksis/core/tables.py b/aleksis/core/tables.py index 7bc4e4e14741d7b5bc0f6c878749511a52e95252..561b9e8b11821f720e6d9a88a7cc8af1298d051c 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,15 @@ class GroupsTable(tables.Table): name = tables.LinkColumn("group_by_id", args=[A("id")]) short_name = tables.LinkColumn("group_by_id", args=[A("id")]) + + +class AdditionalFieldsTable(tables.Table): + """Table to list group types.""" + + class Meta: + attrs = {"class": "table table-striped table-bordered table-hover table-responsive-xl"} + + title = tables.LinkColumn("edit_additional_field_by_id", args=[A("id")]) + delete = tables.LinkColumn( + "delete_additional_field_by_id", args=[A("id")], verbose_name=_("Delete"), text=_("Delete") + ) diff --git a/aleksis/core/templates/core/additional_fields.html b/aleksis/core/templates/core/additional_fields.html new file mode 100644 index 0000000000000000000000000000000000000000..1ba2a77a32c528b8443f4dc5a4c201b5739414fe --- /dev/null +++ b/aleksis/core/templates/core/additional_fields.html @@ -0,0 +1,18 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} + +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block browser_title %}{% blocktrans %}Additional fields{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Additional fields{% endblocktrans %}{% endblock %} + +{% block content %} + <a class="btn green waves-effect waves-light" href="{% url 'create_additional_field' %}"> + <i class="material-icons left">add</i> + {% trans "Create additional field" %} + </a> + + {% render_table additional_fields_table %} +{% endblock %} diff --git a/aleksis/core/templates/core/edit_additional_field.html b/aleksis/core/templates/core/edit_additional_field.html new file mode 100644 index 0000000000000000000000000000000000000000..b1487eb259b44f1425c950574f7df845d4e22129 --- /dev/null +++ b/aleksis/core/templates/core/edit_additional_field.html @@ -0,0 +1,17 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} +{% load material_form i18n %} + +{% block browser_title %}{% blocktrans %}Edit additional field{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Edit additional field{% endblocktrans %}{% endblock %} + +{% block content %} + + <form method="post"> + {% csrf_token %} + {% form form=edit_additional_field_form %}{% endform %} + {% include "core/save_button.html" %} + </form> + +{% endblock %} diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py index ad35fa8c5de7f5bf4045c28fe241aecec74235ed..8f11b26c86c6fbaa110301b2af3426e899949ad5 100644 --- a/aleksis/core/urls.py +++ b/aleksis/core/urls.py @@ -27,7 +27,23 @@ 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/additional_fields", views.additional_fields, name="additional_fields"), path("groups/child_groups/", views.groups_child_groups, name="groups_child_groups"), + path( + "groups/additional_field/<int:id_>/edit", + views.edit_additional_field, + name="edit_additional_field_by_id", + ), + path( + "groups/additional_field/create", + views.edit_additional_field, + name="create_additional_field", + ), + path( + "groups/additional_field/<int:id_>/delete", + views.delete_additional_field, + name="delete_additional_field_by_id", + ), 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 2b856a04846196b73190778c7eee71c02c3d4ad0..cbf63defe75317a730922d667fff8d5947e825e5 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -20,6 +20,7 @@ from .filters import GroupFilter from .forms import ( AnnouncementForm, ChildGroupsForm, + EditAdditionalFieldForm, EditGroupForm, EditPersonForm, GroupPreferenceForm, @@ -27,13 +28,13 @@ from .forms import ( PersonsAccountsFormSet, SitePreferenceForm, ) -from .models import Announcement, DashboardWidget, Group, Notification, Person +from .models import AdditionalField, Announcement, DashboardWidget, Group, Notification, Person from .registries import ( group_preferences_registry, person_preferences_registry, site_preferences_registry, ) -from .tables import GroupsTable, PersonsTable +from .tables import AdditionalFieldsTable, GroupsTable, PersonsTable from .util import messages from .util.apps import AppConfig from .util.core_helpers import objectgetter_optional @@ -444,3 +445,65 @@ def preferences( context["instance"] = instance return render(request, "dynamic_preferences/form.html", context) + + +@permission_required( + "core.edit_additional_field", fn=objectgetter_optional(AdditionalField, None, False) +) +def edit_additional_field(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse: + """View to edit or create a additional_field.""" + context = {} + + additional_field = objectgetter_optional(AdditionalField, None, False)(request, id_) + context["additional_field"] = additional_field + + if id_: + # Edit form for existing additional_field + edit_additional_field_form = EditAdditionalFieldForm( + request.POST or None, instance=additional_field + ) + else: + # Empty form to create a new additional_field + edit_additional_field_form = EditAdditionalFieldForm(request.POST or None) + + if request.method == "POST": + if edit_additional_field_form.is_valid(): + edit_additional_field_form.save(commit=True) + + messages.success(request, _("The additional_field has been saved.")) + + return redirect("additional_fields") + + context["edit_additional_field_form"] = edit_additional_field_form + + return render(request, "core/edit_additional_field.html", context) + + +@permission_required("core.view_additionalfield") +def additional_fields(request: HttpRequest) -> HttpResponse: + """List view for listing all additional fields.""" + context = {} + + # Get all additional fields + additional_fields = get_objects_for_user( + request.user, "core.view_additionalfield", AdditionalField + ) + + # Build table + additional_fields_table = AdditionalFieldsTable(additional_fields) + RequestConfig(request).configure(additional_fields_table) + context["additional_fields_table"] = additional_fields_table + + return render(request, "core/additional_fields.html", context) + + +@permission_required( + "core.delete_additional_field", fn=objectgetter_optional(AdditionalField, None, False) +) +def delete_additional_field(request: HttpRequest, id_: int) -> HttpResponse: + """View to delete an additional_field.""" + additional_field = objectgetter_optional(AdditionalField, None, False)(request, id_) + additional_field.delete() + messages.success(request, _("The additional field has been deleted.")) + + return redirect("additional_fields")