diff --git a/src/api/views/maps.py b/src/api/views/maps.py
index c238affb721217a00f35d065df5907fd645b40a8..245e265d5bef40efad8a46341f33e99e81b244d3 100644
--- a/src/api/views/maps.py
+++ b/src/api/views/maps.py
@@ -141,7 +141,7 @@ class C3NavExportView(ConferenceSlugMixin, APIView):
         qs = self.conference.assemblies.filter(state__in=exportable_states)
         if request.GET.get('all') != '1':
             qs = qs.exclude(location_data__point=None, location_data__boundaries=None)
-        for assembly in qs:  # type: Assembly
+        for assembly in qs.all():  # type: Assembly
             loc_data = assembly.location_data or {}
             data.append(
                 {
@@ -160,6 +160,22 @@ class C3NavExportView(ConferenceSlugMixin, APIView):
                 }
             )
 
+            for project in assembly.projects.conference_accessible(self.conference):
+                data.append(
+                    {
+                        'type': 'project',
+                        'id': str(project.pk),
+                        'slug': project.slug,
+                        'name': project.name,
+                        'description': {'de': project.description_de, 'en': project.description_en},
+                        'public_url': hub_absolute('plainui:project', slug=project.slug),
+                        'assembly_id': str(assembly.pk),
+                        'floor': assembly.get_location_floor_index(),  # TODO: allow project-specific location floor
+                        'location': loc_data.get('point'),  # TODO: allow project-specific location point
+                        'location_text': project.location,
+                    }
+                )
+
         for poi in self.conference.pois.filter(visible=True):  # type: MapPOI
             data.append(
                 {
diff --git a/src/backoffice/views/assemblyteam.py b/src/backoffice/views/assemblyteam.py
index c1a1633e23fba064273768a566970cf8c91751f6..6e9654c63c176d8713dcf37f7e4bd3ae0af9be03 100644
--- a/src/backoffice/views/assemblyteam.py
+++ b/src/backoffice/views/assemblyteam.py
@@ -568,9 +568,9 @@ class AssemblyEditPlacementView(SingleAssemblyTeamMixin, View):
 
         old_values = {
             'state': assembly.state,
-            'location_point': assembly.get_location_point_xy(),
-            'location_boundaries': assembly.get_location_boundaries_xy(),
-            'location_floor': assembly.get_location_floor_index(),
+            'location_point': assembly.get_location_point_xy() or '',
+            'location_boundaries': assembly.get_location_boundaries_xy() or '',
+            'location_floor': str(assembly.get_location_floor_index() or ''),
         }
         changes = {}
         try:
@@ -582,11 +582,11 @@ class AssemblyEditPlacementView(SingleAssemblyTeamMixin, View):
 
                 # assembly.location_point = Point(x=parsed[0], y=parsed[1], srid=self.SRID)
                 assembly.location_data['point'] = parsed
-                changes['location_point'] = str(assembly.location_point)
+                changes['location_point'] = poi
             else:
                 # assembly.location_point = None
                 assembly.location_data['point'] = None
-                changes['location_point'] = str(None)
+                changes['location_point'] = ''
 
             if boundaries and boundaries != '""':
                 parsed = json.loads(boundaries)
@@ -594,20 +594,20 @@ class AssemblyEditPlacementView(SingleAssemblyTeamMixin, View):
 
                 # assembly.location_boundaries = MultiPolygon([Polygon(item) for item in parsed], srid=self.SRID)
                 assembly.location_data['boundaries'] = parsed
-                changes['location_boundaries'] = str(assembly.location_boundaries)
+                changes['location_boundaries'] = boundaries
             else:
                 # assembly.location_boundaries = None
                 assembly.location_data['boundaries'] = None
-                changes['location_boundaries'] = str(None)
+                changes['location_boundaries'] = ''
 
             if floor and floor != '""':
                 parsed = int(floor)
 
                 assembly.location_floor = self.conference.map_floors.filter(index=parsed).first()
-                changes['location_floor'] = str(assembly.location_floor.index)
+                changes['location_floor'] = str(assembly.get_location_floor_index() or '')
             else:
                 assembly.location_floor = None
-                changes['location_floor'] = str(None)
+                changes['location_floor'] = ''
 
         except ValueError:
             logger.exception('Failed to update position of assembly %s', assembly.pk)
diff --git a/src/core/tests/utils.py b/src/core/tests/utils.py
index a69dab3c921ad22cb63b78ace318f5538b256656..b39677a3372b26cdc7bc28b18ced6fabc03143d1 100644
--- a/src/core/tests/utils.py
+++ b/src/core/tests/utils.py
@@ -1,9 +1,13 @@
 import uuid
 from datetime import timedelta
 
+from django.conf import settings
 from django.test import TestCase
+from django.urls import reverse
+from django.utils.timezone import now
 
-from core.utils import GitRepo, mail2uuid, mask_url, scheme_and_netloc_from_url, str2timedelta
+from core.models import Assembly, Conference, ConferenceMember, Event, PlatformUser, Room
+from core.utils import GitRepo, mail2uuid, mask_url, resolve_internal_url, scheme_and_netloc_from_url, str2timedelta
 
 
 class UtilsTests(TestCase):
@@ -47,6 +51,46 @@ class UtilsTests(TestCase):
         self.assertEqual(expected_uuid, mail2uuid('hub@cccv.de'))
 
 
+class InternalUrlTests(TestCase):
+    def setUp(self):
+        self.conference1 = Conference(slug='foo', name='Foo Conference', start=now() - timedelta(days=1), end=now() + timedelta(days=1))
+        self.conference1.save()
+
+        self.user = PlatformUser(username='bernd')
+        self.user.save()
+        ConferenceMember(conference=self.conference1, user=self.user).save()
+
+        self.assembly1a = Assembly(conference=self.conference1, slug='fnord', name='Fnord Assembly', is_official=True)
+        self.assembly1a.save()
+
+        self.room1a = Room(conference=self.conference1, assembly=self.assembly1a, name='Saal 1', room_type=Room.RoomType.LECTURE_HALL)
+        self.room1a.save()
+
+        self.valid_event = {
+            'conference': self.conference1,
+            'assembly': self.assembly1a,
+            'name': 'testEvent',
+            'slug': 'testEvent',
+            'kind': Event.Kind.ASSEMBLY,
+            'room': self.room1a,
+            'schedule_start': now(),
+            'schedule_duration': timedelta(hours=1),
+            'is_public': True,
+        }
+
+    def test_resolve_internal_url(self):
+        base_url = settings.PLAINUI_BASE_URL
+        checks = [
+            ('https://example.com/foo?bar=baz', 'https://example.com/foo?bar=baz'),
+            ('conference://assemblies', base_url + reverse('plainui:assemblies')),
+            ('conference://assemblies?foo=bar', base_url + reverse('plainui:assemblies') + '?foo=bar'),
+            ('assembly://fnord', base_url + reverse('plainui:assembly', kwargs={'assembly_slug': 'fnord'})),
+        ]
+
+        for check in checks:
+            self.assertEqual(check[1], resolve_internal_url(check[0]))
+
+
 class GitRepoOfflineTests(TestCase):
     def test_invalid_url_local_path(self):
         with self.assertRaisesMessage(ValueError, 'Invalid protocol file'):
diff --git a/src/hub/settings/dev.py b/src/hub/settings/dev.py
index 640f276423aa28cd87cf5f42d2ea7223dd2d3e98..38b948d7dcd9f02a69481a94a1bf1983e9bf2eef 100644
--- a/src/hub/settings/dev.py
+++ b/src/hub/settings/dev.py
@@ -29,7 +29,7 @@ if dev_env('SERVE_DEBUGPY'):
 os.environ.setdefault('DJANGO_DEBUG', 'I_KNOW_WHAT_I_AM_DOING')
 
 # set default ALLOWED_HOSTS
-os.environ.setdefault('ALLOWED_HOSTS', 'localhost')
+os.environ.setdefault('ALLOWED_HOSTS', 'localhost,127.0.0.1')
 
 # set PLAINUI link options
 os.environ.setdefault('PLAINUI_BASE_URL', 'http://127.0.0.1:8000')