diff --git a/src/planscape/planning/permissions.py b/src/planscape/planning/permissions.py index 4348d9dd3..8b685371a 100644 --- a/src/planscape/planning/permissions.py +++ b/src/planscape/planning/permissions.py @@ -5,6 +5,7 @@ ProjectAreaNotePermission, ) from planning.models import PlanningArea, ProjectArea +from rest_framework.exceptions import ValidationError class PlanningAreaViewPermission(PlanscapePermission): @@ -55,6 +56,17 @@ def has_permission(self, request, view): return False project_area = ProjectArea.objects.get(id=project_area_id) return ProjectAreaNotePermission.can_add(request.user, project_area) + + case "list": + project_area_id = request.query_params.get("project_area_pk") or None + if not project_area_id: + raise ValidationError(f"Missing required project_area_pk") + project_area = ProjectArea.objects.select_related( + "scenario", "scenario__planning_area" + ).get(id=project_area_id) + planning_area = project_area.scenario.planning_area + return PlanningAreaPermission.can_view(request.user, planning_area) + case _: # TODO: review if this is necessary return True diff --git a/src/planscape/planning/serializers.py b/src/planscape/planning/serializers.py index d6739aa11..008873214 100644 --- a/src/planscape/planning/serializers.py +++ b/src/planscape/planning/serializers.py @@ -391,3 +391,28 @@ class Meta: # "can_delete", ) model = ProjectAreaNote + + +class ProjectAreaNoteListSerializer(serializers.ModelSerializer): + can_delete = serializers.SerializerMethodField() + + def get_can_delete(self, obj): + user = self.context.get("user") + if user: + return (user == obj.user) or ( + user == obj.project_area.scenario.planning_area.user + ) + return False + + class Meta: + fields = ( + "id", + "created_at", + "updated_at", + "content", + "project_area", + "user_id", + "user_name", + "can_delete", + ) + model = ProjectAreaNote diff --git a/src/planscape/planning/tests/test_v2_views.py b/src/planscape/planning/tests/test_v2_views.py index e8de1d79a..8a682695f 100644 --- a/src/planscape/planning/tests/test_v2_views.py +++ b/src/planscape/planning/tests/test_v2_views.py @@ -922,19 +922,19 @@ def test_create_note_without_permission(self): def test_get_notes_for_project_area(self): self.client.force_authenticate(self.user) - new_note1 = ProjectAreaNote.objects.create( + ProjectAreaNote.objects.create( project_area=self.project_area, user=self.user, content="I am a note" ) - new_note2 = ProjectAreaNote.objects.create( + ProjectAreaNote.objects.create( project_area=self.project_area, user=self.user, content="I am a second note" ) - new_note3 = ProjectAreaNote.objects.create( + ProjectAreaNote.objects.create( project_area=self.project_area, user=self.other_user, content="I am a third note", ) # creating a note for a separate project area, so it shouldnt be in results - new_note4 = ProjectAreaNote.objects.create( + ProjectAreaNote.objects.create( project_area=self.other_project_area, user=self.other_user, content="I am a third note", @@ -947,6 +947,37 @@ def test_get_notes_for_project_area(self): response_data = response.json() self.assertEqual(response.status_code, 200) self.assertEqual(len(response_data), 3) + for rec in response_data: + self.assertIn("can_delete", rec) + + def test_get_notes_for_unauthorized_user(self): + self.client.force_authenticate(self.other_user) + ProjectAreaNote.objects.create( + project_area=self.project_area, user=self.user, content="I am a note" + ) + ProjectAreaNote.objects.create( + project_area=self.project_area, user=self.user, content="I am a second note" + ) + response = self.client.get( + reverse("api:planning:projectarea-notes-list"), + {"project_area_pk": self.project_area.pk}, + content_type="application/json", + ) + self.assertEqual(response.status_code, 403) + + def test_get_notes_without_project_area(self): + self.client.force_authenticate(self.user) + ProjectAreaNote.objects.create( + project_area=self.project_area, user=self.user, content="I am a note" + ) + ProjectAreaNote.objects.create( + project_area=self.project_area, user=self.user, content="I am a second note" + ) + response = self.client.get( + reverse("api:planning:projectarea-notes-list"), + content_type="application/json", + ) + self.assertEqual(response.status_code, 400) def test_delete_note(self): self.client.force_authenticate(self.user) diff --git a/src/planscape/planning/views_v2.py b/src/planscape/planning/views_v2.py index 4f05c8298..9d80746c3 100644 --- a/src/planscape/planning/views_v2.py +++ b/src/planscape/planning/views_v2.py @@ -26,6 +26,7 @@ ListScenarioSerializer, PlanningAreaSerializer, ProjectAreaNoteSerializer, + ProjectAreaNoteListSerializer, ProjectAreaSerializer, ScenarioSerializer, ) @@ -221,16 +222,21 @@ def create(self, request, *args, **kwargs): headers=headers, ) - # TODO: get for projectarea using pk... - # ensure that user has projectarea / planningarea permission first - def get_queryset(self): - project_area_id = self.request.query_params.get("project_area_pk") - if project_area_id: - return ( - super() - .get_queryset() - .filter(project_area_id=project_area_id) - .select_related("project_area__scenario__planning_area") + def list(self, request, *args, **kwargs): + # Get the project_area_pk from the request query parameters + project_area_pk = request.query_params.get("project_area_pk") + + # If project_area_pk is provided, filter the queryset + if not project_area_pk: + return Response( + {"error": "projectarea.pk is a required attribute"}, + status=status.HTTP_400_BAD_REQUEST, ) - else: - return super().get_queryset() + queryset = ( + self.get_queryset() + .select_related("project_area__scenario__planning_area") + .filter(project_area__pk=project_area_pk) + ) + + serializer = ProjectAreaNoteListSerializer(queryset, many=True) + return Response(serializer.data)