Geo fields: Allow overriding existing values (#1978)

This commit is contained in:
Richard Schreiber
2021-02-26 09:55:23 +01:00
committed by GitHub
parent 73e7d407cd
commit 1c81792cd7
9 changed files with 208 additions and 213 deletions

View File

@@ -0,0 +1,33 @@
{% load i18n %}
{% load bootstrap3 %}
{% load static %}
<div class="geodata-section">
{% bootstrap_field form.location layout="control" %}
{% include "pretixcontrol/event/fragment_geodata_autoupdate.html" %}
<div class="form-group geodata-group"
data-tiles="{{ global_settings.leaflet_tiles|default_if_none:"" }}"
data-attrib="{{ global_settings.leaflet_tiles_attribution }}"
data-icon="{% static "leaflet/images/marker-icon.png" %}"
data-shadow="{% static "leaflet/images/marker-shadow.png" %}">
<label class="col-md-3 control-label">
{% trans "Geo coordinates" %}<br>
<span class="optional">{% trans "Optional" %}</span>
</label>
<div class="col-md-4">
{% bootstrap_field form.geo_lat layout="inline" %}
{% if global_settings.opencagedata_apikey %}
<p class="attrib">
<a href="https://openstreetmap.org/" target="_blank">
{% trans "Geocoding data © OpenStreetMap" %}
</a>
</p>
{% endif %}
</div>
<div class="col-md-4">
{% bootstrap_field form.geo_lon layout="inline" %}
</div>
<div class="col-md-1">
</div>
</div>
</div>

View File

@@ -0,0 +1,8 @@
{% load i18n %}
<span class="geodata-autoupdate">
<span class="optional" data-notification="error" title="{% trans "Failed to retrieve geo coordinates" %}"><i class="fa fa-warning"></i></span>
<span class="optional" data-notification="loading" title="{% trans "Retrieving geo coordinates " %}"><i class="fa fa-cog fa-spin"></i></span>
<span class="optional" data-notification="updated"><i class="fa fa-check"></i> {% trans "Geo coordinates updated" %}</span>
<span class="optional" data-notification="confirm">New geo coordinates. <button type="button" data-action="update" class="btn btn-link text-nowrap">{% trans "Update map?" %}</button></span>
</span>

View File

@@ -24,34 +24,7 @@
{% endif %}
{% bootstrap_field form.date_from layout="control" %}
{% bootstrap_field form.date_to layout="control" %}
<div class="geodata-section">
{% bootstrap_field form.location layout="control" %}
<div class="form-group geodata-group"
data-tiles="{{ global_settings.leaflet_tiles|default_if_none:"" }}"
data-attrib="{{ global_settings.leaflet_tiles_attribution }}"
data-icon="{% static "leaflet/images/marker-icon.png" %}"
data-shadow="{% static "leaflet/images/marker-shadow.png" %}">
<label class="col-md-3 control-label">
{% trans "Geo coordinates" %}<br>
<span class="optional">{% trans "Optional" %}</span>
</label>
<div class="col-md-4">
{% bootstrap_field form.geo_lat layout="inline" %}
{% if global_settings.opencagedata_apikey %}
<p class="attrib">
<a href="https://openstreetmap.org/" target="_blank">
{% trans "Geocoding data © OpenStreetMap" %}
</a>
</p>
{% endif %}
</div>
<div class="col-md-4">
{% bootstrap_field form.geo_lon layout="inline" %}
</div>
<div class="col-md-1">
</div>
</div>
</div>
{% include "pretixcontrol/event/fragment_geodata.html" %}
{% bootstrap_field form.date_admission layout="control" %}
{% bootstrap_field form.currency layout="control" %}
{% bootstrap_field sform.contact_mail layout="control" %}

View File

@@ -39,30 +39,7 @@
{% if form.date_to %}
{% bootstrap_field form.date_to layout="control" %}
{% endif %}
<div class="geodata-section">
{% bootstrap_field form.location layout="control" %}
<div class="form-group geodata-group" data-tiles="{{ global_settings.leaflet_tiles|default_if_none:"" }}" data-attrib="{{ global_settings.leaflet_tiles_attribution }}" data-icon="{% static "leaflet/images/marker-icon.png" %}" data-shadow="{% static "leaflet/images/marker-shadow.png" %}">
<label class="col-md-3 control-label">
{% trans "Geo coordinates" %}<br>
<span class="optional">{% trans "Optional" %}</span>
</label>
<div class="col-md-4">
{% bootstrap_field form.geo_lat layout="inline" %}
{% if global_settings.opencagedata_apikey %}
<p class="attrib">
<a href="https://openstreetmap.org/" target="_blank">
{% trans "Geocoding data © OpenStreetMap" %}
</a>
</p>
{% endif %}
</div>
<div class="col-md-4">
{% bootstrap_field form.geo_lon layout="inline" %}
</div>
<div class="col-md-1">
</div>
</div>
</div>
{% include "pretixcontrol/event/fragment_geodata.html" %}
{% bootstrap_field form.currency layout="control" %}
{% bootstrap_field form.tax_rate addon_after="%" layout="control" %}
</fieldset>

View File

@@ -384,30 +384,7 @@
<legend>{% trans "General information" %}</legend>
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.active layout="control" %}
<div class="geodata-section">
{% bootstrap_field form.location layout="control" %}
<div class="form-group geodata-group" data-tiles="{{ global_settings.leaflet_tiles|default_if_none:"" }}" data-attrib="{{ global_settings.leaflet_tiles_attribution }}" data-icon="{% static "leaflet/images/marker-icon.png" %}" data-shadow="{% static "leaflet/images/marker-shadow.png" %}">
<label class="col-md-3 control-label">
{% trans "Geo coordinates" %}<br>
<span class="optional">{% trans "Optional" %}</span>
</label>
<div class="col-md-4">
{% bootstrap_field form.geo_lat layout="inline" %}
{% if global_settings.opencagedata_apikey %}
<p class="attrib">
<a href="https://openstreetmap.org/" target="_blank">
{% trans "Geocoding data © OpenStreetMap" %}
</a>
</p>
{% endif %}
</div>
<div class="col-md-4">
{% bootstrap_field form.geo_lon layout="inline" %}
</div>
<div class="col-md-1">
</div>
</div>
</div>
{% include "pretixcontrol/event/fragment_geodata.html" %}
{% bootstrap_field form.frontpage_text layout="control" %}
{% bootstrap_field form.is_public layout="control" %}
{% if meta_forms %}

