umap-project 2.9.0b0__py3-none-any.whl → 2.9.2__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.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (205) hide show
  1. umap/__init__.py +1 -1
  2. umap/admin.py +15 -2
  3. umap/locale/br/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/br/LC_MESSAGES/django.po +111 -67
  5. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/cs_CZ/LC_MESSAGES/django.po +112 -67
  7. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/el/LC_MESSAGES/django.po +132 -87
  9. umap/locale/en/LC_MESSAGES/django.po +11 -10
  10. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/es/LC_MESSAGES/django.po +117 -71
  12. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/fr/LC_MESSAGES/django.po +11 -10
  14. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/gl/LC_MESSAGES/django.po +219 -173
  16. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/it/LC_MESSAGES/django.po +145 -100
  18. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/nl/LC_MESSAGES/django.po +198 -152
  20. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/pt/LC_MESSAGES/django.po +118 -73
  22. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/zh_TW/LC_MESSAGES/django.po +112 -67
  24. umap/middleware.py +30 -1
  25. umap/models.py +20 -10
  26. umap/settings/base.py +2 -1
  27. umap/static/umap/base.css +4 -1
  28. umap/static/umap/css/bar.css +32 -0
  29. umap/static/umap/css/contextmenu.css +14 -2
  30. umap/static/umap/css/form.css +5 -10
  31. umap/static/umap/css/icon.css +39 -3
  32. umap/static/umap/css/panel.css +18 -1
  33. umap/static/umap/css/popup.css +0 -1
  34. umap/static/umap/img/16-white.svg +3 -3
  35. umap/static/umap/img/16.svg +1 -1
  36. umap/static/umap/img/24-white.svg +17 -16
  37. umap/static/umap/img/24.svg +29 -18
  38. umap/static/umap/img/providers/twitter-oauth2.png +0 -0
  39. umap/static/umap/img/source/16-white.svg +4 -4
  40. umap/static/umap/img/source/16.svg +1 -1
  41. umap/static/umap/img/source/24-white.svg +20 -18
  42. umap/static/umap/img/source/24.svg +30 -19
  43. umap/static/umap/js/modules/browser.js +2 -2
  44. umap/static/umap/js/modules/caption.js +4 -4
  45. umap/static/umap/js/modules/data/features.js +80 -32
  46. umap/static/umap/js/modules/data/layer.js +37 -50
  47. umap/static/umap/js/modules/form/builder.js +23 -22
  48. umap/static/umap/js/modules/form/fields.js +13 -5
  49. umap/static/umap/js/modules/formatter.js +6 -2
  50. umap/static/umap/js/modules/help.js +17 -23
  51. umap/static/umap/js/modules/importer.js +5 -2
  52. umap/static/umap/js/modules/permissions.js +6 -2
  53. umap/static/umap/js/modules/rendering/layers/classified.js +1 -1
  54. umap/static/umap/js/modules/rendering/map.js +1 -21
  55. umap/static/umap/js/modules/rendering/ui.js +20 -38
  56. umap/static/umap/js/modules/rules.js +1 -1
  57. umap/static/umap/js/modules/saving.js +5 -0
  58. umap/static/umap/js/modules/schema.js +4 -1
  59. umap/static/umap/js/modules/sync/engine.js +39 -14
  60. umap/static/umap/js/modules/sync/updaters.js +7 -6
  61. umap/static/umap/js/modules/sync/websocket.js +48 -40
  62. umap/static/umap/js/modules/ui/bar.js +84 -0
  63. umap/static/umap/js/modules/ui/base.js +11 -0
  64. umap/static/umap/js/modules/ui/contextmenu.js +9 -2
  65. umap/static/umap/js/modules/ui/panel.js +5 -1
  66. umap/static/umap/js/modules/umap.js +85 -44
  67. umap/static/umap/js/umap.controls.js +11 -341
  68. umap/static/umap/locale/am_ET.js +17 -5
  69. umap/static/umap/locale/am_ET.json +17 -5
  70. umap/static/umap/locale/ar.js +17 -5
  71. umap/static/umap/locale/ar.json +17 -5
  72. umap/static/umap/locale/ast.js +17 -5
  73. umap/static/umap/locale/ast.json +17 -5
  74. umap/static/umap/locale/bg.js +17 -5
  75. umap/static/umap/locale/bg.json +17 -5
  76. umap/static/umap/locale/br.js +20 -15
  77. umap/static/umap/locale/br.json +20 -15
  78. umap/static/umap/locale/ca.js +8 -4
  79. umap/static/umap/locale/ca.json +8 -4
  80. umap/static/umap/locale/cs_CZ.js +8 -4
  81. umap/static/umap/locale/cs_CZ.json +8 -4
  82. umap/static/umap/locale/da.js +17 -5
  83. umap/static/umap/locale/da.json +17 -5
  84. umap/static/umap/locale/de.js +8 -4
  85. umap/static/umap/locale/de.json +8 -4
  86. umap/static/umap/locale/el.js +54 -50
  87. umap/static/umap/locale/el.json +54 -50
  88. umap/static/umap/locale/en.js +9 -4
  89. umap/static/umap/locale/en.json +9 -4
  90. umap/static/umap/locale/en_US.json +17 -5
  91. umap/static/umap/locale/es.js +13 -9
  92. umap/static/umap/locale/es.json +13 -9
  93. umap/static/umap/locale/et.js +17 -5
  94. umap/static/umap/locale/et.json +17 -5
  95. umap/static/umap/locale/eu.js +8 -4
  96. umap/static/umap/locale/eu.json +8 -4
  97. umap/static/umap/locale/fa_IR.js +8 -4
  98. umap/static/umap/locale/fa_IR.json +8 -4
  99. umap/static/umap/locale/fi.js +17 -5
  100. umap/static/umap/locale/fi.json +17 -5
  101. umap/static/umap/locale/fr.js +9 -4
  102. umap/static/umap/locale/fr.json +9 -4
  103. umap/static/umap/locale/gl.js +13 -9
  104. umap/static/umap/locale/gl.json +13 -9
  105. umap/static/umap/locale/he.js +17 -5
  106. umap/static/umap/locale/he.json +17 -5
  107. umap/static/umap/locale/hr.js +17 -5
  108. umap/static/umap/locale/hr.json +17 -5
  109. umap/static/umap/locale/hu.js +8 -4
  110. umap/static/umap/locale/hu.json +8 -4
  111. umap/static/umap/locale/id.js +17 -5
  112. umap/static/umap/locale/id.json +17 -5
  113. umap/static/umap/locale/is.js +17 -5
  114. umap/static/umap/locale/is.json +17 -5
  115. umap/static/umap/locale/it.js +31 -27
  116. umap/static/umap/locale/it.json +31 -27
  117. umap/static/umap/locale/ja.js +17 -5
  118. umap/static/umap/locale/ja.json +17 -5
  119. umap/static/umap/locale/ko.js +17 -5
  120. umap/static/umap/locale/ko.json +17 -5
  121. umap/static/umap/locale/lt.js +17 -5
  122. umap/static/umap/locale/lt.json +17 -5
  123. umap/static/umap/locale/ms.js +8 -4
  124. umap/static/umap/locale/ms.json +8 -4
  125. umap/static/umap/locale/nl.js +132 -127
  126. umap/static/umap/locale/nl.json +132 -127
  127. umap/static/umap/locale/no.js +17 -5
  128. umap/static/umap/locale/no.json +17 -5
  129. umap/static/umap/locale/pl.js +8 -4
  130. umap/static/umap/locale/pl.json +8 -4
  131. umap/static/umap/locale/pl_PL.json +17 -5
  132. umap/static/umap/locale/pt.js +38 -33
  133. umap/static/umap/locale/pt.json +38 -33
  134. umap/static/umap/locale/pt_BR.js +17 -5
  135. umap/static/umap/locale/pt_BR.json +17 -5
  136. umap/static/umap/locale/pt_PT.js +8 -4
  137. umap/static/umap/locale/pt_PT.json +8 -4
  138. umap/static/umap/locale/ro.js +17 -5
  139. umap/static/umap/locale/ro.json +17 -5
  140. umap/static/umap/locale/ru.js +17 -5
  141. umap/static/umap/locale/ru.json +17 -5
  142. umap/static/umap/locale/sk_SK.js +17 -5
  143. umap/static/umap/locale/sk_SK.json +17 -5
  144. umap/static/umap/locale/sl.js +17 -5
  145. umap/static/umap/locale/sl.json +17 -5
  146. umap/static/umap/locale/sr.js +17 -5
  147. umap/static/umap/locale/sr.json +17 -5
  148. umap/static/umap/locale/sv.js +17 -5
  149. umap/static/umap/locale/sv.json +17 -5
  150. umap/static/umap/locale/th_TH.js +8 -4
  151. umap/static/umap/locale/th_TH.json +8 -4
  152. umap/static/umap/locale/tr.js +17 -5
  153. umap/static/umap/locale/tr.json +17 -5
  154. umap/static/umap/locale/uk_UA.js +17 -5
  155. umap/static/umap/locale/uk_UA.json +17 -5
  156. umap/static/umap/locale/vi.js +17 -5
  157. umap/static/umap/locale/vi.json +17 -5
  158. umap/static/umap/locale/vi_VN.json +17 -5
  159. umap/static/umap/locale/zh.js +17 -5
  160. umap/static/umap/locale/zh.json +17 -5
  161. umap/static/umap/locale/zh_CN.json +17 -5
  162. umap/static/umap/locale/zh_TW.Big5.json +17 -5
  163. umap/static/umap/locale/zh_TW.js +14 -10
  164. umap/static/umap/locale/zh_TW.json +14 -10
  165. umap/static/umap/map.css +17 -68
  166. umap/static/umap/nav.css +4 -0
  167. umap/static/umap/vars.css +1 -0
  168. umap/static/umap/vendors/dompurify/purify.es.js +138 -354
  169. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  170. umap/static/umap/vendors/editable/Leaflet.Editable.js +1 -0
  171. umap/sync/app.py +19 -13
  172. umap/sync/payloads.py +8 -1
  173. umap/templates/auth/user_form.html +2 -2
  174. umap/templates/umap/content_footer.html +1 -1
  175. umap/templates/umap/css.html +0 -2
  176. umap/templates/umap/js.html +0 -1
  177. umap/templates/umap/messages.html +5 -1
  178. umap/templates/umap/search_bar.html +1 -0
  179. umap/tests/integration/test_anonymous_owned_map.py +2 -2
  180. umap/tests/integration/test_basics.py +2 -5
  181. umap/tests/integration/test_categorized_layer.py +4 -8
  182. umap/tests/integration/test_choropleth.py +1 -1
  183. umap/tests/integration/test_conditional_rules.py +3 -3
  184. umap/tests/integration/test_draw_polygon.py +11 -19
  185. umap/tests/integration/test_draw_polyline.py +6 -14
  186. umap/tests/integration/test_edit_datalayer.py +10 -10
  187. umap/tests/integration/test_edit_map.py +27 -1
  188. umap/tests/integration/test_edit_marker.py +5 -5
  189. umap/tests/integration/test_edit_polygon.py +5 -5
  190. umap/tests/integration/test_features_id_generation.py +2 -6
  191. umap/tests/integration/test_import.py +93 -29
  192. umap/tests/integration/test_owned_map.py +1 -1
  193. umap/tests/integration/test_save.py +2 -2
  194. umap/tests/integration/test_tableeditor.py +7 -7
  195. umap/tests/integration/test_view_marker.py +10 -0
  196. umap/tests/integration/test_websocket_sync.py +128 -32
  197. umap/utils.py +4 -1
  198. umap/views.py +1 -10
  199. {umap_project-2.9.0b0.dist-info → umap_project-2.9.2.dist-info}/METADATA +13 -13
  200. {umap_project-2.9.0b0.dist-info → umap_project-2.9.2.dist-info}/RECORD +203 -205
  201. umap/static/umap/vendors/toolbar/leaflet.toolbar.css +0 -1
  202. umap/static/umap/vendors/toolbar/leaflet.toolbar.js +0 -1
  203. {umap_project-2.9.0b0.dist-info → umap_project-2.9.2.dist-info}/WHEEL +0 -0
  204. {umap_project-2.9.0b0.dist-info → umap_project-2.9.2.dist-info}/entry_points.txt +0 -0
  205. {umap_project-2.9.0b0.dist-info → umap_project-2.9.2.dist-info}/licenses/LICENSE +0 -0
