default geomtype, permission fix, name fix, remove ungrouped header, fix id filter
This commit is contained in:
Gwendolyn 2024-12-16 12:53:18 +01:00
parent 13cf207ee6
commit 934aa60be4
10 changed files with 96 additions and 41 deletions

View file

@ -422,6 +422,7 @@ def create_editor_form(editor_model):
'color_ground_fill', 'color_obstacles_default_fill', 'color_obstacles_default_border',
'stroke_color', 'stroke_width', 'fill_color', 'interactive', 'point_icon', 'extra_data',
'show_label', 'show_geometry', 'external_url',
'show_label', 'show_geometry', 'external_url', '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)]

View file

@ -1370,6 +1370,7 @@ editor = {
}
form.addClass('creation-lock');
const geomtypes = form.attr('data-geomtype').split(',');
const default_geomtype = form.attr('data-default-geomtype');
const startGeomEditing = (geomtype) => {
editor._creating_type = geomtype;
@ -1387,6 +1388,8 @@ editor = {
}
}
let selected_geomtype = geomtypes[0];
if (geomtypes.length > 1) {
const selector = $('<select id="geomtype-selector"></select>');
const geomtypeNames = {
@ -1395,13 +1398,18 @@ editor = {
point: 'Point'
}; // TODO: translations
for(const geomtype of geomtypes) {
selector.append(`<option value="${geomtype}">${geomtypeNames[geomtype]}</option>`);
const option = $(`<option value="${geomtype}">${geomtypeNames[geomtype]}</option>`);
if (geomtype === default_geomtype) {
option.attr('selected', true);
selected_geomtype = geomtype;
}
selector.append(option);
}
selector.on('change', e => startGeomEditing(e.target.value));
form.prepend(selector);
}
startGeomEditing(geomtypes[0]);
startGeomEditing(selected_geomtype);
}
}
},

View file

@ -20,7 +20,7 @@
{% endif %}
</h3>
{% bootstrap_messages %}
<form action="{{ path }}" method="post" {% if nozoom %}data-nozoom {% endif %}data-onbeforeunload {% if new %}data-new="{{ model_name }}" data-geomtype="{{ geomtype }}"{% else %}data-editing="{{ model_name }}-{{ pk }}"{% endif %}{% if access_restriction_select %} data-access-restriction-select{% endif %}>
<form action="{{ path }}" method="post" {% if nozoom %}data-nozoom {% endif %}data-onbeforeunload {% if new %}data-new="{{ model_name }}" data-geomtype="{{ geomtype }}" {% if default_geomtype %}data-default-geomtype="{{ default_geomtype }}{% endif %}"{% else %}data-editing="{{ model_name }}-{{ pk }}"{% endif %}{% if access_restriction_select %} data-access-restriction-select{% endif %}>
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}

View file

@ -130,6 +130,7 @@ def overlay_feature_edit(request, level=None, overlay=None, pk=None):
'title': obj.title if obj else None,
'geometry_url': geometry_url,
'geomtype': 'polygon,linestring,point',
'default_geomtype': overlay.default_geomtype,
}
space_id = None

View file

@ -0,0 +1,18 @@
# Generated by Django 5.1.3 on 2024-12-16 11:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mapdata', '0117_alter_dataoverlay_fill_color_and_more'),
]
operations = [
migrations.AddField(
model_name='dataoverlay',
name='default_geomtype',
field=models.CharField(blank=True, choices=[('polygon', 'Polygon'), ('line', 'Line'), ('point', 'Point')], max_length=255, null=True, verbose_name='default geometry type'),
),
]

View file

@ -13,10 +13,18 @@ from c3nav.mapdata.utils.json import format_geojson
class DataOverlay(TitledMixin, AccessRestrictionMixin, models.Model):
class GeometryType(models.TextChoices):
POLYGON = "polygon", _("Polygon")
LINESTRING = "linestring", _("Line string")
POINT = "point", _("Point")
description = models.TextField(blank=True, verbose_name=_('Description'))
stroke_color = models.CharField(max_length=255, blank=True, null=True, verbose_name=_('default stroke color'))
stroke_width = models.FloatField(blank=True, null=True, verbose_name=_('default stroke width'))
fill_color = models.CharField(max_length=255, blank=True, null=True, verbose_name=_('default fill color'))
default_geomtype = models.CharField(max_length=255, blank=True, null=True, choices=GeometryType, verbose_name=_('default geometry type'))
pull_url = models.URLField(blank=True, null=True, verbose_name=_('pull URL'))
pull_headers: dict[str, str] = SchemaField(schema=dict[str, str], null=True,
verbose_name=_('headers for pull http request (JSON object)'))

View file