View File

@@ -32,6 +32,7 @@
{% bootstrap_field form.active layout="bulkedit" %}
<div class="geodata-section">
{% bootstrap_field form.location layout="bulkedit" %}
{% include "pretixcontrol/event/fragment_geodata_autoupdate.html" %}
<div class="form-group geodata-group"
data-tiles="{{ global_settings.leaflet_tiles|default_if_none:"" }}"
data-attrib="{{ global_settings.leaflet_tiles_attribution }}"

View File

@@ -25,30 +25,7 @@
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.date_from layout="control" %}
{% bootstrap_field form.date_to layout="control" %}
<div class="geodata-section">
{% bootstrap_field form.location layout="control" %}
<div class="form-group geodata-group" data-tiles="{{ global_settings.leaflet_tiles|default_if_none:"" }}" data-attrib="{{ global_settings.leaflet_tiles_attribution }}" data-icon="{% static "leaflet/images/marker-icon.png" %}" data-shadow="{% static "leaflet/images/marker-shadow.png" %}">
<label class="col-md-3 control-label">
{% trans "Geo coordinates" %}<br>
<span class="optional">{% trans "Optional" %}</span>
</label>
<div class="col-md-4">
{% bootstrap_field form.geo_lat layout="inline" %}
{% if global_settings.opencagedata_apikey %}
<p class="attrib">
<a href="https://openstreetmap.org/" target="_blank">
{% trans "Geocoding data © OpenStreetMap" %}
</a>
</p>
{% endif %}
</div>
<div class="col-md-4">
{% bootstrap_field form.geo_lon layout="inline" %}
</div>
<div class="col-md-1">
</div>
</div>
</div>
{% include "pretixcontrol/event/fragment_geodata.html" %}
{% bootstrap_field form.date_admission layout="control" %}
{% bootstrap_field form.frontpage_text layout="control" %}
{% bootstrap_field form.is_public layout="control" %}

View File

