diff --git a/ephios/core/signals.py b/ephios/core/signals.py index f49a6621d..6f6010257 100644 --- a/ephios/core/signals.py +++ b/ephios/core/signals.py @@ -37,11 +37,11 @@ Receivers will receive a ``request`` keyword argument. """ -administration_settings_section = PluginSignal() +management_settings_sections = PluginSignal() """ -This signal is sent out to get sections for administration settings. Receivers should return a list of dicts -containing key-value-pairs for 'label', 'url' and a boolean flag 'active'. -Receivers will receive a ``request`` keyword argument. +This signal is sent out to get sections for management settings. Receivers should return a list of dicts +containing key-value-pairs for 'label', 'url' and a boolean flag 'active'. Only views that the current user is +allowed to view should be returned. Receivers will receive a ``request`` keyword argument. """ participant_from_request = PluginSignal() diff --git a/ephios/templates/registration/password_change_form.html b/ephios/core/templates/core/settings/password_change_form.html similarity index 60% rename from ephios/templates/registration/password_change_form.html rename to ephios/core/templates/core/settings/password_change_form.html index 3362021f1..aa8787421 100644 --- a/ephios/templates/registration/password_change_form.html +++ b/ephios/core/templates/core/settings/password_change_form.html @@ -1,16 +1,12 @@ -{% extends "base.html" %} +{% extends "core/settings/settings_base.html" %} {% load crispy_forms_filters %} {% load i18n %} -{% block title %} - {% trans "Change password" %} -{% endblock %} - -{% block content %} -

{% trans "Change password" %}

+{% block settings_content %} +

{% trans "Change password" %}

{% csrf_token %} {{ form|crispy }}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/ephios/core/templates/core/settings/settings_base.html b/ephios/core/templates/core/settings/settings_base.html index ad9b06835..589ce8a93 100644 --- a/ephios/core/templates/core/settings/settings_base.html +++ b/ephios/core/templates/core/settings/settings_base.html @@ -12,12 +12,20 @@

{% translate "Settings" %} {% block settings_title %}{% endblock %}

diff --git a/ephios/core/templates/core/settings/general.html b/ephios/core/templates/core/settings/settings_instance.html similarity index 100% rename from ephios/core/templates/core/settings/general.html rename to ephios/core/templates/core/settings/settings_instance.html diff --git a/ephios/core/templates/core/userprofile_notifications.html b/ephios/core/templates/core/settings/settings_notifications.html similarity index 75% rename from ephios/core/templates/core/userprofile_notifications.html rename to ephios/core/templates/core/settings/settings_notifications.html index ba6cea686..26190348b 100644 --- a/ephios/core/templates/core/userprofile_notifications.html +++ b/ephios/core/templates/core/settings/settings_notifications.html @@ -1,22 +1,14 @@ -{% extends "base.html" %} -{% load webpush_notifications %} -{% load webpush_notifications %} -{% load utils %} +{% extends "core/settings/settings_base.html" %} {% load crispy_forms_filters %} {% load i18n %} - -{% block title %} - {% translate "Settings" %} -{% endblock %} +{% load webpush_notifications %} {% block html_head %} {% webpush_header %} {% endblock %} -{% block content %} - +{% block settings_content %} +

{% translate "Notifications" %}

{% translate "You can configure whether you want to receive notifications for the following occasions:" %}

diff --git a/ephios/core/urls.py b/ephios/core/urls.py index c1f34644d..f5aef2907 100644 --- a/ephios/core/urls.py +++ b/ephios/core/urls.py @@ -17,7 +17,6 @@ UserProfileDeleteView, UserProfileDetailView, UserProfileListView, - UserProfileNotificationsView, UserProfilePasswordResetView, UserProfileUpdateView, ) @@ -45,7 +44,11 @@ ) from ephios.core.views.log import LogView from ephios.core.views.pwa import manifest, offline, serviceworker -from ephios.core.views.settings import GeneralSettingsView +from ephios.core.views.settings import ( + InstanceSettingsView, + NotificationSettingsView, + PasswordChangeSettingsView, +) from ephios.core.views.shift import ( ShiftConfigurationFormView, ShiftCreateView, @@ -56,7 +59,7 @@ app_name = "core" urlpatterns = [ - path("", HomeView.as_view(), name="index"), + path("", HomeView.as_view(), name="home"), path("events/", current_event_list_view, name="event_list"), path( "events//edit/", @@ -150,7 +153,17 @@ RRuleOccurrenceView.as_view(), name="rrule_occurrences", ), - path("settings/general/", GeneralSettingsView.as_view(), name="settings_general"), + path( + "settings/notifications/", + NotificationSettingsView.as_view(), + name="settings_notifications", + ), + path( + "settings/password_change/", + PasswordChangeSettingsView.as_view(), + name="settings_password_change", + ), + path("settings/instance/", InstanceSettingsView.as_view(), name="settings_instance"), path("settings/eventtypes/", EventTypeListView.as_view(), name="settings_eventtype_list"), path( "settings/eventtypes/create/", @@ -168,11 +181,6 @@ name="settings_eventtype_delete", ), path("profile/", ProfileView.as_view(), name="profile"), - path( - "profile/notifications/", - UserProfileNotificationsView.as_view(), - name="profile_notifications", - ), path("groups/", GroupListView.as_view(), name="group_list"), path("groups//edit/", GroupUpdateView.as_view(), name="group_edit"), path("groups//delete/", GroupDeleteView.as_view(), name="group_delete"), diff --git a/ephios/core/views/accounts.py b/ephios/core/views/accounts.py index 469f93e96..e69c5de4b 100644 --- a/ephios/core/views/accounts.py +++ b/ephios/core/views/accounts.py @@ -2,7 +2,6 @@ from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import Group -from django.contrib.messages.views import SuccessMessageMixin from django.db.models import Prefetch from django.shortcuts import redirect from django.urls import reverse @@ -11,7 +10,6 @@ CreateView, DeleteView, DetailView, - FormView, ListView, TemplateView, UpdateView, @@ -19,12 +17,7 @@ from django.views.generic.detail import SingleObjectMixin from dynamic_preferences.registries import global_preferences_registry -from ephios.core.forms.users import ( - GroupForm, - QualificationGrantFormset, - UserNotificationPreferenceForm, - UserProfileForm, -) +from ephios.core.forms.users import GroupForm, QualificationGrantFormset, UserProfileForm from ephios.core.models import QualificationGrant, UserProfile from ephios.core.services.notifications.types import ( NewProfileNotification, @@ -203,21 +196,6 @@ def post(self, request, *args, **kwargs): return self.render_to_response({"userprofile": self.object}) -class UserProfileNotificationsView(LoginRequiredMixin, SuccessMessageMixin, FormView): - template_name = "core/userprofile_notifications.html" - success_message = _("Settings succesfully saved.") - - def get_form(self, form_class=None): - return UserNotificationPreferenceForm(self.request.POST or None, user=self.request.user) - - def get_success_url(self): - return reverse("core:profile_notifications") - - def form_valid(self, form): - form.update_preferences() - return super().form_valid(form) - - class GroupListView(CustomPermissionRequiredMixin, ListView): model = Group permission_required = "auth.view_group" diff --git a/ephios/core/views/settings.py b/ephios/core/views/settings.py index e2a5e82ec..e32bb68fb 100644 --- a/ephios/core/views/settings.py +++ b/ephios/core/views/settings.py @@ -1,41 +1,51 @@ import typing +from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.auth.views import PasswordChangeView from django.contrib.messages.views import SuccessMessageMixin -from django.urls import reverse +from django.urls import reverse, reverse_lazy from django.utils.translation import gettext as _ from django.views.generic import FormView from dynamic_preferences.forms import global_preference_form_builder -from ephios.core.signals import administration_settings_section +from ephios.core.forms.users import UserNotificationPreferenceForm +from ephios.core.signals import management_settings_sections from ephios.extra.mixins import StaffRequiredMixin -def get_available_administration_settings_sections(request): - sections = [ - { - "label": _("General"), - "url": reverse("core:settings_general"), - "active": request.resolver_match.url_name == "settings_general", - }, - { - "label": _("Event types"), - "url": reverse("core:settings_eventtype_list"), - "active": request.resolver_match.url_name.startswith("settings_eventtype"), - }, - ] - for __, result in administration_settings_section.send(None, request=request): +def get_available_management_settings_sections(request): + sections = [] + if request.user.is_staff: + sections.append( + { + "label": _("ephios instance"), + "url": reverse("core:settings_instance"), + "active": request.resolver_match.url_name == "settings_instance", + } + ) + if request.user.has_perm("core.view_eventtype"): + sections.append( + { + "label": _("Event types"), + "url": reverse("core:settings_eventtype_list"), + "active": request.resolver_match.url_name.startswith("settings_eventtype"), + } + ) + for __, result in management_settings_sections.send(None, request=request): sections += result return sections class SettingsViewMixin(FormView if typing.TYPE_CHECKING else object): def get_context_data(self, **kwargs): - kwargs["settings_sections"] = get_available_administration_settings_sections(self.request) + kwargs["management_settings_sections"] = get_available_management_settings_sections( + self.request + ) return super().get_context_data(**kwargs) -class GeneralSettingsView(StaffRequiredMixin, SuccessMessageMixin, SettingsViewMixin, FormView): - template_name = "core/settings/general.html" +class InstanceSettingsView(StaffRequiredMixin, SuccessMessageMixin, SettingsViewMixin, FormView): + template_name = "core/settings/settings_instance.html" success_message = _("Settings saved successfully.") def get_form_class(self): @@ -46,4 +56,27 @@ def form_valid(self, form): return super().form_valid(form) def get_success_url(self): - return reverse("core:settings_general") + return reverse("core:settings_instance") + + +class NotificationSettingsView( + LoginRequiredMixin, SuccessMessageMixin, SettingsViewMixin, FormView +): + template_name = "core/settings/settings_notifications.html" + success_message = _("Settings succesfully saved.") + + def get_form(self, form_class=None): + return UserNotificationPreferenceForm(self.request.POST or None, user=self.request.user) + + def get_success_url(self): + return reverse("core:settings_notifications") + + def form_valid(self, form): + form.update_preferences() + return super().form_valid(form) + + +class PasswordChangeSettingsView(SuccessMessageMixin, SettingsViewMixin, PasswordChangeView): + template_name = "core/settings/password_change_form.html" + success_url = reverse_lazy("core:home") + success_message = _("Password changed successfully.") diff --git a/ephios/locale/de/LC_MESSAGES/django.po b/ephios/locale/de/LC_MESSAGES/django.po index 96b320669..feed5659c 100644 --- a/ephios/locale/de/LC_MESSAGES/django.po +++ b/ephios/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-07-06 12:48+0200\n" +"POT-Creation-Date: 2022-07-16 15:50+0200\n" "PO-Revision-Date: 2022-05-30 12:38+0000\n" "Last-Translator: Julian Baumann \n" "Language-Team: German  

\n" -"

167 " -"powered by %(brand)s 

\n" -"

168 " -msgstr "" +#~ msgid "General" +#~ msgstr "Allgemein" -#: htmlcov/d_665b89aa18c0023b_event_list_html.html:99 -#, python-format -msgid "" -"Add 

\n" -"

41 %(title)s" -msgstr "" +#~ msgid "Administration" +#~ msgstr "Administration" -#, python-brace-format #~ msgid "'{choice}' is not a valid signup action." #~ msgstr "'{choice}' ist keinen gültige Anmeldeaktion." @@ -2621,15 +2561,12 @@ msgstr "" #~ msgid "Can decline after confirmation" #~ msgstr "Kann nach Bestätigung absagen" -#, python-brace-format #~ msgid "{min} to {max}" #~ msgstr "{min} bis {max}" -#, python-brace-format #~ msgid "at least {min}" #~ msgstr "mindestens {min}" -#, python-brace-format #~ msgid "at most {max}" #~ msgstr "höchstens {max}" @@ -2663,11 +2600,9 @@ msgstr "" #~ msgid "missing" #~ msgstr "fehlen" -#, python-brace-format #~ msgid "You are rejected from {shift}." #~ msgstr "Sie wurden für {shift} abgelehnt." -#, python-brace-format #~ msgid "You are bindingly signed up for {shift}." #~ msgstr "Sie sind verbindlich für {shift} eingeteilt." @@ -2727,7 +2662,6 @@ msgstr "" #~ msgid "You are not allowed to change group associations." #~ msgstr "Sie sind nicht berechtigt Gruppenmitgliedschaften zu bearbeiten." -#, python-brace-format #~ msgid "" #~ "The participation of {participant} for {shift} was changed. The status is " #~ "now {status}" @@ -2741,7 +2675,6 @@ msgstr "" #~ msgid "Advanced settings" #~ msgstr "Erweiterte Einstellungen" -#, python-format #~ msgid "Add %(title)s" #~ msgstr "%(title)s hinzufügen" diff --git a/ephios/plugins/guests/views.py b/ephios/plugins/guests/views.py index 6f0552e15..d4474e903 100644 --- a/ephios/plugins/guests/views.py +++ b/ephios/plugins/guests/views.py @@ -26,7 +26,7 @@ def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) def get_authenticated_url(self): - return reverse("core:index") + return reverse("core:home") class GuestRegistrationView(RedirectAuthenticatedUserMixin, CreateView): diff --git a/ephios/plugins/pages/signals.py b/ephios/plugins/pages/signals.py index 828b705a3..7018c099c 100644 --- a/ephios/plugins/pages/signals.py +++ b/ephios/plugins/pages/signals.py @@ -2,7 +2,7 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from ephios.core.signals import administration_settings_section, footer_link +from ephios.core.signals import footer_link, management_settings_sections from ephios.plugins.pages.models import Page @@ -15,14 +15,18 @@ def pages_footer_links(sender, request, **kwargs): @receiver( - administration_settings_section, + management_settings_sections, dispatch_uid="ephios.plugins.pages.signals.pages_settings_section", ) def pages_settings_section(sender, request, **kwargs): - return [ - { - "label": _("Pages"), - "url": reverse("pages:settings_page_list"), - "active": request.resolver_match.url_name.startswith("settings_page"), - }, - ] + return ( + [ + { + "label": _("Pages"), + "url": reverse("pages:settings_page_list"), + "active": request.resolver_match.url_name.startswith("settings_page"), + }, + ] + if request.user.is_staff + else [] + ) diff --git a/ephios/plugins/qualification_management/signals.py b/ephios/plugins/qualification_management/signals.py index eeeda83bd..2ae46cbf1 100644 --- a/ephios/plugins/qualification_management/signals.py +++ b/ephios/plugins/qualification_management/signals.py @@ -2,18 +2,22 @@ from django.urls import reverse from django.utils.translation import gettext as _ -from ephios.core.signals import administration_settings_section +from ephios.core.signals import management_settings_sections @receiver( - administration_settings_section, + management_settings_sections, dispatch_uid="ephios.plugins.qualification_management.signals.qualifications_settings_section", ) def qualifications_settings_section(sender, request, **kwargs): - return [ - { - "label": _("Qualifications"), - "url": reverse("qualification_management:settings_qualification_list"), - "active": request.resolver_match.url_name.startswith("settings_qualification"), - }, - ] + return ( + [ + { + "label": _("Qualifications"), + "url": reverse("qualification_management:settings_qualification_list"), + "active": request.resolver_match.url_name.startswith("settings_qualification"), + }, + ] + if request.user.has_perm("core.change_qualification") + else [] + ) diff --git a/ephios/plugins/qualification_management/views.py b/ephios/plugins/qualification_management/views.py index 6beac6537..dba9b9902 100644 --- a/ephios/plugins/qualification_management/views.py +++ b/ephios/plugins/qualification_management/views.py @@ -9,7 +9,7 @@ from ephios.core.models import Qualification, QualificationCategory from ephios.core.views.settings import SettingsViewMixin -from ephios.extra.mixins import StaffRequiredMixin +from ephios.extra.mixins import CustomPermissionRequiredMixin from ephios.plugins.qualification_management.forms import ( QualificationCategoryFormset, QualificationDeleteForm, @@ -23,14 +23,16 @@ # Templates in this plugin are under core/, because Qualification is a core model. -class QualificationListView(StaffRequiredMixin, SettingsViewMixin, ListView): +class QualificationListView(CustomPermissionRequiredMixin, SettingsViewMixin, ListView): model = Qualification ordering = ("category__title", "title") + permission_required = "core.view_qualification" -class QualificationImportView(StaffRequiredMixin, SettingsViewMixin, FormView): +class QualificationImportView(CustomPermissionRequiredMixin, SettingsViewMixin, FormView): template_name = "core/import.html" form_class = QualificationImportForm + permission_required = "core.add_qualification" def get_success_url(self): return reverse("qualification_management:settings_qualification_list") @@ -53,9 +55,10 @@ def form_valid(self, form): return super().form_valid(form) -class QualificationCreateView(StaffRequiredMixin, SettingsViewMixin, CreateView): +class QualificationCreateView(CustomPermissionRequiredMixin, SettingsViewMixin, CreateView): model = Qualification form_class = QualificationForm + permission_required = "core.add_qualification" def get_form_kwargs(self): return { @@ -68,28 +71,33 @@ def get_success_url(self): return reverse("qualification_management:settings_qualification_list") -class QualificationUpdateView(StaffRequiredMixin, SettingsViewMixin, UpdateView): +class QualificationUpdateView(CustomPermissionRequiredMixin, SettingsViewMixin, UpdateView): model = Qualification form_class = QualificationForm + permission_required = "core.change_qualification" def get_success_url(self): messages.success(self.request, _("Qualification was saved.")) return reverse("qualification_management:settings_qualification_list") -class QualificationDeleteView(StaffRequiredMixin, SettingsViewMixin, UpdateView): +class QualificationDeleteView(CustomPermissionRequiredMixin, SettingsViewMixin, UpdateView): model = Qualification form_class = QualificationDeleteForm template_name_suffix = "_confirm_delete" + permission_required = "core.delete_qualification" def get_success_url(self): messages.info(self.request, _("Qualification was deleted.")) return reverse("qualification_management:settings_qualification_list") -class QualificationCategorySetUpdateView(StaffRequiredMixin, SettingsViewMixin, FormView): +class QualificationCategorySetUpdateView( + CustomPermissionRequiredMixin, SettingsViewMixin, FormView +): form_class = QualificationCategoryFormset template_name = "core/qualificationcategories_form.html" + permission_required = "core.change_qualification" def get_form_kwargs(self): return {"queryset": QualificationCategory.objects.all(), **super().get_form_kwargs()} @@ -104,9 +112,10 @@ def get_success_url(self): return reverse("qualification_management:settings_qualification_list") -class QualificationReassignmentView(StaffRequiredMixin, SettingsViewMixin, FormView): +class QualificationReassignmentView(CustomPermissionRequiredMixin, SettingsViewMixin, FormView): form_class = QualificationReassignmentForm template_name = "core/qualification_reassignment.html" + permission_required = "core.change_userprofile" def form_valid(self, form): with transaction.atomic(): @@ -125,7 +134,9 @@ def get_success_url(self): return reverse("qualification_management:settings_qualification_list") -class QualificationExportFixtureView(StaffRequiredMixin, View): +class QualificationExportFixtureView(CustomPermissionRequiredMixin, View): + permission_required = "core.view_qualification" + def get(self, request, *args, **kwargs): qualifications = Qualification.objects.all() serializer = QualificationFixtureSerializer(qualifications, many=True) diff --git a/ephios/templates/base.html b/ephios/templates/base.html index 51110947b..78a092be0 100644 --- a/ephios/templates/base.html +++ b/ephios/templates/base.html @@ -83,7 +83,7 @@