@ -6,7 +6,8 @@ from pydantic import Field as APIField
from c3nav.api.exceptions import APIRequestValidationFailed
from c3nav.api.schema import BaseSchema
from c3nav.mapdata.models import Level, LocationGroup, LocationGroupCategory, MapUpdate, Space, Door, Building
from c3nav.mapdata.models import Level, LocationGroup, LocationGroupCategory, MapUpdate, Space, Door, Building, \
DataOverlay
from c3nav.mapdata.models.access import AccessPermission
@ -145,7 +146,7 @@ class ByOverlayFilter(FilterSchema):
def validate(self, request):
super().validate(request)
if self.overlay is not None:
assert_valid_value(request, Level, "pk", {self.overlay})
assert_valid_value(request, DataOverlay, "pk", {self.overlay})
def filter_qs(self, request, qs: QuerySet) -> QuerySet:
if self.overlay is not None:

View file

@ -6,6 +6,7 @@ from django.utils.translation import ngettext_lazy
from c3nav.mapdata.models import DataOverlay
from c3nav.mapdata.models.access import AccessPermission, AccessRestriction
from c3nav.mapdata.models.locations import Position
from c3nav.mapdata.schemas.models import DataOverlaySchema
def get_user_data(request):
@ -32,16 +33,13 @@ def get_user_data(request):
if request.user.is_authenticated:
result['title'] = request.user.username
# TODO: permissions for overlays
result.update({
'overlays': [{
'id': overlay.pk,
'name': overlay.title,
'group': None, # TODO
'stroke_color': overlay.stroke_color,
'stroke_width': overlay.stroke_width,
'fill_color': overlay.fill_color,
} for overlay in DataOverlay.objects.all()]
'overlays': [
DataOverlaySchema.model_validate(overlay).model_dump()
for overlay
in DataOverlay.qs_for_request(request)
]
})
return result

View file

@ -1811,14 +1811,7 @@ blink {
}
label {
cursor: pointer;
margin-left: 3ch;
margin-bottom: 0;
}
input[type=checkbox] {
margin-right: 0.5rem;
margin-bottom: 0;
}
}
@ -1832,6 +1825,16 @@ blink {
margin-top: 0;
margin-bottom: 0;
}
label {
cursor: pointer;
margin-bottom: 0;
}
input[type=checkbox] {
margin-right: 0.5rem;
margin-bottom: 0;
}
}
&.leaflet-control-overlays-expanded > .content {

View file

@ -2615,6 +2615,7 @@ KeyControl = L.Control.extend({
OverlayControl = L.Control.extend({
options: {position: 'topright', addClasses: '', levels: {}},
_overlays: {},
_ungrouped: [],
_groups: {},
_initialActiveOverlays: null,
_initialCollapsedGroups: null,
@ -2685,6 +2686,9 @@ OverlayControl = L.Control.extend({
addOverlay: function (overlay) {
this._overlays[overlay.id] = overlay;
if (overlay.group == null) {
this._ungrouped.push(overlay);
} else {
if (overlay.group in this._groups) {
this._groups[overlay.group].overlays.push(overlay);
} else {
@ -2693,6 +2697,8 @@ OverlayControl = L.Control.extend({
overlays: [overlay],
};
}
}
this.render();
},
@ -2709,7 +2715,28 @@ OverlayControl = L.Control.extend({
render: function () {
if (!this._content) return;
const ungrouped = document.createDocumentFragment();
const groups = document.createDocumentFragment();
const render_overlays = (overlays, container) => {
for (const overlay of overlays) {
const label = document.createElement('label');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.dataset.id = overlay.id;
if (overlay.visible) {
checkbox.checked = true;
}
label.append(checkbox, overlay.title);
container.append(label);
}
};
render_overlays(this._ungrouped, ungrouped);
for (const group in this._groups) {
const group_container = document.createElement('div');
group_container.classList.add('overlay-group');
@ -2721,20 +2748,10 @@ OverlayControl = L.Control.extend({
const title = document.createElement('h4');
title.innerText = group;
group_container.append(title);
for (const overlay of this._groups[group].overlays) {
const label = document.createElement('label');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.dataset.id = overlay.id;
if (overlay.visible) {
checkbox.checked = true;
}
label.append(checkbox, overlay.name);
group_container.append(label);
}
render_overlays(this._groups[group].overlays, group_container);
groups.append(group_container);
}
this._content.replaceChildren(...groups.children);
this._content.replaceChildren(...ungrouped.children, ...groups.children);
},
expand: function () {
@ -2832,8 +2849,8 @@ class DataOverlay {
constructor(options) {
this.id = options.id;
this.name = options.name;
this.group = options.group ?? 'ungrouped';
this.title = options.title;
this.group = options.group;
this.default_stroke_color = options.stroke_color;
this.default_stroke_width = options.stroke_width;
this.default_fill_color = options.fill_color;