@@ -1,128 +1,160 @@
/*globals $*/
$(function () {
var j = 0;
$(".geodata-section").each(function () {
// Geocoding
var $sec = $(this);
var $inp = $(this).find("textarea[lang=en], input[lang=en]").first();
if ($inp.length === 0) {
$inp = $(this).find("textarea, input").first();
function cleanup(l) {
return $.trim(l.replace(/\n/g, ", "));
}
var timer, timer2;
var touched = $sec.find("input[name$=geo_lat]").val() !== "";
$(".geodata-section").each(function () {
// Geocoding
// detach notifications and append them to first label (should be from location)
var $notifications = $(".geodata-autoupdate", this).detach().appendTo($("label", this).first());
var $lat = $("input[name$=geo_lat]", this).first();
var $lon = $("input[name$=geo_lon]", this).first();
var lat;
var lon;
var $updateButton = $("[data-action=update]", this);
var $location = $("textarea[lang=en], input[lang=en]", this).first();
if (!$location.length) $location = $("textarea, input[type=text]", this).first();
function load() {
window.clearTimeout(timer);
var q = $.trim($inp.val().replace(/\n/g, ", "));
if ((touched && $sec.find("input[name$=geo_lat]").val() !== "") || $.trim(q) === "") {
return;
}
$sec.find(".col-md-1").html("<span class='fa fa-cog fa-spin'></span>");
$.getJSON('/control/geocode/?q=' + encodeURIComponent(q), function (res) {
var q2 = $.trim($inp.val().replace(/\n/g, ", "));
if (q2 !== q) {
return; // lost race
if (!$lat.length || !$lon.length || !$location.length) {
return;
}
if (res.results) {
$sec.find("input[name$=geo_lat]").val(res.results[0].lat);
$sec.find("input[name$=geo_lon]").val(res.results[0].lon);
center(13);
var debounceLoad, debounceLatLonChange, delayUpdateDismissal;
var touched = $lat.val() !== "";
var xhr;
var lastLocation = cleanup($location.val());
function load() {
window.clearTimeout(debounceLoad);
if (xhr) {
xhr.abort();
xhr = null;
}
var q = cleanup($location.val());
if (q === "" || q === lastLocation) return;
lastLocation = q;
$notifications.attr("data-notify", "loading");
xhr = $.getJSON('/control/geocode/?q=' + encodeURIComponent(q), function (res) {
if (!res.results || !res.results.length) {
$notifications.attr("data-notify", "error");
return;
}
lat = res.results[0].lat;
lon = res.results[0].lon;
if (touched) {
$notifications.attr("data-notify", "confirm");
}
else {
$notifications.attr("data-notify", "");
$lat.val(lat);
$lon.val(lon);
center(13);
}
})
}
$sec.find(".col-md-1").html("");
})
}
$sec.find("input[name$=geo_lat], input[name$=geo_lon]").change(function () {
touched = $sec.find("input[name$=geo_lat]").val() !== "";
center(13);
}).keyup(function () {
if (timer2) {
window.clearTimeout(timer2);
}
timer2 = window.setTimeout(center, 300);
});
$lat.add($lon).change(function () {
if (this.value !== "") touched = true;
center(13);
}).keyup(function () {
window.clearTimeout(debounceLatLonChange);
debounceLatLonChange = window.setTimeout(center, 300);
});
$inp.change(load);
$inp.keyup(function () {
if (timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(load, 1000);
});
$location.change(load);
$location.keyup(function () {
window.clearTimeout(debounceLoad);
debounceLoad = window.setTimeout(load, 1000);
if ($notifications.attr("data-notify") == "confirm" && lastLocation !== cleanup(this.value)) $notifications.attr("data-notify", "");
});
// Map
var $grp = $sec.find(".geodata-group");
var tiles = $grp.attr("data-tiles");
var attrib = $grp.attr("data-attrib");
if (tiles) {
var $map = $("<div>");
$grp.append($("<div>").addClass("col-md-9 col-md-offset-3").append($map));
var map = L.map($map.get(0));
L.tileLayer(tiles, {
attribution: attrib,
maxZoom: 18,
}).addTo(map);
var $lat = $sec.find("input[name$=geo_lat]");
var $lon = $sec.find("input[name$=geo_lon]");
$updateButton.click(function() {
$lat.val(lat);
$lon.val(lon).trigger("change");// change-event is needed by bulk-edit
touched = false;
center(13);
$notifications.attr("data-notify", "updated");
delayUpdateDismissal = window.setTimeout(function() {
if ($notifications.attr("data-notify") == "updated") $notifications.attr("data-notify", "");
}, 2500);
});
function getpoint() {
if ($lat.val() !== "" && $lon.val() !== "") {
var p = [parseFloat($lat.val().replace(",", ".")), parseFloat($lon.val().replace(",", "."))];
// Clip to valid ranges. Very invalid lon/lat values can even lead to browser crashes in leaflet apparently
if (p[0] < -90) p[0] = -90
if (p[0] > 90) p[0] = 90
if (p[1] < -180) p[1] = -180
if (p[1] > 180) p[1] = 180
return p
// Map
var $grp = $(".geodata-group", this);
var tiles = $grp.attr("data-tiles");
var attrib = $grp.attr("data-attrib");
if (tiles) {
var $map = $("<div>");
$grp.append($("<div>").addClass("col-md-9 col-md-offset-3").append($map));
var map = L.map($map.get(0));
L.tileLayer(tiles, {
attribution: attrib,
maxZoom: 18,
}).addTo(map);
function getpoint() {
if ($lat.val() !== "" && $lon.val() !== "") {
var p = [parseFloat($lat.val().replace(",", ".")), parseFloat($lon.val().replace(",", "."))];
// Clip to valid ranges. Very invalid lon/lat values can even lead to browser crashes in leaflet apparently
if (p[0] < -90) p[0] = -90
if (p[0] > 90) p[0] = 90
if (p[1] < -180) p[1] = -180
if (p[1] > 180) p[1] = 180
return p
} else {
return [0.0, 0.0];
}
}
var marker = L.marker(getpoint(), {
draggable: 'true',
icon: L.icon({
iconUrl: $grp.attr("data-icon"),
shadowUrl: $grp.attr("data-shadow"),
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
tooltipAnchor: [16, -28],
shadowSize: [41, 41]
})
});
marker.addTo(map);
marker.on("dragend", function (event) {
var position = marker.getLatLng();
marker.setLatLng(position, {
draggable: 'true'
}).bindPopup(position).update();
$lat.val(position.lat.toFixed(7));
$lon.val(position.lng.toFixed(7));
touched = true;
center(null);
});
function center(zoom) {
if ($lat.val() !== "" && $lon.val() !== "") {
if (zoom) {
map.setView(getpoint(), zoom);
} else {
map.panTo(getpoint());
}
marker.setLatLng(getpoint(), {
draggable: 'true'
}).bindPopup(getpoint()).update();
} else {
map.fitWorld();
}
}
center(13);
} else {
return [0.0, 0.0];
function center(zoom) {
}
}
}
var marker = L.marker(getpoint(), {
draggable: 'true',
icon: L.icon({
iconUrl: $grp.attr("data-icon"),
shadowUrl: $grp.attr("data-shadow"),
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
tooltipAnchor: [16, -28],
shadowSize: [41, 41]
})
});
marker.addTo(map);
marker.on("dragend", function (event) {
var position = marker.getLatLng();
marker.setLatLng(position, {
draggable: 'true'
}).bindPopup(position).update();
$lat.val(position.lat.toFixed(7));
$lon.val(position.lng.toFixed(7));
center(null);
});
function center(zoom) {
if ($lat.val() !== "" && $lon.val() !== "") {
if (zoom) {
map.setView(getpoint(), zoom);
} else {
map.panTo(getpoint());
}
marker.setLatLng(getpoint(), {
draggable: 'true'
}).bindPopup(getpoint()).update();
} else {
map.fitWorld();
}
}
center(13);
} else {
function center(zoom) {
}
}
});
});
});

View File

@@ -241,6 +241,23 @@ div.mail-preview {
input[type=number].short {
width: 80px !important;
}
.geodata-autoupdate {
display: block;
}
.geodata-autoupdate .btn-link {
padding: 0;
}
.geodata-autoupdate [data-notification] {
display: none;
}
[data-notify=error] [data-notification=error],
[data-notify=loading] [data-notification=loading],
[data-notify=updated] [data-notification=updated],
[data-notify=confirm] [data-notification=confirm] {
display: block;
}
.geodata-group {
.form-group {
margin: 0 0 10px 0;