From 48cb444691e57f17e97b11b55d33ca1f0d1b808f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Thu, 19 Dec 2024 11:59:11 +0100 Subject: [PATCH] external_url_label and external_url for locationgroup as well --- src/c3nav/editor/forms.py | 6 ++--- ...122_locationgroup_external_url_and_more.py | 24 +++++++++++++++++ src/c3nav/mapdata/models/locations.py | 27 ++++++++++++++----- src/c3nav/mapdata/schemas/models.py | 1 + src/c3nav/site/static/site/js/c3nav.js | 8 ++++++ src/c3nav/site/templates/site/map.html | 8 +++++- 6 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 src/c3nav/mapdata/migrations/0122_locationgroup_external_url_and_more.py diff --git a/src/c3nav/editor/forms.py b/src/c3nav/editor/forms.py index 66b8dbe5..8f0e0fa1 100644 --- a/src/c3nav/editor/forms.py +++ b/src/c3nav/editor/forms.py @@ -399,8 +399,8 @@ class EditorFormBase(I18nModelFormMixin, ModelForm): def create_editor_form(editor_model): possible_fields = [ 'slug', 'name', 'title', 'title_plural', 'help_text', 'position_secret', 'icon', 'join_edges', - 'up_separate', 'bssid', 'main_point', 'external_url', 'hub_import_type', 'walk', 'ordering', - 'category', 'width', 'groups', 'height', 'color', 'in_legend', 'priority', 'hierarchy', 'icon_name', + 'up_separate', 'bssid', 'main_point', 'external_url', 'external_url_label', 'hub_import_type', 'walk', + 'ordering', 'category', 'width', 'groups', 'height', 'color', 'in_legend', 'priority', 'hierarchy', 'icon_name', 'base_altitude', 'intermediate', 'waytype', 'access_restriction', 'default_height', 'door_height', 'outside', 'can_search', 'can_describe', 'geometry', 'single', 'altitude', 'level_index', 'short_label', 'origin_space', 'target_space', 'data', @@ -418,7 +418,7 @@ def create_editor_form(editor_model): 'color_background', 'color_wall_fill', 'color_wall_border', 'color_door_fill', 'color_ground_fill', 'color_obstacles_default_fill', 'color_obstacles_default_border', 'stroke_color', 'stroke_width', 'stroke_opacity', 'fill_color', 'fill_opacity', 'interactive', 'point_icon', 'extra_data', 'show_label', - 'show_geometry', 'external_url', 'show_label', 'show_geometry', 'external_url', 'default_geomtype', + 'show_geometry', 'show_label', 'show_geometry', 'default_geomtype', ] field_names = [field.name for field in editor_model._meta.get_fields() if not field.one_to_many and not isinstance(field, ManyToManyRel)] diff --git a/src/c3nav/mapdata/migrations/0122_locationgroup_external_url_and_more.py b/src/c3nav/mapdata/migrations/0122_locationgroup_external_url_and_more.py new file mode 100644 index 00000000..62f79b45 --- /dev/null +++ b/src/c3nav/mapdata/migrations/0122_locationgroup_external_url_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.8 on 2024-12-19 10:55 + +import c3nav.mapdata.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mapdata', '0121_level_level_index_alter_level_short_label'), + ] + + operations = [ + migrations.AddField( + model_name='locationgroup', + name='external_url', + field=models.URLField(blank=True, null=True, verbose_name='external URL'), + ), + migrations.AddField( + model_name='locationgroup', + name='external_url_label', + field=c3nav.mapdata.fields.I18nField(blank=True, fallback_any=True, fallback_value='', plural_name='external_url_labels', verbose_name='external URL label'), + ), + ] diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index aa2bf037..c48a6c1e 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -105,6 +105,7 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model): can_search = models.BooleanField(default=True, verbose_name=_('can be searched')) can_describe = models.BooleanField(default=True, verbose_name=_('can describe')) icon = models.CharField(_('icon'), max_length=32, null=True, blank=True, help_text=_('any material icons name')) + external_url = models.URLField(_('external URL'), null=True, blank=True) class Meta: abstract = True @@ -123,6 +124,12 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model): (_('can describe'), _('Yes') if self.can_describe else _('No')), (_('icon'), self.effective_icon), ]) + + if self.external_url: + result['external_url'] = { + 'title': self.external_url_label or _('Open external URL'), + 'url': self.external_url, + } return result @property @@ -158,13 +165,16 @@ class Location(LocationSlug, AccessRestrictionMixin, TitledMixin, models.Model): def effective_icon(self): return self.icon or None + @property + def external_url_label(self): + return None + class SpecificLocation(Location, models.Model): groups = models.ManyToManyField('mapdata.LocationGroup', verbose_name=_('Location Groups'), blank=True) label_settings = models.ForeignKey('mapdata.LabelSettings', null=True, blank=True, on_delete=models.PROTECT, verbose_name=_('label settings')) label_override = I18nField(_('Label override'), plural_name='label_overrides', blank=True, fallback_any=True) - external_url = models.URLField(_('external URL'), null=True, blank=True) import_block_data = models.BooleanField(_('don\'t change metadata on import'), default=False) import_block_geom = models.BooleanField(_('don\'t change geometry on import'), default=False) @@ -214,12 +224,6 @@ class SpecificLocation(Location, models.Model): } for group in sorted(groups, key=attrgetter('priority'), reverse=True)) )) - if self.external_url: - result['display'].insert(3, (_('External URL'), { - 'title': _('Open'), - 'url': self.external_url, - })) - return result @cached_property @@ -252,6 +256,13 @@ class SpecificLocation(Location, models.Model): return group.icon return None + @property + def external_url_label(self): + for group in self.groups.all(): + if group.icon and getattr(group.category, 'allow_' + self.__class__._meta.default_related_name): + return group.external_url_label + return None + class LocationGroupCategory(SerializableMixin, models.Model): name = models.SlugField(_('Name'), unique=True, max_length=50) @@ -343,6 +354,8 @@ class LocationGroup(Location, models.Model): hub_import_type = models.CharField(max_length=100, verbose_name=_('hub import type'), null=True, blank=True, unique=True, help_text=_('assign this group to imported hub locations of this type')) + external_url_label = I18nField(_('external URL label'), plural_name='external_url_labels', blank=True, + fallback_any=True, fallback_value="") objects = LocationGroupManager() diff --git a/src/c3nav/mapdata/schemas/models.py b/src/c3nav/mapdata/schemas/models.py index f8f84443..b88f3584 100644 --- a/src/c3nav/mapdata/schemas/models.py +++ b/src/c3nav/mapdata/schemas/models.py @@ -835,6 +835,7 @@ class LocationDisplay(BaseSchema): description="space", example=3, ) + external_url: Optional[DisplayURL] = None display: list[ tuple[ Annotated[NonEmptyStr, APIField(title="field title")], diff --git a/src/c3nav/site/static/site/js/c3nav.js b/src/c3nav/site/static/site/js/c3nav.js index 05bb2635..3022db3c 100644 --- a/src/c3nav/site/static/site/js/c3nav.js +++ b/src/c3nav/site/static/site/js/c3nav.js @@ -437,6 +437,7 @@ c3nav = { $location_details.find('.details-body').html(''); $location_details.find('.editor').hide(); $location_details.find('.report').hide(); + $location_details.find('.external-url-button').hide(); $location_details.removeClass('loading'); }) } @@ -494,6 +495,13 @@ c3nav = { $location_details.find('.report').hide(); } + if (data.external_url) { + $location_details.find('.external-url-button a').attr('href', data.external_url.url); + $location_details.find('.external-url-button span').text(data.external_url.title); + } else { + $location_details.find('.external-url-button').hide(); + } + if (data.geometry && data.level) { L.geoJSON(data.geometry, { style: { diff --git a/src/c3nav/site/templates/site/map.html b/src/c3nav/site/templates/site/map.html index a9f47bd9..e0ad7542 100644 --- a/src/c3nav/site/templates/site/map.html +++ b/src/c3nav/site/templates/site/map.html @@ -184,8 +184,14 @@

{% trans 'Details' %}

+
+ + open_in_new + + +
-
+