umap-project 3.4.0b3__py3-none-any.whl → 3.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- umap/__init__.py +1 -1
- umap/locale/br/LC_MESSAGES/django.mo +0 -0
- umap/locale/br/LC_MESSAGES/django.po +71 -57
- umap/locale/da/LC_MESSAGES/django.mo +0 -0
- umap/locale/da/LC_MESSAGES/django.po +18 -14
- umap/locale/de/LC_MESSAGES/django.mo +0 -0
- umap/locale/de/LC_MESSAGES/django.po +20 -16
- umap/locale/en/LC_MESSAGES/django.po +18 -14
- umap/locale/es/LC_MESSAGES/django.mo +0 -0
- umap/locale/es/LC_MESSAGES/django.po +20 -16
- umap/locale/fr/LC_MESSAGES/django.mo +0 -0
- umap/locale/fr/LC_MESSAGES/django.po +18 -14
- umap/locale/hu/LC_MESSAGES/django.mo +0 -0
- umap/locale/hu/LC_MESSAGES/django.po +20 -16
- umap/locale/pl/LC_MESSAGES/django.mo +0 -0
- umap/locale/pl/LC_MESSAGES/django.po +101 -95
- umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
- umap/locale/zh_TW/LC_MESSAGES/django.po +20 -16
- umap/management/commands/clean_tilelayer.py +0 -1
- umap/management/commands/search_maps.py +95 -0
- umap/settings/__init__.py +9 -1
- umap/settings/base.py +7 -6
- umap/static/umap/content.css +0 -3
- umap/static/umap/css/bar.css +9 -6
- umap/static/umap/css/form.css +25 -9
- umap/static/umap/css/icon.css +8 -0
- umap/static/umap/css/popup.css +1 -0
- umap/static/umap/img/16-white.svg +5 -2
- umap/static/umap/img/16.svg +1 -1
- umap/static/umap/img/source/16-white.svg +7 -4
- umap/static/umap/img/source/16.svg +1 -1
- umap/static/umap/js/components/copiable.js +47 -0
- umap/static/umap/js/modules/autocomplete.js +32 -67
- umap/static/umap/js/modules/browser.js +31 -14
- umap/static/umap/js/modules/data/features.js +34 -36
- umap/static/umap/js/modules/data/fields.js +199 -23
- umap/static/umap/js/modules/data/layer.js +85 -96
- umap/static/umap/js/modules/domutils.js +25 -1
- umap/static/umap/js/modules/filters.js +24 -50
- umap/static/umap/js/modules/form/builder.js +17 -16
- umap/static/umap/js/modules/form/fields.js +20 -20
- umap/static/umap/js/modules/formatter.js +9 -1
- umap/static/umap/js/modules/help.js +12 -13
- umap/static/umap/js/modules/importer.js +17 -26
- umap/static/umap/js/modules/importers/banfr.js +0 -1
- umap/static/umap/js/modules/importers/cadastrefr.js +19 -19
- umap/static/umap/js/modules/importers/communesfr.js +7 -8
- umap/static/umap/js/modules/importers/datasets.js +14 -14
- umap/static/umap/js/modules/importers/geodatamine.js +20 -22
- umap/static/umap/js/modules/importers/opendata.js +10 -0
- umap/static/umap/js/modules/importers/overpass.js +19 -18
- umap/static/umap/js/modules/managers.js +1 -1
- umap/static/umap/js/modules/permissions.js +15 -5
- umap/static/umap/js/modules/rendering/controls.js +203 -10
- umap/static/umap/js/modules/rendering/icon.js +5 -9
- umap/static/umap/js/modules/rendering/layers/base.js +1 -1
- umap/static/umap/js/modules/rendering/layers/classified.js +16 -11
- umap/static/umap/js/modules/rendering/layers/heat.js +1 -0
- umap/static/umap/js/modules/rendering/map.js +67 -57
- umap/static/umap/js/modules/rendering/popup.js +6 -3
- umap/static/umap/js/modules/rendering/template.js +40 -40
- umap/static/umap/js/modules/rendering/ui.js +1 -2
- umap/static/umap/js/modules/rules.js +34 -41
- umap/static/umap/js/modules/schema.js +0 -7
- umap/static/umap/js/modules/share.js +36 -69
- umap/static/umap/js/modules/slideshow.js +3 -3
- umap/static/umap/js/modules/tableeditor.js +0 -1
- umap/static/umap/js/modules/ui/bar.js +53 -33
- umap/static/umap/js/modules/ui/hash.js +36 -0
- umap/static/umap/js/modules/ui/loader.js +26 -0
- umap/static/umap/js/modules/ui/panel.js +33 -21
- umap/static/umap/js/modules/ui/tooltip.js +1 -1
- umap/static/umap/js/modules/umap.js +81 -80
- umap/static/umap/js/modules/utils.js +13 -3
- umap/static/umap/js/umap.controls.js +16 -179
- umap/static/umap/locale/am_ET.js +7 -8
- umap/static/umap/locale/am_ET.json +7 -8
- umap/static/umap/locale/ar.js +7 -8
- umap/static/umap/locale/ar.json +7 -8
- umap/static/umap/locale/ast.js +7 -8
- umap/static/umap/locale/ast.json +7 -8
- umap/static/umap/locale/bg.js +7 -8
- umap/static/umap/locale/bg.json +7 -8
- umap/static/umap/locale/br.js +44 -36
- umap/static/umap/locale/br.json +44 -36
- umap/static/umap/locale/ca.js +7 -8
- umap/static/umap/locale/ca.json +7 -8
- umap/static/umap/locale/cs_CZ.js +7 -8
- umap/static/umap/locale/cs_CZ.json +7 -8
- umap/static/umap/locale/da.js +8 -9
- umap/static/umap/locale/da.json +8 -9
- umap/static/umap/locale/de.js +62 -63
- umap/static/umap/locale/de.json +62 -63
- umap/static/umap/locale/el.js +7 -8
- umap/static/umap/locale/el.json +7 -8
- umap/static/umap/locale/en.js +7 -8
- umap/static/umap/locale/en.json +7 -8
- umap/static/umap/locale/en_US.json +7 -8
- umap/static/umap/locale/es.js +19 -20
- umap/static/umap/locale/es.json +19 -20
- umap/static/umap/locale/et.js +7 -8
- umap/static/umap/locale/et.json +7 -8
- umap/static/umap/locale/eu.js +23 -24
- umap/static/umap/locale/eu.json +23 -24
- umap/static/umap/locale/fa_IR.js +7 -8
- umap/static/umap/locale/fa_IR.json +7 -8
- umap/static/umap/locale/fi.js +7 -8
- umap/static/umap/locale/fi.json +7 -8
- umap/static/umap/locale/fr.js +11 -12
- umap/static/umap/locale/fr.json +11 -12
- umap/static/umap/locale/gl.js +147 -148
- umap/static/umap/locale/gl.json +147 -148
- umap/static/umap/locale/he.js +7 -8
- umap/static/umap/locale/he.json +7 -8
- umap/static/umap/locale/hr.js +7 -8
- umap/static/umap/locale/hr.json +7 -8
- umap/static/umap/locale/hu.js +8 -9
- umap/static/umap/locale/hu.json +8 -9
- umap/static/umap/locale/id.js +7 -8
- umap/static/umap/locale/id.json +7 -8
- umap/static/umap/locale/is.js +7 -8
- umap/static/umap/locale/is.json +7 -8
- umap/static/umap/locale/it.js +7 -8
- umap/static/umap/locale/it.json +7 -8
- umap/static/umap/locale/ja.js +7 -8
- umap/static/umap/locale/ja.json +7 -8
- umap/static/umap/locale/ko.js +7 -8
- umap/static/umap/locale/ko.json +7 -8
- umap/static/umap/locale/lt.js +7 -8
- umap/static/umap/locale/lt.json +7 -8
- umap/static/umap/locale/ms.js +7 -8
- umap/static/umap/locale/ms.json +7 -8
- umap/static/umap/locale/nl.js +7 -8
- umap/static/umap/locale/nl.json +7 -8
- umap/static/umap/locale/no.js +7 -8
- umap/static/umap/locale/no.json +7 -8
- umap/static/umap/locale/pl.js +53 -54
- umap/static/umap/locale/pl.json +53 -54
- umap/static/umap/locale/pl_PL.json +7 -8
- umap/static/umap/locale/pt.js +7 -8
- umap/static/umap/locale/pt.json +7 -8
- umap/static/umap/locale/pt_BR.js +7 -8
- umap/static/umap/locale/pt_BR.json +7 -8
- umap/static/umap/locale/pt_PT.js +7 -8
- umap/static/umap/locale/pt_PT.json +7 -8
- umap/static/umap/locale/ro.js +7 -8
- umap/static/umap/locale/ro.json +7 -8
- umap/static/umap/locale/ru.js +7 -8
- umap/static/umap/locale/ru.json +7 -8
- umap/static/umap/locale/sk_SK.js +7 -8
- umap/static/umap/locale/sk_SK.json +7 -8
- umap/static/umap/locale/sl.js +7 -8
- umap/static/umap/locale/sl.json +7 -8
- umap/static/umap/locale/sr.js +7 -8
- umap/static/umap/locale/sr.json +7 -8
- umap/static/umap/locale/sv.js +7 -8
- umap/static/umap/locale/sv.json +7 -8
- umap/static/umap/locale/th_TH.js +7 -8
- umap/static/umap/locale/th_TH.json +7 -8
- umap/static/umap/locale/tr.js +7 -8
- umap/static/umap/locale/tr.json +7 -8
- umap/static/umap/locale/uk_UA.js +7 -8
- umap/static/umap/locale/uk_UA.json +7 -8
- umap/static/umap/locale/vi.js +7 -8
- umap/static/umap/locale/vi.json +7 -8
- umap/static/umap/locale/vi_VN.json +7 -8
- umap/static/umap/locale/zh.js +7 -8
- umap/static/umap/locale/zh.json +7 -8
- umap/static/umap/locale/zh_CN.json +7 -8
- umap/static/umap/locale/zh_TW.Big5.json +7 -8
- umap/static/umap/locale/zh_TW.js +20 -21
- umap/static/umap/locale/zh_TW.json +20 -21
- umap/static/umap/map.css +6 -21
- umap/static/umap/unittests/utils.js +7 -7
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.esm.js +942 -0
- umap/static/umap/vendors/photon/leaflet.photon.esm.js +472 -0
- umap/sync/app.py +4 -1
- umap/templates/umap/content_footer.html +1 -0
- umap/templates/umap/css.html +0 -4
- umap/templates/umap/js.html +1 -8
- umap/templates/umap/team_form.html +2 -1
- umap/tests/integration/conftest.py +3 -2
- umap/tests/integration/test_anonymous_owned_map.py +1 -1
- umap/tests/integration/test_conditional_rules.py +106 -51
- umap/tests/integration/test_draw_polygon.py +4 -0
- umap/tests/integration/test_draw_polyline.py +11 -0
- umap/tests/integration/test_edit_datalayer.py +1 -1
- umap/tests/integration/test_edit_map.py +2 -0
- umap/tests/integration/test_fields.py +19 -0
- umap/tests/integration/test_filters.py +24 -0
- umap/tests/integration/test_iframe.py +1 -1
- umap/tests/integration/test_import.py +26 -0
- umap/tests/integration/test_map.py +3 -3
- umap/tests/integration/test_optimistic_merge.py +7 -1
- umap/tests/integration/test_owned_map.py +2 -2
- umap/tests/integration/test_popup.py +31 -0
- umap/tests/integration/test_remote_data.py +5 -5
- umap/tests/integration/test_search.py +41 -0
- umap/tests/integration/test_share.py +2 -2
- umap/tests/integration/test_team.py +1 -1
- umap/tests/integration/test_websocket_sync.py +6 -1
- umap/tests/test_search_maps_command.py +44 -0
- umap/tests/test_utils.py +4 -1
- umap/utils.py +10 -3
- umap/views.py +17 -4
- {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/METADATA +29 -23
- {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/RECORD +210 -214
- {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/WHEEL +1 -1
- umap/static/umap/js/umap.core.js +0 -93
- umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.css +0 -46
- umap/static/umap/vendors/editinosm/Leaflet.EditInOSM.js +0 -240
- umap/static/umap/vendors/editinosm/edit-in-osm.png +0 -0
- umap/static/umap/vendors/hash/leaflet-hash.js +0 -162
- umap/static/umap/vendors/loading/Control.Loading.css +0 -26
- umap/static/umap/vendors/loading/Control.Loading.js +0 -351
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.css +0 -1
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.css.map +0 -1
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js +0 -4
- umap/static/umap/vendors/locatecontrol/L.Control.Locate.min.js.map +0 -1
- umap/static/umap/vendors/photon/leaflet.photon.js +0 -487
- {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/entry_points.txt +0 -0
- {umap_project-3.4.0b3.dist-info → umap_project-3.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -147,16 +147,16 @@ def test_default_view_latest_with_polygon(map, live_server, page):
|
|
|
147
147
|
expect(layers).to_have_count(1)
|
|
148
148
|
|
|
149
149
|
|
|
150
|
-
def test_default_view_locate(browser, live_server, map):
|
|
150
|
+
def test_default_view_locate(browser, live_server, map, new_page):
|
|
151
151
|
context = browser.new_context(
|
|
152
152
|
geolocation={"longitude": 8.52967, "latitude": 39.16267},
|
|
153
153
|
permissions=["geolocation"],
|
|
154
154
|
)
|
|
155
155
|
map.settings["properties"]["defaultView"] = "locate"
|
|
156
156
|
map.save()
|
|
157
|
-
page =
|
|
157
|
+
page = new_page(custom_context=context)
|
|
158
158
|
page.goto(f"{live_server.url}{map.get_absolute_url()}")
|
|
159
|
-
expect(page).to_have_url(re.compile(r".*#18/39\.
|
|
159
|
+
expect(page).to_have_url(re.compile(r".*#18/39\.162670/8\.529670"))
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
def test_remote_layer_should_not_be_used_as_datalayer_for_created_features(
|
|
@@ -3,6 +3,7 @@ import re
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from time import sleep
|
|
5
5
|
|
|
6
|
+
import pytest
|
|
6
7
|
from playwright.sync_api import expect
|
|
7
8
|
|
|
8
9
|
from umap.models import DataLayer
|
|
@@ -12,7 +13,12 @@ from ..base import DataLayerFactory, MapFactory
|
|
|
12
13
|
DATALAYER_UPDATE = re.compile(r".*/datalayer/update/.*")
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
# Test with in memory upload AND streamed upload
|
|
17
|
+
@pytest.mark.parametrize("upload_limit", (2621440, 0))
|
|
18
|
+
def test_created_markers_are_merged(
|
|
19
|
+
new_page, live_server, tilelayer, settings, upload_limit
|
|
20
|
+
):
|
|
21
|
+
settings.FILE_UPLOAD_MAX_MEMORY_SIZE = upload_limit
|
|
16
22
|
# Let's create a new map with an empty datalayer
|
|
17
23
|
map = MapFactory(name="server-side merge")
|
|
18
24
|
datalayer = DataLayerFactory(map=map, edit_status=DataLayer.ANONYMOUS, data={})
|
|
@@ -183,7 +183,7 @@ def test_can_change_perms_after_create(tilelayer, live_server, login, user):
|
|
|
183
183
|
page.goto(f"{live_server.url}/en/map/new")
|
|
184
184
|
# Create a layer
|
|
185
185
|
page.get_by_title("Manage layers").click()
|
|
186
|
-
page.
|
|
186
|
+
page.get_by_role("button", name="Add a layer").click()
|
|
187
187
|
page.locator("input[name=name]").fill("Layer 1")
|
|
188
188
|
expect(
|
|
189
189
|
page.get_by_role("button", name="Visibility: Draft (private)")
|
|
@@ -217,7 +217,7 @@ def test_can_change_owner(map, live_server, login, user):
|
|
|
217
217
|
page.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
|
|
218
218
|
edit_permissions = page.get_by_title("Update permissions and editors")
|
|
219
219
|
edit_permissions.click()
|
|
220
|
-
close = page.locator(".umap-field-owner .close")
|
|
220
|
+
close = page.locator(".umap-field-owner .icon-close")
|
|
221
221
|
close.click()
|
|
222
222
|
input = page.locator("input.edit-owner")
|
|
223
223
|
with page.expect_response(re.compile(r".*/agnocomplete/.*")):
|
|
@@ -42,3 +42,34 @@ def test_openstreetmap_popup(live_server, map, page):
|
|
|
42
42
|
"src",
|
|
43
43
|
"https://api.panoramax.xyz/api/pictures/d811b398-d930-4cf8-95a2-0c29c34d9fca/sd.jpg",
|
|
44
44
|
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_table_popup(live_server, map, page):
|
|
48
|
+
data = {
|
|
49
|
+
"type": "FeatureCollection",
|
|
50
|
+
"features": [
|
|
51
|
+
{
|
|
52
|
+
"type": "Feature",
|
|
53
|
+
"geometry": {"type": "Point", "coordinates": [2.49, 48.79]},
|
|
54
|
+
"properties": {
|
|
55
|
+
"url": "https://restaurant.org",
|
|
56
|
+
"formatted": "with **bold**",
|
|
57
|
+
},
|
|
58
|
+
"id": "AzMjk",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
"_umap_options": {
|
|
62
|
+
"popupTemplate": "Table",
|
|
63
|
+
"fields": [
|
|
64
|
+
{"key": "url", "type": "String"},
|
|
65
|
+
{"key": "formatted", "type": "Text"},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
DataLayerFactory(map=map, data=data)
|
|
70
|
+
page.goto(f"{live_server.url}{map.get_absolute_url()}#18/48.79/2.49")
|
|
71
|
+
page.locator(".leaflet-marker-icon").click()
|
|
72
|
+
expect(page.get_by_role("link", name="https://restaurant.org")).to_be_visible()
|
|
73
|
+
expect(page.get_by_role("cell", name="with bold").locator("strong")).to_have_text(
|
|
74
|
+
"bold"
|
|
75
|
+
)
|
|
@@ -16,7 +16,7 @@ def intercept_remote_data(page):
|
|
|
16
16
|
"features": [
|
|
17
17
|
{
|
|
18
18
|
"type": "Feature",
|
|
19
|
-
"properties": {"name": "Point 2"},
|
|
19
|
+
"properties": {"name": "Point 2", "foobar": "bla"},
|
|
20
20
|
"geometry": {
|
|
21
21
|
"type": "Point",
|
|
22
22
|
"coordinates": [4.3375, 11.2707],
|
|
@@ -29,7 +29,7 @@ def intercept_remote_data(page):
|
|
|
29
29
|
"features": [
|
|
30
30
|
{
|
|
31
31
|
"type": "Feature",
|
|
32
|
-
"properties": {"name": "Point 1"},
|
|
32
|
+
"properties": {"name": "Point 1", "foobar": "baz"},
|
|
33
33
|
"geometry": {
|
|
34
34
|
"type": "Point",
|
|
35
35
|
"coordinates": [4.3375, 12.2707],
|
|
@@ -44,7 +44,6 @@ def intercept_remote_data(page):
|
|
|
44
44
|
|
|
45
45
|
# Intercept the route to the proxy
|
|
46
46
|
page.route("https://remote.org/data.json", handle)
|
|
47
|
-
page.on("request", lambda *a, **k: print(a, k))
|
|
48
47
|
|
|
49
48
|
|
|
50
49
|
def test_dynamic_remote_data(page, live_server, tilelayer, map):
|
|
@@ -104,6 +103,7 @@ def test_create_remote_data_layer(page, live_server, tilelayer, settings):
|
|
|
104
103
|
expect(page.locator(".leaflet-marker-icon")).to_have_count(1)
|
|
105
104
|
with page.expect_response(re.compile(".*/datalayer/create/.*")):
|
|
106
105
|
page.get_by_role("button", name="Save draft", exact=True).click()
|
|
106
|
+
assert DataLayer.objects.count() == 1
|
|
107
107
|
datalayer = DataLayer.objects.last()
|
|
108
108
|
data = json.loads(Path(datalayer.geojson.path).read_text())
|
|
109
109
|
assert data == {
|
|
@@ -117,8 +117,8 @@ def test_create_remote_data_layer(page, live_server, tilelayer, settings):
|
|
|
117
117
|
"type": "String",
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
|
-
"key": "
|
|
121
|
-
"type": "
|
|
120
|
+
"key": "foobar",
|
|
121
|
+
"type": "String",
|
|
122
122
|
},
|
|
123
123
|
],
|
|
124
124
|
"id": str(datalayer.pk),
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from playwright.sync_api import expect
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_reverse_search(live_server, page, tilelayer):
|
|
5
|
+
photon_response = {
|
|
6
|
+
"type": "FeatureCollection",
|
|
7
|
+
"features": [
|
|
8
|
+
{
|
|
9
|
+
"type": "Feature",
|
|
10
|
+
"properties": {
|
|
11
|
+
"osm_type": "N",
|
|
12
|
+
"osm_id": 5055853416,
|
|
13
|
+
"osm_key": "place",
|
|
14
|
+
"osm_value": "locality",
|
|
15
|
+
"type": "locality",
|
|
16
|
+
"postcode": "10200",
|
|
17
|
+
"countrycode": "FR",
|
|
18
|
+
"name": "Le Haut Sentier",
|
|
19
|
+
"country": "France",
|
|
20
|
+
"city": "Thors",
|
|
21
|
+
"state": "Grand Est",
|
|
22
|
+
"county": "Aube",
|
|
23
|
+
},
|
|
24
|
+
"geometry": {"type": "Point", "coordinates": [4.7995256, 48.2985251]},
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
}
|
|
28
|
+
page.goto(f"{live_server.url}/en/map/new")
|
|
29
|
+
|
|
30
|
+
def handle_search(route):
|
|
31
|
+
route.fulfill(json=photon_response)
|
|
32
|
+
|
|
33
|
+
# Intercept the route
|
|
34
|
+
page.route(
|
|
35
|
+
"https://photon.komoot.io/reverse/?limit=1&lat=48.3&lon=4.8",
|
|
36
|
+
handle_search,
|
|
37
|
+
)
|
|
38
|
+
page.get_by_role("button", name="Search location").click()
|
|
39
|
+
page.get_by_role("searchbox", name="Type a place name or").fill("48.3 4.8")
|
|
40
|
+
expect(page.get_by_text("48.3 4.8")).to_be_visible()
|
|
41
|
+
expect(page.get_by_text("Le Haut Sentier")).to_be_visible()
|
|
@@ -8,7 +8,7 @@ pytestmark = pytest.mark.django_db
|
|
|
8
8
|
|
|
9
9
|
def test_iframe_code_can_contain_datalayers(map, live_server, datalayer, page):
|
|
10
10
|
page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
|
|
11
|
-
textarea = page.
|
|
11
|
+
textarea = page.get_by_label("Iframe")
|
|
12
12
|
expect(textarea).to_be_visible()
|
|
13
13
|
expect(textarea).to_have_text(re.compile('src="'))
|
|
14
14
|
expect(textarea).to_have_text(re.compile('href="'))
|
|
@@ -28,7 +28,7 @@ def test_iframe_code_can_contain_datalayers(map, live_server, datalayer, page):
|
|
|
28
28
|
def test_iframe_code_can_contain_feature(map, live_server, datalayer, page):
|
|
29
29
|
page.goto(f"{live_server.url}{map.get_absolute_url()}?share")
|
|
30
30
|
page.locator(".icon-container").click()
|
|
31
|
-
textarea = page.
|
|
31
|
+
textarea = page.get_by_label("Iframe")
|
|
32
32
|
expect(textarea).to_be_visible()
|
|
33
33
|
expect(textarea).not_to_have_text(re.compile("feature=Here"))
|
|
34
34
|
# Open options
|
|
@@ -39,7 +39,7 @@ def test_can_remove_user_from_team(live_server, map, user, user2, team, login):
|
|
|
39
39
|
page.get_by_role("link", name="My teams").click()
|
|
40
40
|
with page.expect_navigation():
|
|
41
41
|
page.get_by_role("link", name="Edit").click()
|
|
42
|
-
page.locator("li").filter(has_text="Averell").locator(".close").click()
|
|
42
|
+
page.locator("li").filter(has_text="Averell").locator(".icon-close").click()
|
|
43
43
|
page.get_by_role("button", name="Save").click()
|
|
44
44
|
assert Team.objects.count() == 1
|
|
45
45
|
modified = Team.objects.first()
|
|
@@ -53,6 +53,7 @@ def test_websocket_connection_can_sync_markers(
|
|
|
53
53
|
|
|
54
54
|
a_map_el = peerA.locator("#map")
|
|
55
55
|
a_map_el.click(position={"x": 220, "y": 220})
|
|
56
|
+
peerA.wait_for_timeout(300) # Time for the panel animation to finish
|
|
56
57
|
expect(a_marker_pane).to_have_count(1)
|
|
57
58
|
expect(b_marker_pane).to_have_count(1)
|
|
58
59
|
# Peer B should not be in state dirty
|
|
@@ -330,6 +331,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
|
|
|
330
331
|
|
|
331
332
|
a_map_el = peerA.locator("#map")
|
|
332
333
|
a_map_el.click(position={"x": 220, "y": 220})
|
|
334
|
+
peerA.wait_for_timeout(300) # Time for the panel animation to finish
|
|
333
335
|
peerA.locator("body").type("First marker")
|
|
334
336
|
peerA.locator("body").press("Escape")
|
|
335
337
|
peerA.wait_for_timeout(300)
|
|
@@ -555,8 +557,10 @@ def test_create_and_sync_map(
|
|
|
555
557
|
|
|
556
558
|
# Add a marker from peer A
|
|
557
559
|
peerA.get_by_role("button", name="Edit").click()
|
|
560
|
+
peerA.wait_for_timeout(300) # Time for the animation to finish
|
|
558
561
|
peerA.get_by_title("Draw a marker").click()
|
|
559
562
|
peerA.locator("#map").click(position={"x": 220, "y": 220})
|
|
563
|
+
peerA.wait_for_timeout(300) # Time for the panel animation to finish
|
|
560
564
|
expect(markersA).to_have_count(1)
|
|
561
565
|
expect(markersB).to_have_count(1)
|
|
562
566
|
|
|
@@ -582,6 +586,7 @@ def test_create_and_sync_map(
|
|
|
582
586
|
# Add a marker from peer B
|
|
583
587
|
peerB.get_by_title("Draw a marker").click()
|
|
584
588
|
peerB.locator("#map").click(position={"x": 200, "y": 200})
|
|
589
|
+
peerA.wait_for_timeout(300) # Time for the panel animation to finish
|
|
585
590
|
expect(markersB).to_have_count(2)
|
|
586
591
|
expect(markersA).to_have_count(1)
|
|
587
592
|
with peerB.expect_response(re.compile("./datalayer/update/.*")):
|
|
@@ -607,7 +612,7 @@ def test_saved_datalayer_are_not_duplicated(
|
|
|
607
612
|
wait_for_loaded(peerA)
|
|
608
613
|
# Create a new datalayer
|
|
609
614
|
peerA.get_by_title("Manage layers").click()
|
|
610
|
-
peerA.
|
|
615
|
+
peerA.get_by_role("button", name="Add a layer").click()
|
|
611
616
|
peerA.locator("#map").click(position={"x": 220, "y": 220})
|
|
612
617
|
# Save layer to the server, so now the datalayer exist on the server AND
|
|
613
618
|
# is still in the live operations of peer A
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from django.core.management import call_command
|
|
3
|
+
|
|
4
|
+
from umap.models import Map
|
|
5
|
+
|
|
6
|
+
from .base import MapFactory
|
|
7
|
+
|
|
8
|
+
pytestmark = pytest.mark.django_db
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_search_and_delete_maps(map, team):
|
|
12
|
+
target = MapFactory(name="find me")
|
|
13
|
+
assert Map.objects.filter(share_status=Map.DELETED).count() == 0
|
|
14
|
+
|
|
15
|
+
call_command("search_maps", "find", "--delete", "--dry-run", "--no-input")
|
|
16
|
+
assert Map.objects.filter(share_status=Map.DELETED).count() == 0
|
|
17
|
+
|
|
18
|
+
call_command("search_maps", "find", "--delete", "--no-input")
|
|
19
|
+
assert Map.objects.filter(share_status=Map.DELETED).count() == 1
|
|
20
|
+
|
|
21
|
+
assert not Map.public.filter(pk=target.pk)
|
|
22
|
+
|
|
23
|
+
call_command("search_maps", "find", "--restore", "--no-input")
|
|
24
|
+
assert Map.objects.filter(share_status=Map.DELETED).count() == 0
|
|
25
|
+
|
|
26
|
+
assert Map.objects.get(pk=target.pk).share_status == Map.DRAFT
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_search_and_block_maps(map, team):
|
|
30
|
+
target = MapFactory(name="find me")
|
|
31
|
+
assert Map.objects.filter(share_status=Map.BLOCKED).count() == 0
|
|
32
|
+
|
|
33
|
+
call_command("search_maps", "find", "--block", "--dry-run", "--no-input")
|
|
34
|
+
assert Map.objects.filter(share_status=Map.BLOCKED).count() == 0
|
|
35
|
+
|
|
36
|
+
call_command("search_maps", "find", "--block", "--no-input")
|
|
37
|
+
assert Map.objects.filter(share_status=Map.BLOCKED).count() == 1
|
|
38
|
+
|
|
39
|
+
assert not Map.public.filter(pk=target.pk)
|
|
40
|
+
|
|
41
|
+
call_command("search_maps", "find", "--restore", "--no-input")
|
|
42
|
+
assert Map.objects.filter(share_status=Map.BLOCKED).count() == 0
|
|
43
|
+
|
|
44
|
+
assert Map.objects.get(pk=target.pk).share_status == Map.DRAFT
|
umap/tests/test_utils.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import stat
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
@@ -5,7 +6,8 @@ import pytest
|
|
|
5
6
|
from umap.utils import gzip_file, normalize_string
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
def test_gzip_file():
|
|
9
|
+
def test_gzip_file(settings):
|
|
10
|
+
settings.FILE_UPLOAD_PERMISSIONS = 0o666
|
|
9
11
|
# Let's use any old file so we can check that the date of the gzip file is set.
|
|
10
12
|
src = Path(__file__).parent / "settings.py"
|
|
11
13
|
dest = Path("/tmp/test_settings.py.gz")
|
|
@@ -14,6 +16,7 @@ def test_gzip_file():
|
|
|
14
16
|
dest_stat = dest.stat()
|
|
15
17
|
dest.unlink()
|
|
16
18
|
assert src_stat.st_mtime == dest_stat.st_mtime
|
|
19
|
+
assert stat.filemode(dest_stat.st_mode) == "-rw-rw-rw-"
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
@pytest.mark.parametrize(
|
umap/utils.py
CHANGED
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
from django.conf import settings
|
|
8
8
|
from django.contrib.staticfiles import finders
|
|
9
9
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
10
|
-
from django.urls import URLPattern, URLResolver, get_resolver
|
|
10
|
+
from django.urls import URLPattern, URLResolver, get_resolver, reverse
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def _get_url_names(module):
|
|
@@ -29,14 +29,20 @@ def _urls_for_js():
|
|
|
29
29
|
"""
|
|
30
30
|
Return templated URLs prepared for javascript.
|
|
31
31
|
"""
|
|
32
|
-
urls = {
|
|
32
|
+
urls = {
|
|
33
|
+
"agnocomplete": f"{reverse('agnocomplete:agnocomplete', kwargs={'klass': 'AutocompleteUser'})}?q={{q}}"
|
|
34
|
+
}
|
|
33
35
|
modules = ["umap.urls"]
|
|
34
36
|
if settings.REALTIME_ENABLED:
|
|
35
37
|
modules.append("umap.sync.app")
|
|
36
38
|
for module in modules:
|
|
37
39
|
names = _get_url_names(module)
|
|
40
|
+
prefix = settings.FORCE_SCRIPT_NAME or ""
|
|
38
41
|
urls.update(
|
|
39
|
-
|
|
42
|
+
zip(
|
|
43
|
+
names,
|
|
44
|
+
[get_uri_template(url, prefix=prefix, module=module) for url in names],
|
|
45
|
+
)
|
|
40
46
|
)
|
|
41
47
|
urls.update(getattr(settings, "UMAP_EXTRA_URLS", {}))
|
|
42
48
|
return urls
|
|
@@ -150,6 +156,7 @@ def gzip_file(from_path, to_path):
|
|
|
150
156
|
with gzip.open(to_path, "wb") as f_out:
|
|
151
157
|
f_out.writelines(f_in)
|
|
152
158
|
os.utime(to_path, ns=(stat.st_mtime_ns, stat.st_mtime_ns))
|
|
159
|
+
os.chmod(to_path, settings.FILE_UPLOAD_PERMISSIONS)
|
|
153
160
|
|
|
154
161
|
|
|
155
162
|
def is_ajax(request):
|
umap/views.py
CHANGED
|
@@ -6,7 +6,6 @@ import socket
|
|
|
6
6
|
import zipfile
|
|
7
7
|
from datetime import datetime, timedelta
|
|
8
8
|
from http.client import InvalidURL
|
|
9
|
-
from io import BytesIO
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
from smtplib import SMTPException
|
|
12
11
|
from urllib.error import HTTPError, URLError
|
|
@@ -22,6 +21,7 @@ from django.contrib.postgres.search import SearchQuery, SearchVector
|
|
|
22
21
|
from django.contrib.sessions.models import Session
|
|
23
22
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
24
23
|
from django.core.exceptions import PermissionDenied
|
|
24
|
+
from django.core.files.uploadedfile import InMemoryUploadedFile
|
|
25
25
|
from django.core.mail import send_mail
|
|
26
26
|
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
|
27
27
|
from django.core.signing import BadSignature, Signer, TimestampSigner
|
|
@@ -610,7 +610,8 @@ class MapDetailMixin(SessionMixin):
|
|
|
610
610
|
|
|
611
611
|
def set_preconnect(self, properties, context):
|
|
612
612
|
# Try to extract the tilelayer domain, in order to but a preconnect meta.
|
|
613
|
-
|
|
613
|
+
tilelayer = properties.get("tilelayer") or {}
|
|
614
|
+
url_template = tilelayer.get("url_template")
|
|
614
615
|
# Not explicit tilelayer set, take the first of the list, which will be
|
|
615
616
|
# used by frontend too.
|
|
616
617
|
if not url_template:
|
|
@@ -1335,8 +1336,20 @@ class DataLayerUpdate(FormLessEditMixin, UpdateView):
|
|
|
1335
1336
|
return HttpResponse(status=412)
|
|
1336
1337
|
|
|
1337
1338
|
# Replace the uploaded file by the merged version.
|
|
1338
|
-
|
|
1339
|
-
|
|
1339
|
+
# The geojson here can be either a NamedTemporaryFile or an
|
|
1340
|
+
# InMemoryUploadedFile, depending on whether is bigger than the
|
|
1341
|
+
# FILE_UPLOAD_MAX_MEMORY_SIZE setting (2.5Mo by default).
|
|
1342
|
+
# Now that we loaded all in RAM, let's use an InMemoryUploadedFile.
|
|
1343
|
+
orig = self.request.FILES["geojson"]
|
|
1344
|
+
file = io.BytesIO(json_dumps(merged).encode("utf-8"))
|
|
1345
|
+
file_size = file.getbuffer().nbytes
|
|
1346
|
+
self.request.FILES["geojson"] = InMemoryUploadedFile(
|
|
1347
|
+
file=file,
|
|
1348
|
+
field_name="geojson",
|
|
1349
|
+
name=orig.name,
|
|
1350
|
+
content_type="application/geo+json",
|
|
1351
|
+
size=file_size,
|
|
1352
|
+
charset="utf-8",
|
|
1340
1353
|
)
|
|
1341
1354
|
|
|
1342
1355
|
# Mark the data to be reloaded by form_valid
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: umap-project
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
4
4
|
Summary: Create maps with OpenStreetMap layers in a minute and embed them in your site.
|
|
5
5
|
Author-email: Yohan Boniface <yb@enix.org>
|
|
6
6
|
Maintainer-email: David Larlet <david@larlet.fr>
|
|
@@ -18,43 +18,49 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
19
|
Requires-Dist: django-agnocomplete==2.2.0
|
|
20
20
|
Requires-Dist: django-environ==0.12.0
|
|
21
|
-
Requires-Dist: django-probes==1.
|
|
22
|
-
Requires-Dist: django
|
|
23
|
-
Requires-Dist: pillow==
|
|
24
|
-
Requires-Dist: psycopg==3.2
|
|
25
|
-
Requires-Dist: rcssmin==1.2.
|
|
21
|
+
Requires-Dist: django-probes==1.8.0
|
|
22
|
+
Requires-Dist: django<6.0,>=5.1
|
|
23
|
+
Requires-Dist: pillow==12.1.0
|
|
24
|
+
Requires-Dist: psycopg==3.3.2
|
|
25
|
+
Requires-Dist: rcssmin==1.2.2
|
|
26
26
|
Requires-Dist: requests==2.32.5
|
|
27
|
-
Requires-Dist: rjsmin==1.2.
|
|
28
|
-
Requires-Dist: social-auth-app-django==5.
|
|
29
|
-
Requires-Dist: social-auth-core==4.
|
|
27
|
+
Requires-Dist: rjsmin==1.2.5
|
|
28
|
+
Requires-Dist: social-auth-app-django==5.7.0
|
|
29
|
+
Requires-Dist: social-auth-core==4.8.3
|
|
30
|
+
Provides-Extra: deploy
|
|
31
|
+
Requires-Dist: django==5.2.10; extra == 'deploy'
|
|
30
32
|
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: django==5.2.9; extra == 'dev'
|
|
31
34
|
Requires-Dist: djlint==1.36.4; extra == 'dev'
|
|
32
|
-
Requires-Dist: hatch==1.
|
|
33
|
-
Requires-Dist: isort==
|
|
34
|
-
Requires-Dist: mkdocs-material==9.
|
|
35
|
+
Requires-Dist: hatch==1.15.1; extra == 'dev'
|
|
36
|
+
Requires-Dist: isort==7.0.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: mkdocs-material==9.7.1; extra == 'dev'
|
|
35
38
|
Requires-Dist: mkdocs-static-i18n==1.3.0; extra == 'dev'
|
|
36
39
|
Requires-Dist: mkdocs==1.6.1; extra == 'dev'
|
|
37
|
-
Requires-Dist: pymdown-extensions==10.
|
|
38
|
-
Requires-Dist: ruff==0.14.
|
|
39
|
-
Requires-Dist: vermin==1.
|
|
40
|
-
Provides-Extra: docker
|
|
41
|
-
Requires-Dist: uvicorn==0.37.0; extra == 'docker'
|
|
40
|
+
Requires-Dist: pymdown-extensions==10.20.1; extra == 'dev'
|
|
41
|
+
Requires-Dist: ruff==0.14.14; extra == 'dev'
|
|
42
|
+
Requires-Dist: vermin==1.8.0; extra == 'dev'
|
|
42
43
|
Provides-Extra: s3
|
|
43
44
|
Requires-Dist: django-storages[s3]==1.14.6; extra == 's3'
|
|
44
45
|
Provides-Extra: sync
|
|
45
|
-
Requires-Dist: pydantic==2.12.
|
|
46
|
-
Requires-Dist: redis==
|
|
47
|
-
Requires-Dist:
|
|
46
|
+
Requires-Dist: pydantic==2.12.5; extra == 'sync'
|
|
47
|
+
Requires-Dist: redis==7.1.0; extra == 'sync'
|
|
48
|
+
Requires-Dist: uvicorn==0.40.0; extra == 'sync'
|
|
49
|
+
Requires-Dist: websockets==16.0; extra == 'sync'
|
|
48
50
|
Provides-Extra: test
|
|
49
51
|
Requires-Dist: daphne==4.2.1; extra == 'test'
|
|
50
52
|
Requires-Dist: factory-boy==3.3.3; extra == 'test'
|
|
51
|
-
Requires-Dist: moto[s3]==5.1.
|
|
53
|
+
Requires-Dist: moto[s3]==5.1.20; extra == 'test'
|
|
52
54
|
Requires-Dist: playwright>=1.39; extra == 'test'
|
|
53
55
|
Requires-Dist: pytest-django==4.11.1; extra == 'test'
|
|
54
|
-
Requires-Dist: pytest-playwright==0.7.
|
|
56
|
+
Requires-Dist: pytest-playwright==0.7.2; extra == 'test'
|
|
55
57
|
Requires-Dist: pytest-rerunfailures==16.1; extra == 'test'
|
|
56
58
|
Requires-Dist: pytest-xdist<4,>=3.5.0; extra == 'test'
|
|
57
|
-
Requires-Dist: pytest==
|
|
59
|
+
Requires-Dist: pytest==9.0.2; extra == 'test'
|
|
60
|
+
Provides-Extra: yunohost
|
|
61
|
+
Requires-Dist: django-yunohost-integration==0.10.9; extra == 'yunohost'
|
|
62
|
+
Requires-Dist: django==5.1.15; extra == 'yunohost'
|
|
63
|
+
Requires-Dist: uvicorn==0.40.0; extra == 'yunohost'
|
|
58
64
|
Description-Content-Type: text/markdown
|
|
59
65
|
|
|
60
66
|
[](https://matrix.to/#/#umap:matrix.org)
|