From 6b3b361e07f7aacbcaaebf3ca19cd7f4e7be0ecc Mon Sep 17 00:00:00 2001 From: Laetitia Date: Mon, 4 Mar 2024 20:35:07 +0100 Subject: [PATCH] feat 159: allow staff users to see grouped order overview pages --- la_chariotte/order/views/grouped_order.py | 47 +++++++++---------- la_chariotte/order/views/helpers.py | 13 +++++ la_chariotte/order/views/item.py | 7 ++- la_chariotte/order/views/order.py | 7 ++- .../tests/test_order_views_grouped_order.py | 19 ++++++++ 5 files changed, 64 insertions(+), 29 deletions(-) create mode 100644 la_chariotte/order/views/helpers.py diff --git a/la_chariotte/order/views/grouped_order.py b/la_chariotte/order/views/grouped_order.py index 7beee05..094358d 100644 --- a/la_chariotte/order/views/grouped_order.py +++ b/la_chariotte/order/views/grouped_order.py @@ -13,6 +13,7 @@ from icalendar import Calendar, Event, vCalAddress, vText from ..forms import GroupedOrderForm, Item, JoinGroupedOrderForm from ..models import GroupedOrder, OrderAuthor +from .helpers import UserCanAccessGroupedOrderOverviewMixin class IndexView(LoginRequiredMixin, generic.ListView): @@ -127,7 +128,7 @@ class GroupedOrderDetailView(generic.DetailView): return context -class GroupedOrderOverview(UserPassesTestMixin, generic.DetailView): +class GroupedOrderOverview(UserCanAccessGroupedOrderOverviewMixin, generic.DetailView): model = GroupedOrder template_name = "order/grouped_order_overview.html" context_object_name = "grouped_order" @@ -136,8 +137,8 @@ class GroupedOrderOverview(UserPassesTestMixin, generic.DetailView): return get_object_or_404(GroupedOrder, code=self.kwargs.get("code")) def test_func(self): - # Restrict access to the manager - return self.get_object().orga == self.request.user + # Access for grouped order manager, superuser, and staff (staff can see but not edit grouped orders) + return super().test_func() or self.request.user.is_staff def get(self, request, *args, **kwargs): # Compute grouped order total price before display @@ -175,7 +176,9 @@ class GroupedOrderCreateView(LoginRequiredMixin, generic.CreateView): return super().form_valid(form) -class GroupedOrderUpdateView(UserPassesTestMixin, generic.UpdateView): +class GroupedOrderUpdateView( + UserCanAccessGroupedOrderOverviewMixin, generic.UpdateView +): model = GroupedOrder template_name = "order/grouped_order_update.html" context_object_name = "grouped_order" @@ -184,24 +187,18 @@ class GroupedOrderUpdateView(UserPassesTestMixin, generic.UpdateView): def get_object(self, queryset=None): return get_object_or_404(GroupedOrder, code=self.kwargs.get("code")) - def test_func(self): - # Restrict access to the manager - return self.get_object().orga == self.request.user - def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["user"] = self.request.user return kwargs -class GroupedOrderDuplicateView(UserPassesTestMixin, generic.RedirectView): +class GroupedOrderDuplicateView( + UserCanAccessGroupedOrderOverviewMixin, generic.RedirectView +): def get_object(self, queryset=None): return get_object_or_404(GroupedOrder, code=self.kwargs.get("code")) - def test_func(self): - # Restrict access to the manager - return self.get_object().orga == self.request.user - def get(self, request, *args, **kwargs): # overwrite the ``get`` function to copy the initial grouped order before # redirecting to the update view of the new grouped order @@ -238,7 +235,9 @@ class GroupedOrderDuplicateView(UserPassesTestMixin, generic.RedirectView): ) -class GroupedOrderDeleteView(UserPassesTestMixin, generic.DeleteView): +class GroupedOrderDeleteView( + UserCanAccessGroupedOrderOverviewMixin, generic.DeleteView +): model = GroupedOrder template_name = "order/grouped_order_confirm_delete.html" context_object_name = "grouped_order" @@ -249,10 +248,6 @@ class GroupedOrderDeleteView(UserPassesTestMixin, generic.DeleteView): def get_success_url(self): return reverse_lazy("order:index") - def test_func(self): - # Restrict access to the manager - return self.get_object().orga == self.request.user - def form_valid(self, form): # Delete related OrderAuthors grouped_order = self.get_object() @@ -284,18 +279,20 @@ class GroupedOrderAddItemsView(UserPassesTestMixin, generic.ListView): return grouped_order.orga == self.request.user -class GroupedOrderExportView(UserPassesTestMixin, generic.DetailView): +class GroupedOrderExportView( + UserCanAccessGroupedOrderOverviewMixin, generic.DetailView +): model = GroupedOrder template_name = "order/grouped_order_sheet.html" context_object_name = "grouped_order" + def test_func(self): + # Grouped order orga, superuser and staff can get this view + return super().test_func() or self.request.user.is_staff + def get_object(self, queryset=None): return get_object_or_404(GroupedOrder, code=self.kwargs.get("code")) - def test_func(self): - # Restrict access to the manager - return self.get_object().orga == self.request.user - def get_context_data(self, **kwargs): context = super(GroupedOrderExportView, self).get_context_data(**kwargs) grouped_order = self.get_object() @@ -328,9 +325,9 @@ class DownloadGroupedOrderSheetView(WeasyTemplateResponseMixin, GroupedOrderExpo class ExportGroupOrderEmailAdressesToDownloadView(UserPassesTestMixin, generic.View): def test_func(self): - # Restrict access to the manager + # Restrict access to the manager, superuser and staff origin = get_object_or_404(GroupedOrder, code=self.kwargs.get("code")) - return origin.orga == self.request.user + return origin.orga == self.request.user or self.request.user.is_staff def get(self, request, *args, **kwargs): grouped_order = get_object_or_404(GroupedOrder, code=self.kwargs.get("code")) diff --git a/la_chariotte/order/views/helpers.py b/la_chariotte/order/views/helpers.py new file mode 100644 index 0000000..781a6a1 --- /dev/null +++ b/la_chariotte/order/views/helpers.py @@ -0,0 +1,13 @@ +from django.contrib.auth.mixins import UserPassesTestMixin + + +class UserCanAccessGroupedOrderOverviewMixin(UserPassesTestMixin): + """ + The view is accessible only if the request user is orga or superuser + """ + + def test_func(self): + return ( + self.get_object().orga == self.request.user + or self.request.user.is_superuser + ) diff --git a/la_chariotte/order/views/item.py b/la_chariotte/order/views/item.py index a116fd6..16b3733 100644 --- a/la_chariotte/order/views/item.py +++ b/la_chariotte/order/views/item.py @@ -29,5 +29,8 @@ class ItemDeleteView(UserPassesTestMixin, generic.DeleteView): return reverse_lazy("order:manage_items", args=[self.object.grouped_order.code]) def test_func(self): - # Restrict access to the manager - return self.get_object().grouped_order.orga == self.request.user + # Restrict access to the manager or a superuser + return ( + self.get_object().grouped_order.orga == self.request.user + or self.request.user.is_superuser + ) diff --git a/la_chariotte/order/views/order.py b/la_chariotte/order/views/order.py index 3754fa2..47b1e11 100644 --- a/la_chariotte/order/views/order.py +++ b/la_chariotte/order/views/order.py @@ -136,5 +136,8 @@ class OrderDeleteView(UserPassesTestMixin, generic.DeleteView): ) def test_func(self): - """Accessible only if the requesting user is the grouped order organizer""" - return self.get_object().grouped_order.orga == self.request.user + # Restrict access to the manager or a superuser + return ( + self.get_object().grouped_order.orga == self.request.user + or self.request.user.is_superuser + ) diff --git a/la_chariotte/tests/test_order_views_grouped_order.py b/la_chariotte/tests/test_order_views_grouped_order.py index 6367bc8..ef1d247 100644 --- a/la_chariotte/tests/test_order_views_grouped_order.py +++ b/la_chariotte/tests/test_order_views_grouped_order.py @@ -735,6 +735,25 @@ class TestGroupedOrderOverview: response = client_log.get(orga_view_url) assert response.status_code == 403 + def test_user_superuser_ok(self, other_user, admin_client): + """ + A superuser that is not orga can get the GroupedOrderOverview + """ + grouped_order = create_grouped_order( + days_before_delivery_date=5, + days_before_deadline=2, + name="gr order test", + orga_user=other_user, + ) + orga_view_url = reverse( + "order:grouped_order_overview", + kwargs={ + "code": grouped_order.code, + }, + ) + response = admin_client.get(orga_view_url) + assert response.status_code == 200 + def test_deadline_passed(self, client_log): """ If the deadline is passed, the user sees a message but cannot share link