diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py index b2fd7980f947bc3cbd76c6bb9a1b6a0465ffe959..980dd7eab3edc5416d91b3e199f6324251db0cd0 100644 --- a/aleksis/core/forms.py +++ b/aleksis/core/forms.py @@ -9,8 +9,8 @@ from django_select2.forms import ModelSelect2MultipleWidget, Select2Widget from dynamic_preferences.forms import PreferenceForm from material import Fieldset, Layout, Row -from .mixins import ExtensibleForm -from .models import Announcement, Group, GroupType, Person +from .mixins import ExtensibleForm, SchoolYearRelatedExtensibleForm +from .models import Announcement, Group, GroupType, Person, SchoolYear from .registries import ( group_preferences_registry, person_preferences_registry, @@ -289,3 +289,13 @@ class EditGroupTypeForm(forms.ModelForm): class Meta: model = GroupType exclude = [] + + +class SchoolYearForm(ExtensibleForm): + """Form for managing school years.""" + + layout = Layout("name", Row("date_start", "date_end")) + + class Meta: + model = SchoolYear + exclude = [] diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py index 4767a8f3492c7401a22164fe25f54da4ec9fd702..2657cc18105ce918e327b1c29e4e21937720d76d 100644 --- a/aleksis/core/menus.py +++ b/aleksis/core/menus.py @@ -82,6 +82,17 @@ MENUS = { ), ], }, + { + "name": _("School years"), + "url": "school_years", + "icon": "date_range", + "validators": [ + ( + "aleksis.core.util.predicates.permission_validator", + "core.view_schoolyear", + ), + ], + }, { "name": _("Data management"), "url": "data_management", diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py index 3325f136af03b52deb9be44f765cc82e2e832b70..2cbcad73fb3544a52d23da2fdd5254ce612589c5 100644 --- a/aleksis/core/rules.py +++ b/aleksis/core/rules.py @@ -219,3 +219,13 @@ 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) + +# School years +view_school_year_predicate = has_person & has_global_perm("core.view_schoolyear") +add_perm("core.view_schoolyear", view_school_year_predicate) + +create_school_year_predicate = has_person & has_global_perm("core.add_schoolyear") +add_perm("core.create_schoolyear", create_school_year_predicate) + +edit_school_year_predicate = has_person & has_global_perm("core.change_schoolyear") +add_perm("core.edit_schoolyear", edit_school_year_predicate) diff --git a/aleksis/core/tables.py b/aleksis/core/tables.py index ff8fd871e75e9b97453784928d65456c5f857e8b..b6c8f3a157aa31e19808d86a703abfdb387e81b4 100644 --- a/aleksis/core/tables.py +++ b/aleksis/core/tables.py @@ -4,6 +4,24 @@ import django_tables2 as tables from django_tables2.utils import A +class SchoolYearTable(tables.Table): + """Table to list persons.""" + + class Meta: + attrs = {"class": "responsive-table highlight"} + + name = tables.LinkColumn("edit_school_year", args=[A("id")]) + date_start = tables.Column() + date_end = tables.Column() + edit = tables.LinkColumn( + "edit_school_year", + args=[A("id")], + text=_("Edit"), + attrs={"a": {"class": "btn-flat waves-effect waves-orange orange-text"}}, + verbose_name=_("Actions"), + ) + + class PersonsTable(tables.Table): """Table to list persons.""" diff --git a/aleksis/core/templates/core/school_year/create.html b/aleksis/core/templates/core/school_year/create.html new file mode 100644 index 0000000000000000000000000000000000000000..65ad9e7efa181b4a57cf9d797ce5da84aec687b1 --- /dev/null +++ b/aleksis/core/templates/core/school_year/create.html @@ -0,0 +1,17 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} +{% load material_form i18n %} + +{% block browser_title %}{% blocktrans %}Manage school year{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Manage school year{% endblocktrans %}{% endblock %} + +{% block content %} + + <form method="post"> + {% csrf_token %} + {% form form=form %}{% endform %} + {% include "core/save_button.html" %} + </form> + +{% endblock %} diff --git a/aleksis/core/templates/core/school_year/edit.html b/aleksis/core/templates/core/school_year/edit.html new file mode 100644 index 0000000000000000000000000000000000000000..123a033a2d162c9b7e828de060ebcc5a746ca855 --- /dev/null +++ b/aleksis/core/templates/core/school_year/edit.html @@ -0,0 +1,17 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} +{% load material_form i18n %} + +{% block browser_title %}{% blocktrans %}Edit school year{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Edit school year{% endblocktrans %}{% endblock %} + +{% block content %} + + <form method="post"> + {% csrf_token %} + {% form form=form %}{% endform %} + {% include "core/save_button.html" %} + </form> + +{% endblock %} diff --git a/aleksis/core/templates/core/school_year/list.html b/aleksis/core/templates/core/school_year/list.html new file mode 100644 index 0000000000000000000000000000000000000000..5a432e91019c00b3a351ff68a29efa81d4f0ae36 --- /dev/null +++ b/aleksis/core/templates/core/school_year/list.html @@ -0,0 +1,18 @@ +{# -*- engine:django -*- #} + +{% extends "core/base.html" %} + +{% load i18n %} +{% load render_table from django_tables2 %} + +{% block browser_title %}{% blocktrans %}School years{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}School years{% endblocktrans %}{% endblock %} + +{% block content %} + <a class="btn green waves-effect waves-light" href="{% url 'create_school_year' %}"> + <i class="material-icons left">add</i> + {% trans "Create school year" %} + </a> + + {% render_table table %} +{% endblock %} diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py index 47cb1d63c85c41b562e54909d553febac99c7625..7d3c9135465573de377e1903f0e1c5be890a42f2 100644 --- a/aleksis/core/urls.py +++ b/aleksis/core/urls.py @@ -21,6 +21,9 @@ urlpatterns = [ path("status/", views.system_status, name="system_status"), path("", include(tf_urls)), path("accounts/logout/", auth_views.LogoutView.as_view(), name="logout"), + path("school_years/", views.SchoolYearListView.as_view(), name="school_years"), + path("school_years/create/", views.SchoolYearCreateView.as_view(), name="create_school_year"), + path("school_years/<int:pk>/", views.SchoolYearEditView.as_view(), name="edit_school_year"), path("persons", views.persons, name="persons"), path("persons/accounts", views.persons_accounts, name="persons_accounts"), path("person", views.person, name="person"), diff --git a/aleksis/core/views.py b/aleksis/core/views.py index b5415c45d293218ac64d7003c905350ea02eb3ad..7687a161fd0b45818e98f8accc886d58cc03bbf8 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -2,20 +2,20 @@ from typing import Optional from django.apps import apps from django.conf import settings -from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import PermissionDenied from django.core.paginator import Paginator from django.http import HttpRequest, HttpResponse, HttpResponseNotFound from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ -from django_tables2 import RequestConfig +from django_tables2 import RequestConfig, SingleTableView from dynamic_preferences.forms import preference_form_builder from guardian.shortcuts import get_objects_for_user from haystack.inputs import AutoQuery from haystack.query import SearchQuerySet from haystack.views import SearchView -from rules.contrib.views import permission_required +from rules.contrib.views import PermissionRequiredMixin, permission_required from .filters import GroupFilter from .forms import ( @@ -27,15 +27,25 @@ from .forms import ( GroupPreferenceForm, PersonPreferenceForm, PersonsAccountsFormSet, + SchoolYearForm, SitePreferenceForm, ) -from .models import Announcement, DashboardWidget, Group, GroupType, Notification, Person +from .mixins import AdvancedCreateView, AdvancedEditView +from .models import ( + Announcement, + DashboardWidget, + Group, + GroupType, + Notification, + Person, + SchoolYear, +) from .registries import ( group_preferences_registry, person_preferences_registry, site_preferences_registry, ) -from .tables import GroupsTable, GroupTypesTable, PersonsTable +from .tables import GroupsTable, GroupTypesTable, PersonsTable, SchoolYearTable from .util import messages from .util.apps import AppConfig from .util.core_helpers import objectgetter_optional @@ -82,6 +92,37 @@ def about(request: HttpRequest) -> HttpResponse: return render(request, "core/about.html", context) +class SchoolYearListView(SingleTableView, PermissionRequiredMixin): + """Table of all school years.""" + + model = SchoolYear + table_class = SchoolYearTable + permission_required = "core.view_schoolyear" + template_name = "core/school_year/list.html" + + +class SchoolYearCreateView(AdvancedCreateView, PermissionRequiredMixin): + """Create view for school years.""" + + model = SchoolYear + form_class = SchoolYearForm + permission_required = "core.add_schoolyear" + template_name = "core/school_year/create.html" + success_url = reverse_lazy("school_years") + success_message = _("The school year has been created.") + + +class SchoolYearEditView(AdvancedEditView, PermissionRequiredMixin): + """Edit view for school years.""" + + model = SchoolYear + form_class = SchoolYearForm + permission_required = "core.edit_schoolyear" + template_name = "core/school_year/edit.html" + success_url = reverse_lazy("school_years") + success_message = _("The school year has been saved.") + + @permission_required("core.view_persons") def persons(request: HttpRequest) -> HttpResponse: """List view listing all persons."""