diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index 929c6009..7935eaeb 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -233,18 +233,35 @@ L.U.Map.include({ // Creation mode if (!this.options.umap_id) { - this.isDirty = true + if (!this.options.preview) { + this.isDirty = true + this.enableEdit() + } this._default_extent = true this.options.name = L._('Untitled map') - this.options.editMode = 'advanced' - this.enableEdit() + let style = L.Util.queryString('style', null) + if (style) { + style = decodeURIComponent(style) + try { + style = JSON.parse(style) + L.Util.setOptions(this, style) + } catch (error) { + console.error(error) + } + } + let data = L.Util.queryString('data', null) let dataUrl = L.Util.queryString('dataUrl', null) const dataFormat = L.Util.queryString('dataFormat', 'geojson') if (dataUrl) { dataUrl = decodeURIComponent(dataUrl) dataUrl = this.localizeUrl(dataUrl) dataUrl = this.proxyUrl(dataUrl) + const datalayer = this.createDataLayer() datalayer.importFromUrl(dataUrl, dataFormat) + } else if (data) { + data = decodeURIComponent(data) + const datalayer = this.createDataLayer() + datalayer.importRaw(data, dataFormat) } } @@ -290,7 +307,7 @@ L.U.Map.include({ } }) - window.onbeforeunload = () => this.isDirty || null + window.onbeforeunload = () => this.editEnabled && this.isDirty || null this.backup() this.initContextMenu() this.on('click contextmenu.show', this.closeInplaceToolbar) diff --git a/umap/tests/integration/test_map_preview.py b/umap/tests/integration/test_map_preview.py new file mode 100644 index 00000000..9b4ba0d3 --- /dev/null +++ b/umap/tests/integration/test_map_preview.py @@ -0,0 +1,78 @@ +import json +from urllib.parse import quote + +import pytest +from playwright.sync_api import expect + +pytestmark = pytest.mark.django_db + +GEOJSON = { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "name": "Niagara Falls", + }, + "geometry": { + "type": "Point", + "coordinates": [-79.04, 43.08], + }, + } + ], +} +CSV = "name,latitude,longitude\nNiagara Falls,43.08,-79.04" + + +def test_map_preview(page, live_server, tilelayer): + page.goto(f"{live_server.url}/map/") + # Edit mode is not enabled + edit_button = page.get_by_role("button", name="Edit") + expect(edit_button).to_be_visible() + + +def test_map_preview_can_load_remote_geojson(page, live_server, tilelayer): + def handle(route): + route.fulfill(json=GEOJSON) + + # Intercept the route to the proxy + page.route("*/**/ajax-proxy/**", handle) + + page.goto(f"{live_server.url}/map/?dataUrl=http://some.org/geo.json") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(1) + + +def test_map_preview_can_load_remote_csv(page, live_server, tilelayer): + def handle(route): + csv = """name,latitude,longitude\nNiagara Falls,43.08,-79.04""" + route.fulfill(body=csv) + + # Intercept the route to the proxy + page.route("*/**/ajax-proxy/**", handle) + + page.goto(f"{live_server.url}/map/?dataUrl=http://some.org/geo.csv&dataFormat=csv") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(1) + + +def test_map_preview_can_load_geojson_in_querystring(page, live_server, tilelayer): + page.goto(f"{live_server.url}/map/?data={quote(json.dumps(GEOJSON))}") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(1) + + +def test_map_preview_can_load_csv_in_querystring(page, live_server, tilelayer): + page.goto(f"{live_server.url}/map/?data={quote(CSV)}&dataFormat=csv") + markers = page.locator(".leaflet-marker-icon") + expect(markers).to_have_count(1) + + +def test_map_preview_can_change_styling_from_querystring(page, live_server, tilelayer): + style = {"color": "DarkRed"} + page.goto( + f"{live_server.url}/map/?data={quote(json.dumps(GEOJSON))}&style={quote(json.dumps(style))}" + ) + markers = page.locator(".leaflet-marker-icon .icon_container") + expect(markers).to_have_count(1) + expect(markers).to_have_css("background-color", "rgb(139, 0, 0)") diff --git a/umap/urls.py b/umap/urls.py index 422c2e78..a62c1efc 100644 --- a/umap/urls.py +++ b/umap/urls.py @@ -96,6 +96,7 @@ i18n_urls += decorated_patterns( ) i18n_urls += decorated_patterns( [ensure_csrf_cookie], + re_path(r"^map/$", views.MapPreview.as_view(), name="map_preview"), re_path(r"^map/new/$", views.MapNew.as_view(), name="map_new"), ) i18n_urls += decorated_patterns( diff --git a/umap/views.py b/umap/views.py index f4484269..0d670058 100644 --- a/umap/views.py +++ b/umap/views.py @@ -455,8 +455,7 @@ class MapDetailMixin: if domain and "{" not in domain: context["preconnect_domains"] = [f"//{domain}"] - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) + def get_map_properties(self): user = self.request.user properties = { "urls": _urls_for_js(), @@ -486,6 +485,17 @@ class MapDetailMixin: if self.get_short_url(): properties["shortUrl"] = self.get_short_url() + if not user.is_anonymous: + properties["user"] = { + "id": user.pk, + "name": str(user), + "url": reverse("user_dashboard"), + } + return properties + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + properties = self.get_map_properties() if settings.USE_I18N: lang = settings.LANGUAGE_CODE # Check attr in case the middleware is not active @@ -495,19 +505,13 @@ class MapDetailMixin: locale = to_locale(lang) properties["locale"] = locale context["locale"] = locale - if not user.is_anonymous: - properties["user"] = { - "id": user.pk, - "name": str(user), - "url": reverse("user_dashboard"), - } - map_settings = self.get_geojson() - if "properties" not in map_settings: - map_settings["properties"] = {} - map_settings["properties"].update(properties) - map_settings["properties"]["datalayers"] = self.get_datalayers() - context["map_settings"] = json.dumps(map_settings, indent=settings.DEBUG) - self.set_preconnect(map_settings["properties"], context) + geojson = self.get_geojson() + if "properties" not in geojson: + geojson["properties"] = {} + geojson["properties"].update(properties) + geojson["properties"]["datalayers"] = self.get_datalayers() + context["map_settings"] = json.dumps(geojson, indent=settings.DEBUG) + self.set_preconnect(geojson["properties"], context) return context def get_datalayers(self): @@ -709,6 +713,15 @@ class MapNew(MapDetailMixin, TemplateView): template_name = "umap/map_detail.html" +class MapPreview(MapDetailMixin, TemplateView): + template_name = "umap/map_detail.html" + + def get_map_properties(self): + properties = super().get_map_properties() + properties["preview"] = True + return properties + + class MapCreate(FormLessEditMixin, PermissionsMixin, CreateView): model = Map form_class = MapSettingsForm