@@ -59,7 +59,7 @@ def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilel
59
59
  peerA.wait_for_timeout(300)
60
60
 
61
61
  peerB.locator(".leaflet-marker-icon").first.click()
62
- peerB.get_by_role("link", name="Toggle edit mode (⇧+Click)").click()
62
+ peerB.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
63
63
  expect(peerB.locator('input[name="name"]')).to_have_value("Synced name")
64
64
 
65
65
  a_first_marker = peerA.locator("div:nth-child(4) > div:nth-child(2)").first
@@ -107,9 +107,7 @@ def test_websocket_connection_can_sync_polygons(context, asgi_live_server, tilel
107
107
  b_map_el = peerB.locator("#map")
108
108
 
109
109
  # Click on the Draw a polygon button on a new map.
110
- create_line = peerA.locator(".leaflet-control-toolbar ").get_by_title(
111
- "Draw a polygon"
112
- )
110
+ create_line = peerA.locator(".umap-edit-bar ").get_by_title("Draw a polygon")
113
111
  create_line.click()
114
112
 
115
113
  a_polygons = peerA.locator(".leaflet-overlay-pane path[fill='DarkBlue']")
@@ -124,11 +122,11 @@ def test_websocket_connection_can_sync_polygons(context, asgi_live_server, tilel
124
122
  map.click(position={"x": 100, "y": 100})
125
123
  map.click(position={"x": 100, "y": 100})
126
124
 
127
- # It is created on peerA, but not yet synced
125
+ # It is created on peerA, and should be on peerB
128
126
  expect(a_polygons).to_have_count(1)
129
- expect(b_polygons).to_have_count(0)
127
+ expect(b_polygons).to_have_count(1)
130
128
 
131
- # Escaping the edition syncs
129
+ # Escaping the edition should not duplicate
132
130
  peerA.keyboard.press("Escape")
133
131
  expect(a_polygons).to_have_count(1)
134
132
  expect(b_polygons).to_have_count(1)
@@ -141,7 +139,7 @@ def test_websocket_connection_can_sync_polygons(context, asgi_live_server, tilel
141
139
  assert b_polygon_bbox_t1 == a_polygon_bbox_t1
142
140
 
143
141
  b_polygon.click()
144
- peerB.get_by_role("link", name="Toggle edit mode (⇧+Click)").click()
142
+ peerB.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
145
143
 
146
144
  edited_vertex = peerB.locator("div:nth-child(6)").first
147
145
  edited_vertex.drag_to(b_map_el, target_position={"x": 233, "y": 126})
@@ -155,7 +153,7 @@ def test_websocket_connection_can_sync_polygons(context, asgi_live_server, tilel
155
153
 
156
154
  # Move the polygon on peer B and check it moved also on peer A
157
155
  b_polygon.click()
158
- peerB.get_by_role("link", name="Toggle edit mode (⇧+Click)").click()
156
+ peerB.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
159
157
 
160
158
  b_polygon.drag_to(b_map_el, target_position={"x": 400, "y": 400})
161
159
  peerB.keyboard.press("Escape")
@@ -189,14 +187,14 @@ def test_websocket_connection_can_sync_map_properties(
189
187
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
190
188
 
191
189
  # Name change is synced
192
- peerA.get_by_role("link", name="Edit map name and caption").click()
190
+ peerA.get_by_role("button", name="Edit map name and caption").click()
193
191
  peerA.locator('input[name="name"]').click()
194
192
  peerA.locator('input[name="name"]').fill("it syncs!")
195
193
 
196
194
  expect(peerB.locator(".map-name").last).to_have_text("it syncs!")
197
195
 
198
196
  # Zoom control is synced
199
- peerB.get_by_role("link", name="Map advanced properties").click()
197
+ peerB.get_by_role("button", name="Map advanced properties").click()
200
198
  peerB.locator("summary").filter(has_text="User interface options").click()
201
199
  switch = peerB.locator("div.formbox").filter(
202
200
  has_text=re.compile("Display the zoom control")
@@ -223,14 +221,14 @@ def test_websocket_connection_can_sync_datalayer_properties(
223
221
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
224
222
 
225
223
  # Layer addition, name and type are synced
226
- peerA.get_by_role("link", name="Manage layers").click()
224
+ peerA.get_by_role("button", name="Manage layers").click()
227
225
  peerA.get_by_role("button", name="Add a layer").click()
228
226
  peerA.locator('input[name="name"]').click()
229
227
  peerA.locator('input[name="name"]').fill("synced layer!")
230
228
  peerA.get_by_role("combobox").select_option("Choropleth")
231
229
  peerA.locator("body").press("Escape")
232
230
 
233
- peerB.get_by_role("link", name="Manage layers").click()
231
+ peerB.get_by_role("button", name="Manage layers").click()
234
232
  peerB.locator(".panel.right").get_by_role("button", name="Edit").first.click()
235
233
  expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!")
236
234
  expect(peerB.get_by_role("combobox")).to_have_value("Choropleth")
@@ -254,9 +252,7 @@ def test_websocket_connection_can_sync_cloned_polygons(
254
252
  b_map_el = peerB.locator("#map")
255
253
 
256
254
  # Click on the Draw a polygon button on a new map.
257
- create_line = peerA.locator(".leaflet-control-toolbar ").get_by_title(
258
- "Draw a polygon"
259
- )
255
+ create_line = peerA.locator(".umap-edit-bar ").get_by_title("Draw a polygon")
260
256
  create_line.click()
261
257
 
262
258
  a_polygons = peerA.locator(".leaflet-overlay-pane path[fill='DarkBlue']")
@@ -324,9 +320,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
324
320
  peerA.wait_for_timeout(300)
325
321
 
326
322
  # Add a polygon from peer A
327
- create_polygon = peerA.locator(".leaflet-control-toolbar ").get_by_title(
328
- "Draw a polygon"
329
- )
323
+ create_polygon = peerA.locator(".umap-edit-bar ").get_by_title("Draw a polygon")
330
324
  create_polygon.click()
331
325
 
332
326
  a_map_el.click(position={"x": 200, "y": 200})
@@ -349,7 +343,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
349
343
 
350
344
  # Verify marker properties
351
345
  peerB.locator(".leaflet-marker-icon").first.click()
352
- peerB.get_by_role("link", name="Toggle edit mode (⇧+Click)").click()
346
+ peerB.get_by_role("button", name="Toggle edit mode (⇧+Click)").click()
353
347
  expect(peerB.locator('input[name="name"]')).to_have_value("First marker")
354
348
 
355
349
  # Verify polygon exists (we've already checked the count)
@@ -377,7 +371,7 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
377
371
  peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
378
372
 
379
373
  # Create a new layer from peerA
380
- peerA.get_by_role("link", name="Manage layers").click()
374
+ peerA.get_by_role("button", name="Manage layers").click()
381
375
  peerA.get_by_role("button", name="Add a layer").click()
382
376
 
383
377
  # Check layer has been sync to peerB
@@ -385,7 +379,7 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
385
379
  expect(peerB.get_by_text("Layer 1")).to_be_visible()
386
380
 
387
381
  # Draw a marker in layer 1 from peerA
388
- peerA.get_by_role("link", name="Draw a marker (Ctrl+M)").click()
382
+ peerA.get_by_role("button", name="Draw a marker (Ctrl+M)").click()
389
383
  peerA.locator("#map").click()
390
384
 
391
385
  # Check marker is visible from peerB
@@ -398,20 +392,22 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
398
392
  assert DataLayer.objects.count() == 1
399
393
 
400
394
  # Create another layer from peerA and draw a marker on it (without saving to server)
401
- peerA.get_by_role("link", name="Manage layers").click()
395
+ peerA.get_by_role("button", name="Manage layers").click()
402
396
  peerA.get_by_role("button", name="Add a layer").click()
403
- peerA.get_by_role("link", name="Draw a marker (Ctrl+M)").click()
397
+ peerA.get_by_role("button", name="Draw a marker (Ctrl+M)").click()
404
398
  peerA.locator("#map").click()
405
399
 
406
400
  # Make sure this new marker is in Layer 2 for peerB
407
- expect(peerB.get_by_text("Layer 2")).to_be_visible()
401
+ # Show features for this layer in the brower.
402
+ peerB.get_by_role("heading", name="Layer 2").locator(".datalayer-name").click()
403
+ expect(peerB.locator("li").filter(has_text="Layer 2")).to_be_visible()
408
404
  peerB.locator(".panel.left").get_by_role("button", name="Show/hide layer").nth(
409
405
  1
410
406
  ).click()
411
407
  expect(peerB.locator(".leaflet-marker-icon")).to_be_visible()
412
408
 
413
409
  # Now draw a marker from peerB
414
- peerB.get_by_role("link", name="Draw a marker (Ctrl+M)").click()
410
+ peerB.get_by_role("button", name="Draw a marker (Ctrl+M)").click()
415
411
  peerB.locator("#map").click()
416
412
  peerB.locator('input[name="name"]').fill("marker from peerB")
417
413
 
@@ -427,9 +423,11 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
427
423
  1
428
424
  ).click()
429
425
 
430
- # Now peerA saves the layer 2 to the server
431
- with peerA.expect_response(re.compile(".*/datalayer/update/.*")):
432
- peerA.get_by_role("button", name="Save").click()
426
+ # Peer A should not be in dirty state
427
+ expect(peerA.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
428
+
429
+ # Peer A should only have two markers
430
+ expect(peerA.locator(".leaflet-marker-icon")).to_have_count(2)
433
431
 
434
432
  assert DataLayer.objects.count() == 2
435
433
 
@@ -503,15 +501,15 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
503
501
  # Create a syncable map with peerA
504
502
  peerA = login(user, prefix="Page A")
505
503
  peerA.goto(f"{asgi_live_server.url}/en/map/new/")
506
- peerA.get_by_role("link", name="Map advanced properties").click()
504
+ peerA.get_by_role("button", name="Map advanced properties").click()
507
505
  expect(peerA.get_by_text("Real-time collaboration", exact=True)).to_be_hidden()
508
506
  with peerA.expect_response(re.compile("./map/create/.*")):
509
507
  peerA.get_by_role("button", name="Save Draft").click()
510
- peerA.get_by_role("link", name="Map advanced properties").click()
508
+ peerA.get_by_role("button", name="Map advanced properties").click()
511
509
  expect(peerA.get_by_text("Real-time collaboration", exact=True)).to_be_visible()
512
510
  peerA.get_by_text("Real-time collaboration", exact=True).click()
513
511
  peerA.get_by_text("Enable real-time").click()
514
- peerA.get_by_role("link", name="Update permissions and editors").click()
512
+ peerA.get_by_role("button", name="Update permissions and editors").click()
515
513
  peerA.locator('select[name="share_status"]').select_option(str(Map.PUBLIC))
516
514
  with peerA.expect_response(re.compile("./update/settings/.*")):
517
515
  peerA.get_by_role("button", name="Save").click()
@@ -537,6 +535,11 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
537
535
  expect(markersA).to_have_count(1)
538
536
  expect(markersB).to_have_count(1)
539
537
 
538
+ # Make sure only one layer has been created on peer B
539
+ peerB.get_by_role("button", name="Open browser").click()
540
+ expect(peerB.locator("h5").get_by_text("Layer 1")).to_be_visible()
541
+ peerB.get_by_role("button", name="Close").click()
542
+
540
543
  # Save and quit edit mode again
541
544
  with peerA.expect_response(re.compile("./datalayer/create/.*")):
542
545
  peerA.get_by_role("button", name="Save").click()
@@ -563,3 +566,96 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
563
566
  peerA.get_by_role("button", name="Edit").click()
564
567
  expect(markersA).to_have_count(2)
565
568
  expect(markersB).to_have_count(2)
569
+
570
+
571
+ @pytest.mark.xdist_group(name="websockets")
572
+ def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelayer):
573
+ map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
574
+ map.settings["properties"]["syncEnabled"] = True
575
+ map.save()
576
+
577
+ # Create one tab
578
+ peerA = new_page("Page A")
579
+ peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
580
+ # Create a new datalayer
581
+ peerA.get_by_title("Manage layers").click()
582
+ peerA.get_by_title("Add a layer").click()
583
+ peerA.locator("#map").click(position={"x": 220, "y": 220})
584
+ # Save layer to the server, so now the datalayer exist on the server AND
585
+ # is still in the live operations of peer A
586
+ with peerA.expect_response(re.compile(".*/datalayer/create/.*")):
587
+ peerA.get_by_role("button", name="Save").click()
588
+
589
+ # Now load the map from another tab
590
+ peerB = new_page("Page B")
591
+ peerB.goto(peerA.url)
592
+ peerB.get_by_role("button", name="Open browser").click()
593
+ expect(peerB.get_by_text("Layer 1")).to_be_visible()
594
+ peerB.get_by_role("button", name="Edit").click()
595
+ peerA.wait_for_timeout(300) # Let the synchro roll on.
596
+ expect(peerB.get_by_text("Layer 1")).to_be_visible()
597
+
598
+
599
+ @pytest.mark.xdist_group(name="websockets")
600
+ def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
601
+ map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
602
+ map.settings["properties"]["syncEnabled"] = True
603
+ map.save()
604
+
605
+ # Create two tabs
606
+ peerA = new_page("Page A")
607
+ peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
608
+ peerB = new_page("Page B")
609
+ peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
610
+
611
+ # Create a new marker from peerA
612
+ peerA.get_by_title("Draw a marker").click()
613
+ peerA.locator("#map").click(position={"x": 220, "y": 220})
614
+
615
+ # Peer A should be in dirty state
616
+ expect(peerA.locator("body")).to_have_class(re.compile(".*umap-is-dirty.*"))
617
+
618
+ # Peer B should not be in dirty state
619
+ expect(peerB.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
620
+
621
+ # Create a new marker from peerB
622
+ peerB.get_by_title("Draw a marker").click()
623
+ peerB.locator("#map").click(position={"x": 200, "y": 250})
624
+
625
+ # Peer B should be in dirty state
626
+ expect(peerB.locator("body")).to_have_class(re.compile(".*umap-is-dirty.*"))
627
+
628
+ # Peer A should still be in dirty state
629
+ expect(peerA.locator("body")).to_have_class(re.compile(".*umap-is-dirty.*"))
630
+
631
+ # Save layer to the server from peerA
632
+ with peerA.expect_response(re.compile(".*/datalayer/create/.*")):
633
+ peerA.get_by_role("button", name="Save").click()
634
+
635
+ # Peer B should not be in dirty state
636
+ expect(peerB.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
637
+
638
+ # Peer A should not be in dirty state
639
+ expect(peerA.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
640
+
641
+
642
+ @pytest.mark.xdist_group(name="websockets")
643
+ def test_should_sync_line_on_escape(new_page, asgi_live_server, tilelayer):
644
+ map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
645
+ map.settings["properties"]["syncEnabled"] = True
646
+ map.save()
647
+
648
+ # Create two tabs
649
+ peerA = new_page("Page A")
650
+ peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
651
+ peerB = new_page("Page B")
652
+ peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
653
+
654
+ # Create a new marker from peerA
655
+ peerA.get_by_title("Draw a polyline").click()
656
+ peerA.locator("#map").click(position={"x": 220, "y": 220})
657
+ peerA.locator("#map").click(position={"x": 200, "y": 200})
658
+ peerA.locator("body").press("Escape")
659
+
660
+ expect(peerA.locator("path")).to_have_count(1)
661
+ expect(peerB.locator("path")).to_have_count(1)
umap/utils.py CHANGED
@@ -27,7 +27,10 @@ def _urls_for_js():
27
27
  Return templated URLs prepared for javascript.
28
28
  """
29
29
  urls = {}
30
- for module in ["umap.urls", "umap.sync.app"]:
30
+ modules = ["umap.urls"]
31
+ if settings.WEBSOCKET_ENABLED:
32
+ modules.append("umap.sync.app")
33
+ for module in modules:
31
34
  names = _get_url_names(module)
32
35
  urls.update(
33
36
  dict(zip(names, [get_uri_template(url, module=module) for url in names]))
umap/views.py CHANGED
@@ -1421,16 +1421,7 @@ class LoginPopupEnd(TemplateView):
1421
1421
  template_name = "umap/login_popup_end.html"
1422
1422
 
1423
1423
  def get(self, *args, **kwargs):
1424
- backend = self.request.session[BACKEND_SESSION_KEY]
1424
+ backend = self.request.session.get(BACKEND_SESSION_KEY)
1425
1425
  if backend in settings.DEPRECATED_AUTHENTICATION_BACKENDS:
1426
- name = backend.split(".")[-1]
1427
- messages.error(
1428
- self.request,
1429
- _(
1430
- "Using “%(name)s” to authenticate is deprecated. "
1431
- "Please configure another provider in your profile page."
1432
- )
1433
- % {"name": name},
1434
- )
1435
1426
  return HttpResponseRedirect(reverse("user_profile"))
1436
1427
  return super().get(*args, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: umap-project
3
- Version: 2.9.0b0
3
+ Version: 2.9.2
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>
@@ -19,23 +19,23 @@ Requires-Python: >=3.10
19
19
  Requires-Dist: django-agnocomplete==2.2.0
20
20
  Requires-Dist: django-environ==0.12.0
21
21
  Requires-Dist: django-probes==1.7.0
22
- Requires-Dist: django==5.1.5
22
+ Requires-Dist: django==5.1.6
23
23
  Requires-Dist: pillow==11.1.0
24
24
  Requires-Dist: psycopg==3.2.4
25
- Requires-Dist: rcssmin==1.2.0
25
+ Requires-Dist: rcssmin==1.2.1
26
26
  Requires-Dist: requests==2.32.3
27
- Requires-Dist: rjsmin==1.2.3
27
+ Requires-Dist: rjsmin==1.2.4
28
28
  Requires-Dist: social-auth-app-django==5.4.2
29
29
  Requires-Dist: social-auth-core==4.5.4
30
30
  Provides-Extra: dev
31
31
  Requires-Dist: djlint==1.36.4; extra == 'dev'
32
32
  Requires-Dist: hatch==1.14.0; extra == 'dev'
33
- Requires-Dist: isort==5.13.2; extra == 'dev'
34
- Requires-Dist: mkdocs-material==9.5.50; extra == 'dev'
35
- Requires-Dist: mkdocs-static-i18n==1.2.3; extra == 'dev'
33
+ Requires-Dist: isort==6.0.1; extra == 'dev'
34
+ Requires-Dist: mkdocs-material==9.6.4; extra == 'dev'
35
+ Requires-Dist: mkdocs-static-i18n==1.3.0; extra == 'dev'
36
36
  Requires-Dist: mkdocs==1.6.1; extra == 'dev'
37
- Requires-Dist: pymdown-extensions==10.14.1; extra == 'dev'
38
- Requires-Dist: ruff==0.9.3; extra == 'dev'
37
+ Requires-Dist: pymdown-extensions==10.14.3; extra == 'dev'
38
+ Requires-Dist: ruff==0.9.9; extra == 'dev'
39
39
  Requires-Dist: vermin==1.6.0; extra == 'dev'
40
40
  Provides-Extra: docker
41
41
  Requires-Dist: uwsgi==2.0.28; extra == 'docker'
@@ -46,11 +46,11 @@ Requires-Dist: pydantic==2.10.6; extra == 'sync'
46
46
  Requires-Dist: redis==5.2.1; extra == 'sync'
47
47
  Provides-Extra: test
48
48
  Requires-Dist: daphne==4.1.2; extra == 'test'
49
- Requires-Dist: factory-boy==3.3.1; extra == 'test'
50
- Requires-Dist: moto[s3]==5.0.27; extra == 'test'
49
+ Requires-Dist: factory-boy==3.3.3; extra == 'test'
50
+ Requires-Dist: moto[s3]==5.0.28; extra == 'test'
51
51
  Requires-Dist: playwright>=1.39; extra == 'test'
52
- Requires-Dist: pytest-django==4.9.0; extra == 'test'
53
- Requires-Dist: pytest-playwright==0.6.2; extra == 'test'
52
+ Requires-Dist: pytest-django==4.10.0; extra == 'test'
53
+ Requires-Dist: pytest-playwright==0.7.0; extra == 'test'
54
54
  Requires-Dist: pytest-rerunfailures==15.0; extra == 'test'
55
55
  Requires-Dist: pytest-xdist<4,>=3.5.0; extra == 'test'
56
56
  Requires-Dist: pytest==8.3.4; extra == 'test'