Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/backoffice/views/maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ class MapLayerCreateView(CampViewMixin, AnyTeamMapperRequiredMixin, CreateView):
"description",
"icon",
"invisible",
"public",
"group",
"responsible_team",
]
Expand Down Expand Up @@ -208,6 +209,7 @@ class MapLayerUpdateView(CampViewMixin, LayerMapperViewMixin, UpdateView):
"description",
"icon",
"invisible",
"public",
"group",
"responsible_team",
]
Expand Down
20 changes: 20 additions & 0 deletions src/maps/migrations/0005_layer_public.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.21 on 2025-05-29 05:03
from __future__ import annotations

from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

dependencies = [
("maps", "0004_userlocationtype_userlocation"),
]

operations = [
migrations.AddField(
model_name="layer",
name="public",
field=models.BooleanField(default=True, help_text="Make the layer visible to the public. A non-public layer is only visible for team members"),
),
]
7 changes: 7 additions & 0 deletions src/maps/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def setup(self, *args, **kwargs) -> None:
"""Set self.layer based on layer_slug in url kwargs."""
super().setup(*args, **kwargs)
self.layer = get_object_or_404(Layer, slug=self.kwargs["layer_slug"])
if not self.layer.public:
if ((self.layer.responsible_team and
self.layer.responsible_team.member_permission_set in self.request.user.get_all_permissions()) or
self.request.user.has_perm("camps.gis_team_member")):
return
raise PermissionDenied


def get_context_data(self, *args, **kwargs) -> dict:
"""Add self.layer to context."""
Expand Down
5 changes: 5 additions & 0 deletions src/maps/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ class Layer(ExportModelOperationsMixin("layer"), UUIDModel):
blank=True,
)

public = models.BooleanField(
default=True,
help_text="Make the layer visible to the public. A non-public layer is only visible for team members",
)

@property
def camp(self) -> Camp:
"""Camp object reference."""
Expand Down
40 changes: 38 additions & 2 deletions src/maps/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from maps.models import UserLocationType
from maps.views import MapProxyView
from maps.views import MissingCredentialsError
from teams.models import TeamMember
from utils.tests import BornhackTestBase

USER = "user"
Expand Down Expand Up @@ -100,6 +101,7 @@ class MapsViewTest(BornhackTestBase):
"""Test Maps View"""

layer: Layer
hidden_layer: Layer
group: Group

@classmethod
Expand All @@ -117,21 +119,55 @@ def setUpTestData(cls) -> None:
description="Test Layer",
icon="fas fa-tractor",
group=cls.group,
responsible_team=cls.teams['noc'],
public=True,
responsible_team=cls.teams["noc"],
)
cls.layer.save()

cls.hidden_layer = Layer.objects.create(
name="Non public layer",
slug="hidden_layer",
description="Hidden layer",
icon="fa fa-list-ul",
group=cls.group,
public=False,
responsible_team=cls.teams["noc"],
)
cls.hidden_layer.save()

TeamMember.objects.create(
team=cls.teams["noc"],
user=cls.users[0],
approved=True,
lead=True,
).save()

def test_geojson_layer_views(self) -> None:
"""Test the geojson view."""
url = reverse("maps:map_layer_geojson", kwargs={"layer_slug": self.layer.slug})
response = self.client.get(url)
assert response.status_code == 200

# Test 404 of geojson layer
# test 404 of geojson layer
url = reverse("maps:map_layer_geojson", kwargs={"layer_slug": "123test"})
response = self.client.get(url)
assert response.status_code == 404

# test layer not being public
url = reverse("maps:map_layer_geojson", kwargs={"layer_slug": self.hidden_layer.slug})
response = self.client.get(url)
content = response.content.decode()
soup = BeautifulSoup(content, "html.parser")
rows = soup.select("p.lead")
matches = [s for s in rows if "403" in str(s)]
self.assertEqual(len(matches), 1, "geojson layer did not return a 403")

# test layer access when not being public
self.client.force_login(self.users[0])
url = reverse("maps:map_layer_geojson", kwargs={"layer_slug": self.hidden_layer.slug})
response = self.client.get(url)
assert response.status_code == 200

def test_map_views(self) -> None:
"""Test the map view."""
url = reverse("maps_map", kwargs={"camp_slug": self.camp.slug})
Expand Down
17 changes: 14 additions & 3 deletions src/maps/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,26 @@ class MapView(CampViewMixin, TemplateView):
template_name = "maps_map.html"
context_object_name = "maps_map"

def get_layers(self) -> QuerySet:
"""Method to get the layers the user has access to."""
user_teams=[]
if not self.request.user.is_anonymous:
user_teams = self.request.user.teammember_set.filter(
team__camp=self.camp,
).values_list("team__name", flat=True)
return Layer.objects.filter(
((Q(responsible_team__camp=self.camp) | Q(responsible_team=None)) & Q(public=True))
| (Q(responsible_team__name__in=user_teams) & Q(public=False)),

)

def get_context_data(self, **kwargs) -> dict:
"""Get the context data."""
context = super().get_context_data(**kwargs)
context["facilitytype_list"] = FacilityType.objects.filter(
responsible_team__camp=self.camp,
)
context["layers"] = Layer.objects.filter(
Q(responsible_team__camp=self.camp) | Q(responsible_team=None),
)
context["layers"] = self.get_layers()
context["user_location_types"] = UserLocationType.objects.filter(
user_locations__isnull=False,
).distinct()
Expand Down
13 changes: 13 additions & 0 deletions src/utils/bootstrap/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1961,6 +1961,7 @@ def create_maps_layer_generic(self) -> None:
description="Venue areas",
icon="fa fa-list-ul",
group=group,
public=True,
)
Feature.objects.create(
layer=layer,
Expand Down Expand Up @@ -1989,12 +1990,22 @@ def create_camp_map_layer(self, camp: Camp) -> None:
"""Create map layers for camp."""
group = MapGroup.objects.get(name="Generic")
team = Team.objects.get(name="Orga", camp=camp)
Layer.objects.create(
name="Non public layer",
slug="hiddenlayer",
description="Hidden layer",
icon="fa fa-list-ul",
group=group,
public=False,
team=team,
)
layer = Layer.objects.create(
name="Team Area",
description="Team areas",
icon="fa fa-list-ul",
group=group,
responsible_team=team,
public=True,
)
Feature.objects.create(
layer=layer,
Expand Down Expand Up @@ -2146,6 +2157,8 @@ def bootstrap_tests(self) -> None:
year = camp.camp.lower.year

teams[year] = self.create_camp_teams(camp)
if year == 2025:
self.add_team_permissions(camp)

camp.read_only = read_only
camp.call_for_participation_open = not read_only
Expand Down
2 changes: 1 addition & 1 deletion src/utils/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

from camps.models import Camp
from teams.models import Team

from utils.bootstrap.base import Bootstrap


class TestBootstrapScript(TestCase):
"""Test bootstrap_devsite script (touching many codepaths)"""

Expand Down
Loading